Skip to content

Commit

Permalink
Fix bug with referencing non-object types
Browse files Browse the repository at this point in the history
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
  • Loading branch information
cebe committed Dec 4, 2019
1 parent 1316f57 commit fc0cee6
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 9 deletions.
25 changes: 21 additions & 4 deletions src/SpecBaseObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ public function __construct(array $data)
$this->_errors[] = "property '$property' must be array, but " . gettype($data[$property]) . " given.";
continue;
}
if (isset($data[$property]['$ref'])) {
$this->_properties[$property] = new Reference($data[$property], null);
unset($data[$property]);
continue;
}
switch (\count($type)) {
case 1:
// array
Expand All @@ -83,8 +88,14 @@ public function __construct(array $data)
$this->_errors[] = "property '$property' must be array of strings, but array has " . gettype($item) . " element.";
}
$this->_properties[$property][] = $item;
} elseif ($type[0] === Type::ANY || Type::isScalar($type[0])) {
} elseif (Type::isScalar($type[0])) {
$this->_properties[$property][] = $item;
} elseif ($type[0] === Type::ANY) {
if (is_array($item) && isset($item['$ref'])) {
$this->_properties[$property][] = new Reference($item, null);
} else {
$this->_properties[$property][] = $item;
}
} else {
$this->_properties[$property][] = $this->instantiate($type[0], $item);
}
Expand All @@ -110,8 +121,14 @@ public function __construct(array $data)
}
break;
}
} elseif ($type === Type::ANY || Type::isScalar($type)) {
} elseif (Type::isScalar($type)) {
$this->_properties[$property] = $data[$property];
} elseif ($type === Type::ANY) {
if (is_array($data[$property]) && isset($data[$property]['$ref'])) {
$this->_properties[$property] = new Reference($data[$property], null);
} else {
$this->_properties[$property] = $data[$property];
}
} else {
$this->_properties[$property] = $this->instantiate($type, $data[$property]);
}
Expand Down Expand Up @@ -353,7 +370,7 @@ public function resolveReferences(ReferenceContext $context = null)
if ($value instanceof Reference) {
$referencedObject = $value->resolve($context);
$this->_properties[$property] = $referencedObject;
if (!$referencedObject instanceof Reference && $referencedObject !== null) {
if (!$referencedObject instanceof Reference && $referencedObject instanceof SpecObjectInterface) {
$referencedObject->resolveReferences();
}
} elseif ($value instanceof SpecObjectInterface) {
Expand All @@ -363,7 +380,7 @@ public function resolveReferences(ReferenceContext $context = null)
if ($item instanceof Reference) {
$referencedObject = $item->resolve($context);
$this->_properties[$property][$k] = $referencedObject;
if (!$referencedObject instanceof Reference && $referencedObject !== null) {
if (!$referencedObject instanceof Reference && $referencedObject instanceof SpecObjectInterface) {
$referencedObject->resolveReferences();
}
} elseif ($item instanceof SpecObjectInterface) {
Expand Down
14 changes: 9 additions & 5 deletions src/spec/Reference.php
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,9 @@ public function resolve(ReferenceContext $context = null)
// TODO type error if resolved object does not match $this->_to ?
/** @var $referencedObject SpecObjectInterface */
$referencedObject = $jsonReference->getJsonPointer()->evaluate($baseSpec);
$referencedObject->setReferenceContext($context);
if ($referencedObject instanceof SpecObjectInterface) {
$referencedObject->setReferenceContext($context);
}
return $referencedObject;
} else {
// if current document was loaded via reference, it may be null,
Expand All @@ -196,10 +198,10 @@ public function resolve(ReferenceContext $context = null)
// transitive reference
if (isset($referencedData['$ref'])) {
return (new Reference($referencedData, $this->_to))->resolve(new ReferenceContext(null, $file));
} else {
/** @var $referencedObject SpecObjectInterface */
$referencedObject = new $this->_to($referencedData);
}
/** @var $referencedObject SpecObjectInterface|array */
$referencedObject = $this->_to !== null ? new $this->_to($referencedData) : $referencedData;

if ($jsonReference->getJsonPointer()->getPointer() === '') {
$newContext = new ReferenceContext($referencedObject, $file);
if ($referencedObject instanceof DocumentContextInterface) {
Expand All @@ -212,7 +214,9 @@ public function resolve(ReferenceContext $context = null)
$newContext = new ReferenceContext(null, $file);
}
$newContext->throwException = $context->throwException;
$referencedObject->setReferenceContext($newContext);
if ($referencedObject instanceof SpecObjectInterface) {
$referencedObject->setReferenceContext($newContext);
}

return $referencedObject;
} catch (NonexistentJsonPointerReferenceException $e) {
Expand Down
47 changes: 47 additions & 0 deletions tests/spec/ReferenceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -257,4 +257,51 @@ public function testResolvePaths()
$this->assertInstanceOf(RequestBody::class, $newPlaylistBody);
$this->assertSame($playlistsBody, $newPlaylistBody);
}

public function testReferenceToArray()
{
$schema = <<<'YAML'
openapi: 3.0.0
components:
schemas:
Pet:
type: object
properties:
id:
type: integer
typeA:
type: string
enum:
- "One"
- "Two"
typeB:
type: string
enum:
$ref: '#/components/schemas/Pet/properties/typeA/enum'
typeC:
type: string
enum:
- "Three"
- $ref: '#/components/schemas/Pet/properties/typeA/enum/1'
typeD:
type: string
enum:
$ref: 'definitions.yaml#/Dog/properties/typeD/enum'
typeE:
type: string
enum:
- $ref: 'definitions.yaml#/Dog/properties/typeD/enum/1'
- "Six"

YAML;
$openapi = Reader::readFromYaml($schema);
$openapi->resolveReferences(new \cebe\openapi\ReferenceContext($openapi, 'file://' . __DIR__ . '/data/reference/definitions.yaml'));

$this->assertTrue(isset($openapi->components->schemas['Pet']));
$this->assertEquals(['One', 'Two'], $openapi->components->schemas['Pet']->properties['typeA']->enum);
$this->assertEquals(['One', 'Two'], $openapi->components->schemas['Pet']->properties['typeB']->enum);
$this->assertEquals(['Three', 'Two'], $openapi->components->schemas['Pet']->properties['typeC']->enum);
$this->assertEquals(['Four', 'Five'], $openapi->components->schemas['Pet']->properties['typeD']->enum);
$this->assertEquals(['Five', 'Six'], $openapi->components->schemas['Pet']->properties['typeE']->enum);
}
}
5 changes: 5 additions & 0 deletions tests/spec/data/reference/definitions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@ Dog:
type: string
food:
$ref: 'Food.yaml'
typeD:
type: string
enum:
- "Four"
- "Five"

0 comments on commit fc0cee6

Please sign in to comment.