From 584f4ccb2882c1586f0f410684adc576e7bfd81d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20H=C3=A9rault?= Date: Fri, 20 Jul 2012 22:26:48 +0200 Subject: [PATCH 1/2] Make url retriever property dynamic --- src/JsonSchema/Constraints/Constraint.php | 65 +++++++++++++++---- src/JsonSchema/Constraints/Undefined.php | 2 +- src/JsonSchema/Validator.php | 40 +----------- .../JsonSchema/Tests/Uri/UriRetrieverTest.php | 42 ++++++------ 4 files changed, 75 insertions(+), 74 deletions(-) diff --git a/src/JsonSchema/Constraints/Constraint.php b/src/JsonSchema/Constraints/Constraint.php index 8629cba4..06027c95 100644 --- a/src/JsonSchema/Constraints/Constraint.php +++ b/src/JsonSchema/Constraints/Constraint.php @@ -9,6 +9,12 @@ namespace JsonSchema\Constraints; +use JsonSchema\Uri\Retrievers\FileGetContents; +use JsonSchema\Uri\Retrievers\UriRetrieverInterface; +use JsonSchema\Validator; +use JsonSchema\Exception\InvalidSchemaMediaTypeException; +use JsonSchema\Exception\JsonDecodingException; + /** * The Base Constraints, all Validators should extend this class * @@ -18,6 +24,7 @@ abstract class Constraint implements ConstraintInterface { protected $checkMode = self::CHECK_MODE_NORMAL; + protected $uriRetriever; protected $errors = array(); protected $inlineSchemaProperty = '$schema'; @@ -25,11 +32,21 @@ abstract class Constraint implements ConstraintInterface const CHECK_MODE_TYPE_CAST = 2; /** - * @param int $checkMode + * @param int $checkMode + * @param UriRetrieverInterface $uriRetriever + */ + public function __construct($checkMode = self::CHECK_MODE_NORMAL, UriRetrieverInterface $uriRetriever = null) + { + $this->checkMode = $checkMode; + $this->uriRetriever = $uriRetriever; + } + + /** + * @param UriRetrieverInterface $uriRetriever */ - public function __construct($checkMode = self::CHECK_MODE_NORMAL) + public function setUriRetriever(UriRetrieverInterface $uriRetriever) { - $this->checkMode = $checkMode; + $this->uriRetriever = $uriRetriever; } /** @@ -111,7 +128,7 @@ protected function incrementPath($path, $i) */ protected function checkArray($value, $schema = null, $path = null, $i = null) { - $validator = new Collection($this->checkMode); + $validator = new Collection($this->checkMode, $this->uriRetriever); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); @@ -128,7 +145,7 @@ protected function checkArray($value, $schema = null, $path = null, $i = null) */ protected function checkObject($value, $schema = null, $path = null, $i = null, $patternProperties = null) { - $validator = new Object($this->checkMode); + $validator = new Object($this->checkMode, $this->uriRetriever); $validator->check($value, $schema, $path, $i, $patternProperties); $this->addErrors($validator->getErrors()); @@ -144,7 +161,7 @@ protected function checkObject($value, $schema = null, $path = null, $i = null, */ protected function checkType($value, $schema = null, $path = null, $i = null) { - $validator = new Type($this->checkMode); + $validator = new Type($this->checkMode, $this->uriRetriever); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); @@ -160,7 +177,7 @@ protected function checkType($value, $schema = null, $path = null, $i = null) */ protected function checkUndefined($value, $schema = null, $path = null, $i = null) { - $validator = new Undefined($this->checkMode); + $validator = new Undefined($this->checkMode, $this->uriRetriever); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); @@ -176,7 +193,7 @@ protected function checkUndefined($value, $schema = null, $path = null, $i = nul */ protected function checkString($value, $schema = null, $path = null, $i = null) { - $validator = new String($this->checkMode); + $validator = new String($this->checkMode, $this->uriRetriever); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); @@ -192,7 +209,7 @@ protected function checkString($value, $schema = null, $path = null, $i = null) */ protected function checkNumber($value, $schema = null, $path = null, $i = null) { - $validator = new Number($this->checkMode); + $validator = new Number($this->checkMode, $this->uriRetriever); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); @@ -208,7 +225,7 @@ protected function checkNumber($value, $schema = null, $path = null, $i = null) */ protected function checkEnum($value, $schema = null, $path = null, $i = null) { - $validator = new Enum($this->checkMode); + $validator = new Enum($this->checkMode, $this->uriRetriever); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); @@ -216,9 +233,33 @@ protected function checkEnum($value, $schema = null, $path = null, $i = null) protected function checkFormat($value, $schema = null, $path = null, $i = null) { - $validator = new Format($this->checkMode); + $validator = new Format($this->checkMode, $this->uriRetriever); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); } -} \ No newline at end of file + + /** + * @param string $uri JSON Schema URI + * @return string JSON Schema contents + * @throws InvalidSchemaMediaType for invalid media types + */ + protected function retrieveUri($uri) + { + if (null === $this->uriRetriever) { + $this->setUriRetriever(new FileGetContents); + } + $contents = $this->uriRetriever->retrieve($uri); + if (Validator::SCHEMA_MEDIA_TYPE !== $this->uriRetriever->getContentType()) { + throw new InvalidSchemaMediaTypeException(sprintf('Media type %s expected', Validator::SCHEMA_MEDIA_TYPE)); + } + $jsonSchema = json_decode($contents); + if (JSON_ERROR_NONE < $error = json_last_error()) { + throw new JsonDecodingException($error); + } + + // TODO validate using schema) + $jsonSchema->id = $uri; + return $jsonSchema; + } +} diff --git a/src/JsonSchema/Constraints/Undefined.php b/src/JsonSchema/Constraints/Undefined.php index c62e4465..46624ffb 100644 --- a/src/JsonSchema/Constraints/Undefined.php +++ b/src/JsonSchema/Constraints/Undefined.php @@ -127,7 +127,7 @@ protected function validateUri($schemaUri = null, $schema, $path = null, $i = nu if ($resolver->isValid($schemaUri)) { $schemaId = property_exists($schema, 'id') ? $schema->id : null; - return Validator::retrieveUri($resolver->resolve($schemaUri, $schemaId)); + return $this->retrieveUri($resolver->resolve($schemaUri, $schemaId)); } } } diff --git a/src/JsonSchema/Validator.php b/src/JsonSchema/Validator.php index c98ed229..02feaca6 100644 --- a/src/JsonSchema/Validator.php +++ b/src/JsonSchema/Validator.php @@ -27,8 +27,6 @@ class Validator extends Constraint { const SCHEMA_MEDIA_TYPE = 'application/schema+json'; - - private static $uriRetriever; /** * Validates the given data against the schema and returns an object containing the results @@ -39,43 +37,9 @@ class Validator extends Constraint */ public function check($value, $schema = null, $path = null, $i = null) { - $validator = new Schema($this->checkMode); + $validator = new Schema($this->checkMode, $this->uriRetriever); $validator->check($value, $schema); $this->addErrors($validator->getErrors()); } - - /** - * Sets the URI retriever the validator will use. FileGetContents by default - * - * @param UriRetrieverInterface $retriever - */ - public static function setUriRetriever(UriRetrieverInterface $retriever) - { - self::$uriRetriever = $retriever; - } - - /** - * @param string $uri JSON Schema URI - * @return string JSON Schema contents - * @throws InvalidSchemaMediaType for invalid media types - */ - public static function retrieveUri($uri) - { - if (null === self::$uriRetriever) { - self::setUriRetriever(new Uri\Retrievers\FileGetContents); - } - $contents = self::$uriRetriever->retrieve($uri); - if (self::SCHEMA_MEDIA_TYPE !== self::$uriRetriever->getContentType()) { - throw new InvalidSchemaMediaTypeException(sprintf('Media type %s expected', self::SCHEMA_MEDIA_TYPE)); - } - $jsonSchema = json_decode($contents); - if (JSON_ERROR_NONE < $error = json_last_error()) { - throw new JsonDecodingException($error); - } - - // TODO validate using schema) - $jsonSchema->id = $uri; - return $jsonSchema; - } -} \ No newline at end of file +} diff --git a/tests/JsonSchema/Tests/Uri/UriRetrieverTest.php b/tests/JsonSchema/Tests/Uri/UriRetrieverTest.php index acf58b69..0102bb33 100644 --- a/tests/JsonSchema/Tests/Uri/UriRetrieverTest.php +++ b/tests/JsonSchema/Tests/Uri/UriRetrieverTest.php @@ -20,20 +20,20 @@ protected function setUp() $this->validator = new Validator(); } - private function getCurlRetrieverMock($returnSchema, $returnMediaType = Validator::SCHEMA_MEDIA_TYPE) + private function getRetrieverMock($returnSchema, $returnMediaType = Validator::SCHEMA_MEDIA_TYPE) { - $curlRetriever = $this->getMock('JsonSchema\Uri\Retrievers\Curl', array('retrieve', 'getContentType')); + $retriever = $this->getMock('JsonSchema\Uri\Retrievers\UriRetrieverInterface', array('retrieve', 'getContentType')); - $curlRetriever->expects($this->at(0)) - ->method('retrieve') - ->with($this->equalTo('http://some.host.at/somewhere/parent')) - ->will($this->returnValue($returnSchema)); + $retriever->expects($this->at(0)) + ->method('retrieve') + ->with($this->equalTo('http://some.host.at/somewhere/parent')) + ->will($this->returnValue($returnSchema)); - $curlRetriever->expects($this->atLeastOnce()) // index 1 and/or 3 - ->method('getContentType') - ->will($this->returnValue($returnMediaType)); + $retriever->expects($this->atLeastOnce()) // index 1 and/or 3 + ->method('getContentType') + ->will($this->returnValue($returnMediaType)); - return $curlRetriever; + return $retriever; } /** @@ -41,14 +41,13 @@ private function getCurlRetrieverMock($returnSchema, $returnMediaType = Validato */ public function testChildExtendsParent($childSchema, $parentSchema) { - $curlRetrieverMock = $this->getCurlRetrieverMock($parentSchema); - - Validator::setUriRetriever($curlRetrieverMock); + $retrieverMock = $this->getRetrieverMock($parentSchema); $json = '{"childProp":"infant", "parentProp":false}'; $decodedJson = json_decode($json); $decodedJsonSchema = json_decode($childSchema); + $this->validator->setUriRetriever($retrieverMock); $this->validator->check($decodedJson, $decodedJsonSchema); $this->assertTrue($this->validator->isValid()); } @@ -59,19 +58,18 @@ public function testChildExtendsParent($childSchema, $parentSchema) public function testResolveRelativeUri($childSchema, $parentSchema) { self::setParentSchemaExtendsValue($parentSchema, 'grandparent'); - $curlRetrieverMock = $this->getCurlRetrieverMock($parentSchema); + $retrieverMock = $this->getRetrieverMock($parentSchema); - $curlRetrieverMock->expects($this->at(2)) + $retrieverMock->expects($this->at(2)) ->method('retrieve') ->with($this->equalTo('http://some.host.at/somewhere/grandparent')) ->will($this->returnValue('{"type":"object","title":"grand-parent"}')); - Validator::setUriRetriever($curlRetrieverMock); - $json = '{"childProp":"infant", "parentProp":false}'; $decodedJson = json_decode($json); $decodedJsonSchema = json_decode($childSchema); + $this->validator->setUriRetriever($retrieverMock); $this->validator->check($decodedJson, $decodedJsonSchema); $this->assertTrue($this->validator->isValid()); } @@ -89,14 +87,13 @@ private static function setParentSchemaExtendsValue(&$parentSchema, $value) */ public function testInvalidSchemaMediaType($childSchema, $parentSchema) { - $curlRetrieverMock = $this->getCurlRetrieverMock($parentSchema, 'text/html'); - - Validator::setUriRetriever($curlRetrieverMock); + $retrieverMock = $this->getRetrieverMock($parentSchema, 'text/html'); $json = '{"childProp":"infant", "parentProp":false}'; $decodedJson = json_decode($json); $decodedJsonSchema = json_decode($childSchema); + $this->validator->setUriRetriever($retrieverMock); $this->validator->check($decodedJson, $decodedJsonSchema); } @@ -106,14 +103,13 @@ public function testInvalidSchemaMediaType($childSchema, $parentSchema) */ public function testParentJsonError($childSchema, $parentSchema) { - $curlRetrieverMock = $this->getCurlRetrieverMock('', 'application/schema+json'); - - Validator::setUriRetriever($curlRetrieverMock); + $retrieverMock = $this->getRetrieverMock('', 'application/schema+json'); $json = '{}'; $decodedJson = json_decode($json); $decodedJsonSchema = json_decode($childSchema); + $this->validator->setUriRetriever($retrieverMock); $this->validator->check($decodedJson, $decodedJsonSchema); } From 539ac403ab7f1c25fc50dbfac197dc2e3d28506f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20H=C3=A9rault?= Date: Fri, 20 Jul 2012 22:45:34 +0200 Subject: [PATCH 2/2] Add predefined array based uri retriever --- .../Uri/Retrievers/PredefinedArray.php | 59 +++++++++++++++++++ .../Uri/Retrievers/PredefinedArrayTest.php | 40 +++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 src/JsonSchema/Uri/Retrievers/PredefinedArray.php create mode 100644 tests/JsonSchema/Tests/Uri/Retrievers/PredefinedArrayTest.php diff --git a/src/JsonSchema/Uri/Retrievers/PredefinedArray.php b/src/JsonSchema/Uri/Retrievers/PredefinedArray.php new file mode 100644 index 00000000..ed7c97b3 --- /dev/null +++ b/src/JsonSchema/Uri/Retrievers/PredefinedArray.php @@ -0,0 +1,59 @@ + '{ ... }', + * 'http://acme.com/schemas/address#' => '{ ... }', + * )) + * + * $schema = $retriever->retrieve('http://acme.com/schemas/person#'); + */ +class PredefinedArray implements UriRetrieverInterface +{ + private $schemas; + private $contentType; + + /** + * Constructor + * + * @param array $schemas + * @param string $contentType + */ + public function __construct(array $schemas, $contentType = Validator::SCHEMA_MEDIA_TYPE) + { + $this->schemas = $schemas; + $this->contentType = $contentType; + } + + /** + * {@inheritDoc} + */ + public function retrieve($uri) + { + if (!array_key_exists($uri, $this->schemas)) { + throw new ResourceNotFoundException(sprintf( + 'The JSON schema "%s" was not found.', + $uri + )); + } + + return $this->schemas[$uri]; + } + + /** + * {@inheritDoc} + */ + public function getContentType() + { + return $this->contentType; + } +} diff --git a/tests/JsonSchema/Tests/Uri/Retrievers/PredefinedArrayTest.php b/tests/JsonSchema/Tests/Uri/Retrievers/PredefinedArrayTest.php new file mode 100644 index 00000000..427e0efe --- /dev/null +++ b/tests/JsonSchema/Tests/Uri/Retrievers/PredefinedArrayTest.php @@ -0,0 +1,40 @@ +retriever = new PredefinedArray( + array( + 'http://acme.com/schemas/person#' => 'THE_PERSON_SCHEMA', + 'http://acme.com/schemas/address#' => 'THE_ADDRESS_SCHEMA', + ), + 'THE_CONTENT_TYPE' + ); + } + + public function testRetrieve() + { + $this->assertEquals('THE_PERSON_SCHEMA', $this->retriever->retrieve('http://acme.com/schemas/person#')); + $this->assertEquals('THE_ADDRESS_SCHEMA', $this->retriever->retrieve('http://acme.com/schemas/address#')); + } + + /** + * @expectedException JsonSchema\Exception\ResourceNotFoundException + */ + public function testRetrieveNonExistsingSchema() + { + $this->retriever->retrieve('http://acme.com/schemas/plop#'); + } + + public function testGetContentType() + { + $this->assertEquals('THE_CONTENT_TYPE', $this->retriever->getContentType()); + } +}