jenkins-bot has submitted this change and it was merged. Change subject: Upgrade justinrainbow/json-schema to 3.0.0 ......................................................................
Upgrade justinrainbow/json-schema to 3.0.0 mediawiki/core change: Ie37e2ebc48684783abf8d99d2f775ee6a5988da7 Bug: T141281 Change-Id: I5687286da9f7fa2bb2b84699fa43ab3c2547fe03 --- M composer.json M composer.lock M composer/autoload_classmap.php M composer/installed.json M justinrainbow/json-schema/LICENSE M justinrainbow/json-schema/README.md M justinrainbow/json-schema/bin/validate-json M justinrainbow/json-schema/composer.json M justinrainbow/json-schema/src/JsonSchema/Constraints/CollectionConstraint.php M justinrainbow/json-schema/src/JsonSchema/Constraints/Constraint.php M justinrainbow/json-schema/src/JsonSchema/Constraints/ConstraintInterface.php M justinrainbow/json-schema/src/JsonSchema/Constraints/EnumConstraint.php M justinrainbow/json-schema/src/JsonSchema/Constraints/Factory.php M justinrainbow/json-schema/src/JsonSchema/Constraints/FormatConstraint.php M justinrainbow/json-schema/src/JsonSchema/Constraints/NumberConstraint.php M justinrainbow/json-schema/src/JsonSchema/Constraints/ObjectConstraint.php M justinrainbow/json-schema/src/JsonSchema/Constraints/SchemaConstraint.php M justinrainbow/json-schema/src/JsonSchema/Constraints/StringConstraint.php A justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/LooseTypeCheck.php A justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/StrictTypeCheck.php A justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/TypeCheckInterface.php M justinrainbow/json-schema/src/JsonSchema/Constraints/TypeConstraint.php M justinrainbow/json-schema/src/JsonSchema/Constraints/UndefinedConstraint.php A justinrainbow/json-schema/src/JsonSchema/Entity/JsonPointer.php A justinrainbow/json-schema/src/JsonSchema/Exception/ExceptionInterface.php M justinrainbow/json-schema/src/JsonSchema/Exception/InvalidArgumentException.php M justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSchemaMediaTypeException.php M justinrainbow/json-schema/src/JsonSchema/Exception/JsonDecodingException.php M justinrainbow/json-schema/src/JsonSchema/Exception/ResourceNotFoundException.php A justinrainbow/json-schema/src/JsonSchema/Exception/RuntimeException.php A justinrainbow/json-schema/src/JsonSchema/Exception/UnresolvableJsonPointerException.php M justinrainbow/json-schema/src/JsonSchema/Exception/UriResolverException.php A justinrainbow/json-schema/src/JsonSchema/Iterator/ObjectIterator.php D justinrainbow/json-schema/src/JsonSchema/RefResolver.php A justinrainbow/json-schema/src/JsonSchema/Rfc3339.php A justinrainbow/json-schema/src/JsonSchema/SchemaStorage.php M justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/FileGetContents.php M justinrainbow/json-schema/src/JsonSchema/Uri/UriResolver.php M justinrainbow/json-schema/src/JsonSchema/Uri/UriRetriever.php A justinrainbow/json-schema/src/JsonSchema/UriResolverInterface.php A justinrainbow/json-schema/src/JsonSchema/UriRetrieverInterface.php M justinrainbow/json-schema/src/JsonSchema/Validator.php 42 files changed, 1,189 insertions(+), 722 deletions(-) Approvals: Legoktm: Looks good to me, approved jenkins-bot: Verified diff --git a/composer.json b/composer.json index 7b93d52..1016ecc 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ "composer/semver": "1.4.1", "cssjanus/cssjanus": "1.1.2", "firebase/php-jwt": "3.0.0", - "justinrainbow/json-schema": "1.6.1", + "justinrainbow/json-schema": "3.0.0", "liuggio/statsd-php-client": "1.0.18", "mediawiki/at-ease": "1.1.0", "monolog/monolog": "1.18.2", diff --git a/composer.lock b/composer.lock index d2e78bb..c6c2b85 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "734c21496ddd54f2917041bdc05df0b5", - "content-hash": "1ea81f4b4d9c4f4ca0977e563301ced4", + "hash": "1cc6a5de43fe526c49c5f85b20122f91", + "content-hash": "a98e839402d2e58bf05a4b999e86c14d", "packages": [ { "name": "composer/semver", @@ -149,25 +149,25 @@ }, { "name": "justinrainbow/json-schema", - "version": "1.6.1", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "cc84765fb7317f6b07bd8ac78364747f95b86341" + "reference": "12965831120bb18852e9865804a6089906323b8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/cc84765fb7317f6b07bd8ac78364747f95b86341", - "reference": "cc84765fb7317f6b07bd8ac78364747f95b86341", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/12965831120bb18852e9865804a6089906323b8e", + "reference": "12965831120bb18852e9865804a6089906323b8e", "shasum": "" }, "require": { - "php": ">=5.3.29" + "php": ">=5.3.3" }, "require-dev": { - "json-schema/json-schema-test-suite": "1.1.0", + "json-schema/json-schema-test-suite": "1.2.0", "phpdocumentor/phpdocumentor": "~2", - "phpunit/phpunit": "~3.7" + "phpunit/phpunit": "^4.8.22" }, "bin": [ "bin/validate-json" @@ -175,7 +175,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.6.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -185,7 +185,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { @@ -211,7 +211,7 @@ "json", "schema" ], - "time": "2016-01-25 15:43:01" + "time": "2016-08-15 16:39:11" }, { "name": "liuggio/statsd-php-client", diff --git a/composer/autoload_classmap.php b/composer/autoload_classmap.php index 3b5596c..db509a9 100644 --- a/composer/autoload_classmap.php +++ b/composer/autoload_classmap.php @@ -309,15 +309,26 @@ 'JsonSchema\\Constraints\\ObjectConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/ObjectConstraint.php', 'JsonSchema\\Constraints\\SchemaConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/SchemaConstraint.php', 'JsonSchema\\Constraints\\StringConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/StringConstraint.php', + 'JsonSchema\\Constraints\\TypeCheck\\LooseTypeCheck' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/LooseTypeCheck.php', + 'JsonSchema\\Constraints\\TypeCheck\\StrictTypeCheck' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/StrictTypeCheck.php', + 'JsonSchema\\Constraints\\TypeCheck\\TypeCheckInterface' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/TypeCheckInterface.php', 'JsonSchema\\Constraints\\TypeConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeConstraint.php', 'JsonSchema\\Constraints\\UndefinedConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/UndefinedConstraint.php', + 'JsonSchema\\Entity\\JsonPointer' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Entity/JsonPointer.php', + 'JsonSchema\\Exception\\ExceptionInterface' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Exception/ExceptionInterface.php', 'JsonSchema\\Exception\\InvalidArgumentException' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidArgumentException.php', 'JsonSchema\\Exception\\InvalidSchemaMediaTypeException' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSchemaMediaTypeException.php', 'JsonSchema\\Exception\\InvalidSourceUriException' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSourceUriException.php', 'JsonSchema\\Exception\\JsonDecodingException' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Exception/JsonDecodingException.php', 'JsonSchema\\Exception\\ResourceNotFoundException' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Exception/ResourceNotFoundException.php', + 'JsonSchema\\Exception\\RuntimeException' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Exception/RuntimeException.php', + 'JsonSchema\\Exception\\UnresolvableJsonPointerException' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Exception/UnresolvableJsonPointerException.php', 'JsonSchema\\Exception\\UriResolverException' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Exception/UriResolverException.php', - 'JsonSchema\\RefResolver' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/RefResolver.php', + 'JsonSchema\\Iterator\\ObjectIterator' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Iterator/ObjectIterator.php', + 'JsonSchema\\Rfc3339' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Rfc3339.php', + 'JsonSchema\\SchemaStorage' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/SchemaStorage.php', + 'JsonSchema\\UriResolverInterface' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/UriResolverInterface.php', + 'JsonSchema\\UriRetrieverInterface' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/UriRetrieverInterface.php', 'JsonSchema\\Uri\\Retrievers\\AbstractRetriever' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/AbstractRetriever.php', 'JsonSchema\\Uri\\Retrievers\\Curl' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/Curl.php', 'JsonSchema\\Uri\\Retrievers\\FileGetContents' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/FileGetContents.php', diff --git a/composer/installed.json b/composer/installed.json index 994c9c5..8d04c7a 100644 --- a/composer/installed.json +++ b/composer/installed.json @@ -1466,74 +1466,6 @@ "homepage": "https://www.mediawiki.org/wiki/HtmlFormatter" }, { - "name": "justinrainbow/json-schema", - "version": "1.6.1", - "version_normalized": "1.6.1.0", - "source": { - "type": "git", - "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "cc84765fb7317f6b07bd8ac78364747f95b86341" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/cc84765fb7317f6b07bd8ac78364747f95b86341", - "reference": "cc84765fb7317f6b07bd8ac78364747f95b86341", - "shasum": "" - }, - "require": { - "php": ">=5.3.29" - }, - "require-dev": { - "json-schema/json-schema-test-suite": "1.1.0", - "phpdocumentor/phpdocumentor": "~2", - "phpunit/phpunit": "~3.7" - }, - "time": "2016-01-25 15:43:01", - "bin": [ - "bin/validate-json" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.6.x-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "JsonSchema\\": "src/JsonSchema/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Bruno Prieto Reis", - "email": "bruno.p.r...@gmail.com" - }, - { - "name": "Justin Rainbow", - "email": "justin.rain...@gmail.com" - }, - { - "name": "Igor Wiedler", - "email": "i...@wiedler.ch" - }, - { - "name": "Robert Schönthal", - "email": "seros...@googlemail.com" - } - ], - "description": "A library to validate a json schema.", - "homepage": "https://github.com/justinrainbow/json-schema", - "keywords": [ - "json", - "schema" - ] - }, - { "name": "ruflin/elastica", "version": "3.1.1", "version_normalized": "3.1.1.0", @@ -1879,5 +1811,73 @@ ], "description": "Provides library of common widgets, layouts, and windows.", "homepage": "https://www.mediawiki.org/wiki/OOjs_UI" + }, + { + "name": "justinrainbow/json-schema", + "version": "3.0.0", + "version_normalized": "3.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "12965831120bb18852e9865804a6089906323b8e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/12965831120bb18852e9865804a6089906323b8e", + "reference": "12965831120bb18852e9865804a6089906323b8e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "json-schema/json-schema-test-suite": "1.2.0", + "phpdocumentor/phpdocumentor": "~2", + "phpunit/phpunit": "^4.8.22" + }, + "time": "2016-08-15 16:39:11", + "bin": [ + "bin/validate-json" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.r...@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rain...@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "i...@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seros...@googlemail.com" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ] } ] diff --git a/justinrainbow/json-schema/LICENSE b/justinrainbow/json-schema/LICENSE index 6210e7c..fa020fc 100644 --- a/justinrainbow/json-schema/LICENSE +++ b/justinrainbow/json-schema/LICENSE @@ -1,29 +1,21 @@ -Copyright (c) 2008, Gradua Networks -Author: Bruno Prieto Reis -All rights reserved. +MIT License +Copyright (c) 2016 -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * Neither the name of the Gradua Networks nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/justinrainbow/json-schema/README.md b/justinrainbow/json-schema/README.md index 3f9c58c..c0c9feb 100644 --- a/justinrainbow/json-schema/README.md +++ b/justinrainbow/json-schema/README.md @@ -19,26 +19,18 @@ #### [`Composer`](https://github.com/composer/composer) (*will use the Composer ClassLoader*) $ wget http://getcomposer.org/composer.phar - $ php composer.phar require justinrainbow/json-schema:~1.3 + $ php composer.phar require justinrainbow/json-schema:~2.0 ## Usage ```php <?php -// Get the schema and data as objects -$retriever = new JsonSchema\Uri\UriRetriever; -$schema = $retriever->retrieve('file://' . realpath('schema.json')); $data = json_decode(file_get_contents('data.json')); -// If you use $ref or if you are unsure, resolve those references here -// This modifies the $schema object -$refResolver = new JsonSchema\RefResolver($retriever); -$refResolver->resolve($schema, 'file://' . __DIR__); - // Validate -$validator = new JsonSchema\Validator(); -$validator->check($data, $schema); +$validator = new JsonSchema\Validator; +$validator->check($data, (object)['$ref' => 'file://' . realpath('schema.json')]); if ($validator->isValid()) { echo "The supplied JSON validates against the schema.\n"; diff --git a/justinrainbow/json-schema/bin/validate-json b/justinrainbow/json-schema/bin/validate-json index e93d53a..915b5a3 100755 --- a/justinrainbow/json-schema/bin/validate-json +++ b/justinrainbow/json-schema/bin/validate-json @@ -200,22 +200,14 @@ echo $urlSchema . "\n"; exit(); } - - $schema = $retriever->retrieve($urlSchema); - if ($schema === null) { - echo "Error loading JSON schema file\n"; - echo $urlSchema . "\n"; - showJsonError(); - exit(2); - } } catch (Exception $e) { echo "Error loading JSON schema file\n"; echo $urlSchema . "\n"; echo $e->getMessage() . "\n"; exit(2); } -$refResolver = new JsonSchema\RefResolver($retriever); -$refResolver->resolve($schema, $urlSchema); +$refResolver = new JsonSchema\RefResolver($retriever, $resolver); +$schema = $refResolver->resolve($urlSchema); if (isset($arOptions['--dump-schema'])) { $options = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; @@ -242,4 +234,3 @@ echo "Error code: " . $e->getCode() . "\n"; exit(24); } -?> diff --git a/justinrainbow/json-schema/composer.json b/justinrainbow/json-schema/composer.json index 68b3933..a6d7b42 100644 --- a/justinrainbow/json-schema/composer.json +++ b/justinrainbow/json-schema/composer.json @@ -4,7 +4,7 @@ "keywords": ["json", "schema"], "homepage": "https://github.com/justinrainbow/json-schema", "type": "library", - "license": "BSD-3-Clause", + "license": "MIT", "authors": [ { "name": "Bruno Prieto Reis", @@ -27,32 +27,32 @@ "type": "package", "package": { "name": "json-schema/JSON-Schema-Test-Suite", - "version": "1.1.0", + "version": "1.2.0", "source": { "url": "https://github.com/json-schema/JSON-Schema-Test-Suite", "type": "git", - "reference": "1.1.0" + "reference": "1.2.0" } } }], "require": { - "php": ">=5.3.29" + "php": ">=5.3.3" }, "require-dev": { - "json-schema/JSON-Schema-Test-Suite": "1.1.0", - "phpunit/phpunit": "~3.7", + "json-schema/JSON-Schema-Test-Suite": "1.2.0", + "phpunit/phpunit": "^4.8.22", "phpdocumentor/phpdocumentor": "~2" }, "autoload": { "psr-4": { "JsonSchema\\": "src/JsonSchema/" } }, "autoload-dev": { - "psr-4": { "JsonSchema\\Tests\\": "tests/JsonSchema/Tests/" } + "psr-4": { "JsonSchema\\Tests\\": "tests/" } }, "bin": ["bin/validate-json"], "extra": { "branch-alias": { - "dev-master": "1.6.x-dev" + "dev-master": "2.0.x-dev" } } } diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/CollectionConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/CollectionConstraint.php index b43bace..1b1554a 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Constraints/CollectionConstraint.php +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/CollectionConstraint.php @@ -9,6 +9,8 @@ namespace JsonSchema\Constraints; +use JsonSchema\Entity\JsonPointer; + /** * The CollectionConstraint Constraints, validates an array against a given schema * @@ -20,7 +22,7 @@ /** * {@inheritDoc} */ - public function check($value, $schema = null, $path = null, $i = null) + public function check($value, $schema = null, JsonPointer $path = null, $i = null) { // Verify minItems if (isset($schema->minItems) && count($value) < $schema->minItems) { @@ -52,12 +54,12 @@ /** * Validates the items * - * @param array $value - * @param \stdClass $schema - * @param string $path - * @param string $i + * @param array $value + * @param \stdClass $schema + * @param JsonPointer|null $path + * @param string $i */ - protected function validateItems($value, $schema = null, $path = null, $i = null) + protected function validateItems($value, $schema = null, JsonPointer $path = null, $i = null) { if (is_object($schema->items)) { // just one type definition for the whole array @@ -76,7 +78,7 @@ // Reset errors if needed if (isset($secondErrors) && count($secondErrors) < count($this->getErrors())) { $this->errors = $secondErrors; - } else if (isset($secondErrors) && count($secondErrors) === count($this->getErrors())) { + } elseif (isset($secondErrors) && count($secondErrors) === count($this->getErrors())) { $this->errors = $initErrors; } } @@ -102,7 +104,7 @@ } // Treat when we have more schema definitions than values, not for empty arrays - if(count($value) > 0) { + if (count($value) > 0) { for ($k = count($value); $k < count($schema->items); $k++) { $this->checkUndefined(new UndefinedConstraint(), $schema->items[$k], $path, $k); } diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Constraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Constraint.php index cb3ee80..521a0e0 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Constraints/Constraint.php +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Constraint.php @@ -9,7 +9,10 @@ namespace JsonSchema\Constraints; +use JsonSchema\SchemaStorage; use JsonSchema\Uri\UriRetriever; +use JsonSchema\UriRetrieverInterface; +use JsonSchema\Entity\JsonPointer; /** * The Base Constraints, all Validators should extend this class @@ -19,6 +22,7 @@ */ abstract class Constraint implements ConstraintInterface { + protected $schemaStorage; protected $checkMode = self::CHECK_MODE_NORMAL; protected $uriRetriever; protected $errors = array(); @@ -33,24 +37,29 @@ private $factory; /** - * @param int $checkMode - * @param UriRetriever $uriRetriever - * @param Factory $factory + * @param int $checkMode + * @param SchemaStorage $schemaStorage + * @param UriRetrieverInterface $uriRetriever + * @param Factory $factory */ - public function __construct($checkMode = self::CHECK_MODE_NORMAL, UriRetriever $uriRetriever = null, Factory $factory = null) - { - $this->checkMode = $checkMode; - $this->uriRetriever = $uriRetriever; - $this->factory = $factory; + public function __construct( + $checkMode = self::CHECK_MODE_NORMAL, + SchemaStorage $schemaStorage = null, + UriRetrieverInterface $uriRetriever = null, + Factory $factory = null + ) { + $this->checkMode = $checkMode; + $this->uriRetriever = $uriRetriever; + $this->factory = $factory; + $this->schemaStorage = $schemaStorage; } /** - * @return UriRetriever $uriRetriever + * @return UriRetrieverInterface $uriRetriever */ public function getUriRetriever() { - if (is_null($this->uriRetriever)) - { + if (is_null($this->uriRetriever)) { $this->setUriRetriever(new UriRetriever); } @@ -63,16 +72,28 @@ public function getFactory() { if (!$this->factory) { - $this->factory = new Factory($this->getUriRetriever()); + $this->factory = new Factory($this->getSchemaStorage(), $this->getUriRetriever(), $this->checkMode); } return $this->factory; } /** - * @param UriRetriever $uriRetriever + * @return SchemaStorage */ - public function setUriRetriever(UriRetriever $uriRetriever) + public function getSchemaStorage() + { + if (is_null($this->schemaStorage)) { + $this->schemaStorage = new SchemaStorage($this->getUriRetriever()); + } + + return $this->schemaStorage; + } + + /** + * @param UriRetrieverInterface $uriRetriever + */ + public function setUriRetriever(UriRetrieverInterface $uriRetriever) { $this->uriRetriever = $uriRetriever; } @@ -80,10 +101,11 @@ /** * {@inheritDoc} */ - public function addError($path, $message, $constraint='', array $more=null) + public function addError(JsonPointer $path = null, $message, $constraint='', array $more=null) { $error = array( - 'property' => $path, + 'property' => $this->convertJsonPointerIntoPropertyPath($path ?: new JsonPointer('')), + 'pointer' => ltrim(strval($path ?: new JsonPointer('')), '#'), 'message' => $message, 'constraint' => $constraint, ); @@ -132,37 +154,32 @@ /** * Bubble down the path * - * @param string $path Current path - * @param mixed $i What to append to the path + * @param JsonPointer|null $path Current path + * @param mixed $i What to append to the path * - * @return string + * @return JsonPointer; */ - protected function incrementPath($path, $i) + protected function incrementPath(JsonPointer $path = null, $i) { - if ($path !== '') { - if (is_int($i)) { - $path .= '[' . $i . ']'; - } elseif ($i == '') { - $path .= ''; - } else { - $path .= '.' . $i; - } - } else { - $path = $i; - } - + $path = $path ?: new JsonPointer(''); + $path = $path->withPropertyPaths( + array_merge( + $path->getPropertyPaths(), + array_filter(array($i), 'strlen') + ) + ); return $path; } /** * Validates an array * - * @param mixed $value - * @param mixed $schema - * @param mixed $path - * @param mixed $i + * @param mixed $value + * @param mixed $schema + * @param JsonPointer|null $path + * @param mixed $i */ - protected function checkArray($value, $schema = null, $path = null, $i = null) + protected function checkArray($value, $schema = null, JsonPointer $path = null, $i = null) { $validator = $this->getFactory()->createInstanceFor('collection'); $validator->check($value, $schema, $path, $i); @@ -173,13 +190,13 @@ /** * Validates an object * - * @param mixed $value - * @param mixed $schema - * @param mixed $path - * @param mixed $i - * @param mixed $patternProperties + * @param mixed $value + * @param mixed $schema + * @param JsonPointer|null $path + * @param mixed $i + * @param mixed $patternProperties */ - protected function checkObject($value, $schema = null, $path = null, $i = null, $patternProperties = null) + protected function checkObject($value, $schema = null, JsonPointer $path = null, $i = null, $patternProperties = null) { $validator = $this->getFactory()->createInstanceFor('object'); $validator->check($value, $schema, $path, $i, $patternProperties); @@ -190,12 +207,12 @@ /** * Validates the type of a property * - * @param mixed $value - * @param mixed $schema - * @param mixed $path - * @param mixed $i + * @param mixed $value + * @param mixed $schema + * @param JsonPointer|null $path + * @param mixed $i */ - protected function checkType($value, $schema = null, $path = null, $i = null) + protected function checkType($value, $schema = null, JsonPointer $path = null, $i = null) { $validator = $this->getFactory()->createInstanceFor('type'); $validator->check($value, $schema, $path, $i); @@ -206,15 +223,15 @@ /** * Checks a undefined element * - * @param mixed $value - * @param mixed $schema - * @param mixed $path - * @param mixed $i + * @param mixed $value + * @param mixed $schema + * @param JsonPointer|null $path + * @param mixed $i */ - protected function checkUndefined($value, $schema = null, $path = null, $i = null) + protected function checkUndefined($value, $schema = null, JsonPointer $path = null, $i = null) { $validator = $this->getFactory()->createInstanceFor('undefined'); - $validator->check($value, $schema, $path, $i); + $validator->check($value, $this->schemaStorage->resolveRefSchema($schema), $path, $i); $this->addErrors($validator->getErrors()); } @@ -222,12 +239,12 @@ /** * Checks a string element * - * @param mixed $value - * @param mixed $schema - * @param mixed $path - * @param mixed $i + * @param mixed $value + * @param mixed $schema + * @param JsonPointer|null $path + * @param mixed $i */ - protected function checkString($value, $schema = null, $path = null, $i = null) + protected function checkString($value, $schema = null, JsonPointer $path = null, $i = null) { $validator = $this->getFactory()->createInstanceFor('string'); $validator->check($value, $schema, $path, $i); @@ -238,12 +255,12 @@ /** * Checks a number element * - * @param mixed $value - * @param mixed $schema - * @param mixed $path - * @param mixed $i + * @param mixed $value + * @param mixed $schema + * @param JsonPointer $path + * @param mixed $i */ - protected function checkNumber($value, $schema = null, $path = null, $i = null) + protected function checkNumber($value, $schema = null, JsonPointer $path = null, $i = null) { $validator = $this->getFactory()->createInstanceFor('number'); $validator->check($value, $schema, $path, $i); @@ -254,12 +271,12 @@ /** * Checks a enum element * - * @param mixed $value - * @param mixed $schema - * @param mixed $path - * @param mixed $i + * @param mixed $value + * @param mixed $schema + * @param JsonPointer|null $path + * @param mixed $i */ - protected function checkEnum($value, $schema = null, $path = null, $i = null) + protected function checkEnum($value, $schema = null, JsonPointer $path = null, $i = null) { $validator = $this->getFactory()->createInstanceFor('enum'); $validator->check($value, $schema, $path, $i); @@ -267,7 +284,15 @@ $this->addErrors($validator->getErrors()); } - protected function checkFormat($value, $schema = null, $path = null, $i = null) + /** + * Checks format of an element + * + * @param mixed $value + * @param mixed $schema + * @param JsonPointer|null $path + * @param mixed $i + */ + protected function checkFormat($value, $schema = null, JsonPointer $path = null, $i = null) { $validator = $this->getFactory()->createInstanceFor('format'); $validator->check($value, $schema, $path, $i); @@ -276,16 +301,27 @@ } /** - * @param string $uri JSON Schema URI - * @return string JSON Schema contents + * Get the type check based on the set check mode. + * + * @return TypeCheck\TypeCheckInterface */ - protected function retrieveUri($uri) + protected function getTypeCheck() { - if (null === $this->uriRetriever) { - $this->setUriRetriever(new UriRetriever); - } - $jsonSchema = $this->uriRetriever->retrieve($uri); - // TODO validate using schema - return $jsonSchema; + return $this->getFactory()->getTypeCheck(); + } + + /** + * @param JsonPointer $pointer + * @return string property path + */ + protected function convertJsonPointerIntoPropertyPath(JsonPointer $pointer) + { + $result = array_map( + function($path) { + return sprintf(is_numeric($path) ? '[%d]' : '.%s', $path); + }, + $pointer->getPropertyPaths() + ); + return trim(implode('', $result), '.'); } } diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/ConstraintInterface.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/ConstraintInterface.php index 34280f4..ca12363 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Constraints/ConstraintInterface.php +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/ConstraintInterface.php @@ -9,6 +9,8 @@ namespace JsonSchema\Constraints; +use JsonSchema\Entity\JsonPointer; + /** * The Constraints Interface * @@ -33,12 +35,12 @@ /** * adds an error * - * @param string $path - * @param string $message - * @param string $constraint the constraint/rule that is broken, e.g.: 'minLength' - * @param array $more more array elements to add to the error + * @param JsonPointer|null $path + * @param string $message + * @param string $constraint the constraint/rule that is broken, e.g.: 'minLength' + * @param array $more more array elements to add to the error */ - public function addError($path, $message, $constraint='', array $more=null); + public function addError(JsonPointer $path = null, $message, $constraint='', array $more=null); /** * checks if the validator has not raised errors @@ -51,10 +53,11 @@ * invokes the validation of an element * * @abstract - * @param mixed $value - * @param mixed $schema - * @param mixed $path - * @param mixed $i + * @param mixed $value + * @param mixed $schema + * @param JsonPointer|null $path + * @param mixed $i + * @throws \JsonSchema\Exception\ExceptionInterface */ - public function check($value, $schema = null, $path = null, $i = null); + public function check($value, $schema = null, JsonPointer $path = null, $i = null); } \ No newline at end of file diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/EnumConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/EnumConstraint.php index df413e4..69fd030 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Constraints/EnumConstraint.php +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/EnumConstraint.php @@ -8,6 +8,8 @@ */ namespace JsonSchema\Constraints; +use JsonSchema\Validator; +use JsonSchema\Entity\JsonPointer; /** * The EnumConstraint Constraints, validates an element against a given set of possibilities @@ -20,27 +22,33 @@ /** * {@inheritDoc} */ - public function check($element, $schema = null, $path = null, $i = null) + public function check($element, $schema = null, JsonPointer $path = null, $i = null) { // Only validate enum if the attribute exists if ($element instanceof UndefinedConstraint && (!isset($schema->required) || !$schema->required)) { return; } + $type = gettype($element); foreach ($schema->enum as $enum) { - $type = gettype($element); + $enumType = gettype($enum); + if ($this->checkMode === self::CHECK_MODE_TYPE_CAST && $type == "array" && $enumType == "object") { + if ((object)$element == $enum) { + return; + } + } + if ($type === gettype($enum)) { if ($type == "object") { - if ($element == $enum) + if ($element == $enum) { return; - } else { - if ($element === $enum) - return; - + } + } elseif ($element === $enum) { + return; } } } - $this->addError($path, "Does not have a value in the enumeration " . print_r($schema->enum, true), 'enum', array('enum' => $schema->enum,)); + $this->addError($path, "Does not have a value in the enumeration " . json_encode($schema->enum), 'enum', array('enum' => $schema->enum,)); } } diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/Factory.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/Factory.php index 8cd25c1..bb12ece 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Constraints/Factory.php +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/Factory.php @@ -10,8 +10,9 @@ namespace JsonSchema\Constraints; use JsonSchema\Exception\InvalidArgumentException; +use JsonSchema\SchemaStorage; use JsonSchema\Uri\UriRetriever; -use JsonSchema\Validator; +use JsonSchema\UriRetrieverInterface; /** * Factory for centralize constraint initialization. @@ -19,9 +20,24 @@ class Factory { /** + * @var SchemaStorage + */ + protected $schemaStorage; + + /** * @var UriRetriever $uriRetriever */ protected $uriRetriever; + + /** + * @var int + */ + private $checkMode; + + /** + * @var TypeCheck\TypeCheckInterface[] + */ + private $typeCheck = array(); /** * @var array $constraintMap @@ -41,23 +57,42 @@ ); /** - * @param UriRetriever $uriRetriever + * @param SchemaStorage $schemaStorage + * @param UriRetrieverInterface $uriRetriever + * @param int $checkMode */ - public function __construct(UriRetriever $uriRetriever = null) - { - if (!$uriRetriever) { - $uriRetriever = new UriRetriever(); - } - - $this->uriRetriever = $uriRetriever; + public function __construct( + SchemaStorage $schemaStorage = null, + UriRetrieverInterface $uriRetriever = null, + $checkMode = Constraint::CHECK_MODE_NORMAL + ) { + $this->uriRetriever = $uriRetriever ?: new UriRetriever; + $this->schemaStorage = $schemaStorage ?: new SchemaStorage($this->uriRetriever); + $this->checkMode = $checkMode; } /** - * @return UriRetriever + * @return UriRetrieverInterface */ public function getUriRetriever() { return $this->uriRetriever; + } + + public function getSchemaStorage() + { + return $this->schemaStorage; + } + + public function getTypeCheck() + { + if (!isset($this->typeCheck[$this->checkMode])) { + $this->typeCheck[$this->checkMode] = $this->checkMode === Constraint::CHECK_MODE_TYPE_CAST + ? new TypeCheck\LooseTypeCheck + : new TypeCheck\StrictTypeCheck; + } + + return $this->typeCheck[$this->checkMode]; } /** @@ -67,16 +102,16 @@ */ public function setConstraintClass($name, $class) { - // Ensure class exists - if (!class_exists($class)) { - throw new InvalidArgumentException('Unknown constraint ' . $name); - } - // Ensure class is appropriate - if (!in_array('JsonSchema\Constraints\ConstraintInterface', class_implements($class))) { - throw new InvalidArgumentException('Invalid class ' . $name); - } - $this->constraintMap[$name] = $class; - return $this; + // Ensure class exists + if (!class_exists($class)) { + throw new InvalidArgumentException('Unknown constraint ' . $name); + } + // Ensure class is appropriate + if (!in_array('JsonSchema\Constraints\ConstraintInterface', class_implements($class))) { + throw new InvalidArgumentException('Invalid class ' . $name); + } + $this->constraintMap[$name] = $class; + return $this; } /** @@ -89,7 +124,12 @@ public function createInstanceFor($constraintName) { if (array_key_exists($constraintName, $this->constraintMap)) { - return new $this->constraintMap[$constraintName](Constraint::CHECK_MODE_NORMAL, $this->uriRetriever, $this); + return new $this->constraintMap[$constraintName]( + $this->checkMode, + $this->schemaStorage, + $this->uriRetriever, + $this + ); } throw new InvalidArgumentException('Unknown constraint ' . $constraintName); } diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/FormatConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/FormatConstraint.php index c789753..05d6de9 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Constraints/FormatConstraint.php +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/FormatConstraint.php @@ -9,6 +9,9 @@ namespace JsonSchema\Constraints; +use JsonSchema\Rfc3339; +use JsonSchema\Entity\JsonPointer; + /** * Validates against the "format" property * @@ -20,7 +23,7 @@ /** * {@inheritDoc} */ - public function check($element, $schema = null, $path = null, $i = null) + public function check($element, $schema = null, JsonPointer $path = null, $i = null) { if (!isset($schema->format)) { return; @@ -40,11 +43,7 @@ break; case 'date-time': - if (!$this->validateDateTime($element, 'Y-m-d\TH:i:s\Z') && - !$this->validateDateTime($element, 'Y-m-d\TH:i:s.u\Z') && - !$this->validateDateTime($element, 'Y-m-d\TH:i:sP') && - !$this->validateDateTime($element, 'Y-m-d\TH:i:sO') - ) { + if (null === Rfc3339::createFromString($element)) { $this->addError($path, sprintf('Invalid date-time %s, expected format YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DDThh:mm:ss+hh:mm', json_encode($element)), 'format', array('format' => $schema->format,)); } break; @@ -138,7 +137,7 @@ // which will fail the above string comparison because the passed // $datetime may be '2000-05-01T12:12:12.123Z' but format() will return // '2000-05-01T12:12:12.123000Z' - if ((strpos('u', $format) !== -1) && (intval($dt->format('u')) > 0)) { + if ((strpos('u', $format) !== -1) && (preg_match('/\.\d+Z$/', $datetime))) { return true; } @@ -176,6 +175,7 @@ protected function validateHostname($host) { - return preg_match('/^[_a-z]+\.([_a-z]+\.?)+$/i', $host); + $hostnameRegex = '/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/i'; + return preg_match($hostnameRegex, $host); } } diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/NumberConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/NumberConstraint.php index c5aaf6a..601b86a 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Constraints/NumberConstraint.php +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/NumberConstraint.php @@ -9,6 +9,8 @@ namespace JsonSchema\Constraints; +use JsonSchema\Entity\JsonPointer; + /** * The NumberConstraint Constraints, validates an number against a given schema * @@ -20,20 +22,20 @@ /** * {@inheritDoc} */ - public function check($element, $schema = null, $path = null, $i = null) + public function check($element, $schema = null, JsonPointer $path = null, $i = null) { // Verify minimum if (isset($schema->exclusiveMinimum)) { if (isset($schema->minimum)) { if ($schema->exclusiveMinimum && $element <= $schema->minimum) { $this->addError($path, "Must have a minimum value of " . $schema->minimum, 'exclusiveMinimum', array('minimum' => $schema->minimum,)); - } else if ($element < $schema->minimum) { + } elseif ($element < $schema->minimum) { $this->addError($path, "Must have a minimum value of " . $schema->minimum, 'minimum', array('minimum' => $schema->minimum,)); } } else { $this->addError($path, "Use of exclusiveMinimum requires presence of minimum", 'missingMinimum'); } - } else if (isset($schema->minimum) && $element < $schema->minimum) { + } elseif (isset($schema->minimum) && $element < $schema->minimum) { $this->addError($path, "Must have a minimum value of " . $schema->minimum, 'minimum', array('minimum' => $schema->minimum,)); } @@ -42,13 +44,13 @@ if (isset($schema->maximum)) { if ($schema->exclusiveMaximum && $element >= $schema->maximum) { $this->addError($path, "Must have a maximum value of " . $schema->maximum, 'exclusiveMaximum', array('maximum' => $schema->maximum,)); - } else if ($element > $schema->maximum) { + } elseif ($element > $schema->maximum) { $this->addError($path, "Must have a maximum value of " . $schema->maximum, 'maximum', array('maximum' => $schema->maximum,)); } } else { - $this->addError($path, "Use of exclusiveMaximum requires presence of maximum", 'missingMinimum'); + $this->addError($path, "Use of exclusiveMaximum requires presence of maximum", 'missingMaximum'); } - } else if (isset($schema->maximum) && $element > $schema->maximum) { + } elseif (isset($schema->maximum) && $element > $schema->maximum) { $this->addError($path, "Must have a maximum value of " . $schema->maximum, 'maximum', array('maximum' => $schema->maximum,)); } diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/ObjectConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/ObjectConstraint.php index 0e5cf1b..5a12b87 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Constraints/ObjectConstraint.php +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/ObjectConstraint.php @@ -9,6 +9,8 @@ namespace JsonSchema\Constraints; +use JsonSchema\Entity\JsonPointer; + /** * The ObjectConstraint Constraints, validates an object against a given schema * @@ -20,7 +22,7 @@ /** * {@inheritDoc} */ - function check($element, $definition = null, $path = null, $additionalProp = null, $patternProperties = null) + public function check($element, $definition = null, JsonPointer $path = null, $additionalProp = null, $patternProperties = null) { if ($element instanceof UndefinedConstraint) { return; @@ -40,7 +42,7 @@ $this->validateElement($element, $matches, $definition, $path, $additionalProp); } - public function validatePatternProperties($element, $path, $patternProperties) + public function validatePatternProperties($element, JsonPointer $path = null, $patternProperties) { $try = array('/','#','+','~','%'); $matches = array(); @@ -71,17 +73,16 @@ /** * Validates the element properties * - * @param \stdClass $element Element to validate - * @param array $matches Matches from patternProperties (if any) - * @param \stdClass $objectDefinition ObjectConstraint definition - * @param string $path Path to test? - * @param mixed $additionalProp Additional properties + * @param \stdClass $element Element to validate + * @param array $matches Matches from patternProperties (if any) + * @param \stdClass $objectDefinition ObjectConstraint definition + * @param JsonPointer|null $path Path to test? + * @param mixed $additionalProp Additional properties */ - public function validateElement($element, $matches, $objectDefinition = null, $path = null, $additionalProp = null) + public function validateElement($element, $matches, $objectDefinition = null, JsonPointer $path = null, $additionalProp = null) { + $this->validateMinMaxConstraint($element, $objectDefinition, $path); foreach ($element as $i => $value) { - - $property = $this->getProperty($element, $i, new UndefinedConstraint()); $definition = $this->getProperty($objectDefinition, $i); // no additional properties allowed @@ -108,20 +109,25 @@ // normal property verification $this->checkUndefined($value, new \stdClass(), $path, $i); } + + $property = $this->getProperty($element, $i, new UndefinedConstraint()); + if (is_object($property)) { + $this->validateMinMaxConstraint(!($property instanceof UndefinedConstraint) ? $property : $element, $definition, $path); + } } } /** * Validates the definition properties * - * @param \stdClass $element Element to validate - * @param \stdClass $objectDefinition ObjectConstraint definition - * @param string $path Path? + * @param \stdClass $element Element to validate + * @param \stdClass $objectDefinition ObjectConstraint definition + * @param JsoinPointer|null $path Path? */ - public function validateDefinition($element, $objectDefinition = null, $path = null) + public function validateDefinition($element, $objectDefinition = null, JsonPointer $path = null) { foreach ($objectDefinition as $i => $value) { - $property = $this->getProperty($element, $i, new UndefinedConstraint()); + $property = $this->getProperty($element, $i, $this->getFactory()->createInstanceFor('undefined')); $definition = $this->getProperty($objectDefinition, $i); $this->checkUndefined($property, $definition, $path, $i); } @@ -146,4 +152,26 @@ return $fallback; } + + /** + * validating minimum and maximum property constraints (if present) against an element + * + * @param \stdClass $element Element to validate + * @param \stdClass $objectDefinition ObjectConstraint definition + * @param JsonPointer|null $path Path to test? + */ + protected function validateMinMaxConstraint($element, $objectDefinition, JsonPointer $path = null) { + // Verify minimum number of properties + if (isset($objectDefinition->minProperties) && !is_object($objectDefinition->minProperties)) { + if ($this->getTypeCheck()->propertyCount($element) < $objectDefinition->minProperties) { + $this->addError($path, "Must contain a minimum of " . $objectDefinition->minProperties . " properties", 'minProperties', array('minProperties' => $objectDefinition->minProperties,)); + } + } + // Verify maximum number of properties + if (isset($objectDefinition->maxProperties) && !is_object($objectDefinition->maxProperties)) { + if ($this->getTypeCheck()->propertyCount($element) > $objectDefinition->maxProperties) { + $this->addError($path, "Must contain no more than " . $objectDefinition->maxProperties . " properties", 'maxProperties', array('maxProperties' => $objectDefinition->maxProperties,)); + } + } + } } diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/SchemaConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/SchemaConstraint.php index b856a11..071cd12 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Constraints/SchemaConstraint.php +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/SchemaConstraint.php @@ -10,6 +10,7 @@ namespace JsonSchema\Constraints; use JsonSchema\Exception\InvalidArgumentException; +use JsonSchema\Entity\JsonPointer; /** * The SchemaConstraint Constraints, validates an element against a given schema @@ -22,16 +23,20 @@ /** * {@inheritDoc} */ - public function check($element, $schema = null, $path = null, $i = null) + public function check($element, $schema = null, JsonPointer $path = null, $i = null) { if ($schema !== null) { // passed schema - $this->checkUndefined($element, $schema, '', ''); - } elseif (property_exists($element, $this->inlineSchemaProperty)) { + $this->checkUndefined($element, $schema, $path, $i); + } elseif ($this->getTypeCheck()->propertyExists($element, $this->inlineSchemaProperty)) { + $inlineSchema = $this->getTypeCheck()->propertyGet($element, $this->inlineSchemaProperty); + if (is_array($inlineSchema)) { + $inlineSchema = json_decode(json_encode($inlineSchema)); + } // inline schema - $this->checkUndefined($element, $element->{$this->inlineSchemaProperty}, '', ''); + $this->checkUndefined($element, $inlineSchema, $path, $i); } else { throw new InvalidArgumentException('no schema found to verify against'); } } -} \ No newline at end of file +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/StringConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/StringConstraint.php index f57f64c..44313cb 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Constraints/StringConstraint.php +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/StringConstraint.php @@ -9,6 +9,8 @@ namespace JsonSchema\Constraints; +use JsonSchema\Entity\JsonPointer; + /** * The StringConstraint Constraints, validates an string against a given schema * @@ -20,7 +22,7 @@ /** * {@inheritDoc} */ - public function check($element, $schema = null, $path = null, $i = null) + public function check($element, $schema = null, JsonPointer $path = null, $i = null) { // Verify maxLength if (isset($schema->maxLength) && $this->strlen($element) > $schema->maxLength) { diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/LooseTypeCheck.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/LooseTypeCheck.php new file mode 100644 index 0000000..7499c2e --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/LooseTypeCheck.php @@ -0,0 +1,59 @@ +<?php + +namespace JsonSchema\Constraints\TypeCheck; + +class LooseTypeCheck implements TypeCheckInterface +{ + public static function isObject($value) + { + return + is_object($value) || + (is_array($value) && (count($value) == 0 || self::isAssociativeArray($value))); + } + + public static function isArray($value) + { + return + is_array($value) && + (count($value) == 0 || !self::isAssociativeArray($value)); + } + + public static function propertyGet($value, $property) + { + if (is_object($value)) { + return $value->{$property}; + } + + return $value[$property]; + } + + public static function propertyExists($value, $property) + { + if (is_object($value)) { + return property_exists($value, $property); + } + + return array_key_exists($property, $value); + } + + public static function propertyCount($value) + { + if (is_object($value)) { + return count(get_object_vars($value)); + } + + return count($value); + } + + /** + * Check if the provided array is associative or not + * + * @param array $arr + * + * @return bool + */ + private static function isAssociativeArray($arr) + { + return (array_keys($arr) !== range(0, count($arr) - 1)); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/StrictTypeCheck.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/StrictTypeCheck.php new file mode 100644 index 0000000..73bbc38 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/StrictTypeCheck.php @@ -0,0 +1,31 @@ +<?php + +namespace JsonSchema\Constraints\TypeCheck; + +class StrictTypeCheck implements TypeCheckInterface +{ + public static function isObject($value) + { + return is_object($value); + } + + public static function isArray($value) + { + return is_array($value); + } + + public static function propertyGet($value, $property) + { + return $value->{$property}; + } + + public static function propertyExists($value, $property) + { + return property_exists($value, $property); + } + + public static function propertyCount($value) + { + return count(get_object_vars($value)); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/TypeCheckInterface.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/TypeCheckInterface.php new file mode 100644 index 0000000..5fd68ac --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeCheck/TypeCheckInterface.php @@ -0,0 +1,16 @@ +<?php + +namespace JsonSchema\Constraints\TypeCheck; + +interface TypeCheckInterface +{ + public static function isObject($value); + + public static function isArray($value); + + public static function propertyGet($value, $property); + + public static function propertyExists($value, $property); + + public static function propertyCount($value); +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeConstraint.php index 837cfd8..d082f6a 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeConstraint.php +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/TypeConstraint.php @@ -10,6 +10,7 @@ namespace JsonSchema\Constraints; use JsonSchema\Exception\InvalidArgumentException; +use JsonSchema\Entity\JsonPointer; use UnexpectedValueException as StandardUnexpectedValueException; /** @@ -38,51 +39,102 @@ /** * {@inheritDoc} */ - public function check($value = null, $schema = null, $path = null, $i = null) + public function check($value = null, $schema = null, JsonPointer $path = null, $i = null) { $type = isset($schema->type) ? $schema->type : null; - $isValid = true; + $isValid = false; + $wording = array(); if (is_array($type)) { - // @TODO refactor - $validatedOneType = false; - $errors = array(); - foreach ($type as $tp) { - $validator = new TypeConstraint($this->checkMode); - $subSchema = new \stdClass(); - $subSchema->type = $tp; - $validator->check($value, $subSchema, $path, null); - $error = $validator->getErrors(); - - if (!count($error)) { - $validatedOneType = true; - break; - } - - $errors = $error; - } - - if (!$validatedOneType) { - $this->addErrors($errors); - - return; - } + $this->validateTypesArray($value, $type, $wording, $isValid, $path); } elseif (is_object($type)) { $this->checkUndefined($value, $type, $path); + return; } else { $isValid = $this->validateType($value, $type); } if ($isValid === false) { - if (!isset(self::$wording[$type])) { - throw new StandardUnexpectedValueException( - sprintf( - "No wording for %s available, expected wordings are: [%s]", - var_export($type, true), - implode(', ', array_filter(self::$wording))) - ); + if (!is_array($type)) { + $this->validateTypeNameWording($type); + $wording[] = self::$wording[$type]; } - $this->addError($path, ucwords(gettype($value)) . " value found, but " . self::$wording[$type] . " is required", 'type'); + $this->addError($path, ucwords(gettype($value)) . " value found, but " . + $this->implodeWith($wording, ', ', 'or') . " is required", 'type'); + } + } + + /** + * Validates the given $value against the array of types in $type. Sets the value + * of $isValid to true, if at least one $type mateches the type of $value or the value + * passed as $isValid is already true. + * + * @param mixed $value Value to validate + * @param array $type TypeConstraints to check agains + * @param array $wording An array of wordings of the valid types of the array $type + * @param boolean $isValid The current validation value + */ + protected function validateTypesArray($value, array $type, &$validTypesWording, &$isValid, + $path) { + foreach ($type as $tp) { + // $tp can be an object, if it's a schema instead of a simple type, validate it + // with a new type constraint + if (is_object($tp)) { + if (!$isValid) { + $validator = $this->getFactory()->createInstanceFor('type'); + $subSchema = new \stdClass(); + $subSchema->type = $tp; + $validator->check($value, $subSchema, $path, null); + $error = $validator->getErrors(); + $isValid = !(bool)$error; + $validTypesWording[] = self::$wording['object']; + } + } else { + $this->validateTypeNameWording( $tp ); + $validTypesWording[] = self::$wording[$tp]; + if (!$isValid) { + $isValid = $this->validateType( $value, $tp ); + } + } + } + } + + /** + * Implodes the given array like implode() with turned around parameters and with the + * difference, that, if $listEnd isn't false, the last element delimiter is $listEnd instead of + * $delimiter. + * + * @param array $elements The elements to implode + * @param string $delimiter The delimiter to use + * @param bool $listEnd The last delimiter to use (defaults to $delimiter) + * @return string + */ + protected function implodeWith(array $elements, $delimiter = ', ', $listEnd = false) { + if ($listEnd === false || !isset($elements[1])) { + return implode(', ', $elements); + } + $lastElement = array_slice($elements, -1); + $firsElements = join(', ', array_slice($elements, 0, -1)); + $implodedElements = array_merge(array($firsElements), $lastElement); + return join(" $listEnd ", $implodedElements); + } + + /** + * Validates the given $type, if there's an associated self::$wording. If not, throws an + * exception. + * + * @param string $type The type to validate + * + * @throws StandardUnexpectedValueException + */ + protected function validateTypeNameWording( $type) { + if (!isset(self::$wording[$type])) { + throw new StandardUnexpectedValueException( + sprintf( + "No wording for %s available, expected wordings are: [%s]", + var_export($type, true), + implode(', ', array_filter(self::$wording))) + ); } } @@ -116,18 +168,17 @@ } if ('object' === $type) { - return is_object($value); - //return ($this::CHECK_MODE_TYPE_CAST == $this->checkMode) ? is_array($value) : is_object($value); + return $this->getTypeCheck()->isObject($value); } if ('array' === $type) { - return is_array($value); + return $this->getTypeCheck()->isArray($value); } if ('string' === $type) { return is_string($value); } - + if ('email' === $type) { return is_string($value); } diff --git a/justinrainbow/json-schema/src/JsonSchema/Constraints/UndefinedConstraint.php b/justinrainbow/json-schema/src/JsonSchema/Constraints/UndefinedConstraint.php index c033720..7815e24 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Constraints/UndefinedConstraint.php +++ b/justinrainbow/json-schema/src/JsonSchema/Constraints/UndefinedConstraint.php @@ -9,8 +9,8 @@ namespace JsonSchema\Constraints; -use JsonSchema\Exception\InvalidArgumentException; use JsonSchema\Uri\UriResolver; +use JsonSchema\Entity\JsonPointer; /** * The UndefinedConstraint Constraints @@ -23,21 +23,13 @@ /** * {@inheritDoc} */ - public function check($value, $schema = null, $path = null, $i = null) + public function check($value, $schema = null, JsonPointer $path = null, $i = null) { - if (is_null($schema)) { + if (is_null($schema) || !is_object($schema)) { return; } - if (!is_object($schema)) { - throw new InvalidArgumentException( - 'Given schema must be an object in ' . $path - . ' but is a ' . gettype($schema) - ); - } - - $i = is_null($i) ? "" : $i; - $path = $this->incrementPath($path, $i); + $path = $this->incrementPath($path ?: new JsonPointer(''), $i); // check special properties $this->validateCommonProperties($value, $schema, $path); @@ -52,23 +44,23 @@ /** * Validates the value against the types * - * @param mixed $value - * @param mixed $schema - * @param string $path - * @param string $i + * @param mixed $value + * @param mixed $schema + * @param JsonPointer $path + * @param string $i */ - public function validateTypes($value, $schema = null, $path = null, $i = null) + public function validateTypes($value, $schema = null, JsonPointer $path, $i = null) { // check array - if (is_array($value)) { + if ($this->getTypeCheck()->isArray($value)) { $this->checkArray($value, $schema, $path, $i); } // check object - if (is_object($value) && (isset($schema->properties) || isset($schema->patternProperties) || isset($schema->additionalProperties))) { + if ($this->getTypeCheck()->isObject($value)) { $this->checkObject( $value, - isset($schema->properties) ? $schema->properties : null, + isset($schema->properties) ? $schema->properties : $schema, $path, isset($schema->additionalProperties) ? $schema->additionalProperties : null, isset($schema->patternProperties) ? $schema->patternProperties : null @@ -94,12 +86,12 @@ /** * Validates common properties * - * @param mixed $value - * @param mixed $schema - * @param string $path - * @param string $i + * @param mixed $value + * @param mixed $schema + * @param JsonPointer $path + * @param string $i */ - protected function validateCommonProperties($value, $schema = null, $path = null, $i = "") + protected function validateCommonProperties($value, $schema = null, JsonPointer $path, $i = "") { // if it extends another schema, it must pass that schema as well if (isset($schema->extends)) { @@ -116,17 +108,21 @@ } // Verify required values - if (is_object($value)) { - if (!($value instanceof UndefinedConstraint) && isset($schema->required) && is_array($schema->required) ) { + if ($this->getTypeCheck()->isObject($value)) { + if (!($value instanceof UndefinedConstraint) && isset($schema->required) && is_array($schema->required)) { // Draft 4 - Required is an array of strings - e.g. "required": ["foo", ...] foreach ($schema->required as $required) { - if (!property_exists($value, $required)) { - $this->addError((!$path) ? $required : "$path.$required", "The property " . $required . " is required", 'required'); + if (!$this->getTypeCheck()->propertyExists($value, $required)) { + $this->addError( + $this->incrementPath($path ?: new JsonPointer(''), $required), + "The property " . $required . " is required", + 'required' + ); } } - } else if (isset($schema->required) && !is_array($schema->required)) { + } elseif (isset($schema->required) && !is_array($schema->required)) { // Draft 3 - Required attribute - e.g. "foo": {"type": "string", "required": true} - if ( $schema->required && $value instanceof UndefinedConstraint) { + if ($schema->required && $value instanceof UndefinedConstraint) { $this->addError($path, "Is missing and it is required", 'required'); } } @@ -165,22 +161,8 @@ } } - // Verify minimum and maximum number of properties - if (is_object($value)) { - if (isset($schema->minProperties)) { - if (count(get_object_vars($value)) < $schema->minProperties) { - $this->addError($path, "Must contain a minimum of " . $schema->minProperties . " properties", 'minProperties', array('minProperties' => $schema->minProperties,)); - } - } - if (isset($schema->maxProperties)) { - if (count(get_object_vars($value)) > $schema->maxProperties) { - $this->addError($path, "Must contain no more than " . $schema->maxProperties . " properties", 'maxProperties', array('maxProperties' => $schema->maxProperties,)); - } - } - } - // Verify that dependencies are met - if (is_object($value) && isset($schema->dependencies)) { + if (isset($schema->dependencies) && $this->getTypeCheck()->isObject($value)) { $this->validateDependencies($value, $schema->dependencies, $path); } } @@ -188,12 +170,12 @@ /** * Validate allOf, anyOf, and oneOf properties * - * @param mixed $value - * @param mixed $schema - * @param string $path - * @param string $i + * @param mixed $value + * @param mixed $schema + * @param JsonPointer $path + * @param string $i */ - protected function validateOfProperties($value, $schema, $path, $i = "") + protected function validateOfProperties($value, $schema, JsonPointer $path, $i = "") { // Verify type if ($value instanceof UndefinedConstraint) { @@ -242,17 +224,8 @@ $allErrors = array_merge($allErrors, array_values($this->getErrors())); } if ($matchedSchemas !== 1) { - $this->addErrors( - array_merge( - $allErrors, - array(array( - 'property' => $path, - 'message' => "Failed to match exactly one schema", - 'constraint' => 'oneOf', - ),), - $startErrors - ) - ); + $this->addErrors(array_merge($allErrors, $startErrors)); + $this->addError($path, "Failed to match exactly one schema", 'oneOf'); } else { $this->errors = $startErrors; } @@ -262,28 +235,28 @@ /** * Validate dependencies * - * @param mixed $value - * @param mixed $dependencies - * @param string $path - * @param string $i + * @param mixed $value + * @param mixed $dependencies + * @param JsonPointer $path + * @param string $i */ - protected function validateDependencies($value, $dependencies, $path, $i = "") + protected function validateDependencies($value, $dependencies, JsonPointer $path, $i = "") { foreach ($dependencies as $key => $dependency) { - if (property_exists($value, $key)) { + if ($this->getTypeCheck()->propertyExists($value, $key)) { if (is_string($dependency)) { // Draft 3 string is allowed - e.g. "dependencies": {"bar": "foo"} - if (!property_exists($value, $dependency)) { + if (!$this->getTypeCheck()->propertyExists($value, $dependency)) { $this->addError($path, "$key depends on $dependency and $dependency is missing", 'dependencies'); } - } else if (is_array($dependency)) { + } elseif (is_array($dependency)) { // Draft 4 must be an array - e.g. "dependencies": {"bar": ["foo"]} foreach ($dependency as $d) { - if (!property_exists($value, $d)) { + if (!$this->getTypeCheck()->propertyExists($value, $d)) { $this->addError($path, "$key depends on $d and $d is missing", 'dependencies'); } } - } else if (is_object($dependency)) { + } elseif (is_object($dependency)) { // Schema - e.g. "dependencies": {"bar": {"properties": {"foo": {...}}}} $this->checkUndefined($value, $dependency, $path, $i); } diff --git a/justinrainbow/json-schema/src/JsonSchema/Entity/JsonPointer.php b/justinrainbow/json-schema/src/JsonSchema/Entity/JsonPointer.php new file mode 100644 index 0000000..00e14e0 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Entity/JsonPointer.php @@ -0,0 +1,129 @@ +<?php + +/* + * This file is part of the JsonSchema package. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JsonSchema\Entity; + +/** + * @package JsonSchema\Entity + * @author Joost Nijhuis <jnijhui...@gmail.com> + */ +class JsonPointer +{ + /** @var string */ + private $filename; + + /** @var string[] */ + private $propertyPaths = array(); + + /** + * @param string $value + * @throws \InvalidArgumentException when $value is not a string + */ + public function __construct($value) + { + if (!is_string($value)) { + throw new \InvalidArgumentException('Ref value must be a string'); + } + + $splitRef = explode('#', $value, 2); + $this->filename = $splitRef[0]; + if (array_key_exists(1, $splitRef)) { + $this->propertyPaths = $this->decodePropertyPaths($splitRef[1]); + } + } + + /** + * @param string $propertyPathString + * @return string[] + */ + private function decodePropertyPaths($propertyPathString) + { + $paths = array(); + foreach (explode('/', trim($propertyPathString, '/')) as $path) { + $path = $this->decodePath($path); + if (is_string($path) && '' !== $path) { + $paths[] = $path; + } + } + + return $paths; + } + + /** + * @return array + */ + private function encodePropertyPaths() + { + return array_map( + array($this, 'encodePath'), + $this->getPropertyPaths() + ); + } + + /** + * @param string $path + * @return string + */ + private function decodePath($path) + { + return strtr($path, array('~1' => '/', '~0' => '~', '%25' => '%')); + } + + /** + * @param string $path + * @return string + */ + private function encodePath($path) + { + return strtr($path, array('/' => '~1', '~' => '~0', '%' => '%25')); + } + + /** + * @return string + */ + public function getFilename() + { + return $this->filename; + } + + /** + * @return string[] + */ + public function getPropertyPaths() + { + return $this->propertyPaths; + } + + /** + * @param array $propertyPaths + * @return JsonPointer + */ + public function withPropertyPaths(array $propertyPaths) + { + $new = clone $this; + $new->propertyPaths = $propertyPaths; + return $new; + } + + /** + * @return string + */ + public function getPropertyPathAsString() + { + return rtrim('#/' . implode('/', $this->encodePropertyPaths()), '/'); + } + + /** + * @return string + */ + public function __toString() + { + return $this->getFilename() . $this->getPropertyPathAsString(); + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Exception/ExceptionInterface.php b/justinrainbow/json-schema/src/JsonSchema/Exception/ExceptionInterface.php new file mode 100644 index 0000000..14d532a --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Exception/ExceptionInterface.php @@ -0,0 +1,8 @@ +<?php + +namespace JsonSchema\Exception; + +interface ExceptionInterface +{ + +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidArgumentException.php b/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidArgumentException.php index ec702a7..ac37135 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidArgumentException.php +++ b/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidArgumentException.php @@ -12,6 +12,6 @@ /** * Wrapper for the InvalidArgumentException */ -class InvalidArgumentException extends \InvalidArgumentException +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface { } \ No newline at end of file diff --git a/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSchemaMediaTypeException.php b/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSchemaMediaTypeException.php index 2e94677..93cc97e 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSchemaMediaTypeException.php +++ b/justinrainbow/json-schema/src/JsonSchema/Exception/InvalidSchemaMediaTypeException.php @@ -12,6 +12,6 @@ /** * Wrapper for the InvalidSchemaMediaType */ -class InvalidSchemaMediaTypeException extends \RuntimeException +class InvalidSchemaMediaTypeException extends RuntimeException { } \ No newline at end of file diff --git a/justinrainbow/json-schema/src/JsonSchema/Exception/JsonDecodingException.php b/justinrainbow/json-schema/src/JsonSchema/Exception/JsonDecodingException.php index 2c22881..a755626 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Exception/JsonDecodingException.php +++ b/justinrainbow/json-schema/src/JsonSchema/Exception/JsonDecodingException.php @@ -12,7 +12,7 @@ /** * Wrapper for the JsonDecodingException */ -class JsonDecodingException extends \RuntimeException +class JsonDecodingException extends RuntimeException { public function __construct($code = JSON_ERROR_NONE, \Exception $previous = null) { diff --git a/justinrainbow/json-schema/src/JsonSchema/Exception/ResourceNotFoundException.php b/justinrainbow/json-schema/src/JsonSchema/Exception/ResourceNotFoundException.php index 87b4ac1..3341a90 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Exception/ResourceNotFoundException.php +++ b/justinrainbow/json-schema/src/JsonSchema/Exception/ResourceNotFoundException.php @@ -12,6 +12,6 @@ /** * Wrapper for the ResourceNotFoundException */ -class ResourceNotFoundException extends \RuntimeException +class ResourceNotFoundException extends RuntimeException { } \ No newline at end of file diff --git a/justinrainbow/json-schema/src/JsonSchema/Exception/RuntimeException.php b/justinrainbow/json-schema/src/JsonSchema/Exception/RuntimeException.php new file mode 100644 index 0000000..3a4d3f9 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Exception/RuntimeException.php @@ -0,0 +1,17 @@ +<?php + +/* + * This file is part of the JsonSchema package. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JsonSchema\Exception; + +/** + * Wrapper for the RuntimeException + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} \ No newline at end of file diff --git a/justinrainbow/json-schema/src/JsonSchema/Exception/UnresolvableJsonPointerException.php b/justinrainbow/json-schema/src/JsonSchema/Exception/UnresolvableJsonPointerException.php new file mode 100644 index 0000000..44c562e --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Exception/UnresolvableJsonPointerException.php @@ -0,0 +1,18 @@ +<?php + +/* + * This file is part of the JsonSchema package. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JsonSchema\Exception; + +/** + * @package JsonSchema\Exception + * @author Joost Nijhuis <jnijhui...@gmail.com> + */ +class UnresolvableJsonPointerException extends InvalidArgumentException +{ +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Exception/UriResolverException.php b/justinrainbow/json-schema/src/JsonSchema/Exception/UriResolverException.php index 6d0b094..f9e7f28 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Exception/UriResolverException.php +++ b/justinrainbow/json-schema/src/JsonSchema/Exception/UriResolverException.php @@ -12,6 +12,6 @@ /** * Wrapper for the UriResolverException */ -class UriResolverException extends \RuntimeException +class UriResolverException extends RuntimeException { } \ No newline at end of file diff --git a/justinrainbow/json-schema/src/JsonSchema/Iterator/ObjectIterator.php b/justinrainbow/json-schema/src/JsonSchema/Iterator/ObjectIterator.php new file mode 100644 index 0000000..61bf5be --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Iterator/ObjectIterator.php @@ -0,0 +1,147 @@ +<?php + +/* + * This file is part of the JsonSchema package. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JsonSchema\Iterator; + +/** + * @package JsonSchema\Iterator + * @author Joost Nijhuis <jnijhui...@gmail.com> + */ +class ObjectIterator implements \Iterator, \Countable +{ + /** @var object */ + private $object; + + /** @var int */ + private $position = 0; + + /** @var array */ + private $data = array(); + + /** @var bool */ + private $initialized = false; + + /** + * @param object $object + */ + public function __construct($object) + { + $this->object = $object; + } + + /** + * {@inheritdoc} + */ + public function current() + { + $this->initialize(); + + return $this->data[$this->position]; + } + + /** + * {@inheritdoc} + */ + public function next() + { + $this->initialize(); + $this->position++; + } + + /** + * {@inheritdoc} + */ + public function key() + { + $this->initialize(); + + return $this->position; + } + + /** + * {@inheritdoc} + */ + public function valid() + { + $this->initialize(); + + return isset($this->data[$this->position]); + } + + /** + * {@inheritdoc} + */ + public function rewind() + { + $this->initialize(); + $this->position = 0; + } + + /** + * {@inheritdoc} + */ + public function count() + { + $this->initialize(); + + return count($this->data); + } + + /** + * Initializer + */ + private function initialize() + { + if (!$this->initialized) { + $this->data = $this->buildDataFromObject($this->object); + $this->initialized = true; + } + } + + /** + * @param object $object + * @return array + */ + private function buildDataFromObject($object) + { + $result = array(); + + $stack = new \SplStack(); + $stack->push($object); + + while (!$stack->isEmpty()) { + + $current = $stack->pop(); + if (is_object($current)) { + array_push($result, $current); + } + + foreach ($this->getDataFromItem($current) as $propertyName => $propertyValue) { + if (is_object($propertyValue) || is_array($propertyValue)) { + $stack->push($propertyValue); + } + } + } + + return $result; + } + + /** + * @param object|array $item + * @return array + */ + private function getDataFromItem($item) + { + if (!is_object($item) && !is_array($item)) { + return array(); + } + + return is_object($item) ? get_object_vars($item) : $item; + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/RefResolver.php b/justinrainbow/json-schema/src/JsonSchema/RefResolver.php deleted file mode 100644 index ea0fafd..0000000 --- a/justinrainbow/json-schema/src/JsonSchema/RefResolver.php +++ /dev/null @@ -1,277 +0,0 @@ -<?php - -/* - * This file is part of the JsonSchema package. - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace JsonSchema; - -use JsonSchema\Exception\JsonDecodingException; -use JsonSchema\Uri\Retrievers\UriRetrieverInterface; -use JsonSchema\Uri\UriRetriever; - -/** - * Take in an object that's a JSON schema and take care of all $ref references - * - * @author Tyler Akins <fid...@rumkin.com> - * @see README.md - */ -class RefResolver -{ - /** - * HACK to prevent too many recursive expansions. - * Happens e.g. when you want to validate a schema against the schema - * definition. - * - * @var integer - */ - protected static $depth = 0; - - /** - * maximum references depth - * @var integer - */ - public static $maxDepth = 7; - - /** - * @var UriRetrieverInterface - */ - protected $uriRetriever = null; - - /** - * @var object - */ - protected $rootSchema = null; - - /** - * @param UriRetriever $retriever - */ - public function __construct($retriever = null) - { - $this->uriRetriever = $retriever; - } - - /** - * Retrieves a given schema given a ref and a source URI - * - * @param string $ref Reference from schema - * @param string $sourceUri URI where original schema was located - * @return object Schema - */ - public function fetchRef($ref, $sourceUri) - { - $retriever = $this->getUriRetriever(); - $jsonSchema = $retriever->retrieve($ref, $sourceUri); - $this->resolve($jsonSchema); - - return $jsonSchema; - } - - /** - * Return the URI Retriever, defaulting to making a new one if one - * was not yet set. - * - * @return UriRetriever - */ - public function getUriRetriever() - { - if (is_null($this->uriRetriever)) { - $this->setUriRetriever(new UriRetriever); - } - - return $this->uriRetriever; - } - - /** - * Resolves all $ref references for a given schema. Recurses through - * the object to resolve references of any child schemas. - * - * The 'format' property is omitted because it isn't required for - * validation. Theoretically, this class could be extended to look - * for URIs in formats: "These custom formats MAY be expressed as - * an URI, and this URI MAY reference a schema of that format." - * - * The 'id' property is not filled in, but that could be made to happen. - * - * @param object $schema JSON Schema to flesh out - * @param string $sourceUri URI where this schema was located - */ - public function resolve($schema, $sourceUri = null) - { - if (self::$depth > self::$maxDepth) { - self::$depth = 0; - throw new JsonDecodingException(JSON_ERROR_DEPTH); - } - ++self::$depth; - - if (! is_object($schema)) { - --self::$depth; - return; - } - - if (null === $sourceUri && ! empty($schema->id)) { - $sourceUri = $schema->id; - } - - if (null === $this->rootSchema) { - $this->rootSchema = $schema; - } - - // Resolve $ref first - $this->resolveRef($schema, $sourceUri); - - // These properties are just schemas - // eg. items can be a schema or an array of schemas - foreach (array('additionalItems', 'additionalProperties', 'extends', 'items') as $propertyName) { - $this->resolveProperty($schema, $propertyName, $sourceUri); - } - - // These are all potentially arrays that contain schema objects - // eg. type can be a value or an array of values/schemas - // eg. items can be a schema or an array of schemas - foreach (array('disallow', 'extends', 'items', 'type', 'allOf', 'anyOf', 'oneOf') as $propertyName) { - $this->resolveArrayOfSchemas($schema, $propertyName, $sourceUri); - } - - // These are all objects containing properties whose values are schemas - foreach (array('dependencies', 'patternProperties', 'properties') as $propertyName) { - $this->resolveObjectOfSchemas($schema, $propertyName, $sourceUri); - } - - --self::$depth; - } - - /** - * Given an object and a property name, that property should be an - * array whose values can be schemas. - * - * @param object $schema JSON Schema to flesh out - * @param string $propertyName Property to work on - * @param string $sourceUri URI where this schema was located - */ - public function resolveArrayOfSchemas($schema, $propertyName, $sourceUri) - { - if (! isset($schema->$propertyName) || ! is_array($schema->$propertyName)) { - return; - } - - foreach ($schema->$propertyName as $possiblySchema) { - $this->resolve($possiblySchema, $sourceUri); - } - } - - /** - * Given an object and a property name, that property should be an - * object whose properties are schema objects. - * - * @param object $schema JSON Schema to flesh out - * @param string $propertyName Property to work on - * @param string $sourceUri URI where this schema was located - */ - public function resolveObjectOfSchemas($schema, $propertyName, $sourceUri) - { - if (! isset($schema->$propertyName) || ! is_object($schema->$propertyName)) { - return; - } - - foreach (get_object_vars($schema->$propertyName) as $possiblySchema) { - $this->resolve($possiblySchema, $sourceUri); - } - } - - /** - * Given an object and a property name, that property should be a - * schema object. - * - * @param object $schema JSON Schema to flesh out - * @param string $propertyName Property to work on - * @param string $sourceUri URI where this schema was located - */ - public function resolveProperty($schema, $propertyName, $sourceUri) - { - if (! isset($schema->$propertyName)) { - return; - } - - $this->resolve($schema->$propertyName, $sourceUri); - } - - /** - * Look for the $ref property in the object. If found, remove the - * reference and augment this object with the contents of another - * schema. - * - * @param object $schema JSON Schema to flesh out - * @param string $sourceUri URI where this schema was located - */ - public function resolveRef($schema, $sourceUri) - { - $ref = '$ref'; - - if (empty($schema->$ref)) { - return; - } - - $splitRef = explode('#', $schema->$ref, 2); - - $refDoc = $splitRef[0]; - $refPath = null; - if (count($splitRef) === 2) { - $refPath = explode('/', $splitRef[1]); - array_shift($refPath); - } - - if (empty($refDoc) && empty($refPath)) { - // TODO: Not yet implemented - root pointer ref, causes recursion issues - return; - } - - if (!empty($refDoc)) { - $refSchema = $this->fetchRef($refDoc, $sourceUri); - } else { - $refSchema = $this->rootSchema; - } - - if (null !== $refPath) { - $refSchema = $this->resolveRefSegment($refSchema, $refPath); - } - - unset($schema->$ref); - - // Augment the current $schema object with properties fetched - foreach (get_object_vars($refSchema) as $prop => $value) { - $schema->$prop = $value; - } - } - - /** - * Set URI Retriever for use with the Ref Resolver - * - * @param UriRetriever $retriever - * @return $this for chaining - */ - public function setUriRetriever(UriRetriever $retriever) - { - $this->uriRetriever = $retriever; - - return $this; - } - - protected function resolveRefSegment($data, $pathParts) - { - foreach ($pathParts as $path) { - $path = strtr($path, array('~1' => '/', '~0' => '~', '%25' => '%')); - - if (is_array($data)) { - $data = $data[$path]; - } else { - $data = $data->{$path}; - } - } - - return $data; - } -} diff --git a/justinrainbow/json-schema/src/JsonSchema/Rfc3339.php b/justinrainbow/json-schema/src/JsonSchema/Rfc3339.php new file mode 100644 index 0000000..5afdaed --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/Rfc3339.php @@ -0,0 +1,29 @@ +<?php + +namespace JsonSchema; + +class Rfc3339 +{ + const REGEX = '/(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(\.\d+)?(Z|([+-]\d{2}):?(\d{2}))/'; + + /** + * Try creating a DateTime instance + * + * @param string $string + * @return \DateTime|null + */ + public static function createFromString($string) + { + if (!preg_match(self::REGEX, strtoupper($string), $matches)) { + return null; + } + + $dateAndTime = $matches[1]; + $microseconds = $matches[2] ?: '.000000'; + $timeZone = 'Z' !== $matches[3] ? $matches[4] . ':' . $matches[5] : '+00:00'; + + $dateTime = \DateTime::createFromFormat('Y-m-d\TH:i:s.uP', $dateAndTime . $microseconds . $timeZone, new \DateTimeZone('UTC')); + + return $dateTime ?: null; + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/SchemaStorage.php b/justinrainbow/json-schema/src/JsonSchema/SchemaStorage.php new file mode 100644 index 0000000..97f88eb --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/SchemaStorage.php @@ -0,0 +1,109 @@ +<?php + +namespace JsonSchema; + +use JsonSchema\Entity\JsonPointer; +use JsonSchema\Exception\UnresolvableJsonPointerException; +use JsonSchema\Iterator\ObjectIterator; +use JsonSchema\Uri\UriResolver; +use JsonSchema\Uri\UriRetriever; + +class SchemaStorage +{ + protected $uriRetriever; + protected $uriResolver; + protected $schemas = array(); + + public function __construct( + UriRetrieverInterface $uriRetriever = null, + UriResolverInterface $uriResolver = null + ) { + $this->uriRetriever = $uriRetriever ?: new UriRetriever; + $this->uriResolver = $uriResolver ?: new UriResolver; + } + + /** + * @return UriRetrieverInterface + */ + public function getUriRetriever() + { + return $this->uriRetriever; + } + + /** + * @return UriResolverInterface + */ + public function getUriResolver() + { + return $this->uriResolver; + } + + /** + * @param string $id + * @param object $schema + */ + public function addSchema($id, $schema = null) + { + if (is_null($schema)) { + $schema = $this->uriRetriever->retrieve($id); + } + $objectIterator = new ObjectIterator($schema); + foreach ($objectIterator as $toResolveSchema) { + if (property_exists($toResolveSchema, '$ref') && is_string($toResolveSchema->{'$ref'})) { + $jsonPointer = new JsonPointer($this->uriResolver->resolve($toResolveSchema->{'$ref'}, $id)); + $toResolveSchema->{'$ref'} = (string)$jsonPointer; + } + } + $this->schemas[$id] = $schema; + } + + /** + * @param string $id + * @return object + */ + public function getSchema($id) + { + if (!array_key_exists($id, $this->schemas)) { + $this->addSchema($id); + } + + return $this->schemas[$id]; + } + + public function resolveRef($ref) + { + $jsonPointer = new JsonPointer($ref); + $refSchema = $this->getSchema($jsonPointer->getFilename()); + + foreach ($jsonPointer->getPropertyPaths() as $path) { + if (is_object($refSchema) && property_exists($refSchema, $path)) { + $refSchema = $this->resolveRefSchema($refSchema->{$path}); + } elseif (is_array($refSchema) && array_key_exists($path, $refSchema)) { + $refSchema = $this->resolveRefSchema($refSchema[$path]); + } else { + throw new UnresolvableJsonPointerException(sprintf( + 'File: %s is found, but could not resolve fragment: %s', + $jsonPointer->getFilename(), + $jsonPointer->getPropertyPathAsString() + )); + } + } + + return $refSchema; + } + + /** + * @param $refSchema + * @return object + */ + public function resolveRefSchema($refSchema) + { + if (is_object($refSchema) && property_exists($refSchema, '$ref')) { + $newSchema = $this->resolveRef($refSchema->{'$ref'}); + $refSchema = (object) (get_object_vars($refSchema) + get_object_vars($newSchema)); + unset($refSchema->{'$ref'}); + } + + return $refSchema; + } +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/FileGetContents.php b/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/FileGetContents.php index bc43de6..0c4d54a 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/FileGetContents.php +++ b/justinrainbow/json-schema/src/JsonSchema/Uri/Retrievers/FileGetContents.php @@ -14,51 +14,51 @@ /** * Tries to retrieve JSON schemas from a URI using file_get_contents() - * - * @author Sander Coolen <san...@jibber.nl> + * + * @author Sander Coolen <san...@jibber.nl> */ class FileGetContents extends AbstractRetriever { protected $messageBody; - + /** * {@inheritDoc} * @see \JsonSchema\Uri\Retrievers\UriRetrieverInterface::retrieve() */ public function retrieve($uri) { - $context = stream_context_create(array( - 'http' => array( - 'method' => 'GET', - 'header' => "Accept: " . Validator::SCHEMA_MEDIA_TYPE - ))); - - set_error_handler(function() use ($uri) { - throw new ResourceNotFoundException('JSON schema not found at ' . $uri); + $errorMessage = null; + set_error_handler(function ($errno, $errstr) use (&$errorMessage) { + $errorMessage = $errstr; }); $response = file_get_contents($uri); restore_error_handler(); - if (false === $response) { - throw new ResourceNotFoundException('JSON schema not found at ' . $uri); + if ($errorMessage) { + throw new ResourceNotFoundException($errorMessage); } + + if (false === $response) { + throw new ResourceNotFoundException('JSON schema not found at '.$uri); + } + if ($response == '' && substr($uri, 0, 7) == 'file://' && substr($uri, -1) == '/' ) { - throw new ResourceNotFoundException('JSON schema not found at ' . $uri); + throw new ResourceNotFoundException('JSON schema not found at '.$uri); } $this->messageBody = $response; - if (! empty($http_response_header)) { + if (!empty($http_response_header)) { $this->fetchContentType($http_response_header); } else { // Could be a "file://" url or something else - fake up the response $this->contentType = null; } - + return $this->messageBody; } - + /** * @param array $headers HTTP Response Headers * @return boolean Whether the Content-Type header was found or not @@ -70,10 +70,10 @@ return true; } } - + return false; } - + /** * @param string $header * @return string|null diff --git a/justinrainbow/json-schema/src/JsonSchema/Uri/UriResolver.php b/justinrainbow/json-schema/src/JsonSchema/Uri/UriResolver.php index 9784114..a4a6323 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Uri/UriResolver.php +++ b/justinrainbow/json-schema/src/JsonSchema/Uri/UriResolver.php @@ -10,13 +10,14 @@ namespace JsonSchema\Uri; use JsonSchema\Exception\UriResolverException; +use JsonSchema\UriResolverInterface; /** * Resolves JSON Schema URIs * * @author Sander Coolen <san...@jibber.nl> */ -class UriResolver +class UriResolver implements UriResolverInterface { /** * Parses a URI into five main components @@ -69,11 +70,7 @@ } /** - * Resolves a URI - * - * @param string $uri Absolute or relative - * @param string $baseUri Optional base URI - * @return string Absolute URI + * {@inheritdoc} */ public function resolve($uri, $baseUri = null) { diff --git a/justinrainbow/json-schema/src/JsonSchema/Uri/UriRetriever.php b/justinrainbow/json-schema/src/JsonSchema/Uri/UriRetriever.php index c723cd9..b2445a7 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Uri/UriRetriever.php +++ b/justinrainbow/json-schema/src/JsonSchema/Uri/UriRetriever.php @@ -11,6 +11,7 @@ use JsonSchema\Uri\Retrievers\FileGetContents; use JsonSchema\Uri\Retrievers\UriRetrieverInterface; +use JsonSchema\UriRetrieverInterface as BaseUriRetrieverInterface; use JsonSchema\Validator; use JsonSchema\Exception\InvalidSchemaMediaTypeException; use JsonSchema\Exception\JsonDecodingException; @@ -21,7 +22,7 @@ * * @author Tyler Akins <fid...@rumkin.com> */ -class UriRetriever +class UriRetriever implements BaseUriRetrieverInterface { /** * @var null|UriRetrieverInterface @@ -50,7 +51,7 @@ return; } - if (Validator::SCHEMA_MEDIA_TYPE === $contentType) { + if (in_array($contentType, array(Validator::SCHEMA_MEDIA_TYPE, 'application/json'))) { return; } @@ -128,11 +129,7 @@ } /** - * Retrieve a URI - * - * @param string $uri JSON Schema URI - * @param string|null $baseUri - * @return object JSON Schema contents + * {@inheritdoc} */ public function retrieve($uri, $baseUri = null) { diff --git a/justinrainbow/json-schema/src/JsonSchema/UriResolverInterface.php b/justinrainbow/json-schema/src/JsonSchema/UriResolverInterface.php new file mode 100644 index 0000000..1386c18 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/UriResolverInterface.php @@ -0,0 +1,25 @@ +<?php + +/* + * This file is part of the JsonSchema package. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JsonSchema; + +/** + * @package JsonSchema + */ +interface UriResolverInterface +{ + /** + * Resolves a URI + * + * @param string $uri Absolute or relative + * @param null|string $baseUri Optional base URI + * @return string Absolute URI + */ + public function resolve($uri, $baseUri = null); +} diff --git a/justinrainbow/json-schema/src/JsonSchema/UriRetrieverInterface.php b/justinrainbow/json-schema/src/JsonSchema/UriRetrieverInterface.php new file mode 100644 index 0000000..3b683f9 --- /dev/null +++ b/justinrainbow/json-schema/src/JsonSchema/UriRetrieverInterface.php @@ -0,0 +1,25 @@ +<?php + +/* + * This file is part of the JsonSchema package. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JsonSchema; + +/** + * @package JsonSchema + */ +interface UriRetrieverInterface +{ + /** + * Retrieve a URI + * + * @param string $uri JSON Schema URI + * @param null|string $baseUri + * @return object JSON Schema contents + */ + public function retrieve($uri, $baseUri = null); +} diff --git a/justinrainbow/json-schema/src/JsonSchema/Validator.php b/justinrainbow/json-schema/src/JsonSchema/Validator.php index 14dbb60..b627d78 100644 --- a/justinrainbow/json-schema/src/JsonSchema/Validator.php +++ b/justinrainbow/json-schema/src/JsonSchema/Validator.php @@ -11,6 +11,7 @@ use JsonSchema\Constraints\SchemaConstraint; use JsonSchema\Constraints\Constraint; +use JsonSchema\Entity\JsonPointer; /** * A JsonSchema Constraint @@ -30,7 +31,7 @@ * * {@inheritDoc} */ - public function check($value, $schema = null, $path = null, $i = null) + public function check($value, $schema = null, JsonPointer $path = null, $i = null) { $validator = $this->getFactory()->createInstanceFor('schema'); $validator->check($value, $schema); -- To view, visit https://gerrit.wikimedia.org/r/305360 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I5687286da9f7fa2bb2b84699fa43ab3c2547fe03 Gerrit-PatchSet: 3 Gerrit-Project: mediawiki/vendor Gerrit-Branch: master Gerrit-Owner: Florianschmidtwelzow <florian.schmidt.stargatewis...@gmail.com> Gerrit-Reviewer: Legoktm <legoktm.wikipe...@gmail.com> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits