Skip to content

Commit cc1a5d2

Browse files
committed
Fix bug with referencing non-object types
a reference to an array was crashing with an invalid function call. Proper instanceof check has been added in Reference. also creating references in non-object types and Type::ANY is working correctly now. fixes #47
1 parent 06ff709 commit cc1a5d2

File tree

4 files changed

+82
-9
lines changed

4 files changed

+82
-9
lines changed

src/SpecBaseObject.php

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ public function __construct(array $data)
7373
$this->_errors[] = "property '$property' must be array, but " . gettype($data[$property]) . " given.";
7474
continue;
7575
}
76+
if (isset($data[$property]['$ref'])) {
77+
$this->_properties[$property] = new Reference($data[$property], null);
78+
unset($data[$property]);
79+
continue;
80+
}
7681
switch (\count($type)) {
7782
case 1:
7883
// array
@@ -83,8 +88,14 @@ public function __construct(array $data)
8388
$this->_errors[] = "property '$property' must be array of strings, but array has " . gettype($item) . " element.";
8489
}
8590
$this->_properties[$property][] = $item;
86-
} elseif ($type[0] === Type::ANY || Type::isScalar($type[0])) {
91+
} elseif (Type::isScalar($type[0])) {
8792
$this->_properties[$property][] = $item;
93+
} elseif ($type[0] === Type::ANY) {
94+
if (is_array($item) && isset($item['$ref'])) {
95+
$this->_properties[$property][] = new Reference($item, null);
96+
} else {
97+
$this->_properties[$property][] = $item;
98+
}
8899
} else {
89100
$this->_properties[$property][] = $this->instantiate($type[0], $item);
90101
}
@@ -110,8 +121,14 @@ public function __construct(array $data)
110121
}
111122
break;
112123
}
113-
} elseif ($type === Type::ANY || Type::isScalar($type)) {
124+
} elseif (Type::isScalar($type)) {
114125
$this->_properties[$property] = $data[$property];
126+
} elseif ($type === Type::ANY) {
127+
if (is_array($data[$property]) && isset($data[$property]['$ref'])) {
128+
$this->_properties[$property] = new Reference($data[$property], null);
129+
} else {
130+
$this->_properties[$property] = $data[$property];
131+
}
115132
} else {
116133
$this->_properties[$property] = $this->instantiate($type, $data[$property]);
117134
}
@@ -353,7 +370,7 @@ public function resolveReferences(ReferenceContext $context = null)
353370
if ($value instanceof Reference) {
354371
$referencedObject = $value->resolve($context);
355372
$this->_properties[$property] = $referencedObject;
356-
if (!$referencedObject instanceof Reference && $referencedObject !== null) {
373+
if (!$referencedObject instanceof Reference && $referencedObject instanceof SpecObjectInterface) {
357374
$referencedObject->resolveReferences();
358375
}
359376
} elseif ($value instanceof SpecObjectInterface) {
@@ -363,7 +380,7 @@ public function resolveReferences(ReferenceContext $context = null)
363380
if ($item instanceof Reference) {
364381
$referencedObject = $item->resolve($context);
365382
$this->_properties[$property][$k] = $referencedObject;
366-
if (!$referencedObject instanceof Reference && $referencedObject !== null) {
383+
if (!$referencedObject instanceof Reference && $referencedObject instanceof SpecObjectInterface) {
367384
$referencedObject->resolveReferences();
368385
}
369386
} 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/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)