From 497683f8470bf95e8fe8b554799fdd70d539a120 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Mon, 16 Dec 2019 14:38:27 +0100 Subject: [PATCH] Fix recursion issue with reading references which are included multiple times fixes #44 --- Makefile | 7 +++-- src/SpecBaseObject.php | 46 +++++++++++++++++++++------------ tests/spec/data/recursion2.yaml | 44 +++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 18 deletions(-) create mode 100644 tests/spec/data/recursion2.yaml diff --git a/Makefile b/Makefile index c7e1d77..29c64e7 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ TESTCASE= -PHPARGS= -#PHPARGS=-dzend_extension=xdebug.so -dxdebug.remote_enable=1 +PHPARGS=-dmemory_limit=64M +#PHPARGS=-dmemory_limit=64M -dzend_extension=xdebug.so -dxdebug.remote_enable=1 all: @@ -18,10 +18,13 @@ install: test: php $(PHPARGS) vendor/bin/phpunit $(TESTCASE) + php $(PHPARGS) bin/php-openapi validate tests/spec/data/recursion.json + php $(PHPARGS) bin/php-openapi validate tests/spec/data/recursion2.yaml lint: php $(PHPARGS) bin/php-openapi validate tests/spec/data/reference/playlist.json php $(PHPARGS) bin/php-openapi validate tests/spec/data/recursion.json + php $(PHPARGS) bin/php-openapi validate tests/spec/data/recursion2.yaml node_modules/.bin/speccy lint tests/spec/data/reference/playlist.json node_modules/.bin/speccy lint tests/spec/data/recursion.json diff --git a/src/SpecBaseObject.php b/src/SpecBaseObject.php index ecc1c87..ebe854e 100644 --- a/src/SpecBaseObject.php +++ b/src/SpecBaseObject.php @@ -24,7 +24,13 @@ abstract class SpecBaseObject implements SpecObjectInterface, DocumentContextInt { private $_properties = []; private $_errors = []; - private $_recursing = false; + + private $_recursingSerializableData = false; + private $_recursingValidate = false; + private $_recursingErrors = false; + private $_recursingReferences = false; + private $_recursingReferenceContext = false; + private $_recursingDocumentContext = false; private $_baseDocument; private $_jsonPointer; @@ -168,11 +174,11 @@ protected function instantiate($type, $data) */ public function getSerializableData() { - if ($this->_recursing) { + if ($this->_recursingSerializableData) { // return a reference return (object) ['$ref' => JsonReference::createFromUri('', $this->getDocumentPosition())->getReference()]; } - $this->_recursing = true; + $this->_recursingSerializableData = true; $data = $this->_properties; foreach ($data as $k => $v) { @@ -195,7 +201,7 @@ public function getSerializableData() } } - $this->_recursing = false; + $this->_recursingSerializableData = false; return (object) $data; } @@ -208,10 +214,10 @@ public function getSerializableData() public function validate(): bool { // avoid recursion to get stuck in a loop - if ($this->_recursing) { + if ($this->_recursingValidate) { return true; } - $this->_recursing = true; + $this->_recursingValidate = true; $valid = true; foreach ($this->_properties as $v) { if ($v instanceof SpecObjectInterface) { @@ -228,7 +234,7 @@ public function validate(): bool } } } - $this->_recursing = false; + $this->_recursingValidate = false; $this->performValidation(); @@ -246,10 +252,10 @@ public function validate(): bool public function getErrors(): array { // avoid recursion to get stuck in a loop - if ($this->_recursing) { + if ($this->_recursingErrors) { return []; } - $this->_recursing = true; + $this->_recursingErrors = true; if (($pos = $this->getDocumentPosition()) !== null) { $errors = [ @@ -272,7 +278,7 @@ public function getErrors(): array } } - $this->_recursing = false; + $this->_recursingErrors = false; return array_merge(...$errors); } @@ -360,10 +366,10 @@ public function __unset($name) public function resolveReferences(ReferenceContext $context = null) { // avoid recursion to get stuck in a loop - if ($this->_recursing) { + if ($this->_recursingReferences) { return; } - $this->_recursing = true; + $this->_recursingReferences = true; foreach ($this->_properties as $property => $value) { if ($value instanceof Reference) { @@ -389,7 +395,7 @@ public function resolveReferences(ReferenceContext $context = null) } } - $this->_recursing = false; + $this->_recursingReferences = false; } /** @@ -397,6 +403,12 @@ public function resolveReferences(ReferenceContext $context = null) */ public function setReferenceContext(ReferenceContext $context) { + // avoid recursion to get stuck in a loop + if ($this->_recursingReferenceContext) { + return; + } + $this->_recursingReferenceContext = true; + foreach ($this->_properties as $property => $value) { if ($value instanceof Reference) { $value->setContext($context); @@ -412,6 +424,8 @@ public function setReferenceContext(ReferenceContext $context) } } } + + $this->_recursingReferenceContext = false; } /** @@ -428,10 +442,10 @@ public function setDocumentContext(SpecObjectInterface $baseDocument, JsonPointe $this->_jsonPointer = $jsonPointer; // avoid recursion to get stuck in a loop - if ($this->_recursing) { + if ($this->_recursingDocumentContext) { return; } - $this->_recursing = true; + $this->_recursingDocumentContext = true; foreach ($this->_properties as $property => $value) { if ($value instanceof DocumentContextInterface) { @@ -445,7 +459,7 @@ public function setDocumentContext(SpecObjectInterface $baseDocument, JsonPointe } } - $this->_recursing = false; + $this->_recursingDocumentContext = false; } /** diff --git a/tests/spec/data/recursion2.yaml b/tests/spec/data/recursion2.yaml new file mode 100644 index 0000000..3eb7976 --- /dev/null +++ b/tests/spec/data/recursion2.yaml @@ -0,0 +1,44 @@ +openapi: 3.0.2 +info: + title: My API + version: "123" +components: + schemas: + SomeResponse: + type: object + properties: + name: + type: string + description: Name of SomeResponse + recursive: + $ref: '#/components/schemas/RecursiveItem' + + AnotherResponse: + type: object + properties: + uuid: + type: string + format: uuid + description: UUID of AnotherResponse + recursive: + $ref: '#/components/schemas/RecursiveItem' + + + RecursiveItem: + type: object + properties: + children: + type: array + items: + oneOf: + - $ref: '#/components/schemas/RecursiveItem' + - $ref: '#/components/schemas/SomeResponse' + + +paths: + '/': + get: + description: default + responses: + 200: + description: ok \ No newline at end of file