From 70857d58c0a0a858b8ffbe5ff33e709f757b33de Mon Sep 17 00:00:00 2001 From: Jan Tvrdik Date: Fri, 22 Apr 2016 10:57:29 +0200 Subject: [PATCH 01/11] Change Resolver::enter() $schema argument to required --- src/Resolver.php | 8 +++----- src/Walker.php | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Resolver.php b/src/Resolver.php index 1e2601c..e75f1be 100644 --- a/src/Resolver.php +++ b/src/Resolver.php @@ -109,16 +109,14 @@ public function setPreFetchHook(Closure $preFetchHook) /** * Pushes an URI and its associated schema onto the resolution stack, - * making them the current URI/schema pair. If no schema is passed, the - * current schema is reused (useful when entering a resolution scope - * within the current schema). + * making them the current URI/schema pair. * * @param Uri $uri * @param stdClass $schema * * @throws EmptyStackException */ - public function enter(Uri $uri, stdClass $schema = null) + public function enter(Uri $uri, stdClass $schema) { $currentUri = $this->getCurrentUri(); @@ -126,7 +124,7 @@ public function enter(Uri $uri, stdClass $schema = null) $uri->resolveAgainst($currentUri); } - $this->stack[] = [$uri, $schema ?: $this->getCurrentSchema()]; + $this->stack[] = [$uri, $schema]; } /** diff --git a/src/Walker.php b/src/Walker.php index 051ca17..6b9cdfa 100644 --- a/src/Walker.php +++ b/src/Walker.php @@ -87,7 +87,7 @@ private function doResolveReferences(stdClass $schema, Uri $uri) $inScope = false; if (property_exists($schema, 'id') && is_string($schema->id)) { - $this->resolver->enter(new Uri($schema->id)); + $this->resolver->enter(new Uri($schema->id), $schema); $inScope = true; } From 4b37c104d9992094332fb7146516e6830b3b3cdd Mon Sep 17 00:00:00 2001 From: Jan Tvrdik Date: Fri, 22 Apr 2016 10:59:25 +0200 Subject: [PATCH 02/11] Change structure of Resolver's schema resolution stack --- src/Resolver.php | 49 +++++++++++++++++++++++++++--------------- tests/ResolverTest.php | 22 +++++++++++++++++-- 2 files changed, 52 insertions(+), 19 deletions(-) diff --git a/src/Resolver.php b/src/Resolver.php index e75f1be..a7e5aa7 100644 --- a/src/Resolver.php +++ b/src/Resolver.php @@ -29,12 +29,16 @@ class Resolver { /** - * Schema resolution stack. Each item on the stack is an array - * containing an uri and a schema. + * @var stdClass + */ + private $rootSchema; + + /** + * Stack of URIs used for resolving relative URIs. * - * @var array + * @var Uri[] */ - private $stack = []; + private $uriStack = []; /** * Schema cache. Each schema visited at a given URI is stored @@ -60,39 +64,50 @@ class Resolver public function initialize(stdClass $schema, Uri $uri) { $this->registerSchema($schema, $uri); - $this->stack = [[$uri, $schema]]; + $this->rootSchema = $schema; + $this->uriStack = [$uri]; } /** - * Returns the URI of the current schema. + * Returns URI of root schema. * * @return Uri * * @throws EmptyStackException */ - public function getCurrentUri() + public function getRootUri() { - if (count($this->stack) === 0) { + if (count($this->uriStack) === 0) { throw new EmptyStackException(); } - return end($this->stack)[0]; + return reset($this->uriStack); } /** - * Returns the current schema. + * Returns root schema. * - * @return stdClass + * @return stdClass|null + */ + public function getRootSchema() + { + return $this->rootSchema; + } + + /** + * Returns the URI of the current schema. + * + * @return Uri * * @throws EmptyStackException */ - public function getCurrentSchema() + public function getCurrentUri() { - if (count($this->stack) === 0) { + if (count($this->uriStack) === 0) { throw new EmptyStackException(); } - return end($this->stack)[1]; + return end($this->uriStack); } /** @@ -124,7 +139,7 @@ public function enter(Uri $uri, stdClass $schema) $uri->resolveAgainst($currentUri); } - $this->stack[] = [$uri, $schema]; + $this->uriStack[] = $uri; } /** @@ -135,11 +150,11 @@ public function enter(Uri $uri, stdClass $schema) */ public function leave() { - if (count($this->stack) === 0) { + if (count($this->uriStack) === 0) { throw new EmptyStackException(); } - array_pop($this->stack); + array_pop($this->uriStack); } /** diff --git a/tests/ResolverTest.php b/tests/ResolverTest.php index 5894897..3a72ec4 100644 --- a/tests/ResolverTest.php +++ b/tests/ResolverTest.php @@ -24,12 +24,30 @@ protected function setUp() $this->resolver = new Resolver(); } + public function testBasicStackHandling() + { + $schemaA = new stdClass(); + $uriA = new Uri('file:///foo/bar/a'); + $this->resolver->initialize($schemaA, $uriA); + + $this->assertSame($schemaA, $this->resolver->getRootSchema()); + $this->assertSame($uriA, $this->resolver->getRootUri()); + $this->assertSame($uriA, $this->resolver->getCurrentUri()); + + $schemaB = new stdClass(); + $uriB = new Uri('file:///foo/bar/b'); + $this->resolver->enter($uriB, $schemaB); + $this->assertSame($schemaA, $this->resolver->getRootSchema()); + $this->assertSame($uriA, $this->resolver->getRootUri()); + $this->assertSame($uriB, $this->resolver->getCurrentUri()); + } + /** * @expectedException \JVal\Exception\Resolver\EmptyStackException */ - public function testGetCurrentSchemaThrowsIfStackIsEmpty() + public function testGetRootUriThrowsIfStackIsEmpty() { - $this->resolver->getCurrentSchema(); + $this->resolver->getRootUri(); } /** From ac937d14dc4bb795f1e341f6e6d5ea5c96172cc8 Mon Sep 17 00:00:00 2001 From: Jan Tvrdik Date: Fri, 22 Apr 2016 14:57:20 +0200 Subject: [PATCH 03/11] Change Uri to immutable class --- src/Resolver.php | 4 ++-- src/Uri.php | 5 ++--- tests/UriTest.php | 17 ++++++++++------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/Resolver.php b/src/Resolver.php index a7e5aa7..e3fe055 100644 --- a/src/Resolver.php +++ b/src/Resolver.php @@ -136,7 +136,7 @@ public function enter(Uri $uri, stdClass $schema) $currentUri = $this->getCurrentUri(); if (!$uri->isAbsolute()) { - $uri->resolveAgainst($currentUri); + $uri = $uri->resolveAgainst($currentUri); } $this->uriStack[] = $uri; @@ -175,7 +175,7 @@ public function resolve(stdClass $reference) $uri = new Uri($reference->{'$ref'}); if (!$uri->isAbsolute()) { - $uri->resolveAgainst($baseUri); + $uri = $uri->resolveAgainst($baseUri); } $identifier = $uri->getPrimaryResourceIdentifier(); diff --git a/src/Uri.php b/src/Uri.php index 406139d..e2cbf60 100644 --- a/src/Uri.php +++ b/src/Uri.php @@ -146,7 +146,7 @@ public function getPrimaryResourceIdentifier() * * @param Uri $uri * - * @return string + * @return Uri */ public function resolveAgainst(Uri $uri) { @@ -163,9 +163,8 @@ public function resolveAgainst(Uri $uri) } $resolved = $this->buildResolvedUriAgainst($uri); - $this->buildFromRawUri($resolved); - return $resolved; + return new self($resolved); } /** diff --git a/tests/UriTest.php b/tests/UriTest.php index eddd0b6..157e9d5 100644 --- a/tests/UriTest.php +++ b/tests/UriTest.php @@ -72,18 +72,21 @@ public function testResolveAgainstAnotherUri($uri, $againstUri, $expectedResolve { $pointer = new Uri($uri); $resolved = $pointer->resolveAgainst(new Uri($againstUri)); - $this->assertEquals($expectedResolved, $resolved); + $this->assertEquals($expectedResolved, $resolved->getRawUri()); } - public function testResolveAgainstUriChangesInternalState() + public function testResolveAgainstUriDoesNotChangeInternalState() { $pointer = new Uri('#quz/123'); $against = new Uri('http://localhost:1234/foo/bar#baz'); - $pointer->resolveAgainst($against); - $this->assertEquals('http://localhost:1234/foo/bar#quz/123', $pointer->getRawUri()); - $this->assertEquals('http', $pointer->getScheme()); - $this->assertEquals('http://localhost:1234/foo/bar', $pointer->getPrimaryResourceIdentifier()); - $this->assertEquals(['quz', '123'], $pointer->getPointerSegments()); + $resolved = $pointer->resolveAgainst($against); + + $this->assertEquals('#quz/123', $pointer->getRawUri()); + + $this->assertEquals('http://localhost:1234/foo/bar#quz/123', $resolved->getRawUri()); + $this->assertEquals('http', $resolved->getScheme()); + $this->assertEquals('http://localhost:1234/foo/bar', $resolved->getPrimaryResourceIdentifier()); + $this->assertEquals(['quz', '123'], $resolved->getPointerSegments()); } /** From bc58f89ddbc2c9978b14501dde764580c47ae076 Mon Sep 17 00:00:00 2001 From: Jan Tvrdik Date: Fri, 22 Apr 2016 14:58:32 +0200 Subject: [PATCH 04/11] Change Uri::resolveAgainst() to support absolute URIs --- src/Resolver.php | 13 +++---------- src/Uri.php | 15 +++++---------- tests/UriTest.php | 10 +--------- 3 files changed, 9 insertions(+), 29 deletions(-) diff --git a/src/Resolver.php b/src/Resolver.php index e3fe055..24cf9a7 100644 --- a/src/Resolver.php +++ b/src/Resolver.php @@ -134,12 +134,8 @@ public function setPreFetchHook(Closure $preFetchHook) public function enter(Uri $uri, stdClass $schema) { $currentUri = $this->getCurrentUri(); - - if (!$uri->isAbsolute()) { - $uri = $uri->resolveAgainst($currentUri); - } - - $this->uriStack[] = $uri; + $resolvedUri = $uri->resolveAgainst($currentUri); + $this->uriStack[] = $resolvedUri; } /** @@ -173,10 +169,7 @@ public function resolve(stdClass $reference) { $baseUri = $this->getCurrentUri(); $uri = new Uri($reference->{'$ref'}); - - if (!$uri->isAbsolute()) { - $uri = $uri->resolveAgainst($baseUri); - } + $uri = $uri->resolveAgainst($baseUri); $identifier = $uri->getPrimaryResourceIdentifier(); diff --git a/src/Uri.php b/src/Uri.php index e2cbf60..cf44b60 100644 --- a/src/Uri.php +++ b/src/Uri.php @@ -151,20 +151,15 @@ public function getPrimaryResourceIdentifier() public function resolveAgainst(Uri $uri) { if ($this->isAbsolute()) { - throw new \LogicException( - 'Cannot resolve against another URI: URI is already absolute' - ); - } - - if (!$uri->isAbsolute()) { + return $this; + } elseif (!$uri->isAbsolute()) { throw new \LogicException( 'Cannot resolve against another URI: reference URI is not absolute' ); + } else { + $resolvedUri = $this->buildResolvedUriAgainst($uri); + return new self($resolvedUri); } - - $resolved = $this->buildResolvedUriAgainst($uri); - - return new self($resolved); } /** diff --git a/tests/UriTest.php b/tests/UriTest.php index 157e9d5..6a65a6e 100644 --- a/tests/UriTest.php +++ b/tests/UriTest.php @@ -43,15 +43,6 @@ public function testPointerSegments($uri, array $expectedSegments) $this->assertEquals($expectedSegments, $pointer->getPointerSegments()); } - /** - * @expectedException \LogicException - */ - public function testCannotResolveAgainstOtherUriIfAlreadyAbsolute() - { - $pointer = new Uri('http://localhost'); - $pointer->resolveAgainst(new Uri('file:///foo')); - } - /** * @expectedException \LogicException */ @@ -141,6 +132,7 @@ public function uriSegmentsProvider() public function againstUriProvider() { return [ + ['http://localhost', 'file:///foo', 'http://localhost'], ['foo.json', 'http://localhost/bar', 'http://localhost/foo.json'], ['foo.json', 'http://localhost/bar/baz', 'http://localhost/bar/foo.json'], ['/foo.json', 'http://localhost/bar/baz', 'http://localhost/foo.json'], From 80b26015e119e70d22d2069ea989109e2588a7ec Mon Sep 17 00:00:00 2001 From: Jan Tvrdik Date: Fri, 22 Apr 2016 11:07:38 +0200 Subject: [PATCH 05/11] Change Uri::buildPrimaryIdentifier() to include non-JSON-pointer fragment --- src/Uri.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Uri.php b/src/Uri.php index cf44b60..1f1d813 100644 --- a/src/Uri.php +++ b/src/Uri.php @@ -248,16 +248,20 @@ private function buildPrimaryIdentifier() { $identifier = ''; - if ($this->parts['scheme']) { + if ($this->parts['scheme'] !== '') { $identifier .= $this->parts['scheme'].'://'; } $identifier .= $this->authority.$this->parts['path']; - if ($this->parts['query']) { + if ($this->parts['query'] !== '') { $identifier .= '?'.$this->parts['query']; } + if ($this->parts['fragment'] !== '' && $this->parts['fragment'][0] !== '/') { + $identifier .= '#'.$this->parts['fragment']; + } + return $identifier; } From 0d9eae61848170ed4628c3dda6488882d0e5b2ba Mon Sep 17 00:00:00 2001 From: Jan Tvrdik Date: Fri, 22 Apr 2016 11:10:10 +0200 Subject: [PATCH 06/11] Optimize Utils::doAreEqual() speed when comparing identical instances --- src/Utils.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Utils.php b/src/Utils.php index 334bd27..7f32e51 100644 --- a/src/Utils.php +++ b/src/Utils.php @@ -125,6 +125,10 @@ public static function lastJsonErrorMessage() private static function doAreEqual($a, $b, array $stack) { + if ($a === $b) { + return true; + } + // keep track of object references to avoid infinite recursion if (is_object($a)) { if (in_array($a, $stack)) { From 3f92d63f0b2b2b08a7ea4fb39f13d3d2dc3f107d Mon Sep 17 00:00:00 2001 From: Jan Tvrdik Date: Fri, 22 Apr 2016 13:13:23 +0200 Subject: [PATCH 07/11] Simplify Uri::getRawPointer() --- src/Uri.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Uri.php b/src/Uri.php index 1f1d813..b28cccd 100644 --- a/src/Uri.php +++ b/src/Uri.php @@ -74,7 +74,7 @@ public function getRawUri() */ public function getRawPointer() { - return isset($this->parts['fragment']) ? $this->parts['fragment'] : ''; + return $this->parts['fragment']; } /** From 53945cdcb325808df891106e0ceb0d9f3099bf16 Mon Sep 17 00:00:00 2001 From: Jan Tvrdik Date: Fri, 22 Apr 2016 13:57:57 +0200 Subject: [PATCH 08/11] Fix JSON pointer parsing to respect RFC 6901 --- src/Uri.php | 14 +++++--------- tests/Data/schemas/valid/root-reference-2.json | 7 ------- tests/Data/schemas/valid/root-reference-3.json | 7 ------- ...ot-reference-1.json => root-reference.json} | 0 tests/ResolverTest.php | 12 ++++-------- tests/UriTest.php | 18 ++++++++++-------- 6 files changed, 19 insertions(+), 39 deletions(-) delete mode 100644 tests/Data/schemas/valid/root-reference-2.json delete mode 100644 tests/Data/schemas/valid/root-reference-3.json rename tests/Data/schemas/valid/{root-reference-1.json => root-reference.json} (100%) diff --git a/src/Uri.php b/src/Uri.php index b28cccd..d16860f 100644 --- a/src/Uri.php +++ b/src/Uri.php @@ -227,17 +227,13 @@ private function buildSegments() { $segments = []; - if (isset($this->parts['fragment'])) { - $rawSegments = explode('/', $this->parts['fragment']); + if (substr($this->parts['fragment'], 0, 1) === '/') { + $rawSegments = explode('/', substr($this->parts['fragment'], 1)); foreach ($rawSegments as $segment) { - $segment = trim($segment); - - if ($segment !== '') { - $segment = str_replace('~1', '/', $segment); - $segment = str_replace('~0', '~', $segment); - $segments[] = $segment; - } + $segment = str_replace('~1', '/', $segment); + $segment = str_replace('~0', '~', $segment); + $segments[] = $segment; } } diff --git a/tests/Data/schemas/valid/root-reference-2.json b/tests/Data/schemas/valid/root-reference-2.json deleted file mode 100644 index 0473e64..0000000 --- a/tests/Data/schemas/valid/root-reference-2.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "foo": { - "bar": { - "$ref": "#/" - } - } -} diff --git a/tests/Data/schemas/valid/root-reference-3.json b/tests/Data/schemas/valid/root-reference-3.json deleted file mode 100644 index 4d01798..0000000 --- a/tests/Data/schemas/valid/root-reference-3.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "foo": { - "bar": { - "$ref": "#///" - } - } -} diff --git a/tests/Data/schemas/valid/root-reference-1.json b/tests/Data/schemas/valid/root-reference.json similarity index 100% rename from tests/Data/schemas/valid/root-reference-1.json rename to tests/Data/schemas/valid/root-reference.json diff --git a/tests/ResolverTest.php b/tests/ResolverTest.php index 3a72ec4..cf62bab 100644 --- a/tests/ResolverTest.php +++ b/tests/ResolverTest.php @@ -241,9 +241,7 @@ public function testResolveThrowsOnInvalidRemoteSchema() public function rootRefProvider() { return [ - ['valid/root-reference-1'], - ['valid/root-reference-2'], - ['valid/root-reference-3'], + ['valid/root-reference'], ]; } @@ -252,12 +250,10 @@ public function chainProvider() $schema = $this->loadSchema('valid/resolution-chains'); return [ - [$schema, '#foo', $schema->foo], [$schema, '#/foo', $schema->foo], [$schema, '#/foo/baz', $schema->foo->baz], [$schema, '#/bar/baz', $schema->bar->baz], [$schema, '#/bar/baz/bat', $schema->bar->baz->bat], - [$schema, '#/bar/baz/bat/', $schema->bar->baz->bat], [$schema, '#/bat/0', $schema->bat[0]], [$schema, '#/bat/1/quz/0', $schema->bat[1]->quz[0]], [$schema, '#/with%25percent', $schema->{'with%percent'}], @@ -272,9 +268,9 @@ public function unresolvablePointerPropertyProvider() $schema = $this->loadSchema('valid/resolution-chains'); return [ - [$schema, '#nope'], + [$schema, '#/nope'], [$schema, '#/foo/nope'], - [$schema, '#bar/baz/nope'], + [$schema, '#/bar/baz/nope'], ]; } @@ -314,7 +310,7 @@ public function invalidPointerTargetProvider() return [ [$schema, '#/foo/bat'], - [$schema, '#bat'], + [$schema, '#/bat'], ]; } diff --git a/tests/UriTest.php b/tests/UriTest.php index 6a65a6e..dac6b6f 100644 --- a/tests/UriTest.php +++ b/tests/UriTest.php @@ -68,13 +68,13 @@ public function testResolveAgainstAnotherUri($uri, $againstUri, $expectedResolve public function testResolveAgainstUriDoesNotChangeInternalState() { - $pointer = new Uri('#quz/123'); - $against = new Uri('http://localhost:1234/foo/bar#baz'); + $pointer = new Uri('#/quz/123'); + $against = new Uri('http://localhost:1234/foo/bar#/baz'); $resolved = $pointer->resolveAgainst($against); - $this->assertEquals('#quz/123', $pointer->getRawUri()); + $this->assertEquals('#/quz/123', $pointer->getRawUri()); - $this->assertEquals('http://localhost:1234/foo/bar#quz/123', $resolved->getRawUri()); + $this->assertEquals('http://localhost:1234/foo/bar#/quz/123', $resolved->getRawUri()); $this->assertEquals('http', $resolved->getScheme()); $this->assertEquals('http://localhost:1234/foo/bar', $resolved->getPrimaryResourceIdentifier()); $this->assertEquals(['quz', '123'], $resolved->getPointerSegments()); @@ -122,10 +122,12 @@ public function uriSegmentsProvider() ['http://foo.bar/baz?foo=bar#/foo/bar', ['foo', 'bar']], ['//localhost/foo#/bar/baz', ['bar', 'baz']], ['/quz', []], - ['/quz/#//', []], - ['/quz/#//foo/1%25/bar', ['foo', '1%', 'bar']], - ['/quz/#//foo/1~02/bar', ['foo', '1~2', 'bar']], - ['/quz/#//foo/1~12/bar', ['foo', '1/2', 'bar']], + ['/quz#', []], + ['/quz#/', ['']], + ['/quz#//', ['', '']], + ['/quz#/foo/1%25/bar', ['foo', '1%', 'bar']], + ['/quz#/foo/1~02/bar', ['foo', '1~2', 'bar']], + ['/quz#/foo/1~12/bar', ['foo', '1/2', 'bar']], ]; } From 30e74774414c2cd0286e478b9c2471c16efb7282 Mon Sep 17 00:00:00 2001 From: Jan Tvrdik Date: Fri, 22 Apr 2016 14:06:57 +0200 Subject: [PATCH 09/11] Implement resolving inside current schema without absolute URI [closes #6] --- src/Resolver.php | 18 +++++++++++------- tests/ResolverTest.php | 16 ++++++++++++++++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/Resolver.php b/src/Resolver.php index 24cf9a7..c975415 100644 --- a/src/Resolver.php +++ b/src/Resolver.php @@ -169,15 +169,19 @@ public function resolve(stdClass $reference) { $baseUri = $this->getCurrentUri(); $uri = new Uri($reference->{'$ref'}); - $uri = $uri->resolveAgainst($baseUri); - $identifier = $uri->getPrimaryResourceIdentifier(); - - if (!isset($this->schemas[$identifier])) { - $schema = $this->fetchSchemaAt($identifier); - $this->registerSchema($schema, $uri); + if ($baseUri->getPrimaryResourceIdentifier() === '' && $uri->getPrimaryResourceIdentifier() === '') { + $schema = $this->getRootSchema(); } else { - $schema = $this->schemas[$identifier]; + $uri = $uri->resolveAgainst($baseUri); + $identifier = $uri->getPrimaryResourceIdentifier(); + + if (isset($this->schemas[$identifier])) { + $schema = $this->schemas[$identifier]; + } else { + $schema = $this->fetchSchemaAt($identifier); + $this->registerSchema($schema, $uri); + } } $resolved = $this->resolvePointer($schema, $uri); diff --git a/tests/ResolverTest.php b/tests/ResolverTest.php index cf62bab..fdae3df 100644 --- a/tests/ResolverTest.php +++ b/tests/ResolverTest.php @@ -95,6 +95,22 @@ public function testResolveChain(stdClass $schema, $pointerUri, stdClass $resolv $this->assertSame($actual[1], $resolved); } + /** + * @dataProvider chainProvider + * + * @param stdClass $schema + * @param string $pointerUri + * @param stdClass $resolved + */ + public function testResolveChainWithoutAbsoluteUri(stdClass $schema, $pointerUri, stdClass $resolved) + { + $this->resolver->initialize($schema, new Uri('')); + $reference = new stdClass(); + $reference->{'$ref'} = $pointerUri; + $actual = $this->resolver->resolve($reference); + $this->assertSame($actual[1], $resolved); + } + /** * @dataProvider unresolvablePointerPropertyProvider * @expectedException \JVal\Exception\Resolver\UnresolvedPointerPropertyException From c66f20ef832bc18d315394d1f8f803a29fa2ca48 Mon Sep 17 00:00:00 2001 From: Jan Tvrdik Date: Fri, 22 Apr 2016 14:18:51 +0200 Subject: [PATCH 10/11] Change Resolver::registerSchema() to public method --- src/Resolver.php | 21 ++++++++++++++++----- src/Uri.php | 8 ++++++++ tests/ResolverTest.php | 16 ++++++++++++++++ 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/Resolver.php b/src/Resolver.php index c975415..52441ed 100644 --- a/src/Resolver.php +++ b/src/Resolver.php @@ -63,7 +63,10 @@ class Resolver */ public function initialize(stdClass $schema, Uri $uri) { - $this->registerSchema($schema, $uri); + if ($uri->isAbsolute() && !$uri->hasPointer()) { + $this->registerSchema($schema, $uri); + } + $this->rootSchema = $schema; $this->uriStack = [$uri]; } @@ -198,15 +201,23 @@ public function resolve(stdClass $reference) } /** - * Caches a schema reference for future use. + * Registers a schema reference for future use. * * @param stdClass $schema * @param Uri $uri */ - private function registerSchema(stdClass $schema, Uri $uri) + public function registerSchema(stdClass $schema, Uri $uri) { - if (!isset($this->schemas[$uri->getPrimaryResourceIdentifier()])) { - $this->schemas[$uri->getPrimaryResourceIdentifier()] = $schema; + if (!$uri->isAbsolute()) { + throw new \LogicException('Unable to register schema without absolute URI'); + } + + $identifier = $uri->getPrimaryResourceIdentifier(); + + if (!isset($this->schemas[$identifier])) { + $this->schemas[$identifier] = $schema; + } elseif (!Utils::areEqual($this->schemas[$identifier], $schema)) { + throw new \LogicException('Different schema is already registered with given URI'); } } diff --git a/src/Uri.php b/src/Uri.php index d16860f..21c5e07 100644 --- a/src/Uri.php +++ b/src/Uri.php @@ -125,6 +125,14 @@ public function getPointerSegments() return $this->segments; } + /** + * @return bool + */ + public function hasPointer() + { + return !empty($this->segments); + } + /** * Returns the primary resource identifier part of the URI, i.e. everything * excluding its fragment part. diff --git a/tests/ResolverTest.php b/tests/ResolverTest.php index fdae3df..5d0ad0a 100644 --- a/tests/ResolverTest.php +++ b/tests/ResolverTest.php @@ -42,6 +42,22 @@ public function testBasicStackHandling() $this->assertSame($uriB, $this->resolver->getCurrentUri()); } + public function testRegisterSchemaWithTwoEqualSchemas() + { + $this->resolver->registerSchema((object) ['type' => 'string'], new Uri('file:///foo/bar')); + $this->resolver->registerSchema((object) ['type' => 'string'], new Uri('file:///foo/bar')); + + } + + /** + * @expectedException \LogicException + */ + public function testRegisterSchemaThrowsIfAlreadyRegisteredWithDifferentSchema() + { + $this->resolver->registerSchema((object) ['type' => 'string'], new Uri('file:///foo/bar')); + $this->resolver->registerSchema((object) ['type' => 'array'], new Uri('file:///foo/bar')); + } + /** * @expectedException \JVal\Exception\Resolver\EmptyStackException */ From d037430ab23ae3255bcfaf0d092d9dace5672c6a Mon Sep 17 00:00:00 2001 From: Jan Tvrdik Date: Fri, 22 Apr 2016 14:20:20 +0200 Subject: [PATCH 11/11] Change Resolver::enter() to register new schema --- src/Resolver.php | 4 ++++ tests/Data/schemas/valid/scoped-references.json | 8 -------- tests/WalkerTest.php | 5 ----- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/Resolver.php b/src/Resolver.php index 52441ed..969e83d 100644 --- a/src/Resolver.php +++ b/src/Resolver.php @@ -139,6 +139,10 @@ public function enter(Uri $uri, stdClass $schema) $currentUri = $this->getCurrentUri(); $resolvedUri = $uri->resolveAgainst($currentUri); $this->uriStack[] = $resolvedUri; + + if ($resolvedUri->isAbsolute() && !$resolvedUri->hasPointer()) { + $this->registerSchema($schema, $resolvedUri); + } } /** diff --git a/tests/Data/schemas/valid/scoped-references.json b/tests/Data/schemas/valid/scoped-references.json index 0bf2a61..4bbb923 100644 --- a/tests/Data/schemas/valid/scoped-references.json +++ b/tests/Data/schemas/valid/scoped-references.json @@ -6,14 +6,6 @@ }, "bar": { "$ref": "/valid/exclusiveMinimum-not-present.json" - }, - "baz": { - "id": "exclusiveMaximum-not-present.json", - "oneOf": [ - { - "$ref": "#" - } - ] } } } diff --git a/tests/WalkerTest.php b/tests/WalkerTest.php index be277a2..6002726 100644 --- a/tests/WalkerTest.php +++ b/tests/WalkerTest.php @@ -107,11 +107,6 @@ public function testResolveReferencesWithScopeChanges() $this->assertObjectHasAttribute('bar', $resolved->properties); $this->assertObjectHasAttribute('minimum', $resolved->properties->bar); - - $this->assertObjectHasAttribute('baz', $resolved->properties); - $this->assertObjectHasAttribute('oneOf', $resolved->properties->baz); - $this->assertArrayHasKey(0, $resolved->properties->baz->oneOf); - $this->assertObjectHasAttribute('maximum', $resolved->properties->baz->oneOf[0]); } public function testApplyConstraintsWithRecursiveReference()