Skip to content

Commit 7c1e7ef

Browse files
authored
Merge pull request #48 from cebe/fix-ref-non-objects
Fix bug with referencing non-object types
2 parents b4a47f4 + 3de06bf commit 7c1e7ef

File tree

5 files changed

+95
-20
lines changed

5 files changed

+95
-20
lines changed

src/SpecBaseObject.php

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -75,18 +75,28 @@ public function __construct(array $data)
7575
}
7676
switch (\count($type)) {
7777
case 1:
78-
// array
79-
$this->_properties[$property] = [];
80-
foreach ($data[$property] as $item) {
81-
if ($type[0] === Type::STRING) {
82-
if (!is_string($item)) {
83-
$this->_errors[] = "property '$property' must be array of strings, but array has " . gettype($item) . " element.";
78+
if (isset($data[$property]['$ref'])) {
79+
$this->_properties[$property] = new Reference($data[$property], null);
80+
} else {
81+
// array
82+
$this->_properties[$property] = [];
83+
foreach ($data[$property] as $item) {
84+
if ($type[0] === Type::STRING) {
85+
if (!is_string($item)) {
86+
$this->_errors[] = "property '$property' must be array of strings, but array has " . gettype($item) . " element.";
87+
}
88+
$this->_properties[$property][] = $item;
89+
} elseif (Type::isScalar($type[0])) {
90+
$this->_properties[$property][] = $item;
91+
} elseif ($type[0] === Type::ANY) {
92+
if (is_array($item) && isset($item['$ref'])) {
93+
$this->_properties[$property][] = new Reference($item, null);
94+
} else {
95+
$this->_properties[$property][] = $item;
96+
}
97+
} else {
98+
$this->_properties[$property][] = $this->instantiate($type[0], $item);
8499
}
85-
$this->_properties[$property][] = $item;
86-
} elseif ($type[0] === Type::ANY || Type::isScalar($type[0])) {
87-
$this->_properties[$property][] = $item;
88-
} else {
89-
$this->_properties[$property][] = $this->instantiate($type[0], $item);
90100
}
91101
}
92102
break;
@@ -110,8 +120,14 @@ public function __construct(array $data)
110120
}
111121
break;
112122
}
113-
} elseif ($type === Type::ANY || Type::isScalar($type)) {
123+
} elseif (Type::isScalar($type)) {
114124
$this->_properties[$property] = $data[$property];
125+
} elseif ($type === Type::ANY) {
126+
if (is_array($data[$property]) && isset($data[$property]['$ref'])) {
127+
$this->_properties[$property] = new Reference($data[$property], null);
128+
} else {
129+
$this->_properties[$property] = $data[$property];
130+
}
115131
} else {
116132
$this->_properties[$property] = $this->instantiate($type, $data[$property]);
117133
}
@@ -353,7 +369,7 @@ public function resolveReferences(ReferenceContext $context = null)
353369
if ($value instanceof Reference) {
354370
$referencedObject = $value->resolve($context);
355371
$this->_properties[$property] = $referencedObject;
356-
if (!$referencedObject instanceof Reference && $referencedObject !== null) {
372+
if (!$referencedObject instanceof Reference && $referencedObject instanceof SpecObjectInterface) {
357373
$referencedObject->resolveReferences();
358374
}
359375
} elseif ($value instanceof SpecObjectInterface) {
@@ -363,7 +379,7 @@ public function resolveReferences(ReferenceContext $context = null)
363379
if ($item instanceof Reference) {
364380
$referencedObject = $item->resolve($context);
365381
$this->_properties[$property][$k] = $referencedObject;
366-
if (!$referencedObject instanceof Reference && $referencedObject !== null) {
382+
if (!$referencedObject instanceof Reference && $referencedObject instanceof SpecObjectInterface) {
367383
$referencedObject->resolveReferences();
368384
}
369385
} elseif ($item instanceof SpecObjectInterface) {

src/spec/Reference.php

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,9 @@ public function resolve(ReferenceContext $context = null)
174174
// TODO type error if resolved object does not match $this->_to ?
175175
/** @var $referencedObject SpecObjectInterface */
176176
$referencedObject = $jsonReference->getJsonPointer()->evaluate($baseSpec);
177-
$referencedObject->setReferenceContext($context);
177+
if ($referencedObject instanceof SpecObjectInterface) {
178+
$referencedObject->setReferenceContext($context);
179+
}
178180
return $referencedObject;
179181
} else {
180182
// if current document was loaded via reference, it may be null,
@@ -196,10 +198,10 @@ public function resolve(ReferenceContext $context = null)
196198
// transitive reference
197199
if (isset($referencedData['$ref'])) {
198200
return (new Reference($referencedData, $this->_to))->resolve(new ReferenceContext(null, $file));
199-
} else {
200-
/** @var $referencedObject SpecObjectInterface */
201-
$referencedObject = new $this->_to($referencedData);
202201
}
202+
/** @var $referencedObject SpecObjectInterface|array */
203+
$referencedObject = $this->_to !== null ? new $this->_to($referencedData) : $referencedData;
204+
203205
if ($jsonReference->getJsonPointer()->getPointer() === '') {
204206
$newContext = new ReferenceContext($referencedObject, $file);
205207
if ($referencedObject instanceof DocumentContextInterface) {
@@ -212,7 +214,9 @@ public function resolve(ReferenceContext $context = null)
212214
$newContext = new ReferenceContext(null, $file);
213215
}
214216
$newContext->throwException = $context->throwException;
215-
$referencedObject->setReferenceContext($newContext);
217+
if ($referencedObject instanceof SpecObjectInterface) {
218+
$referencedObject->setReferenceContext($newContext);
219+
}
216220

217221
return $referencedObject;
218222
} catch (NonexistentJsonPointerReferenceException $e) {

tests/spec/OpenApiTest.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,10 @@ public function specProvider()
171171
$nexmoExamples
172172
);
173173
foreach($all as $path) {
174-
yield [substr($path, strlen(__DIR__ . '/../../vendor/')), basename($path)];
174+
yield [
175+
substr($path, strlen(__DIR__ . '/../../vendor/')),
176+
basename(dirname($path, 2)) . DIRECTORY_SEPARATOR . basename(dirname($path, 1)) . DIRECTORY_SEPARATOR . basename($path)
177+
];
175178
}
176179
}
177180

tests/spec/ReferenceTest.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,4 +257,51 @@ public function testResolvePaths()
257257
$this->assertInstanceOf(RequestBody::class, $newPlaylistBody);
258258
$this->assertSame($playlistsBody, $newPlaylistBody);
259259
}
260+
261+
public function testReferenceToArray()
262+
{
263+
$schema = <<<'YAML'
264+
openapi: 3.0.0
265+
components:
266+
schemas:
267+
Pet:
268+
type: object
269+
properties:
270+
id:
271+
type: integer
272+
typeA:
273+
type: string
274+
enum:
275+
- "One"
276+
- "Two"
277+
typeB:
278+
type: string
279+
enum:
280+
$ref: '#/components/schemas/Pet/properties/typeA/enum'
281+
typeC:
282+
type: string
283+
enum:
284+
- "Three"
285+
- $ref: '#/components/schemas/Pet/properties/typeA/enum/1'
286+
typeD:
287+
type: string
288+
enum:
289+
$ref: 'definitions.yaml#/Dog/properties/typeD/enum'
290+
typeE:
291+
type: string
292+
enum:
293+
- $ref: 'definitions.yaml#/Dog/properties/typeD/enum/1'
294+
- "Six"
295+
296+
YAML;
297+
$openapi = Reader::readFromYaml($schema);
298+
$openapi->resolveReferences(new \cebe\openapi\ReferenceContext($openapi, 'file://' . __DIR__ . '/data/reference/definitions.yaml'));
299+
300+
$this->assertTrue(isset($openapi->components->schemas['Pet']));
301+
$this->assertEquals(['One', 'Two'], $openapi->components->schemas['Pet']->properties['typeA']->enum);
302+
$this->assertEquals(['One', 'Two'], $openapi->components->schemas['Pet']->properties['typeB']->enum);
303+
$this->assertEquals(['Three', 'Two'], $openapi->components->schemas['Pet']->properties['typeC']->enum);
304+
$this->assertEquals(['Four', 'Five'], $openapi->components->schemas['Pet']->properties['typeD']->enum);
305+
$this->assertEquals(['Five', 'Six'], $openapi->components->schemas['Pet']->properties['typeE']->enum);
306+
}
260307
}

tests/spec/data/reference/definitions.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,8 @@ Dog:
1111
type: string
1212
food:
1313
$ref: 'Food.yaml'
14+
typeD:
15+
type: string
16+
enum:
17+
- "Four"
18+
- "Five"

0 commit comments

Comments
 (0)