pax_global_header00006660000000000000000000000064135440073770014524gustar00rootroot0000000000000052 comment=556b9a947022314bf389e68ba5ef4cda6e934606 php-json-schema-5.2.9/000077500000000000000000000000001354400737700145355ustar00rootroot00000000000000php-json-schema-5.2.9/.gitignore000066400000000000000000000002011354400737700165160ustar00rootroot00000000000000/vendor /bin !/bin/validate-json coverage .buildpath .project .settings .php_cs .php_cs.cache composer.lock docs-api phpunit.xml php-json-schema-5.2.9/.php_cs.dist000066400000000000000000000017251354400737700167610ustar00rootroot00000000000000in(__DIR__); /* Based on ^2.1 of php-cs-fixer */ $config ->setRules(array( // default '@PSR2' => true, '@Symfony' => true, // additionally 'array_syntax' => array('syntax' => 'long'), 'binary_operator_spaces' => false, 'concat_space' => array('spacing' => 'one'), 'no_useless_else' => true, 'no_useless_return' => true, 'ordered_imports' => true, 'phpdoc_no_package' => false, 'phpdoc_order' => true, 'phpdoc_summary' => false, 'pre_increment' => false, 'increment_style' => false, 'simplified_null_return' => false, 'trailing_comma_in_multiline_array' => false, 'yoda_style' => false, 'phpdoc_types_order' => array('null_adjustment' => 'none', 'sort_algorithm' => 'none'), )) ->setFinder($finder) ; return $config; php-json-schema-5.2.9/.travis.yml000066400000000000000000000023221354400737700166450ustar00rootroot00000000000000sudo: false language: php cache: directories: - $HOME/.composer/cache - $HOME/.phpcsfixer matrix: fast_finish: true include: - php: 5.3 dist: precise - php: 5.4 dist: trusty - php: 5.5 dist: trusty - php: 5.6 - php: 7.0 env: WITH_COVERAGE=true - php: 7.0 env: WITH_PHPCSFIXER=true - php: 7.1 - php: 7.2 - php: 7.3 - php: 'nightly' - php: hhvm dist: trusty allow_failures: - php: 'nightly' - php: hhvm before_install: - if [[ "$WITH_COVERAGE" != "true" && "$TRAVIS_PHP_VERSION" != "hhvm" && "$TRAVIS_PHP_VERSION" != "nightly" && "$TRAVIS_PHP_VERSION" != "7.1" ]]; then phpenv config-rm xdebug.ini; fi - if [[ "$TRAVIS_PHP_VERSION" = "hhvm" || "$TRAVIS_PHP_VERSION" = "nightly" ]]; then sed -i '/^.*friendsofphp\/php-cs-fixer.*$/d' composer.json; fi install: - travis_retry composer install --no-interaction --prefer-dist script: - if [[ "$WITH_COVERAGE" == "true" ]]; then ./vendor/bin/phpunit --coverage-text; else composer test; fi - if [[ "$WITH_PHPCSFIXER" == "true" ]]; then mkdir -p $HOME/.phpcsfixer && vendor/bin/php-cs-fixer fix --cache-file "$HOME/.phpcsfixer/.php_cs.cache" --dry-run --diff --verbose; fi php-json-schema-5.2.9/LICENSE000066400000000000000000000020401354400737700155360ustar00rootroot00000000000000MIT License Copyright (c) 2016 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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. php-json-schema-5.2.9/README.md000066400000000000000000000141721354400737700160210ustar00rootroot00000000000000# JSON Schema for PHP [![Build Status](https://travis-ci.org/justinrainbow/json-schema.svg?branch=master)](https://travis-ci.org/justinrainbow/json-schema) [![Latest Stable Version](https://poser.pugx.org/justinrainbow/json-schema/v/stable.png)](https://packagist.org/packages/justinrainbow/json-schema) [![Total Downloads](https://poser.pugx.org/justinrainbow/json-schema/downloads.png)](https://packagist.org/packages/justinrainbow/json-schema) A PHP Implementation for validating `JSON` Structures against a given `Schema`. See [json-schema](http://json-schema.org/) for more details. ## Installation ### Library ```bash git clone https://github.com/justinrainbow/json-schema.git ``` ### Composer [Install PHP Composer](https://getcomposer.org/doc/00-intro.md) ```bash composer require justinrainbow/json-schema ``` ## Usage ```php validate($data, (object)['$ref' => 'file://' . realpath('schema.json')]); if ($validator->isValid()) { echo "The supplied JSON validates against the schema.\n"; } else { echo "JSON does not validate. Violations:\n"; foreach ($validator->getErrors() as $error) { echo sprintf("[%s] %s\n", $error['property'], $error['message']); } } ``` ### Type coercion If you're validating data passed to your application via HTTP, you can cast strings and booleans to the expected types defined by your schema: ```php "true", 'refundAmount'=>"17" ]; $validator->validate( $request, (object) [ "type"=>"object", "properties"=>(object)[ "processRefund"=>(object)[ "type"=>"boolean" ], "refundAmount"=>(object)[ "type"=>"number" ] ] ], Constraint::CHECK_MODE_COERCE_TYPES ); // validates! is_bool($request->processRefund); // true is_int($request->refundAmount); // true ``` A shorthand method is also available: ```PHP $validator->coerce($request, $schema); // equivalent to $validator->validate($data, $schema, Constraint::CHECK_MODE_COERCE_TYPES); ``` ### Default values If your schema contains default values, you can have these automatically applied during validation: ```php 17 ]; $validator = new Validator(); $validator->validate( $request, (object)[ "type"=>"object", "properties"=>(object)[ "processRefund"=>(object)[ "type"=>"boolean", "default"=>true ] ] ], Constraint::CHECK_MODE_APPLY_DEFAULTS ); //validates, and sets defaults for missing properties is_bool($request->processRefund); // true $request->processRefund; // true ``` ### With inline references ```php addSchema('file://mySchema', $jsonSchemaObject); // Provide $schemaStorage to the Validator so that references can be resolved during validation $jsonValidator = new Validator( new Factory($schemaStorage)); // JSON must be decoded before it can be validated $jsonToValidateObject = json_decode('{"data":123}'); // Do validation (use isValid() and getErrors() to check the result) $jsonValidator->validate($jsonToValidateObject, $jsonSchemaObject); ``` ### Configuration Options A number of flags are available to alter the behavior of the validator. These can be passed as the third argument to `Validator::validate()`, or can be provided as the third argument to `Factory::__construct()` if you wish to persist them across multiple `validate()` calls. | Flag | Description | |------|-------------| | `Constraint::CHECK_MODE_NORMAL` | Validate in 'normal' mode - this is the default | | `Constraint::CHECK_MODE_TYPE_CAST` | Enable fuzzy type checking for associative arrays and objects | | `Constraint::CHECK_MODE_COERCE_TYPES` | Convert data types to match the schema where possible | | `Constraint::CHECK_MODE_APPLY_DEFAULTS` | Apply default values from the schema if not set | | `Constraint::CHECK_MODE_ONLY_REQUIRED_DEFAULTS` | When applying defaults, only set values that are required | | `Constraint::CHECK_MODE_EXCEPTIONS` | Throw an exception immediately if validation fails | | `Constraint::CHECK_MODE_DISABLE_FORMAT` | Do not validate "format" constraints | | `Constraint::CHECK_MODE_VALIDATE_SCHEMA` | Validate the schema as well as the provided document | Please note that using `Constraint::CHECK_MODE_COERCE_TYPES` or `Constraint::CHECK_MODE_APPLY_DEFAULTS` will modify your original data. ## Running the tests ```bash composer test # run all unit tests composer testOnly TestClass # run specific unit test class composer testOnly TestClass::testMethod # run specific unit test method composer style-check # check code style for errors composer style-fix # automatically fix code style errors ``` php-json-schema-5.2.9/bin/000077500000000000000000000000001354400737700153055ustar00rootroot00000000000000php-json-schema-5.2.9/bin/validate-json000077500000000000000000000152371354400737700200030ustar00rootroot00000000000000#!/usr/bin/env php */ /** * Dead simple autoloader * * @param string $className Name of class to load * * @return void */ spl_autoload_register(function ($className) { $className = ltrim($className, '\\'); $fileName = ''; if ($lastNsPos = strrpos($className, '\\')) { $namespace = substr($className, 0, $lastNsPos); $className = substr($className, $lastNsPos + 1); $fileName = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR; } $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php'; if (stream_resolve_include_path($fileName)) { require_once $fileName; } }); // support running this tool from git checkout if (is_dir(__DIR__ . '/../src/JsonSchema')) { set_include_path(__DIR__ . '/../src' . PATH_SEPARATOR . get_include_path()); } $arOptions = array(); $arArgs = array(); array_shift($argv);//script itself foreach ($argv as $arg) { if ($arg{0} == '-') { $arOptions[$arg] = true; } else { $arArgs[] = $arg; } } if (count($arArgs) == 0 || isset($arOptions['--help']) || isset($arOptions['-h']) ) { echo << $value) { if (!strncmp($name, 'JSON_ERROR_', 11)) { $json_errors[$value] = $name; } } output('JSON parse error: ' . $json_errors[json_last_error()] . "\n"); } function getUrlFromPath($path) { if (parse_url($path, PHP_URL_SCHEME) !== null) { //already an URL return $path; } if ($path{0} == '/') { //absolute path return 'file://' . $path; } //relative path: make absolute return 'file://' . getcwd() . '/' . $path; } /** * Take a HTTP header value and split it up into parts. * * @param $headerValue * @return array Key "_value" contains the main value, all others * as given in the header value */ function parseHeaderValue($headerValue) { if (strpos($headerValue, ';') === false) { return array('_value' => $headerValue); } $parts = explode(';', $headerValue); $arData = array('_value' => array_shift($parts)); foreach ($parts as $part) { list($name, $value) = explode('=', $part); $arData[$name] = trim($value, ' "\''); } return $arData; } /** * Send a string to the output stream, but only if --quiet is not enabled * * @param $str string A string output */ function output($str) { global $arOptions; if (!isset($arOptions['--quiet'])) { echo $str; } } $urlData = getUrlFromPath($pathData); $context = stream_context_create( array( 'http' => array( 'header' => array( 'Accept: */*', 'Connection: Close' ), 'max_redirects' => 5 ) ) ); $dataString = file_get_contents($pathData, false, $context); if ($dataString == '') { output("Data file is not readable or empty.\n"); exit(3); } $data = json_decode($dataString); unset($dataString); if ($data === null) { output("Error loading JSON data file\n"); showJsonError(); exit(5); } if ($pathSchema === null) { if (isset($http_response_header)) { array_shift($http_response_header);//HTTP/1.0 line foreach ($http_response_header as $headerLine) { list($hName, $hValue) = explode(':', $headerLine, 2); $hName = strtolower($hName); if ($hName == 'link') { //Link: ; rel="describedBy" $hParts = parseHeaderValue($hValue); if (isset($hParts['rel']) && $hParts['rel'] == 'describedBy') { $pathSchema = trim($hParts['_value'], ' <>'); } } else if ($hName == 'content-type') { //Content-Type: application/my-media-type+json; // profile=http://example.org/schema# $hParts = parseHeaderValue($hValue); if (isset($hParts['profile'])) { $pathSchema = $hParts['profile']; } } } } if (is_object($data) && property_exists($data, '$schema')) { $pathSchema = $data->{'$schema'}; } //autodetect schema if ($pathSchema === null) { output("JSON data must be an object and have a \$schema property.\n"); output("You can pass the schema file on the command line as well.\n"); output("Schema autodetection failed.\n"); exit(6); } } if ($pathSchema{0} == '/') { $pathSchema = 'file://' . $pathSchema; } $resolver = new JsonSchema\Uri\UriResolver(); $retriever = new JsonSchema\Uri\UriRetriever(); try { $urlSchema = $resolver->resolve($pathSchema, $urlData); if (isset($arOptions['--dump-schema-url'])) { echo $urlSchema . "\n"; exit(); } } catch (Exception $e) { output("Error loading JSON schema file\n"); output($urlSchema . "\n"); output($e->getMessage() . "\n"); exit(2); } $refResolver = new JsonSchema\SchemaStorage($retriever, $resolver); $schema = $refResolver->resolveRef($urlSchema); if (isset($arOptions['--dump-schema'])) { $options = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; echo json_encode($schema, $options) . "\n"; exit(); } try { $validator = new JsonSchema\Validator(); $validator->check($data, $schema); if ($validator->isValid()) { if(isset($arOptions['--verbose'])) { output("OK. The supplied JSON validates against the schema.\n"); } } else { output("JSON does not validate. Violations:\n"); foreach ($validator->getErrors() as $error) { output(sprintf("[%s] %s\n", $error['property'], $error['message'])); } exit(23); } } catch (Exception $e) { output("JSON does not validate. Error:\n"); output($e->getMessage() . "\n"); output("Error code: " . $e->getCode() . "\n"); exit(24); } php-json-schema-5.2.9/composer.json000066400000000000000000000036671354400737700172730ustar00rootroot00000000000000{ "name": "justinrainbow/json-schema", "type": "library", "description": "A library to validate a json schema.", "keywords": [ "json", "schema" ], "homepage": "https://github.com/justinrainbow/json-schema", "license": "MIT", "authors": [ { "name": "Bruno Prieto Reis", "email": "bruno.p.reis@gmail.com" }, { "name": "Justin Rainbow", "email": "justin.rainbow@gmail.com" }, { "name": "Igor Wiedler", "email": "igor@wiedler.ch" }, { "name": "Robert Schönthal", "email": "seroscho@googlemail.com" } ], "require": { "php": ">=5.3.3" }, "require-dev": { "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", "json-schema/JSON-Schema-Test-Suite": "1.2.0", "phpunit/phpunit": "^4.8.35" }, "extra": { "branch-alias": { "dev-master": "5.0.x-dev" } }, "autoload": { "psr-4": { "JsonSchema\\": "src/JsonSchema/" } }, "autoload-dev": { "psr-4": { "JsonSchema\\Tests\\": "tests/" } }, "repositories": [ { "type": "package", "package": { "name": "json-schema/JSON-Schema-Test-Suite", "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/json-schema/JSON-Schema-Test-Suite", "reference": "1.2.0" } } } ], "bin": [ "bin/validate-json" ], "scripts": { "coverage": "phpunit --coverage-text", "style-check": "php-cs-fixer fix --dry-run --verbose --diff", "style-fix": "php-cs-fixer fix --verbose", "test": "phpunit", "testOnly": "phpunit --colors --filter" } } php-json-schema-5.2.9/demo/000077500000000000000000000000001354400737700154615ustar00rootroot00000000000000php-json-schema-5.2.9/demo/README.md000066400000000000000000000010541354400737700167400ustar00rootroot00000000000000## JsonSchema\Validator Demo This demo script uses the example from the root README.md and provides sample JSON files for testing `JsonSchema\Validator`. To change or replace the JSON schema document, please edit `/path/to/json-schema/demo/schema.json`. To change or replace the JSON document that is validated, please edit `/path/to/json-schema/demo/data.json`. To run the demo, change the path in the following example and run it in your terminal. ``` cd /path/to/json-schema/demo php demo.php // The supplied JSON validates against the schema. ``` php-json-schema-5.2.9/demo/data.json000066400000000000000000000000221354400737700172570ustar00rootroot00000000000000{ "foo":"bar" } php-json-schema-5.2.9/demo/demo.php000066400000000000000000000010241354400737700171130ustar00rootroot00000000000000check($data, (object) array('$ref' => 'file://' . realpath('schema.json'))); if ($validator->isValid()) { echo "The supplied JSON validates against the schema.\n"; } else { echo "JSON does not validate. Violations:\n"; foreach ($validator->getErrors() as $error) { echo sprintf("[%s] %s\n", $error['property'], $error['message']); } } php-json-schema-5.2.9/demo/schema.json000066400000000000000000000000271354400737700176130ustar00rootroot00000000000000{ "type": "object" } php-json-schema-5.2.9/dist/000077500000000000000000000000001354400737700155005ustar00rootroot00000000000000php-json-schema-5.2.9/dist/schema/000077500000000000000000000000001354400737700167405ustar00rootroot00000000000000php-json-schema-5.2.9/dist/schema/json-schema-draft-03.json000066400000000000000000000074271354400737700233720ustar00rootroot00000000000000{ "$schema": "http://json-schema.org/draft-03/schema#", "id": "http://json-schema.org/draft-03/schema#", "type": "object", "properties": { "type": { "type": [ "string", "array" ], "items": { "type": [ "string", { "$ref": "#" } ] }, "uniqueItems": true, "default": "any" }, "properties": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "patternProperties": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "additionalProperties": { "type": [ { "$ref": "#" }, "boolean" ], "default": {} }, "items": { "type": [ { "$ref": "#" }, "array" ], "items": { "$ref": "#" }, "default": {} }, "additionalItems": { "type": [ { "$ref": "#" }, "boolean" ], "default": {} }, "required": { "type": "boolean", "default": false }, "dependencies": { "type": "object", "additionalProperties": { "type": [ "string", "array", { "$ref": "#" } ], "items": { "type": "string" } }, "default": {} }, "minimum": { "type": "number" }, "maximum": { "type": "number" }, "exclusiveMinimum": { "type": "boolean", "default": false }, "exclusiveMaximum": { "type": "boolean", "default": false }, "minItems": { "type": "integer", "minimum": 0, "default": 0 }, "maxItems": { "type": "integer", "minimum": 0 }, "uniqueItems": { "type": "boolean", "default": false }, "pattern": { "type": "string", "format": "regex" }, "minLength": { "type": "integer", "minimum": 0, "default": 0 }, "maxLength": { "type": "integer" }, "enum": { "type": "array", "minItems": 1, "uniqueItems": true }, "default": { "type": "any" }, "title": { "type": "string" }, "description": { "type": "string" }, "format": { "type": "string" }, "divisibleBy": { "type": "number", "minimum": 0, "exclusiveMinimum": true, "default": 1 }, "disallow": { "type": [ "string", "array" ], "items": { "type": [ "string", { "$ref": "#" } ] }, "uniqueItems": true }, "extends": { "type": [ { "$ref": "#" }, "array" ], "items": { "$ref": "#" }, "default": {} }, "id": { "type": "string", "format": "uri" }, "$ref": { "type": "string", "format": "uri" }, "$schema": { "type": "string", "format": "uri" } }, "dependencies": { "exclusiveMinimum": "minimum", "exclusiveMaximum": "maximum" }, "default": {} } php-json-schema-5.2.9/dist/schema/json-schema-draft-04.json000066400000000000000000000104271354400737700233650ustar00rootroot00000000000000{ "id": "http://json-schema.org/draft-04/schema#", "$schema": "http://json-schema.org/draft-04/schema#", "description": "Core schema meta-schema", "definitions": { "schemaArray": { "type": "array", "minItems": 1, "items": { "$ref": "#" } }, "positiveInteger": { "type": "integer", "minimum": 0 }, "positiveIntegerDefault0": { "allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ] }, "simpleTypes": { "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ] }, "stringArray": { "type": "array", "items": { "type": "string" }, "minItems": 1, "uniqueItems": true } }, "type": "object", "properties": { "id": { "type": "string", "format": "uri" }, "$schema": { "type": "string", "format": "uri" }, "title": { "type": "string" }, "description": { "type": "string" }, "default": {}, "multipleOf": { "type": "number", "minimum": 0, "exclusiveMinimum": true }, "maximum": { "type": "number" }, "exclusiveMaximum": { "type": "boolean", "default": false }, "minimum": { "type": "number" }, "exclusiveMinimum": { "type": "boolean", "default": false }, "maxLength": { "$ref": "#/definitions/positiveInteger" }, "minLength": { "$ref": "#/definitions/positiveIntegerDefault0" }, "pattern": { "type": "string", "format": "regex" }, "additionalItems": { "anyOf": [ { "type": "boolean" }, { "$ref": "#" } ], "default": {} }, "items": { "anyOf": [ { "$ref": "#" }, { "$ref": "#/definitions/schemaArray" } ], "default": {} }, "maxItems": { "$ref": "#/definitions/positiveInteger" }, "minItems": { "$ref": "#/definitions/positiveIntegerDefault0" }, "uniqueItems": { "type": "boolean", "default": false }, "maxProperties": { "$ref": "#/definitions/positiveInteger" }, "minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" }, "required": { "$ref": "#/definitions/stringArray" }, "additionalProperties": { "anyOf": [ { "type": "boolean" }, { "$ref": "#" } ], "default": {} }, "definitions": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "properties": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "patternProperties": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "dependencies": { "type": "object", "additionalProperties": { "anyOf": [ { "$ref": "#" }, { "$ref": "#/definitions/stringArray" } ] } }, "enum": { "type": "array", "minItems": 1, "uniqueItems": true }, "type": { "anyOf": [ { "$ref": "#/definitions/simpleTypes" }, { "type": "array", "items": { "$ref": "#/definitions/simpleTypes" }, "minItems": 1, "uniqueItems": true } ] }, "allOf": { "$ref": "#/definitions/schemaArray" }, "anyOf": { "$ref": "#/definitions/schemaArray" }, "oneOf": { "$ref": "#/definitions/schemaArray" }, "not": { "$ref": "#" } }, "dependencies": { "exclusiveMaximum": [ "maximum" ], "exclusiveMinimum": [ "minimum" ] }, "default": {} } php-json-schema-5.2.9/docs/000077500000000000000000000000001354400737700154655ustar00rootroot00000000000000php-json-schema-5.2.9/docs/.gitignore000066400000000000000000000000111354400737700174450ustar00rootroot00000000000000_build/* php-json-schema-5.2.9/docs/Makefile000066400000000000000000000127141354400737700171320ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/JsonSchema.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/JsonSchema.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/JsonSchema" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/JsonSchema" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." php-json-schema-5.2.9/docs/_build/000077500000000000000000000000001354400737700167235ustar00rootroot00000000000000php-json-schema-5.2.9/docs/_build/.gitkeep000066400000000000000000000000001354400737700203420ustar00rootroot00000000000000php-json-schema-5.2.9/docs/_static/000077500000000000000000000000001354400737700171135ustar00rootroot00000000000000php-json-schema-5.2.9/docs/_static/.gitkeep000066400000000000000000000000001354400737700205320ustar00rootroot00000000000000php-json-schema-5.2.9/docs/_templates/000077500000000000000000000000001354400737700176225ustar00rootroot00000000000000php-json-schema-5.2.9/docs/_templates/.gitkeep000066400000000000000000000000001354400737700212410ustar00rootroot00000000000000php-json-schema-5.2.9/docs/conf.py000066400000000000000000000172351354400737700167740ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # JsonSchema documentation build configuration file, created by # sphinx-quickstart on Sat Dec 10 15:34:44 2011. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'JsonSchema' copyright = u'2011, Justin Rainbow, Bruno Prieto Reis' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '1.0.0' # The full version, including alpha/beta/rc tags. release = '1.0.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'JsonSchemadoc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'JsonSchema.tex', u'JsonSchema Documentation', u'Justin Rainbow, Bruno Prieto Reis', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'jsonschema', u'JsonSchema Documentation', [u'Justin Rainbow, Bruno Prieto Reis'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'JsonSchema', u'JsonSchema Documentation', u'Justin Rainbow, Bruno Prieto Reis', 'JsonSchema', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' php-json-schema-5.2.9/docs/index.rst000066400000000000000000000024111354400737700173240ustar00rootroot00000000000000.. JsonSchema documentation master file, created by sphinx-quickstart on Sat Dec 10 15:34:44 2011. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to JsonSchema's documentation! ====================================== Contents: .. toctree:: :maxdepth: 2 Installation ------------ .. code-block:: console git clone https://github.com/justinrainbow/json-schema.git Composer method ^^^^^^^^^^^^^^^ Add the `justinrainbow/json-schema` package to your `composer.json` file. .. code-block:: javascript { "require": { "justinrainbow/json-schema": "1.1.*" } } Then just run the usual `php composer.phar install` to install. Usage ----- .. code-block:: php validate(json_decode($json), json_decode($schema)); if ($result->valid) { echo "The supplied JSON validates against the schema.\n"; } else { echo "JSON does not validate. Violations:\n"; foreach ($result->errors as $error) { echo "[{$error['property']}] {$error['message']}\n"; } } Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` php-json-schema-5.2.9/docs/make.bat000066400000000000000000000117601354400737700170770ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\JsonSchema.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\JsonSchema.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end php-json-schema-5.2.9/phpunit.xml.dist000066400000000000000000000013001354400737700177020ustar00rootroot00000000000000 tests ./src/JsonSchema/ php-json-schema-5.2.9/src/000077500000000000000000000000001354400737700153245ustar00rootroot00000000000000php-json-schema-5.2.9/src/JsonSchema/000077500000000000000000000000001354400737700173565ustar00rootroot00000000000000php-json-schema-5.2.9/src/JsonSchema/Constraints/000077500000000000000000000000001354400737700216655ustar00rootroot00000000000000php-json-schema-5.2.9/src/JsonSchema/Constraints/BaseConstraint.php000066400000000000000000000074451354400737700253270ustar00rootroot00000000000000factory = $factory ?: new Factory(); } public function addError(JsonPointer $path = null, $message, $constraint = '', array $more = null) { $error = array( 'property' => $this->convertJsonPointerIntoPropertyPath($path ?: new JsonPointer('')), 'pointer' => ltrim(strval($path ?: new JsonPointer('')), '#'), 'message' => $message, 'constraint' => $constraint, 'context' => $this->factory->getErrorContext(), ); if ($this->factory->getConfig(Constraint::CHECK_MODE_EXCEPTIONS)) { throw new ValidationException(sprintf('Error validating %s: %s', $error['pointer'], $error['message'])); } if (is_array($more) && count($more) > 0) { $error += $more; } $this->errors[] = $error; $this->errorMask |= $error['context']; } public function addErrors(array $errors) { if ($errors) { $this->errors = array_merge($this->errors, $errors); $errorMask = &$this->errorMask; array_walk($errors, function ($error) use (&$errorMask) { if (isset($error['context'])) { $errorMask |= $error['context']; } }); } } public function getErrors($errorContext = Validator::ERROR_ALL) { if ($errorContext === Validator::ERROR_ALL) { return $this->errors; } return array_filter($this->errors, function ($error) use ($errorContext) { if ($errorContext & $error['context']) { return true; } }); } public function numErrors($errorContext = Validator::ERROR_ALL) { if ($errorContext === Validator::ERROR_ALL) { return count($this->errors); } return count($this->getErrors($errorContext)); } public function isValid() { return !$this->getErrors(); } /** * Clears any reported errors. Should be used between * multiple validation checks. */ public function reset() { $this->errors = array(); $this->errorMask = Validator::ERROR_NONE; } /** * Get the error mask * * @return int */ public function getErrorMask() { return $this->errorMask; } /** * Recursively cast an associative array to an object * * @param array $array * * @return object */ public static function arrayToObjectRecursive($array) { $json = json_encode($array); if (json_last_error() !== \JSON_ERROR_NONE) { $message = 'Unable to encode schema array as JSON'; if (function_exists('json_last_error_msg')) { $message .= ': ' . json_last_error_msg(); } throw new InvalidArgumentException($message); } return (object) json_decode($json); } } php-json-schema-5.2.9/src/JsonSchema/Constraints/CollectionConstraint.php000066400000000000000000000117061354400737700265430ustar00rootroot00000000000000 * @author Bruno Prieto Reis */ class CollectionConstraint extends Constraint { /** * {@inheritdoc} */ public function check(&$value, $schema = null, JsonPointer $path = null, $i = null) { // Verify minItems if (isset($schema->minItems) && count($value) < $schema->minItems) { $this->addError($path, 'There must be a minimum of ' . $schema->minItems . ' items in the array', 'minItems', array('minItems' => $schema->minItems)); } // Verify maxItems if (isset($schema->maxItems) && count($value) > $schema->maxItems) { $this->addError($path, 'There must be a maximum of ' . $schema->maxItems . ' items in the array', 'maxItems', array('maxItems' => $schema->maxItems)); } // Verify uniqueItems if (isset($schema->uniqueItems) && $schema->uniqueItems) { $unique = $value; if (is_array($value) && count($value)) { $unique = array_map(function ($e) { return var_export($e, true); }, $value); } if (count(array_unique($unique)) != count($value)) { $this->addError($path, 'There are no duplicates allowed in the array', 'uniqueItems'); } } // Verify items if (isset($schema->items)) { $this->validateItems($value, $schema, $path, $i); } } /** * Validates the items * * @param array $value * @param \stdClass $schema * @param JsonPointer|null $path * @param string $i */ protected function validateItems(&$value, $schema = null, JsonPointer $path = null, $i = null) { if (is_object($schema->items)) { // just one type definition for the whole array foreach ($value as $k => &$v) { $initErrors = $this->getErrors(); // First check if its defined in "items" $this->checkUndefined($v, $schema->items, $path, $k); // Recheck with "additionalItems" if the first test fails if (count($initErrors) < count($this->getErrors()) && (isset($schema->additionalItems) && $schema->additionalItems !== false)) { $secondErrors = $this->getErrors(); $this->checkUndefined($v, $schema->additionalItems, $path, $k); } // Reset errors if needed if (isset($secondErrors) && count($secondErrors) < count($this->getErrors())) { $this->errors = $secondErrors; } elseif (isset($secondErrors) && count($secondErrors) === count($this->getErrors())) { $this->errors = $initErrors; } } unset($v); /* remove dangling reference to prevent any future bugs * caused by accidentally using $v elsewhere */ } else { // Defined item type definitions foreach ($value as $k => &$v) { if (array_key_exists($k, $schema->items)) { $this->checkUndefined($v, $schema->items[$k], $path, $k); } else { // Additional items if (property_exists($schema, 'additionalItems')) { if ($schema->additionalItems !== false) { $this->checkUndefined($v, $schema->additionalItems, $path, $k); } else { $this->addError( $path, 'The item ' . $i . '[' . $k . '] is not defined and the definition does not allow additional items', 'additionalItems', array('additionalItems' => $schema->additionalItems)); } } else { // Should be valid against an empty schema $this->checkUndefined($v, new \stdClass(), $path, $k); } } } unset($v); /* remove dangling reference to prevent any future bugs * caused by accidentally using $v elsewhere */ // Treat when we have more schema definitions than values, not for empty arrays if (count($value) > 0) { for ($k = count($value); $k < count($schema->items); $k++) { $undefinedInstance = $this->factory->createInstanceFor('undefined'); $this->checkUndefined($undefinedInstance, $schema->items[$k], $path, $k); } } } } } php-json-schema-5.2.9/src/JsonSchema/Constraints/Constraint.php000066400000000000000000000145301354400737700245250ustar00rootroot00000000000000 * @author Bruno Prieto Reis */ abstract class Constraint extends BaseConstraint implements ConstraintInterface { protected $inlineSchemaProperty = '$schema'; const CHECK_MODE_NONE = 0x00000000; const CHECK_MODE_NORMAL = 0x00000001; const CHECK_MODE_TYPE_CAST = 0x00000002; const CHECK_MODE_COERCE_TYPES = 0x00000004; const CHECK_MODE_APPLY_DEFAULTS = 0x00000008; const CHECK_MODE_EXCEPTIONS = 0x00000010; const CHECK_MODE_DISABLE_FORMAT = 0x00000020; const CHECK_MODE_ONLY_REQUIRED_DEFAULTS = 0x00000080; const CHECK_MODE_VALIDATE_SCHEMA = 0x00000100; /** * Bubble down the path * * @param JsonPointer|null $path Current path * @param mixed $i What to append to the path * * @return JsonPointer; */ protected function incrementPath(JsonPointer $path = null, $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 JsonPointer|null $path * @param mixed $i */ protected function checkArray(&$value, $schema = null, JsonPointer $path = null, $i = null) { $validator = $this->factory->createInstanceFor('collection'); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); } /** * Validates an object * * @param mixed $value * @param mixed $schema * @param JsonPointer|null $path * @param mixed $properties * @param mixed $additionalProperties * @param mixed $patternProperties */ protected function checkObject(&$value, $schema = null, JsonPointer $path = null, $properties = null, $additionalProperties = null, $patternProperties = null, $appliedDefaults = array()) { $validator = $this->factory->createInstanceFor('object'); $validator->check($value, $schema, $path, $properties, $additionalProperties, $patternProperties, $appliedDefaults); $this->addErrors($validator->getErrors()); } /** * Validates the type of a property * * @param mixed $value * @param mixed $schema * @param JsonPointer|null $path * @param mixed $i */ protected function checkType(&$value, $schema = null, JsonPointer $path = null, $i = null) { $validator = $this->factory->createInstanceFor('type'); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); } /** * Checks a undefined element * * @param mixed $value * @param mixed $schema * @param JsonPointer|null $path * @param mixed $i */ protected function checkUndefined(&$value, $schema = null, JsonPointer $path = null, $i = null, $fromDefault = false) { $validator = $this->factory->createInstanceFor('undefined'); $validator->check($value, $this->factory->getSchemaStorage()->resolveRefSchema($schema), $path, $i, $fromDefault); $this->addErrors($validator->getErrors()); } /** * Checks a string element * * @param mixed $value * @param mixed $schema * @param JsonPointer|null $path * @param mixed $i */ protected function checkString($value, $schema = null, JsonPointer $path = null, $i = null) { $validator = $this->factory->createInstanceFor('string'); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); } /** * Checks a number element * * @param mixed $value * @param mixed $schema * @param JsonPointer $path * @param mixed $i */ protected function checkNumber($value, $schema = null, JsonPointer $path = null, $i = null) { $validator = $this->factory->createInstanceFor('number'); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); } /** * Checks a enum element * * @param mixed $value * @param mixed $schema * @param JsonPointer|null $path * @param mixed $i */ protected function checkEnum($value, $schema = null, JsonPointer $path = null, $i = null) { $validator = $this->factory->createInstanceFor('enum'); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); } /** * 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->factory->createInstanceFor('format'); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); } /** * Get the type check based on the set check mode. * * @return TypeCheck\TypeCheckInterface */ protected function getTypeCheck() { return $this->factory->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), '.'); } } php-json-schema-5.2.9/src/JsonSchema/Constraints/ConstraintInterface.php000066400000000000000000000030351354400737700263440ustar00rootroot00000000000000 */ interface ConstraintInterface { /** * returns all collected errors * * @return array */ public function getErrors(); /** * adds errors to this validator * * @param array $errors */ public function addErrors(array $errors); /** * adds an 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(JsonPointer $path = null, $message, $constraint='', array $more = null); /** * checks if the validator has not raised errors * * @return bool */ public function isValid(); /** * invokes the validation of an element * * @abstract * * @param mixed $value * @param mixed $schema * @param JsonPointer|null $path * @param mixed $i * * @throws \JsonSchema\Exception\ExceptionInterface */ public function check(&$value, $schema = null, JsonPointer $path = null, $i = null); } php-json-schema-5.2.9/src/JsonSchema/Constraints/EnumConstraint.php000066400000000000000000000031611354400737700253500ustar00rootroot00000000000000 * @author Bruno Prieto Reis */ class EnumConstraint extends Constraint { /** * {@inheritdoc} */ 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) { $enumType = gettype($enum); if ($this->factory->getConfig(self::CHECK_MODE_TYPE_CAST) && $type == 'array' && $enumType == 'object') { if ((object) $element == $enum) { return; } } if ($type === gettype($enum)) { if ($type == 'object') { if ($element == $enum) { return; } } elseif ($element === $enum) { return; } } } $this->addError($path, 'Does not have a value in the enumeration ' . json_encode($schema->enum), 'enum', array('enum' => $schema->enum)); } } php-json-schema-5.2.9/src/JsonSchema/Constraints/Factory.php000066400000000000000000000131061354400737700240060ustar00rootroot00000000000000 'JsonSchema\Constraints\CollectionConstraint', 'collection' => 'JsonSchema\Constraints\CollectionConstraint', 'object' => 'JsonSchema\Constraints\ObjectConstraint', 'type' => 'JsonSchema\Constraints\TypeConstraint', 'undefined' => 'JsonSchema\Constraints\UndefinedConstraint', 'string' => 'JsonSchema\Constraints\StringConstraint', 'number' => 'JsonSchema\Constraints\NumberConstraint', 'enum' => 'JsonSchema\Constraints\EnumConstraint', 'format' => 'JsonSchema\Constraints\FormatConstraint', 'schema' => 'JsonSchema\Constraints\SchemaConstraint', 'validator' => 'JsonSchema\Validator' ); /** * @var array */ private $instanceCache = array(); /** * @param SchemaStorage $schemaStorage * @param UriRetrieverInterface $uriRetriever * @param int $checkMode */ public function __construct( SchemaStorageInterface $schemaStorage = null, UriRetrieverInterface $uriRetriever = null, $checkMode = Constraint::CHECK_MODE_NORMAL ) { // set provided config options $this->setConfig($checkMode); $this->uriRetriever = $uriRetriever ?: new UriRetriever(); $this->schemaStorage = $schemaStorage ?: new SchemaStorage($this->uriRetriever); } /** * Set config values * * @param int $checkMode Set checkMode options - does not preserve existing flags */ public function setConfig($checkMode = Constraint::CHECK_MODE_NORMAL) { $this->checkMode = $checkMode; } /** * Enable checkMode flags * * @param int $options */ public function addConfig($options) { $this->checkMode |= $options; } /** * Disable checkMode flags * * @param int $options */ public function removeConfig($options) { $this->checkMode &= ~$options; } /** * Get checkMode option * * @param int $options Options to get, if null then return entire bitmask * * @return int */ public function getConfig($options = null) { if ($options === null) { return $this->checkMode; } return $this->checkMode & $options; } /** * @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]; } /** * @param string $name * @param string $class * * @return Factory */ 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; } /** * Create a constraint instance for the given constraint name. * * @param string $constraintName * * @throws InvalidArgumentException if is not possible create the constraint instance * * @return ConstraintInterface|ObjectConstraint */ public function createInstanceFor($constraintName) { if (!isset($this->constraintMap[$constraintName])) { throw new InvalidArgumentException('Unknown constraint ' . $constraintName); } if (!isset($this->instanceCache[$constraintName])) { $this->instanceCache[$constraintName] = new $this->constraintMap[$constraintName]($this); } return clone $this->instanceCache[$constraintName]; } /** * Get the error context * * @return string */ public function getErrorContext() { return $this->errorContext; } /** * Set the error context * * @param string $validationContext */ public function setErrorContext($errorContext) { $this->errorContext = $errorContext; } } php-json-schema-5.2.9/src/JsonSchema/Constraints/FormatConstraint.php000066400000000000000000000212251354400737700256750ustar00rootroot00000000000000 * * @see http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.23 */ class FormatConstraint extends Constraint { /** * {@inheritdoc} */ public function check(&$element, $schema = null, JsonPointer $path = null, $i = null) { if (!isset($schema->format) || $this->factory->getConfig(self::CHECK_MODE_DISABLE_FORMAT)) { return; } switch ($schema->format) { case 'date': if (!$date = $this->validateDateTime($element, 'Y-m-d')) { $this->addError($path, sprintf('Invalid date %s, expected format YYYY-MM-DD', json_encode($element)), 'format', array('format' => $schema->format)); } break; case 'time': if (!$this->validateDateTime($element, 'H:i:s')) { $this->addError($path, sprintf('Invalid time %s, expected format hh:mm:ss', json_encode($element)), 'format', array('format' => $schema->format)); } break; case 'date-time': 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; case 'utc-millisec': if (!$this->validateDateTime($element, 'U')) { $this->addError($path, sprintf('Invalid time %s, expected integer of milliseconds since Epoch', json_encode($element)), 'format', array('format' => $schema->format)); } break; case 'regex': if (!$this->validateRegex($element)) { $this->addError($path, 'Invalid regex format ' . $element, 'format', array('format' => $schema->format)); } break; case 'color': if (!$this->validateColor($element)) { $this->addError($path, 'Invalid color', 'format', array('format' => $schema->format)); } break; case 'style': if (!$this->validateStyle($element)) { $this->addError($path, 'Invalid style', 'format', array('format' => $schema->format)); } break; case 'phone': if (!$this->validatePhone($element)) { $this->addError($path, 'Invalid phone number', 'format', array('format' => $schema->format)); } break; case 'uri': if (null === filter_var($element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE)) { $this->addError($path, 'Invalid URL format', 'format', array('format' => $schema->format)); } break; case 'uriref': case 'uri-reference': if (null === filter_var($element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE)) { // FILTER_VALIDATE_URL does not conform to RFC-3986, and cannot handle relative URLs, but // the json-schema spec uses RFC-3986, so need a bit of hackery to properly validate them. // See https://tools.ietf.org/html/rfc3986#section-4.2 for additional information. if (substr($element, 0, 2) === '//') { // network-path reference $validURL = filter_var('scheme:' . $element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE); } elseif (substr($element, 0, 1) === '/') { // absolute-path reference $validURL = filter_var('scheme://host' . $element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE); } elseif (strlen($element)) { // relative-path reference $pathParts = explode('/', $element, 2); if (strpos($pathParts[0], ':') !== false) { $validURL = null; } else { $validURL = filter_var('scheme://host/' . $element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE); } } else { $validURL = null; } if ($validURL === null) { $this->addError($path, 'Invalid URL format', 'format', array('format' => $schema->format)); } } break; case 'email': $filterFlags = FILTER_NULL_ON_FAILURE; if (defined('FILTER_FLAG_EMAIL_UNICODE')) { // Only available from PHP >= 7.1.0, so ignore it for coverage checks $filterFlags |= constant('FILTER_FLAG_EMAIL_UNICODE'); // @codeCoverageIgnore } if (null === filter_var($element, FILTER_VALIDATE_EMAIL, $filterFlags)) { $this->addError($path, 'Invalid email', 'format', array('format' => $schema->format)); } break; case 'ip-address': case 'ipv4': if (null === filter_var($element, FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE | FILTER_FLAG_IPV4)) { $this->addError($path, 'Invalid IP address', 'format', array('format' => $schema->format)); } break; case 'ipv6': if (null === filter_var($element, FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE | FILTER_FLAG_IPV6)) { $this->addError($path, 'Invalid IP address', 'format', array('format' => $schema->format)); } break; case 'host-name': case 'hostname': if (!$this->validateHostname($element)) { $this->addError($path, 'Invalid hostname', 'format', array('format' => $schema->format)); } break; default: // Empty as it should be: // The value of this keyword is called a format attribute. It MUST be a string. // A format attribute can generally only validate a given set of instance types. // If the type of the instance to validate is not in this set, validation for // this format attribute and instance SHOULD succeed. // http://json-schema.org/latest/json-schema-validation.html#anchor105 break; } } protected function validateDateTime($datetime, $format) { $dt = \DateTime::createFromFormat($format, $datetime); if (!$dt) { return false; } if ($datetime === $dt->format($format)) { return true; } // handles the case where a non-6 digit microsecond datetime is passed // 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) && (preg_match('/\.\d+Z$/', $datetime))) { return true; } return false; } protected function validateRegex($regex) { return false !== @preg_match('/' . $regex . '/u', ''); } protected function validateColor($color) { if (in_array(strtolower($color), array('aqua', 'black', 'blue', 'fuchsia', 'gray', 'green', 'lime', 'maroon', 'navy', 'olive', 'orange', 'purple', 'red', 'silver', 'teal', 'white', 'yellow'))) { return true; } return preg_match('/^#([a-f0-9]{3}|[a-f0-9]{6})$/i', $color); } protected function validateStyle($style) { $properties = explode(';', rtrim($style, ';')); $invalidEntries = preg_grep('/^\s*[-a-z]+\s*:\s*.+$/i', $properties, PREG_GREP_INVERT); return empty($invalidEntries); } protected function validatePhone($phone) { return preg_match('/^\+?(\(\d{3}\)|\d{3}) \d{3} \d{4}$/', $phone); } protected function validateHostname($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); } } php-json-schema-5.2.9/src/JsonSchema/Constraints/NumberConstraint.php000066400000000000000000000066071354400737700257040ustar00rootroot00000000000000 * @author Bruno Prieto Reis */ class NumberConstraint extends Constraint { /** * {@inheritdoc} */ 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)); } 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'); } } elseif (isset($schema->minimum) && $element < $schema->minimum) { $this->addError($path, 'Must have a minimum value of ' . $schema->minimum, 'minimum', array('minimum' => $schema->minimum)); } // Verify maximum if (isset($schema->exclusiveMaximum)) { 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)); } 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', 'missingMaximum'); } } elseif (isset($schema->maximum) && $element > $schema->maximum) { $this->addError($path, 'Must have a maximum value of ' . $schema->maximum, 'maximum', array('maximum' => $schema->maximum)); } // Verify divisibleBy - Draft v3 if (isset($schema->divisibleBy) && $this->fmod($element, $schema->divisibleBy) != 0) { $this->addError($path, 'Is not divisible by ' . $schema->divisibleBy, 'divisibleBy', array('divisibleBy' => $schema->divisibleBy)); } // Verify multipleOf - Draft v4 if (isset($schema->multipleOf) && $this->fmod($element, $schema->multipleOf) != 0) { $this->addError($path, 'Must be a multiple of ' . $schema->multipleOf, 'multipleOf', array('multipleOf' => $schema->multipleOf)); } $this->checkFormat($element, $schema, $path, $i); } private function fmod($number1, $number2) { $modulus = ($number1 - round($number1 / $number2) * $number2); $precision = 0.0000000001; if (-$precision < $modulus && $modulus < $precision) { return 0.0; } return $modulus; } } php-json-schema-5.2.9/src/JsonSchema/Constraints/ObjectConstraint.php000066400000000000000000000176171354400737700256650ustar00rootroot00000000000000 * @author Bruno Prieto Reis */ class ObjectConstraint extends Constraint { /** * @var array List of properties to which a default value has been applied */ protected $appliedDefaults = array(); /** * {@inheritdoc} */ public function check(&$element, $schema = null, JsonPointer $path = null, $properties = null, $additionalProp = null, $patternProperties = null, $appliedDefaults = array()) { if ($element instanceof UndefinedConstraint) { return; } $this->appliedDefaults = $appliedDefaults; $matches = array(); if ($patternProperties) { // validate the element pattern properties $matches = $this->validatePatternProperties($element, $path, $patternProperties); } if ($properties) { // validate the element properties $this->validateProperties($element, $properties, $path); } // validate additional element properties & constraints $this->validateElement($element, $matches, $schema, $path, $properties, $additionalProp); } public function validatePatternProperties($element, JsonPointer $path = null, $patternProperties) { $try = array('/', '#', '+', '~', '%'); $matches = array(); foreach ($patternProperties as $pregex => $schema) { $delimiter = '/'; // Choose delimiter. Necessary for patterns like ^/ , otherwise you get error foreach ($try as $delimiter) { if (strpos($pregex, $delimiter) === false) { // safe to use break; } } // Validate the pattern before using it to test for matches if (@preg_match($delimiter . $pregex . $delimiter . 'u', '') === false) { $this->addError($path, 'The pattern "' . $pregex . '" is invalid', 'pregex', array('pregex' => $pregex)); continue; } foreach ($element as $i => $value) { if (preg_match($delimiter . $pregex . $delimiter . 'u', $i)) { $matches[] = $i; $this->checkUndefined($value, $schema ?: new \stdClass(), $path, $i, in_array($i, $this->appliedDefaults)); } } } return $matches; } /** * Validates the element properties * * @param \StdClass $element Element to validate * @param array $matches Matches from patternProperties (if any) * @param \StdClass $schema ObjectConstraint definition * @param JsonPointer|null $path Current test path * @param \StdClass $properties Properties * @param mixed $additionalProp Additional properties */ public function validateElement($element, $matches, $schema = null, JsonPointer $path = null, $properties = null, $additionalProp = null) { $this->validateMinMaxConstraint($element, $schema, $path); foreach ($element as $i => $value) { $definition = $this->getProperty($properties, $i); // no additional properties allowed if (!in_array($i, $matches) && $additionalProp === false && $this->inlineSchemaProperty !== $i && !$definition) { $this->addError($path, 'The property ' . $i . ' is not defined and the definition does not allow additional properties', 'additionalProp'); } // additional properties defined if (!in_array($i, $matches) && $additionalProp && !$definition) { if ($additionalProp === true) { $this->checkUndefined($value, null, $path, $i, in_array($i, $this->appliedDefaults)); } else { $this->checkUndefined($value, $additionalProp, $path, $i, in_array($i, $this->appliedDefaults)); } } // property requires presence of another $require = $this->getProperty($definition, 'requires'); if ($require && !$this->getProperty($element, $require)) { $this->addError($path, 'The presence of the property ' . $i . ' requires that ' . $require . ' also be present', 'requires'); } $property = $this->getProperty($element, $i, $this->factory->createInstanceFor('undefined')); 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 $properties Property definitions * @param JsonPointer|null $path Path? */ public function validateProperties(&$element, $properties = null, JsonPointer $path = null) { $undefinedConstraint = $this->factory->createInstanceFor('undefined'); foreach ($properties as $i => $value) { $property = &$this->getProperty($element, $i, $undefinedConstraint); $definition = $this->getProperty($properties, $i); if (is_object($definition)) { // Undefined constraint will check for is_object() and quit if is not - so why pass it? $this->checkUndefined($property, $definition, $path, $i, in_array($i, $this->appliedDefaults)); } } } /** * retrieves a property from an object or array * * @param mixed $element Element to validate * @param string $property Property to retrieve * @param mixed $fallback Default value if property is not found * * @return mixed */ protected function &getProperty(&$element, $property, $fallback = null) { if (is_array($element) && (isset($element[$property]) || array_key_exists($property, $element)) /*$this->checkMode == self::CHECK_MODE_TYPE_CAST*/) { return $element[$property]; } elseif (is_object($element) && property_exists($element, $property)) { return $element->$property; } 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)); } } } } php-json-schema-5.2.9/src/JsonSchema/Constraints/SchemaConstraint.php000066400000000000000000000073171354400737700256530ustar00rootroot00000000000000 * @author Bruno Prieto Reis */ class SchemaConstraint extends Constraint { const DEFAULT_SCHEMA_SPEC = 'http://json-schema.org/draft-04/schema#'; /** * {@inheritdoc} */ public function check(&$element, $schema = null, JsonPointer $path = null, $i = null) { if ($schema !== null) { // passed schema $validationSchema = $schema; } elseif ($this->getTypeCheck()->propertyExists($element, $this->inlineSchemaProperty)) { // inline schema $validationSchema = $this->getTypeCheck()->propertyGet($element, $this->inlineSchemaProperty); } else { throw new InvalidArgumentException('no schema found to verify against'); } // cast array schemas to object if (is_array($validationSchema)) { $validationSchema = BaseConstraint::arrayToObjectRecursive($validationSchema); } // validate schema against whatever is defined in $validationSchema->$schema. If no // schema is defined, assume self::DEFAULT_SCHEMA_SPEC (currently draft-04). if ($this->factory->getConfig(self::CHECK_MODE_VALIDATE_SCHEMA)) { if (!$this->getTypeCheck()->isObject($validationSchema)) { throw new RuntimeException('Cannot validate the schema of a non-object'); } if ($this->getTypeCheck()->propertyExists($validationSchema, '$schema')) { $schemaSpec = $this->getTypeCheck()->propertyGet($validationSchema, '$schema'); } else { $schemaSpec = self::DEFAULT_SCHEMA_SPEC; } // get the spec schema $schemaStorage = $this->factory->getSchemaStorage(); if (!$this->getTypeCheck()->isObject($schemaSpec)) { $schemaSpec = $schemaStorage->getSchema($schemaSpec); } // save error count, config & subtract CHECK_MODE_VALIDATE_SCHEMA $initialErrorCount = $this->numErrors(); $initialConfig = $this->factory->getConfig(); $initialContext = $this->factory->getErrorContext(); $this->factory->removeConfig(self::CHECK_MODE_VALIDATE_SCHEMA | self::CHECK_MODE_APPLY_DEFAULTS); $this->factory->addConfig(self::CHECK_MODE_TYPE_CAST); $this->factory->setErrorContext(Validator::ERROR_SCHEMA_VALIDATION); // validate schema try { $this->check($validationSchema, $schemaSpec); } catch (\Exception $e) { if ($this->factory->getConfig(self::CHECK_MODE_EXCEPTIONS)) { throw new InvalidSchemaException('Schema did not pass validation', 0, $e); } } if ($this->numErrors() > $initialErrorCount) { $this->addError($path, 'Schema is not valid', 'schema'); } // restore the initial config $this->factory->setConfig($initialConfig); $this->factory->setErrorContext($initialContext); } // validate element against $validationSchema $this->checkUndefined($element, $validationSchema, $path, $i); } } php-json-schema-5.2.9/src/JsonSchema/Constraints/StringConstraint.php000066400000000000000000000037061354400737700257170ustar00rootroot00000000000000 * @author Bruno Prieto Reis */ class StringConstraint extends Constraint { /** * {@inheritdoc} */ public function check(&$element, $schema = null, JsonPointer $path = null, $i = null) { // Verify maxLength if (isset($schema->maxLength) && $this->strlen($element) > $schema->maxLength) { $this->addError($path, 'Must be at most ' . $schema->maxLength . ' characters long', 'maxLength', array( 'maxLength' => $schema->maxLength, )); } //verify minLength if (isset($schema->minLength) && $this->strlen($element) < $schema->minLength) { $this->addError($path, 'Must be at least ' . $schema->minLength . ' characters long', 'minLength', array( 'minLength' => $schema->minLength, )); } // Verify a regex pattern if (isset($schema->pattern) && !preg_match('#' . str_replace('#', '\\#', $schema->pattern) . '#u', $element)) { $this->addError($path, 'Does not match the regex pattern ' . $schema->pattern, 'pattern', array( 'pattern' => $schema->pattern, )); } $this->checkFormat($element, $schema, $path, $i); } private function strlen($string) { if (extension_loaded('mbstring')) { return mb_strlen($string, mb_detect_encoding($string)); } // mbstring is present on all test platforms, so strlen() can be ignored for coverage return strlen($string); // @codeCoverageIgnore } } php-json-schema-5.2.9/src/JsonSchema/Constraints/TypeCheck/000077500000000000000000000000001354400737700235445ustar00rootroot00000000000000php-json-schema-5.2.9/src/JsonSchema/Constraints/TypeCheck/LooseTypeCheck.php000066400000000000000000000030261354400737700271370ustar00rootroot00000000000000{$property}; } return $value[$property]; } public static function propertySet(&$value, $property, $data) { if (is_object($value)) { $value->{$property} = $data; } else { $value[$property] = $data; } } 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); } } php-json-schema-5.2.9/src/JsonSchema/Constraints/TypeCheck/StrictTypeCheck.php000066400000000000000000000014531354400737700273300ustar00rootroot00000000000000{$property}; } public static function propertySet(&$value, $property, $data) { $value->{$property} = $data; } public static function propertyExists($value, $property) { return property_exists($value, $property); } public static function propertyCount($value) { if (!is_object($value)) { return 0; } return count(get_object_vars($value)); } } php-json-schema-5.2.9/src/JsonSchema/Constraints/TypeCheck/TypeCheckInterface.php000066400000000000000000000006411354400737700277560ustar00rootroot00000000000000 * @author Bruno Prieto Reis */ class TypeConstraint extends Constraint { /** * @var array|string[] type wordings for validation error messages */ public static $wording = array( 'integer' => 'an integer', 'number' => 'a number', 'boolean' => 'a boolean', 'object' => 'an object', 'array' => 'an array', 'string' => 'a string', 'null' => 'a null', 'any' => null, // validation of 'any' is always true so is not needed in message wording 0 => null, // validation of a false-y value is always true, so not needed as well ); /** * {@inheritdoc} */ public function check(&$value = null, $schema = null, JsonPointer $path = null, $i = null) { $type = isset($schema->type) ? $schema->type : null; $isValid = false; $wording = array(); if (is_array($type)) { $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 (!is_array($type)) { $this->validateTypeNameWording($type); $wording[] = self::$wording[$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 $validTypesWording An array of wordings of the valid types of the array $type * @param bool $isValid The current validation value * @param $path */ 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->factory->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($delimiter, $elements); } $lastElement = array_slice($elements, -1); $firsElements = join($delimiter, 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))) ); } } /** * Verifies that a given value is of a certain type * * @param mixed $value Value to validate * @param string $type TypeConstraint to check against * * @throws InvalidArgumentException * * @return bool */ protected function validateType(&$value, $type) { //mostly the case for inline schema if (!$type) { return true; } if ('any' === $type) { return true; } if ('object' === $type) { return $this->getTypeCheck()->isObject($value); } if ('array' === $type) { return $this->getTypeCheck()->isArray($value); } $coerce = $this->factory->getConfig(Constraint::CHECK_MODE_COERCE_TYPES); if ('integer' === $type) { if ($coerce) { $value = $this->toInteger($value); } return is_int($value); } if ('number' === $type) { if ($coerce) { $value = $this->toNumber($value); } return is_numeric($value) && !is_string($value); } if ('boolean' === $type) { if ($coerce) { $value = $this->toBoolean($value); } return is_bool($value); } if ('string' === $type) { return is_string($value); } if ('email' === $type) { return is_string($value); } if ('null' === $type) { return is_null($value); } throw new InvalidArgumentException((is_object($value) ? 'object' : $value) . ' is an invalid type for ' . $type); } /** * Converts a value to boolean. For example, "true" becomes true. * * @param $value The value to convert to boolean * * @return bool|mixed */ protected function toBoolean($value) { if ($value === 'true') { return true; } if ($value === 'false') { return false; } return $value; } /** * Converts a numeric string to a number. For example, "4" becomes 4. * * @param mixed $value the value to convert to a number * * @return int|float|mixed */ protected function toNumber($value) { if (is_numeric($value)) { return $value + 0; // cast to number } return $value; } protected function toInteger($value) { if (is_numeric($value) && (int) $value == $value) { return (int) $value; // cast to number } return $value; } } php-json-schema-5.2.9/src/JsonSchema/Constraints/UndefinedConstraint.php000066400000000000000000000372771354400737700263640ustar00rootroot00000000000000 * @author Bruno Prieto Reis */ class UndefinedConstraint extends Constraint { /** * @var array List of properties to which a default value has been applied */ protected $appliedDefaults = array(); /** * {@inheritdoc} */ public function check(&$value, $schema = null, JsonPointer $path = null, $i = null, $fromDefault = false) { if (is_null($schema) || !is_object($schema)) { return; } $path = $this->incrementPath($path ?: new JsonPointer(''), $i); if ($fromDefault) { $path->setFromDefault(); } // check special properties $this->validateCommonProperties($value, $schema, $path, $i); // check allOf, anyOf, and oneOf properties $this->validateOfProperties($value, $schema, $path, ''); // check known types $this->validateTypes($value, $schema, $path, $i); } /** * Validates the value against the types * * @param mixed $value * @param mixed $schema * @param JsonPointer $path * @param string $i */ public function validateTypes(&$value, $schema = null, JsonPointer $path, $i = null) { // check array if ($this->getTypeCheck()->isArray($value)) { $this->checkArray($value, $schema, $path, $i); } // check object if (LooseTypeCheck::isObject($value)) { // object processing should always be run on assoc arrays, // so use LooseTypeCheck here even if CHECK_MODE_TYPE_CAST // is not set (i.e. don't use $this->getTypeCheck() here). $this->checkObject( $value, $schema, $path, isset($schema->properties) ? $schema->properties : null, isset($schema->additionalProperties) ? $schema->additionalProperties : null, isset($schema->patternProperties) ? $schema->patternProperties : null, $this->appliedDefaults ); } // check string if (is_string($value)) { $this->checkString($value, $schema, $path, $i); } // check numeric if (is_numeric($value)) { $this->checkNumber($value, $schema, $path, $i); } // check enum if (isset($schema->enum)) { $this->checkEnum($value, $schema, $path, $i); } } /** * Validates common properties * * @param mixed $value * @param mixed $schema * @param JsonPointer $path * @param string $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)) { if (is_string($schema->extends)) { $schema->extends = $this->validateUri($schema, $schema->extends); } if (is_array($schema->extends)) { foreach ($schema->extends as $extends) { $this->checkUndefined($value, $extends, $path, $i); } } else { $this->checkUndefined($value, $schema->extends, $path, $i); } } // Apply default values from schema if (!$path->fromDefault()) { $this->applyDefaultValues($value, $schema, $path); } // Verify required values if ($this->getTypeCheck()->isObject($value)) { if (!($value instanceof self) && 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 (!$this->getTypeCheck()->propertyExists($value, $required)) { $this->addError( $this->incrementPath($path ?: new JsonPointer(''), $required), 'The property ' . $required . ' is required', '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 self) { $propertyPaths = $path->getPropertyPaths(); $propertyName = end($propertyPaths); $this->addError( $path, 'The property ' . $propertyName . ' is required', 'required' ); } } else { // If the value is both undefined and not required, skip remaining checks // in this method which assume an actual, defined instance when validating. if ($value instanceof self) { return; } } } // Verify type if (!($value instanceof self)) { $this->checkType($value, $schema, $path, $i); } // Verify disallowed items if (isset($schema->disallow)) { $initErrors = $this->getErrors(); $typeSchema = new \stdClass(); $typeSchema->type = $schema->disallow; $this->checkType($value, $typeSchema, $path); // if no new errors were raised it must be a disallowed value if (count($this->getErrors()) == count($initErrors)) { $this->addError($path, 'Disallowed value was matched', 'disallow'); } else { $this->errors = $initErrors; } } if (isset($schema->not)) { $initErrors = $this->getErrors(); $this->checkUndefined($value, $schema->not, $path, $i); // if no new errors were raised then the instance validated against the "not" schema if (count($this->getErrors()) == count($initErrors)) { $this->addError($path, 'Matched a schema which it should not', 'not'); } else { $this->errors = $initErrors; } } // Verify that dependencies are met if (isset($schema->dependencies) && $this->getTypeCheck()->isObject($value)) { $this->validateDependencies($value, $schema->dependencies, $path); } } /** * Check whether a default should be applied for this value * * @param mixed $schema * @param mixed $parentSchema * @param bool $requiredOnly * * @return bool */ private function shouldApplyDefaultValue($requiredOnly, $schema, $name = null, $parentSchema = null) { // required-only mode is off if (!$requiredOnly) { return true; } // draft-04 required is set if ( $name !== null && isset($parentSchema->required) && is_array($parentSchema->required) && in_array($name, $parentSchema->required) ) { return true; } // draft-03 required is set if (isset($schema->required) && !is_array($schema->required) && $schema->required) { return true; } // default case return false; } /** * Apply default values * * @param mixed $value * @param mixed $schema * @param JsonPointer $path */ protected function applyDefaultValues(&$value, $schema, $path) { // only apply defaults if feature is enabled if (!$this->factory->getConfig(self::CHECK_MODE_APPLY_DEFAULTS)) { return; } // apply defaults if appropriate $requiredOnly = $this->factory->getConfig(self::CHECK_MODE_ONLY_REQUIRED_DEFAULTS); if (isset($schema->properties) && LooseTypeCheck::isObject($value)) { // $value is an object or assoc array, and properties are defined - treat as an object foreach ($schema->properties as $currentProperty => $propertyDefinition) { $propertyDefinition = $this->factory->getSchemaStorage()->resolveRefSchema($propertyDefinition); if ( !LooseTypeCheck::propertyExists($value, $currentProperty) && property_exists($propertyDefinition, 'default') && $this->shouldApplyDefaultValue($requiredOnly, $propertyDefinition, $currentProperty, $schema) ) { // assign default value if (is_object($propertyDefinition->default)) { LooseTypeCheck::propertySet($value, $currentProperty, clone $propertyDefinition->default); } else { LooseTypeCheck::propertySet($value, $currentProperty, $propertyDefinition->default); } $this->appliedDefaults[] = $currentProperty; } } } elseif (isset($schema->items) && LooseTypeCheck::isArray($value)) { $items = array(); if (LooseTypeCheck::isArray($schema->items)) { $items = $schema->items; } elseif (isset($schema->minItems) && count($value) < $schema->minItems) { $items = array_fill(count($value), $schema->minItems - count($value), $schema->items); } // $value is an array, and items are defined - treat as plain array foreach ($items as $currentItem => $itemDefinition) { $itemDefinition = $this->factory->getSchemaStorage()->resolveRefSchema($itemDefinition); if ( !array_key_exists($currentItem, $value) && property_exists($itemDefinition, 'default') && $this->shouldApplyDefaultValue($requiredOnly, $itemDefinition)) { if (is_object($itemDefinition->default)) { $value[$currentItem] = clone $itemDefinition->default; } else { $value[$currentItem] = $itemDefinition->default; } } $path->setFromDefault(); } } elseif ( $value instanceof self && property_exists($schema, 'default') && $this->shouldApplyDefaultValue($requiredOnly, $schema)) { // $value is a leaf, not a container - apply the default directly $value = is_object($schema->default) ? clone $schema->default : $schema->default; $path->setFromDefault(); } } /** * Validate allOf, anyOf, and oneOf properties * * @param mixed $value * @param mixed $schema * @param JsonPointer $path * @param string $i */ protected function validateOfProperties(&$value, $schema, JsonPointer $path, $i = '') { // Verify type if ($value instanceof self) { return; } if (isset($schema->allOf)) { $isValid = true; foreach ($schema->allOf as $allOf) { $initErrors = $this->getErrors(); $this->checkUndefined($value, $allOf, $path, $i); $isValid = $isValid && (count($this->getErrors()) == count($initErrors)); } if (!$isValid) { $this->addError($path, 'Failed to match all schemas', 'allOf'); } } if (isset($schema->anyOf)) { $isValid = false; $startErrors = $this->getErrors(); $caughtException = null; foreach ($schema->anyOf as $anyOf) { $initErrors = $this->getErrors(); try { $this->checkUndefined($value, $anyOf, $path, $i); if ($isValid = (count($this->getErrors()) == count($initErrors))) { break; } } catch (ValidationException $e) { $isValid = false; } } if (!$isValid) { $this->addError($path, 'Failed to match at least one schema', 'anyOf'); } else { $this->errors = $startErrors; } } if (isset($schema->oneOf)) { $allErrors = array(); $matchedSchemas = 0; $startErrors = $this->getErrors(); foreach ($schema->oneOf as $oneOf) { try { $this->errors = array(); $this->checkUndefined($value, $oneOf, $path, $i); if (count($this->getErrors()) == 0) { $matchedSchemas++; } $allErrors = array_merge($allErrors, array_values($this->getErrors())); } catch (ValidationException $e) { // deliberately do nothing here - validation failed, but we want to check // other schema options in the OneOf field. } } if ($matchedSchemas !== 1) { $this->addErrors(array_merge($allErrors, $startErrors)); $this->addError($path, 'Failed to match exactly one schema', 'oneOf'); } else { $this->errors = $startErrors; } } } /** * Validate dependencies * * @param mixed $value * @param mixed $dependencies * @param JsonPointer $path * @param string $i */ protected function validateDependencies($value, $dependencies, JsonPointer $path, $i = '') { foreach ($dependencies as $key => $dependency) { if ($this->getTypeCheck()->propertyExists($value, $key)) { if (is_string($dependency)) { // Draft 3 string is allowed - e.g. "dependencies": {"bar": "foo"} if (!$this->getTypeCheck()->propertyExists($value, $dependency)) { $this->addError($path, "$key depends on $dependency and $dependency is missing", 'dependencies'); } } elseif (is_array($dependency)) { // Draft 4 must be an array - e.g. "dependencies": {"bar": ["foo"]} foreach ($dependency as $d) { if (!$this->getTypeCheck()->propertyExists($value, $d)) { $this->addError($path, "$key depends on $d and $d is missing", 'dependencies'); } } } elseif (is_object($dependency)) { // Schema - e.g. "dependencies": {"bar": {"properties": {"foo": {...}}}} $this->checkUndefined($value, $dependency, $path, $i); } } } } protected function validateUri($schema, $schemaUri = null) { $resolver = new UriResolver(); $retriever = $this->factory->getUriRetriever(); $jsonSchema = null; if ($resolver->isValid($schemaUri)) { $schemaId = property_exists($schema, 'id') ? $schema->id : null; $jsonSchema = $retriever->retrieve($schemaId, $schemaUri); } return $jsonSchema; } } php-json-schema-5.2.9/src/JsonSchema/Entity/000077500000000000000000000000001354400737700206325ustar00rootroot00000000000000php-json-schema-5.2.9/src/JsonSchema/Entity/JsonPointer.php000066400000000000000000000064761354400737700236320ustar00rootroot00000000000000 */ class JsonPointer { /** @var string */ private $filename; /** @var string[] */ private $propertyPaths = array(); /** * @var bool Whether the value at this path was set from a schema default */ private $fromDefault = false; /** * @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(); } /** * Mark the value at this path as being set from a schema default */ public function setFromDefault() { $this->fromDefault = true; } /** * Check whether the value at this path was set from a schema default * * @return bool */ public function fromDefault() { return $this->fromDefault; } } php-json-schema-5.2.9/src/JsonSchema/Exception/000077500000000000000000000000001354400737700213145ustar00rootroot00000000000000php-json-schema-5.2.9/src/JsonSchema/Exception/ExceptionInterface.php000066400000000000000000000001111354400737700255750ustar00rootroot00000000000000 */ class UnresolvableJsonPointerException extends InvalidArgumentException { } php-json-schema-5.2.9/src/JsonSchema/Exception/UriResolverException.php000066400000000000000000000005121354400737700261630ustar00rootroot00000000000000 */ 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; } } php-json-schema-5.2.9/src/JsonSchema/Rfc3339.php000066400000000000000000000015661354400737700211330ustar00rootroot00000000000000uriRetriever = $uriRetriever ?: new UriRetriever(); $this->uriResolver = $uriResolver ?: new UriResolver(); } /** * @return UriRetrieverInterface */ public function getUriRetriever() { return $this->uriRetriever; } /** * @return UriResolverInterface */ public function getUriResolver() { return $this->uriResolver; } /** * {@inheritdoc} */ public function addSchema($id, $schema = null) { if (is_null($schema) && $id !== self::INTERNAL_PROVIDED_SCHEMA_URI) { // if the schema was user-provided to Validator and is still null, then assume this is // what the user intended, as there's no way for us to retrieve anything else. User-supplied // schemas do not have an associated URI when passed via Validator::validate(). $schema = $this->uriRetriever->retrieve($id); } // cast array schemas to object if (is_array($schema)) { $schema = BaseConstraint::arrayToObjectRecursive($schema); } // workaround for bug in draft-03 & draft-04 meta-schemas (id & $ref defined with incorrect format) // see https://github.com/json-schema-org/JSON-Schema-Test-Suite/issues/177#issuecomment-293051367 if (is_object($schema) && property_exists($schema, 'id')) { if ($schema->id == 'http://json-schema.org/draft-04/schema#') { $schema->properties->id->format = 'uri-reference'; } elseif ($schema->id == 'http://json-schema.org/draft-03/schema#') { $schema->properties->id->format = 'uri-reference'; $schema->properties->{'$ref'}->format = 'uri-reference'; } } // resolve references $this->expandRefs($schema, $id); $this->schemas[$id] = $schema; } /** * Recursively resolve all references against the provided base * * @param mixed $schema * @param string $base */ private function expandRefs(&$schema, $base = null) { if (!is_object($schema)) { if (is_array($schema)) { foreach ($schema as &$member) { $this->expandRefs($member, $base); } } return; } if (property_exists($schema, 'id') && is_string($schema->id) && $base != $schema->id) { $base = $this->uriResolver->resolve($schema->id, $base); } if (property_exists($schema, '$ref') && is_string($schema->{'$ref'})) { $refPointer = new JsonPointer($this->uriResolver->resolve($schema->{'$ref'}, $base)); $schema->{'$ref'} = (string) $refPointer; } foreach ($schema as &$member) { $this->expandRefs($member, $base); } } /** * {@inheritdoc} */ public function getSchema($id) { if (!array_key_exists($id, $this->schemas)) { $this->addSchema($id); } return $this->schemas[$id]; } /** * {@inheritdoc} */ public function resolveRef($ref) { $jsonPointer = new JsonPointer($ref); // resolve filename for pointer $fileName = $jsonPointer->getFilename(); if (!strlen($fileName)) { throw new UnresolvableJsonPointerException(sprintf( "Could not resolve fragment '%s': no file is defined", $jsonPointer->getPropertyPathAsString() )); } // get & process the schema $refSchema = $this->getSchema($fileName); 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; } /** * {@inheritdoc} */ public function resolveRefSchema($refSchema) { if (is_object($refSchema) && property_exists($refSchema, '$ref') && is_string($refSchema->{'$ref'})) { $newSchema = $this->resolveRef($refSchema->{'$ref'}); $refSchema = (object) (get_object_vars($refSchema) + get_object_vars($newSchema)); unset($refSchema->{'$ref'}); } return $refSchema; } } php-json-schema-5.2.9/src/JsonSchema/SchemaStorageInterface.php000066400000000000000000000014421354400737700244360ustar00rootroot00000000000000 */ abstract class AbstractRetriever implements UriRetrieverInterface { /** * Media content type * * @var string */ protected $contentType; /** * {@inheritdoc} * * @see \JsonSchema\Uri\Retrievers\UriRetrieverInterface::getContentType() */ public function getContentType() { return $this->contentType; } } php-json-schema-5.2.9/src/JsonSchema/Uri/Retrievers/Curl.php000066400000000000000000000041571354400737700236740ustar00rootroot00000000000000 */ class Curl extends AbstractRetriever { protected $messageBody; public function __construct() { if (!function_exists('curl_init')) { // Cannot test this, because curl_init is present on all test platforms plus mock throw new RuntimeException('cURL not installed'); // @codeCoverageIgnore } } /** * {@inheritdoc} * * @see \JsonSchema\Uri\Retrievers\UriRetrieverInterface::retrieve() */ public function retrieve($uri) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $uri); curl_setopt($ch, CURLOPT_HEADER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: ' . Validator::SCHEMA_MEDIA_TYPE)); $response = curl_exec($ch); if (false === $response) { throw new \JsonSchema\Exception\ResourceNotFoundException('JSON schema not found'); } $this->fetchMessageBody($response); $this->fetchContentType($response); curl_close($ch); return $this->messageBody; } /** * @param string $response cURL HTTP response */ private function fetchMessageBody($response) { preg_match("/(?:\r\n){2}(.*)$/ms", $response, $match); $this->messageBody = $match[1]; } /** * @param string $response cURL HTTP response * * @return bool Whether the Content-Type header was found or not */ protected function fetchContentType($response) { if (0 < preg_match("/Content-Type:(\V*)/ims", $response, $match)) { $this->contentType = trim($match[1]); return true; } return false; } } php-json-schema-5.2.9/src/JsonSchema/Uri/Retrievers/FileGetContents.php000066400000000000000000000051521354400737700260200ustar00rootroot00000000000000 */ class FileGetContents extends AbstractRetriever { protected $messageBody; /** * {@inheritdoc} * * @see \JsonSchema\Uri\Retrievers\UriRetrieverInterface::retrieve() */ public function retrieve($uri) { $errorMessage = null; set_error_handler(function ($errno, $errstr) use (&$errorMessage) { $errorMessage = $errstr; }); $response = file_get_contents($uri); restore_error_handler(); 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); } $this->messageBody = $response; if (!empty($http_response_header)) { // $http_response_header cannot be tested, because it's defined in the method's local scope // See http://php.net/manual/en/reserved.variables.httpresponseheader.php for more info. $this->fetchContentType($http_response_header); // @codeCoverageIgnore } else { // @codeCoverageIgnore // 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 bool Whether the Content-Type header was found or not */ private function fetchContentType(array $headers) { foreach ($headers as $header) { if ($this->contentType = self::getContentTypeMatchInHeader($header)) { return true; } } return false; } /** * @param string $header * * @return string|null */ protected static function getContentTypeMatchInHeader($header) { if (0 < preg_match("/Content-Type:(\V*)/ims", $header, $match)) { return trim($match[1]); } return null; } } php-json-schema-5.2.9/src/JsonSchema/Uri/Retrievers/PredefinedArray.php000066400000000000000000000024241354400737700260260ustar00rootroot00000000000000 '{ ... }', * 'http://acme.com/schemas/address#' => '{ ... }', * )) * * $schema = $retriever->retrieve('http://acme.com/schemas/person#'); */ class PredefinedArray extends AbstractRetriever { /** * Contains schemas as URI => JSON * * @var array */ private $schemas; /** * Constructor * * @param array $schemas * @param string $contentType */ public function __construct(array $schemas, $contentType = Validator::SCHEMA_MEDIA_TYPE) { $this->schemas = $schemas; $this->contentType = $contentType; } /** * {@inheritdoc} * * @see \JsonSchema\Uri\Retrievers\UriRetrieverInterface::retrieve() */ public function retrieve($uri) { if (!array_key_exists($uri, $this->schemas)) { throw new \JsonSchema\Exception\ResourceNotFoundException(sprintf( 'The JSON schema "%s" was not found.', $uri )); } return $this->schemas[$uri]; } } php-json-schema-5.2.9/src/JsonSchema/Uri/Retrievers/UriRetrieverInterface.php000066400000000000000000000013521354400737700272310ustar00rootroot00000000000000 */ interface UriRetrieverInterface { /** * Retrieve a schema from the specified URI * * @param string $uri URI that resolves to a JSON schema * * @throws \JsonSchema\Exception\ResourceNotFoundException * * @return mixed string|null */ public function retrieve($uri); /** * Get media content type * * @return string */ public function getContentType(); } php-json-schema-5.2.9/src/JsonSchema/Uri/UriResolver.php000066400000000000000000000110731354400737700231110ustar00rootroot00000000000000 */ class UriResolver implements UriResolverInterface { /** * Parses a URI into five main components * * @param string $uri * * @return array */ public function parse($uri) { preg_match('|^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?|', $uri, $match); $components = array(); if (5 < count($match)) { $components = array( 'scheme' => $match[2], 'authority' => $match[4], 'path' => $match[5] ); } if (7 < count($match)) { $components['query'] = $match[7]; } if (9 < count($match)) { $components['fragment'] = $match[9]; } return $components; } /** * Builds a URI based on n array with the main components * * @param array $components * * @return string */ public function generate(array $components) { $uri = $components['scheme'] . '://' . $components['authority'] . $components['path']; if (array_key_exists('query', $components) && strlen($components['query'])) { $uri .= '?' . $components['query']; } if (array_key_exists('fragment', $components)) { $uri .= '#' . $components['fragment']; } return $uri; } /** * {@inheritdoc} */ public function resolve($uri, $baseUri = null) { // treat non-uri base as local file path if ( !is_null($baseUri) && !filter_var($baseUri, \FILTER_VALIDATE_URL) && !preg_match('|^[^/]+://|u', $baseUri) ) { if (is_file($baseUri)) { $baseUri = 'file://' . realpath($baseUri); } elseif (is_dir($baseUri)) { $baseUri = 'file://' . realpath($baseUri) . '/'; } else { $baseUri = 'file://' . getcwd() . '/' . $baseUri; } } if ($uri == '') { return $baseUri; } $components = $this->parse($uri); $path = $components['path']; if (!empty($components['scheme'])) { return $uri; } $baseComponents = $this->parse($baseUri); $basePath = $baseComponents['path']; $baseComponents['path'] = self::combineRelativePathWithBasePath($path, $basePath); if (isset($components['fragment'])) { $baseComponents['fragment'] = $components['fragment']; } return $this->generate($baseComponents); } /** * Tries to glue a relative path onto an absolute one * * @param string $relativePath * @param string $basePath * * @throws UriResolverException * * @return string Merged path */ public static function combineRelativePathWithBasePath($relativePath, $basePath) { $relativePath = self::normalizePath($relativePath); if ($relativePath == '') { return $basePath; } if ($relativePath[0] == '/') { return $relativePath; } $basePathSegments = explode('/', $basePath); preg_match('|^/?(\.\./(?:\./)*)*|', $relativePath, $match); $numLevelUp = strlen($match[0]) /3 + 1; if ($numLevelUp >= count($basePathSegments)) { throw new UriResolverException(sprintf("Unable to resolve URI '%s' from base '%s'", $relativePath, $basePath)); } $basePathSegments = array_slice($basePathSegments, 0, -$numLevelUp); $path = preg_replace('|^/?(\.\./(\./)*)*|', '', $relativePath); return implode('/', $basePathSegments) . '/' . $path; } /** * Normalizes a URI path component by removing dot-slash and double slashes * * @param string $path * * @return string */ private static function normalizePath($path) { $path = preg_replace('|((?parse($uri); return !empty($components); } } php-json-schema-5.2.9/src/JsonSchema/Uri/UriRetriever.php000066400000000000000000000222721354400737700232620ustar00rootroot00000000000000 */ class UriRetriever implements BaseUriRetrieverInterface { /** * @var array Map of URL translations */ protected $translationMap = array( // use local copies of the spec schemas '|^https?://json-schema.org/draft-(0[34])/schema#?|' => 'package://dist/schema/json-schema-draft-$1.json' ); /** * @var array A list of endpoints for media type check exclusion */ protected $allowedInvalidContentTypeEndpoints = array( 'http://json-schema.org/', 'https://json-schema.org/' ); /** * @var null|UriRetrieverInterface */ protected $uriRetriever = null; /** * @var array|object[] * * @see loadSchema */ private $schemaCache = array(); /** * Adds an endpoint to the media type validation exclusion list * * @param string $endpoint */ public function addInvalidContentTypeEndpoint($endpoint) { $this->allowedInvalidContentTypeEndpoints[] = $endpoint; } /** * Guarantee the correct media type was encountered * * @param UriRetrieverInterface $uriRetriever * @param string $uri * * @return bool|void */ public function confirmMediaType($uriRetriever, $uri) { $contentType = $uriRetriever->getContentType(); if (is_null($contentType)) { // Well, we didn't get an invalid one return; } if (in_array($contentType, array(Validator::SCHEMA_MEDIA_TYPE, 'application/json'))) { return; } foreach ($this->allowedInvalidContentTypeEndpoints as $endpoint) { if (strpos($uri, $endpoint) === 0) { return true; } } throw new InvalidSchemaMediaTypeException(sprintf('Media type %s expected', Validator::SCHEMA_MEDIA_TYPE)); } /** * Get a URI Retriever * * If none is specified, sets a default FileGetContents retriever and * returns that object. * * @return UriRetrieverInterface */ public function getUriRetriever() { if (is_null($this->uriRetriever)) { $this->setUriRetriever(new FileGetContents()); } return $this->uriRetriever; } /** * Resolve a schema based on pointer * * URIs can have a fragment at the end in the format of * #/path/to/object and we are to look up the 'path' property of * the first object then the 'to' and 'object' properties. * * @param object $jsonSchema JSON Schema contents * @param string $uri JSON Schema URI * * @throws ResourceNotFoundException * * @return object JSON Schema after walking down the fragment pieces */ public function resolvePointer($jsonSchema, $uri) { $resolver = new UriResolver(); $parsed = $resolver->parse($uri); if (empty($parsed['fragment'])) { return $jsonSchema; } $path = explode('/', $parsed['fragment']); while ($path) { $pathElement = array_shift($path); if (!empty($pathElement)) { $pathElement = str_replace('~1', '/', $pathElement); $pathElement = str_replace('~0', '~', $pathElement); if (!empty($jsonSchema->$pathElement)) { $jsonSchema = $jsonSchema->$pathElement; } else { throw new ResourceNotFoundException( 'Fragment "' . $parsed['fragment'] . '" not found' . ' in ' . $uri ); } if (!is_object($jsonSchema)) { throw new ResourceNotFoundException( 'Fragment part "' . $pathElement . '" is no object ' . ' in ' . $uri ); } } } return $jsonSchema; } /** * {@inheritdoc} */ public function retrieve($uri, $baseUri = null, $translate = true) { $resolver = new UriResolver(); $resolvedUri = $fetchUri = $resolver->resolve($uri, $baseUri); //fetch URL without #fragment $arParts = $resolver->parse($resolvedUri); if (isset($arParts['fragment'])) { unset($arParts['fragment']); $fetchUri = $resolver->generate($arParts); } // apply URI translations if ($translate) { $fetchUri = $this->translate($fetchUri); } $jsonSchema = $this->loadSchema($fetchUri); // Use the JSON pointer if specified $jsonSchema = $this->resolvePointer($jsonSchema, $resolvedUri); if ($jsonSchema instanceof \stdClass) { $jsonSchema->id = $resolvedUri; } return $jsonSchema; } /** * Fetch a schema from the given URI, json-decode it and return it. * Caches schema objects. * * @param string $fetchUri Absolute URI * * @return object JSON schema object */ protected function loadSchema($fetchUri) { if (isset($this->schemaCache[$fetchUri])) { return $this->schemaCache[$fetchUri]; } $uriRetriever = $this->getUriRetriever(); $contents = $this->uriRetriever->retrieve($fetchUri); $this->confirmMediaType($uriRetriever, $fetchUri); $jsonSchema = json_decode($contents); if (JSON_ERROR_NONE < $error = json_last_error()) { throw new JsonDecodingException($error); } $this->schemaCache[$fetchUri] = $jsonSchema; return $jsonSchema; } /** * Set the URI Retriever * * @param UriRetrieverInterface $uriRetriever * * @return $this for chaining */ public function setUriRetriever(UriRetrieverInterface $uriRetriever) { $this->uriRetriever = $uriRetriever; return $this; } /** * Parses a URI into five main components * * @param string $uri * * @return array */ public function parse($uri) { preg_match('|^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?|', $uri, $match); $components = array(); if (5 < count($match)) { $components = array( 'scheme' => $match[2], 'authority' => $match[4], 'path' => $match[5] ); } if (7 < count($match)) { $components['query'] = $match[7]; } if (9 < count($match)) { $components['fragment'] = $match[9]; } return $components; } /** * Builds a URI based on n array with the main components * * @param array $components * * @return string */ public function generate(array $components) { $uri = $components['scheme'] . '://' . $components['authority'] . $components['path']; if (array_key_exists('query', $components)) { $uri .= $components['query']; } if (array_key_exists('fragment', $components)) { $uri .= $components['fragment']; } return $uri; } /** * Resolves a URI * * @param string $uri Absolute or relative * @param string $baseUri Optional base URI * * @return string */ public function resolve($uri, $baseUri = null) { $components = $this->parse($uri); $path = $components['path']; if ((array_key_exists('scheme', $components)) && ('http' === $components['scheme'])) { return $uri; } $baseComponents = $this->parse($baseUri); $basePath = $baseComponents['path']; $baseComponents['path'] = UriResolver::combineRelativePathWithBasePath($path, $basePath); return $this->generate($baseComponents); } /** * @param string $uri * * @return bool */ public function isValid($uri) { $components = $this->parse($uri); return !empty($components); } /** * Set a URL translation rule */ public function setTranslation($from, $to) { $this->translationMap[$from] = $to; } /** * Apply URI translation rules */ public function translate($uri) { foreach ($this->translationMap as $from => $to) { $uri = preg_replace($from, $to, $uri); } // translate references to local files within the json-schema package $uri = preg_replace('|^package://|', sprintf('file://%s/', realpath(__DIR__ . '/../../..')), $uri); return $uri; } } php-json-schema-5.2.9/src/JsonSchema/UriResolverInterface.php000066400000000000000000000010221354400737700241640ustar00rootroot00000000000000 * @author Bruno Prieto Reis * * @see README.md */ class Validator extends BaseConstraint { const SCHEMA_MEDIA_TYPE = 'application/schema+json'; const ERROR_NONE = 0x00000000; const ERROR_ALL = 0xFFFFFFFF; const ERROR_DOCUMENT_VALIDATION = 0x00000001; const ERROR_SCHEMA_VALIDATION = 0x00000002; /** * Validates the given data against the schema and returns an object containing the results * Both the php object and the schema are supposed to be a result of a json_decode call. * The validation works as defined by the schema proposal in http://json-schema.org. * * Note that the first argument is passed by reference, so you must pass in a variable. */ public function validate(&$value, $schema = null, $checkMode = null) { // make sure $schema is an object if (is_array($schema)) { $schema = self::arrayToObjectRecursive($schema); } // set checkMode $initialCheckMode = $this->factory->getConfig(); if ($checkMode !== null) { $this->factory->setConfig($checkMode); } // add provided schema to SchemaStorage with internal URI to allow internal $ref resolution if (is_object($schema) && property_exists($schema, 'id')) { $schemaURI = $schema->id; } else { $schemaURI = SchemaStorage::INTERNAL_PROVIDED_SCHEMA_URI; } $this->factory->getSchemaStorage()->addSchema($schemaURI, $schema); $validator = $this->factory->createInstanceFor('schema'); $validator->check( $value, $this->factory->getSchemaStorage()->getSchema($schemaURI) ); $this->factory->setConfig($initialCheckMode); $this->addErrors(array_unique($validator->getErrors(), SORT_REGULAR)); return $validator->getErrorMask(); } /** * Alias to validate(), to maintain backwards-compatibility with the previous API */ public function check($value, $schema) { return $this->validate($value, $schema); } /** * Alias to validate(), to maintain backwards-compatibility with the previous API */ public function coerce(&$value, $schema) { return $this->validate($value, $schema, Constraint::CHECK_MODE_COERCE_TYPES); } } php-json-schema-5.2.9/tests/000077500000000000000000000000001354400737700156775ustar00rootroot00000000000000php-json-schema-5.2.9/tests/Constraints/000077500000000000000000000000001354400737700202065ustar00rootroot00000000000000php-json-schema-5.2.9/tests/Constraints/AdditionalPropertiesTest.php000066400000000000000000000117741354400737700257160ustar00rootroot00000000000000 '', 'pointer' => '', 'message' => 'The property additionalProp is not defined and the definition does not allow additional properties', 'constraint' => 'additionalProp', 'context' => Validator::ERROR_DOCUMENT_VALIDATION ) ) ), array( '{ "prop":"1", "additionalProp":"2" }', '{ "type":"object", "properties":{ "prop":{"type":"string"} }, "additionalProperties": false }' ), array( '{ "prop":"1", "additionalProp":2 }', '{ "type":"object", "properties":{ "prop":{"type":"string"} }, "additionalProperties": {"type":"string"} }' ), array( '{ "prop":"1", "additionalProp":2 }', '{ "type":"object", "properties":{ "prop":{"type":"string"} }, "additionalProperties": {"type":"string"} }' ), array( '{ "prop1": "a", "prop2": "b" }', '{ "type": "object", "additionalProperties": { "type": "boolean" } }' ), array( '{ "prop1": "a", "prop2": "b" }', '{ "type": "object", "additionalProperties": false }' ), ); } public function getValidTests() { return array( array( '{ "prop":"1", "additionalProp":"2" }', '{ "type":"object", "properties":{ "prop":{"type":"string"} } }' ), array( '{ "prop":"1", "additionalProp":"2" }', '{ "type":"object", "properties":{ "prop":{"type":"string"} } }' ), array( '{ "prop":"1", "additionalProp":"2" }', '{ "type":"object", "properties":{ "prop":{"type":"string"} }, "additionalProperties": {"type":"string"} }' ), array( '{ "prop":"1", "additionalProp":[] }', '{ "type":"object", "properties":{ "prop":{"type":"string"} }, "additionalProperties": true }' ), array( '{ "prop1": "a", "prop2": "b" }', '{ "type": "object", "additionalProperties": { "type": "string" } }' ), array( '{ "prop1": "a", "prop2": "b" }', '{ "type": "object", "additionalProperties": true }' ), ); } } php-json-schema-5.2.9/tests/Constraints/ArraysTest.php000066400000000000000000000202641354400737700230240ustar00rootroot00000000000000validateSchema) { $checkMode |= Constraint::CHECK_MODE_VALIDATE_SCHEMA; } $schemaStorage = new SchemaStorage($this->getUriRetrieverMock(json_decode($schema))); $schema = $schemaStorage->getSchema('http://www.my-domain.com/schema.json'); if (is_object($schema) && !isset($schema->{'$schema'})) { $schema->{'$schema'} = $this->schemaSpec; } $validator = new Validator(new Factory($schemaStorage, null, $checkMode)); $checkValue = json_decode($input); $errorMask = $validator->validate($checkValue, $schema); $this->assertTrue((bool) ($errorMask & Validator::ERROR_DOCUMENT_VALIDATION)); $this->assertGreaterThan(0, $validator->numErrors()); if (array() !== $errors) { $this->assertEquals($errors, $validator->getErrors(), print_r($validator->getErrors(), true)); } $this->assertFalse($validator->isValid(), print_r($validator->getErrors(), true)); } /** * @dataProvider getInvalidForAssocTests */ public function testInvalidCasesUsingAssoc($input, $schema, $checkMode = Constraint::CHECK_MODE_TYPE_CAST, $errors = array()) { $checkMode = $checkMode === null ? Constraint::CHECK_MODE_TYPE_CAST : $checkMode; if ($this->validateSchema) { $checkMode |= Constraint::CHECK_MODE_VALIDATE_SCHEMA; } if (!($checkMode & Constraint::CHECK_MODE_TYPE_CAST)) { $this->markTestSkipped('Test indicates that it is not for "CHECK_MODE_TYPE_CAST"'); } $schemaStorage = new SchemaStorage($this->getUriRetrieverMock(json_decode($schema))); $schema = $schemaStorage->getSchema('http://www.my-domain.com/schema.json'); if (is_object($schema) && !isset($schema->{'$schema'})) { $schema->{'$schema'} = $this->schemaSpec; } $validator = new Validator(new Factory($schemaStorage, null, $checkMode)); $checkValue = json_decode($input, true); $errorMask = $validator->validate($checkValue, $schema); $this->assertTrue((bool) ($errorMask & Validator::ERROR_DOCUMENT_VALIDATION)); $this->assertGreaterThan(0, $validator->numErrors()); if (array() !== $errors) { $this->assertEquals($errors, $validator->getErrors(), print_r($validator->getErrors(), true)); } $this->assertFalse($validator->isValid(), print_r($validator->getErrors(), true)); } /** * @dataProvider getValidTests */ public function testValidCases($input, $schema, $checkMode = Constraint::CHECK_MODE_NORMAL) { if ($this->validateSchema) { $checkMode |= Constraint::CHECK_MODE_VALIDATE_SCHEMA; } $schemaStorage = new SchemaStorage($this->getUriRetrieverMock(json_decode($schema))); $schema = $schemaStorage->getSchema('http://www.my-domain.com/schema.json'); if (is_object($schema) && !isset($schema->{'$schema'})) { $schema->{'$schema'} = $this->schemaSpec; } $validator = new Validator(new Factory($schemaStorage, null, $checkMode)); $checkValue = json_decode($input); $errorMask = $validator->validate($checkValue, $schema); $this->assertEquals(0, $errorMask); $this->assertTrue($validator->isValid(), print_r($validator->getErrors(), true)); } /** * @dataProvider getValidForAssocTests */ public function testValidCasesUsingAssoc($input, $schema, $checkMode = Constraint::CHECK_MODE_TYPE_CAST) { if ($this->validateSchema) { $checkMode |= Constraint::CHECK_MODE_VALIDATE_SCHEMA; } if (!($checkMode & Constraint::CHECK_MODE_TYPE_CAST)) { $this->markTestSkipped('Test indicates that it is not for "CHECK_MODE_TYPE_CAST"'); } $schema = json_decode($schema); $schemaStorage = new SchemaStorage($this->getUriRetrieverMock($schema), new UriResolver()); $schema = $schemaStorage->getSchema('http://www.my-domain.com/schema.json'); if (is_object($schema) && !isset($schema->{'$schema'})) { $schema->{'$schema'} = $this->schemaSpec; } $value = json_decode($input, true); $validator = new Validator(new Factory($schemaStorage, null, $checkMode)); $errorMask = $validator->validate($value, $schema); $this->assertEquals(0, $errorMask); $this->assertTrue($validator->isValid(), print_r($validator->getErrors(), true)); } /** * @return array[] */ abstract public function getValidTests(); /** * @return array[] */ public function getValidForAssocTests() { return $this->getValidTests(); } /** * @return array[] */ abstract public function getInvalidTests(); /** * @return array[] */ public function getInvalidForAssocTests() { return $this->getInvalidTests(); } } php-json-schema-5.2.9/tests/Constraints/BasicTypesTest.php000066400000000000000000000101131354400737700236210ustar00rootroot00000000000000getUriRetrieverMock(json_decode($schema))); $schema = $schemaStorage->getSchema('http://www.my-domain.com/schema.json'); $validator = new Validator(new Factory($schemaStorage, null, $checkMode)); $value = json_decode($input, true); $validator->validate($value, $schema, $checkMode); $this->assertTrue($validator->isValid(), print_r($validator->getErrors(), true)); } /** * @dataProvider getValidCoerceTests */ public function testValidCoerceCases($input, $schema, $errors = array()) { $checkMode = Constraint::CHECK_MODE_TYPE_CAST | Constraint::CHECK_MODE_COERCE_TYPES; $schemaStorage = new SchemaStorage($this->getUriRetrieverMock(json_decode($schema))); $schema = $schemaStorage->getSchema('http://www.my-domain.com/schema.json'); $validator = new Validator(new Factory($schemaStorage, null, $checkMode)); $value = json_decode($input); $this->assertTrue(gettype($value->number) == 'string'); $this->assertTrue(gettype($value->integer) == 'string'); $this->assertTrue(gettype($value->boolean) == 'string'); $validator->validate($value, $schema, $checkMode); $this->assertTrue(gettype($value->number) == 'double'); $this->assertTrue(gettype($value->integer) == 'integer'); $this->assertTrue(gettype($value->negativeInteger) == 'integer'); $this->assertTrue(gettype($value->boolean) == 'boolean'); $this->assertTrue($value->number === 1.5); $this->assertTrue($value->integer === 1); $this->assertTrue($value->negativeInteger === -2); $this->assertTrue($value->boolean === true); $this->assertTrue(gettype($value->multitype1) == 'boolean'); $this->assertTrue(gettype($value->multitype2) == 'double'); $this->assertTrue(gettype($value->multitype3) == 'integer'); $this->assertTrue($value->number === 1.5); $this->assertTrue($value->integer === 1); $this->assertTrue($value->negativeInteger === -2); $this->assertTrue($value->boolean === true); $this->assertTrue($validator->isValid(), print_r($validator->getErrors(), true)); } /** * @dataProvider getInvalidCoerceTests */ public function testInvalidCoerceCases($input, $schema, $errors = array()) { $checkMode = Constraint::CHECK_MODE_TYPE_CAST | Constraint::CHECK_MODE_COERCE_TYPES; $schemaStorage = new SchemaStorage($this->getUriRetrieverMock(json_decode($schema))); $schema = $schemaStorage->getSchema('http://www.my-domain.com/schema.json'); $validator = new Validator(new Factory($schemaStorage, null, $checkMode)); $value = json_decode($input); $validator->validate($value, $schema, $checkMode); if (array() !== $errors) { $this->assertEquals($errors, $validator->getErrors(), print_r($validator->getErrors(), true)); } $this->assertFalse($validator->isValid(), print_r($validator->getErrors(), true)); } /** * @dataProvider getInvalidCoerceTests */ public function testInvalidCoerceCasesUsingAssoc($input, $schema, $errors = array()) { $checkMode = Constraint::CHECK_MODE_TYPE_CAST | Constraint::CHECK_MODE_COERCE_TYPES; $schemaStorage = new SchemaStorage($this->getUriRetrieverMock(json_decode($schema))); $schema = $schemaStorage->getSchema('http://www.my-domain.com/schema.json'); $validator = new Validator(new Factory($schemaStorage, null, $checkMode)); $value = json_decode($input, true); $validator->validate($value, $schema, $checkMode); if (array() !== $errors) { $this->assertEquals($errors, $validator->getErrors(), print_r($validator->getErrors(), true)); } $this->assertFalse($validator->isValid(), print_r($validator->getErrors(), true)); } public function testCoerceAPI() { $input = json_decode('{"propertyOne": "10"}'); $schema = json_decode('{"properties":{"propertyOne":{"type":"number"}}}'); $v = new Validator(); $v->coerce($input, $schema); $this->assertEquals('{"propertyOne":10}', json_encode($input)); } public function getValidCoerceTests() { return array( array( '{ "string":"string test", "number":"1.5", "integer":"1", "negativeInteger":"-2", "boolean":"true", "object":{}, "array":[], "null":null, "any": "string", "allOf": "1", "multitype1": "false", "multitype2": "1.2", "multitype3": "7", "arrayOfIntegers":["-1","0","1"], "tupleTyping":["1","2.2","true"], "any1": 2.6, "any2": 4, "any3": false, "any4": {}, "any5": [], "any6": null }', '{ "type":"object", "properties":{ "string":{"type":"string"}, "number":{"type":"number"}, "integer":{"type":"integer"}, "negativeInteger":{"type":"integer"}, "boolean":{"type":"boolean"}, "object":{"type":"object"}, "array":{"type":"array"}, "null":{"type":"null"}, "any": {"type":"any"}, "allOf" : {"allOf":[{ "type" : "string" },{ "type" : "integer" }]}, "multitype1": {"type":["boolean","integer","number"]}, "multitype2": {"type":["boolean","integer","number"]}, "multitype3": {"type":["boolean","integer","number"]}, "arrayOfIntegers":{ "items":{ "type":"integer" } }, "tupleTyping":{ "type":"array", "items":[ {"type":"integer"}, {"type":"number"} ], "additionalItems":{"type":"boolean"} }, "any1": {"type":"any"}, "any2": {"type":"any"}, "any3": {"type":"any"}, "any4": {"type":"any"}, "any5": {"type":"any"}, "any6": {"type":"any"} }, "additionalProperties":false }', ), ); } public function getInvalidCoerceTests() { return array( array( '{ "string":null }', '{ "type":"object", "properties": { "string":{"type":"string"} }, "additionalProperties":false }', ), array( '{ "number":"five" }', '{ "type":"object", "properties": { "number":{"type":"number"} }, "additionalProperties":false }', ), array( '{ "integer":"5.2" }', '{ "type":"object", "properties": { "integer":{"type":"integer"} }, "additionalProperties":false }', ), array( '{ "boolean":"0" }', '{ "type":"object", "properties": { "boolean":{"type":"boolean"} }, "additionalProperties":false }', ), array( '{ "object":null }', '{ "type":"object", "properties": { "object":{"type":"object"} }, "additionalProperties":false }', ), array( '{ "array":null }', '{ "type":"object", "properties": { "array":{"type":"array"} }, "additionalProperties":false }', ), array( '{ "null":1 }', '{ "type":"object", "properties": { "null":{"type":"null"} }, "additionalProperties":false }', ), ); } } php-json-schema-5.2.9/tests/Constraints/DefaultPropertiesTest.php000066400000000000000000000236711354400737700252310ustar00rootroot00000000000000addSchema('local://testSchema', json_decode($schema)); $factory = new Factory($schemaStorage); $validator = new Validator($factory); $validator->validate($inputDecoded, json_decode('{"$ref": "local://testSchema"}'), $checkMode); $this->assertTrue($validator->isValid(), print_r($validator->getErrors(), true)); if ($expectOutput !== null) { $this->assertEquals($expectOutput, json_encode($inputDecoded)); } } /** * @dataProvider getValidTests */ public function testValidCasesUsingAssoc($input, $schema, $expectOutput = null, $checkMode = 0) { $input = json_decode($input, true); $checkMode |= Constraint::CHECK_MODE_TYPE_CAST; self::testValidCases($input, $schema, $expectOutput, $checkMode); } /** * @dataProvider getValidTests */ public function testValidCasesUsingAssocWithoutTypeCast($input, $schema, $expectOutput = null, $checkMode = 0) { $input = json_decode($input, true); self::testValidCases($input, $schema, $expectOutput, $checkMode); } public function testNoModificationViaReferences() { $input = json_decode('{}'); $schema = json_decode('{"properties":{"propertyOne":{"default":"valueOne"}}}'); $validator = new Validator(); $validator->validate($input, $schema, Constraint::CHECK_MODE_TYPE_CAST | Constraint::CHECK_MODE_APPLY_DEFAULTS); $this->assertEquals('{"propertyOne":"valueOne"}', json_encode($input)); $input->propertyOne = 'valueTwo'; $this->assertEquals('valueOne', $schema->properties->propertyOne->default); } public function testLeaveBasicTypesAlone() { $input = json_decode('"ThisIsAString"'); $schema = json_decode('{"properties": {"propertyOne": {"default": "valueOne"}}}'); $validator = new Validator(); $validator->validate($input, $schema, Constraint::CHECK_MODE_APPLY_DEFAULTS); $this->assertEquals('"ThisIsAString"', json_encode($input)); $schema = json_decode('{"items":[{"type":"string","default":"valueOne"}]}'); $validator->validate($input, $schema, Constraint::CHECK_MODE_APPLY_DEFAULTS); $this->assertEquals('"ThisIsAString"', json_encode($input)); } } php-json-schema-5.2.9/tests/Constraints/DependenciesTest.php000066400000000000000000000100231354400737700241410ustar00rootroot00000000000000factory = new Factory(); } /** * @dataProvider constraintNameProvider * * @param string $constraintName * @param string $expectedClass */ public function testCreateInstanceForConstraintName($constraintName, $expectedClass) { $constraint = $this->factory->createInstanceFor($constraintName); $this->assertInstanceOf($expectedClass, $constraint); $this->assertInstanceOf('JsonSchema\Constraints\ConstraintInterface', $constraint); } public function constraintNameProvider() { return array( array('array', 'JsonSchema\Constraints\CollectionConstraint'), array('collection', 'JsonSchema\Constraints\CollectionConstraint'), array('object', 'JsonSchema\Constraints\ObjectConstraint'), array('type', 'JsonSchema\Constraints\TypeConstraint'), array('undefined', 'JsonSchema\Constraints\UndefinedConstraint'), array('string', 'JsonSchema\Constraints\StringConstraint'), array('number', 'JsonSchema\Constraints\NumberConstraint'), array('enum', 'JsonSchema\Constraints\EnumConstraint'), array('format', 'JsonSchema\Constraints\FormatConstraint'), array('schema', 'JsonSchema\Constraints\SchemaConstraint'), ); } /** * @dataProvider invalidConstraintNameProvider * * @param string $constraintName */ public function testExceptionWhenCreateInstanceForInvalidConstraintName($constraintName) { $this->setExpectedException('JsonSchema\Exception\InvalidArgumentException'); $this->factory->createInstanceFor($constraintName); } public function invalidConstraintNameProvider() { return array( array('invalidConstraintName'), ); } /** * @expectedException \JsonSchema\Exception\InvalidArgumentException */ public function testSetConstraintClassExistsCondition() { $this->factory->setConstraintClass('string', 'SomeConstraint'); } /** * @expectedException \JsonSchema\Exception\InvalidArgumentException */ public function testSetConstraintClassImplementsCondition() { $this->factory->setConstraintClass('string', 'JsonSchema\Tests\Constraints\MyBadConstraint'); } public function testSetConstraintClassInstance() { $this->factory->setConstraintClass('string', 'JsonSchema\Tests\Constraints\MyStringConstraint'); $constraint = $this->factory->createInstanceFor('string'); $this->assertInstanceOf('JsonSchema\Tests\Constraints\MyStringConstraint', $constraint); $this->assertInstanceOf('JsonSchema\Constraints\ConstraintInterface', $constraint); } public function testCheckMode() { $f = new Factory(); // test default value $this->assertEquals(Constraint::CHECK_MODE_NORMAL, $f->getConfig()); // test overriding config $f->setConfig(Constraint::CHECK_MODE_COERCE_TYPES); $this->assertEquals(Constraint::CHECK_MODE_COERCE_TYPES, $f->getConfig()); // test adding config $f->addConfig(Constraint::CHECK_MODE_NORMAL); $this->assertEquals(Constraint::CHECK_MODE_NORMAL | Constraint::CHECK_MODE_COERCE_TYPES, $f->getConfig()); // test getting filtered config $this->assertEquals(Constraint::CHECK_MODE_NORMAL, $f->getConfig(Constraint::CHECK_MODE_NORMAL)); // test removing config $f->removeConfig(Constraint::CHECK_MODE_COERCE_TYPES); $this->assertEquals(Constraint::CHECK_MODE_NORMAL, $f->getConfig()); // test resetting to defaults $f->setConfig(Constraint::CHECK_MODE_COERCE_TYPES | Constraint::CHECK_MODE_TYPE_CAST); $f->setConfig(); $this->assertEquals(Constraint::CHECK_MODE_NORMAL, $f->getConfig()); } } php-json-schema-5.2.9/tests/Constraints/FormatTest.php000066400000000000000000000200071354400737700230060ustar00rootroot00000000000000check($checkValue, $schema); $this->assertEmpty($validator->getErrors()); } public function testRegex() { $validator = new FormatConstraint(); $schema = new \stdClass(); $schema->format = 'regex'; $validator->reset(); $checkValue = '\d+'; $validator->check($checkValue, $schema); $this->assertEmpty($validator->getErrors()); $validator->reset(); $checkValue = '^(abc]'; $validator->check($checkValue, $schema); $this->assertCount(1, $validator->getErrors()); $validator->reset(); $checkValue = '^猡猡獛$'; $validator->check($checkValue, $schema); $this->assertEmpty($validator->getErrors()); } /** * @dataProvider getValidFormats */ public function testValidFormat($string, $format) { $validator = new FormatConstraint(); $schema = new \stdClass(); $schema->format = $format; $validator->check($string, $schema); $this->assertEmpty($validator->getErrors()); } /** * @dataProvider getInvalidFormats */ public function testInvalidFormat($string, $format) { $validator = new FormatConstraint(); $schema = new \stdClass(); $schema->format = $format; $validator->check($string, $schema); $this->assertCount(1, $validator->getErrors(), 'Expected 1 error'); } /** * @dataProvider getInvalidFormats */ public function testDisabledFormat($string, $format) { $factory = new Factory(); $validator = new FormatConstraint($factory); $schema = new \stdClass(); $schema->format = $format; $factory->addConfig(Constraint::CHECK_MODE_DISABLE_FORMAT); $validator->check($string, $schema); $this->assertEmpty($validator->getErrors()); } public function getValidFormats() { return array( array('2001-01-23', 'date'), array('2000-02-29', 'date'), array('12:22:01', 'time'), array('00:00:00', 'time'), array('23:59:59', 'time'), array('2000-05-01T12:12:12Z', 'date-time'), array('2000-05-01T12:12:12+0100', 'date-time'), array('2000-05-01T12:12:12+01:00', 'date-time'), array('2000-05-01T12:12:12.123456Z', 'date-time'), array('2000-05-01T12:12:12.123Z', 'date-time'), array('2000-05-01T12:12:12.123000Z', 'date-time'), array('2000-05-01T12:12:12.0Z', 'date-time'), array('2000-05-01T12:12:12.000Z', 'date-time'), array('2000-05-01T12:12:12.000000Z', 'date-time'), array('0', 'utc-millisec'), array('aqua', 'color'), array('black', 'color'), array('blue', 'color'), array('fuchsia', 'color'), array('gray', 'color'), array('green', 'color'), array('lime', 'color'), array('maroon', 'color'), array('navy', 'color'), array('olive', 'color'), array('orange', 'color'), array('purple', 'color'), array('red', 'color'), array('silver', 'color'), array('teal', 'color'), array('white', 'color'), array('yellow', 'color'), array('#fff', 'color'), array('#00cc00', 'color'), array('background: blue', 'style'), array('color: #000;', 'style'), array('555 320 1212', 'phone'), array('http://bluebox.org', 'uri'), array('//bluebox.org', 'uri-reference'), array('/absolutePathReference/', 'uri-reference'), array('./relativePathReference/', 'uri-reference'), array('./relative:PathReference/', 'uri-reference'), array('relativePathReference/', 'uri-reference'), array('relative/Path:Reference/', 'uri-reference'), array('info@something.edu', 'email'), array('10.10.10.10', 'ip-address'), array('127.0.0.1', 'ip-address'), array('::ff', 'ipv6'), array('www.example.com', 'host-name'), array('3v4l.org', 'host-name'), array('a-valid-host.com', 'host-name'), array('localhost', 'host-name'), array('anything', '*'), array('unknown', '*'), ); } public function getInvalidFormats() { return array( array('January 1st, 1910', 'date'), array('199-01-1', 'date'), array('2012-0-11', 'date'), array('2012-10-1', 'date'), array('24:01:00', 'time'), array('00:00:60', 'time'), array('25:00:00', 'time'), array('invalid_value_2000-05-01T12:12:12Z', 'date-time'), array('2000-05-01T12:12:12Z_invalid_value', 'date-time'), array('1999-1-11T00:00:00Z', 'date-time'), array('1999-01-11T00:00:00+100', 'date-time'), array('1999-01-11T00:00:00+1:00', 'date-time'), array('1999.000Z-01-11T00:00:00+1:00', 'date-time'), array(PHP_INT_MAX, 'utc-millisec'), array('grey', 'color'), array('#HHH', 'color'), array('#000a', 'color'), array('#aa', 'color'), array('background; blue', 'style'), array('1 123 4424', 'phone'), array('htt:/bluebox.org', 'uri'), array('.relative:path/reference/', 'uri'), array('', 'uri'), array('//bluebox.org', 'uri'), array('/absolutePathReference/', 'uri'), array('./relativePathReference/', 'uri'), array('./relative:PathReference/', 'uri'), array('relativePathReference/', 'uri'), array('relative/Path:Reference/', 'uri'), array('info@somewhere', 'email'), array('256.2.2.2', 'ip-address'), array(':::ff', 'ipv6'), array('@localhost', 'host-name'), array('..nohost', 'host-name'), ); } public function getValidTests() { return array( array( '{ "counter": "10" }', '{ "type": "object", "properties": { "counter": { "type": "string", "format": "regex", "pattern": "[0-9]+" } } }'), ); } public function getInvalidTests() { return array( array( '{ "counter": "blue" }', '{ "type": "object", "properties": { "counter": { "type": "string", "format": "regex", "pattern": "[0-9]+" } } }' ), array( '{ "color": "blueberry" }', '{ "type": "object", "properties": { "color": { "type": "string", "format": "color" } } }' ) ); } } php-json-schema-5.2.9/tests/Constraints/LongArraysTest.php000066400000000000000000000061231354400737700236420ustar00rootroot00000000000000p_array = array_map(function ($i) { return '#' . $i; }, range(1, 100000)); $input = json_encode($tmp); $schemaStorage = new SchemaStorage($this->getUriRetrieverMock(json_decode($schema))); $schema = $schemaStorage->getSchema('http://www.my-domain.com/schema.json'); $validator = new Validator(new Factory($schemaStorage)); $checkValue = json_decode($input); $validator->check($checkValue, $schema); $this->assertTrue($validator->isValid(), print_r($validator->getErrors(), true)); } public function testLongNumberArray() { $schema = '{ "type":"object", "properties":{ "p_array":{ "type":"array", "items":{"type":"number"} } } }'; $tmp = new \stdClass(); $tmp->p_array = array_map(function ($i) { return rand(1, 1000) / 1000.0; }, range(1, 100000)); $input = json_encode($tmp); $schemaStorage = new SchemaStorage($this->getUriRetrieverMock(json_decode($schema))); $schema = $schemaStorage->getSchema('http://www.my-domain.com/schema.json'); $validator = new Validator(new Factory($schemaStorage)); $checkValue = json_decode($input); $validator->check($checkValue, $schema); $this->assertTrue($validator->isValid(), print_r($validator->getErrors(), true)); } public function testLongIntegerArray() { $schema = '{ "type":"object", "properties":{ "p_array":{ "type":"array", "items":{"type":"integer"} } } }'; $tmp = new \stdClass(); $tmp->p_array = array_map(function ($i) { return $i; }, range(1, 100000)); $input = json_encode($tmp); $schemaStorage = new SchemaStorage($this->getUriRetrieverMock(json_decode($schema))); $schema = $schemaStorage->getSchema('http://www.my-domain.com/schema.json'); $validator = new Validator(new Factory($schemaStorage)); $checkValue = json_decode($input); $validator->check($checkValue, $schema); $this->assertTrue($validator->isValid(), print_r($validator->getErrors(), true)); } } php-json-schema-5.2.9/tests/Constraints/MinItemsMaxItemsTest.php000066400000000000000000000032501354400737700247540ustar00rootroot00000000000000markTestSkipped('mbstring extension is not available'); } } public function getInvalidTests() { return array( array( '{ "value":"☀" }', '{ "type":"object", "properties":{ "value":{"type":"string","minLength":2,"maxLength":4} } }' ), array( '{ "value":"☀☁☂☃☺" }', '{ "type":"object", "properties":{ "value":{"type":"string","minLength":2,"maxLength":4} } }' ) ); } public function getValidTests() { return array( array( '{ "value":"☀☁" }', '{ "type":"object", "properties":{ "value":{"type":"string","minLength":2,"maxLength":4} } }' ), array( '{ "value":"☀☁☂☃" }', '{ "type":"object", "properties":{ "value":{"type":"string","minLength":2,"maxLength":4} } }' ) ); } } php-json-schema-5.2.9/tests/Constraints/MinLengthMaxLengthTest.php000066400000000000000000000032571354400737700252630ustar00rootroot00000000000000 'prop2', 'pointer' => '/prop2', 'message' => 'Array value found, but a string is required', 'constraint' => 'type', 'context' => Validator::ERROR_DOCUMENT_VALIDATION ), array( 'property' => 'prop2', 'pointer' => '/prop2', 'message' => 'Array value found, but a number is required', 'constraint' => 'type', 'context' => Validator::ERROR_DOCUMENT_VALIDATION ), array( 'property' => 'prop2', 'pointer' => '/prop2', 'message' => 'Failed to match exactly one schema', 'constraint' => 'oneOf', 'context' => Validator::ERROR_DOCUMENT_VALIDATION ), ), ), array( '{"prop1": [1,2]}', '{ "type": "object", "properties": { "prop1": { "oneOf": [ { "type": "string", "pattern": "^[a-z]*$" }, { "type": "string", "pattern": "^[A-Z]*$" } ] } } }' ), array( '{"prop1": [1,2]}', '{ "type": "object", "properties": { "prop1": { "anyOf": [ { "type": "string", "pattern": "^[A-Z]*$" } ] } } }' ), array( '{"prop1": [1,2]}', '{ "type": "object", "properties": { "prop1": { "anyOf": [ { "type": "number" }, { "type": "string", "pattern": "^[A-Z]*$" } ] } } }' ), array( '{"prop1": [1,2]}', '{ "type": "object", "properties": { "prop1": { "anyOf": [ { "type": "string" }, { "type": "string", "pattern": "^[A-Z]*$" } ] } } }' ), array( '{"prop1": [1,2]}', '{ "type": "object", "properties": { "prop1": { "anyOf": [ { "type": "string", "pattern": "^[a-z]*$" }, { "type": "string", "pattern": "^[A-Z]*$" } ] } } }' ), array( '{"prop1": [1,2]}', '{ "type": "object", "properties": { "prop1": { "anyOf": [ { "type": "number" }, { "type": "string" }, { "type": "string" } ] } } }' ) ); } public function testNoPrematureAnyOfException() { $schema = json_decode('{ "type": "object", "properties": { "propertyOne": { "anyOf": [ {"type": "number"}, {"type": "string"} ] } } }'); $data = json_decode('{"propertyOne":"ABC"}'); $v = new Validator(); $v->validate($data, $schema, Constraint::CHECK_MODE_EXCEPTIONS); $this->assertTrue($v->isValid()); } public function testNoPrematureOneOfException() { $schema = json_decode('{ "type": "object", "properties": { "propertyOne": { "oneOf": [ {"type": "number"}, {"type": "string"} ] } } }'); $data = json_decode('{"propertyOne":"ABC"}'); $v = new Validator(); $v->validate($data, $schema, Constraint::CHECK_MODE_EXCEPTIONS); $this->assertTrue($v->isValid()); } } php-json-schema-5.2.9/tests/Constraints/PatternPropertiesTest.php000066400000000000000000000172321354400737700252560ustar00rootroot00000000000000 array( 'foobar' => 'foo', 'barfoo' => 'bar', ) )), json_encode(array( 'type' => 'object', 'patternProperties' => array( '^someobject$' => array( 'type' => 'object', 'additionalProperties' => false, 'properties' => array( 'barfoo' => array( 'type' => 'string', ), ) ) ) )) ), // Does not match pattern array( json_encode(array( 'regex_us' => false, )), json_encode(array( 'type' => 'object', 'patternProperties' => array( '^[a-z]+_(jp|de)$' => array( 'type' => array('boolean') ) ), 'additionalProperties' => false )) ), // Does not match pattern with unicode array( json_encode(array( '猡猡獛' => false, )), json_encode(array( 'type' => 'object', 'patternProperties' => array( '^[\\x{0080}-\\x{006FFF}]+$' => array( 'type' => array('boolean') ) ), 'additionalProperties' => false )) ), // An invalid regular expression pattern array( json_encode(array( 'regex_us' => false, )), json_encode(array( 'type' => 'object', 'patternProperties' => array( '^[a-z+_jp|de)$' => array( 'type' => array('boolean') ) ), 'additionalProperties' => false )) ), ); } public function getValidTests() { return array( array( // validates pattern schema json_encode(array( 'someobject' => array( 'foobar' => 'foo', 'barfoo' => 'bar', ), 'someotherobject' => array( 'foobar' => 1234, ), '/products' => array( 'get' => array() ), '#products' => array( 'get' => array() ), '+products' => array( 'get' => array() ), '~products' => array( 'get' => array() ), '*products' => array( 'get' => array() ), '%products' => array( 'get' => array() ) )), json_encode(array( 'type' => 'object', 'additionalProperties' => false, 'patternProperties' => array( '^someobject$' => array( 'type' => 'object', 'properties' => array( 'foobar' => array('type' => 'string'), 'barfoo' => array('type' => 'string'), ), ), '^someotherobject$' => array( 'type' => 'object', 'properties' => array( 'foobar' => array('type' => 'number'), ), ), '^/' => array( 'type' => 'object', 'properties' => array( 'get' => array('type' => 'array') ) ), '^#' => array( 'type' => 'object', 'properties' => array( 'get' => array('type' => 'array') ) ), '^\+' => array( 'type' => 'object', 'properties' => array( 'get' => array('type' => 'array') ) ), '^~' => array( 'type' => 'object', 'properties' => array( 'get' => array('type' => 'array') ) ), '^\*' => array( 'type' => 'object', 'properties' => array( 'get' => array('type' => 'array') ) ), '^%' => array( 'type' => 'object', 'properties' => array( 'get' => array('type' => 'array') ) ) ) )) ), array( json_encode(array( 'foobar' => true, 'regex_us' => 'foo', 'regex_de' => 1234 )), json_encode(array( 'type' => 'object', 'properties' => array( 'foobar' => array('type' => 'boolean') ), 'patternProperties' => array( '^[a-z]+_(us|de)$' => array( 'type' => array('string', 'integer') ) ), 'additionalProperties' => false )) ), // Does match pattern with unicode array( json_encode(array( 'ðæſ' => 'unicode', )), json_encode(array( 'type' => 'object', 'patternProperties' => array( '^[\\x{0080}-\\x{10FFFF}]+$' => array( 'type' => array('string') ) ), 'additionalProperties' => false )) ), ); } } php-json-schema-5.2.9/tests/Constraints/PatternTest.php000066400000000000000000000054221354400737700231770ustar00rootroot00000000000000 'object', 'required' => array('prop1', 'prop2', 'prop3', 'prop4'), 'properties' => array( 'prop1' => array( 'type' => 'string' ), 'prop2' => array( 'type' => 'object', 'required' => array('prop2.1'), 'properties' => array( 'prop2.1' => array( 'type' => 'string' ) ) ), 'prop3' => array( 'type' => 'object', 'required' => array('prop3/1'), 'properties' => array( 'prop3/1' => array( 'type' => 'object', 'required' => array('prop3/1.1'), 'properties' => array( 'prop3/1.1' => array( 'type' => 'string' ) ) ) ) ), 'prop4' => array( 'type' => 'array', 'minItems' => 1, 'items' => array( 'type' => 'object', 'required' => array('prop4-child'), 'properties' => array( 'prop4-child' => array( 'type' => 'string' ) ) ) ) ) ); $value = array( 'prop2' => array( 'foo' => 'bar' ), 'prop3' => array( 'prop3/1' => array( 'foo' => 'bar' ) ), 'prop4' => array( array( 'foo' => 'bar' ) ) ); $validator = new Validator(); $checkValue = json_decode(json_encode($value)); $validator->check($checkValue, json_decode(json_encode($schema))); $this->assertEquals( array( array( 'property' => 'prop1', 'pointer' => '/prop1', 'message' => 'The property prop1 is required', 'constraint' => 'required', 'context' => Validator::ERROR_DOCUMENT_VALIDATION ), array( 'property' => 'prop2.prop2.1', 'pointer' => '/prop2/prop2.1', 'message' => 'The property prop2.1 is required', 'constraint' => 'required', 'context' => Validator::ERROR_DOCUMENT_VALIDATION ), array( 'property' => 'prop3.prop3/1.prop3/1.1', 'pointer' => '/prop3/prop3~11/prop3~11.1', 'message' => 'The property prop3/1.1 is required', 'constraint' => 'required', 'context' => Validator::ERROR_DOCUMENT_VALIDATION ), array( 'property' => 'prop4[0].prop4-child', 'pointer' => '/prop4/0/prop4-child', 'message' => 'The property prop4-child is required', 'constraint' => 'required', 'context' => Validator::ERROR_DOCUMENT_VALIDATION ) ), $validator->getErrors() ); } } php-json-schema-5.2.9/tests/Constraints/ReadOnlyTest.php000066400000000000000000000020621354400737700232740ustar00rootroot00000000000000check($document, $schema); $error = $validator->getErrors(); $this->assertErrorHasExpectedPropertyValue($error, 'foo'); } public function testPathErrorPropertyIsPopulatedForRequiredIfMissingInInput() { $validator = new UndefinedConstraint(); $document = json_decode( '{ "foo": [{"baz": 1.5}] }' ); $schema = json_decode( '{ "type": "object", "properties": { "foo": { "type": "array", "items": { "type": "object", "properties": { "bar": {"type": "number"}, "baz": {"type": "number"} }, "required": ["bar"] } } }, "required": ["foo"] }' ); $validator->check($document, $schema); $error = $validator->getErrors(); $this->assertErrorHasExpectedPropertyValue($error, 'foo[0].bar'); } public function testErrorPropertyIsPopulatedForRequiredIfEmptyValueInInput() { $validator = new UndefinedConstraint(); $document = json_decode( '{ "bar": 42, "foo": null }' ); $schema = json_decode( '{ "type": "object", "properties": { "foo": {"type": "number"}, "bar": {"type": "number"} }, "required": ["foo"] }' ); $validator->check($document, $schema); $error = $validator->getErrors(); $this->assertErrorHasExpectedPropertyValue($error, 'foo'); } protected function assertErrorHasExpectedPropertyValue($error, $propertyValue) { if (!(isset($error[0]) && is_array($error[0]) && isset($error[0]['property']))) { $this->fail( "Malformed error response. Expected to have subset in form: array(0 => array('property' => )))" . ' . Error response was: ' . json_encode($error) ); } $this->assertEquals($propertyValue, $error[0]['property']); } public function getInvalidTests() { return array( array( '{}', '{ "type":"object", "properties":{ "number":{"type":"number","required":true} } }' ), array( '{}', '{ "type": "object", "properties": { "number": {"type": "number"} }, "required": ["number"] }' ), array( '{ "foo": {} }', '{ "type": "object", "properties": { "foo": { "type": "object", "properties": { "bar": {"type": "number"} }, "required": ["bar"] } } }' ), array( '{ "bar": 1.4 }', '{ "type": "object", "properties": { "foo": {"type": "string", "required": true}, "bar": {"type": "number"} }, "required": ["bar"] }' ), array( '{}', '{ "required": ["foo"] }' ), array( '{ }', '{ "type": "object", "properties": { "foo": { "required": true } } }' ), array( '{ "string":{} }', '{ "type":"object", "properties": { "string":{"type":"string", "required": true} } }' ), array( '{ "number":{} }', '{ "type":"object", "properties": { "number":{"type":"number", "required": true} } }' ), array( '{ "integer":{} }', '{ "type":"object", "properties": { "integer":{"type":"integer", "required": true} } }' ), array( '{ "boolean":{} }', '{ "type":"object", "properties": { "boolean":{"type":"boolean", "required": true} } }' ), array( '{ "array":{} }', '{ "type":"object", "properties": { "array":{"type":"array", "required": true} } }', Constraint::CHECK_MODE_NORMAL ), array( '{ "null":{} }', '{ "type":"object", "properties": { "null":{"type":"null", "required": true} } }' ), array( '{ "foo": {"baz": 1.5} }', '{ "type": "object", "properties": { "foo": { "type": "object", "properties": { "bar": {"type": "number"} }, "required": ["bar"] } } }' ), array( '{ "foo": {"baz": 1.5} }', '{ "type": "object", "properties": { "foo": { "type": "object", "properties": { "bar": {"type": "number", "required": true} } } } }' ), ); } public function getValidTests() { return array( array( '{ "number": 1.4 }', '{ "type":"object", "properties":{ "number":{"type":"number","required":true} } }' ), array( '{}', '{ "type":"object", "properties":{ "number":{"type":"number"} } }' ), array( '{}', '{ "type":"object", "properties":{ "number":{"type":"number","required":false} } }' ), array( '{ "number": 0 }', '{ "type":"object", "properties":{ "number":{"type":"integer","required":true} } }' ), array( '{ "is_active": false }', '{ "type":"object", "properties":{ "is_active":{"type":"boolean","required":true} } }' ), array( '{ "status": null }', '{ "type":"object", "properties":{ "status":{"type":"null","required":true} } }' ), array( '{ "users": [] }', '{ "type":"object", "properties":{ "users":{"type":"array","required":true} } }' ), array( '{ "foo": "foo", "bar": 1.4 }', '{ "type": "object", "properties": { "foo": {"type": "string", "required": true}, "bar": {"type": "number"} }, "required": ["bar"] }' ), array( '{ "foo": {"bar": 1.5} }', '{ "type": "object", "properties": { "foo": { "type": "object", "properties": { "bar": {"type": "number"} }, "required": ["bar"] } }, "required": ["foo"] }' ), array( '{ "foo": {} }', '{ "type": "object", "properties": { "foo": { "required": true } } }' ), array( '{ "boo": {"bar": 1.5} }', '{ "type": "object", "properties": { "foo": { "type": "object", "properties": { "bar": {"type": "number"} }, "required": ["bar"] } } }' ), array( '{ "boo": {"bar": 1.5} }', '{ "type": "object", "properties": { "foo": { "type": "object", "properties": { "bar": {"type": "number", "required": true} } } } }' ), ); } } php-json-schema-5.2.9/tests/Constraints/SchemaValidationTest.php000066400000000000000000000075211354400737700247770ustar00rootroot00000000000000validate($input, $schema, Constraint::CHECK_MODE_VALIDATE_SCHEMA); $this->assertTrue((bool) (Validator::ERROR_SCHEMA_VALIDATION & $errorMask)); $this->assertGreaterThan(0, $v->numErrors(Validator::ERROR_SCHEMA_VALIDATION)); $this->assertEquals(0, $v->numErrors(Validator::ERROR_DOCUMENT_VALIDATION)); $this->assertFalse($v->isValid(), 'Validation succeeded for an invalid test case'); foreach ($v->getErrors() as $error) { $this->assertEquals(Validator::ERROR_SCHEMA_VALIDATION, $error['context']); } } /** * @dataProvider getValidTests */ public function testValidCases($schema) { $input = json_decode('{"propertyOne":"valueOne"}'); $schema = json_decode($schema); $v = new Validator(); $errorMask = $v->validate($input, $schema, Constraint::CHECK_MODE_VALIDATE_SCHEMA); $this->assertEquals(0, $errorMask); if (!$v->isValid()) { var_dump($v->getErrors(Validator::ERROR_SCHEMA_VALIDATION)); } $this->assertTrue($v->isValid(), 'Validation failed on a valid test case'); } public function testNonObjectSchema() { $this->setExpectedException( '\JsonSchema\Exception\RuntimeException', 'Cannot validate the schema of a non-object' ); $this->testValidCases('"notAnObject"'); } public function testInvalidSchemaException() { $this->setExpectedException( '\JsonSchema\Exception\InvalidSchemaException', 'Schema did not pass validation' ); $input = json_decode('{}'); $schema = json_decode('{"properties":{"propertyOne":{"type":"string","required":true}}}'); $v = new Validator(); $v->validate($input, $schema, Constraint::CHECK_MODE_VALIDATE_SCHEMA | Constraint::CHECK_MODE_EXCEPTIONS); } } php-json-schema-5.2.9/tests/Constraints/SelfDefinedSchemaTest.php000066400000000000000000000041151354400737700250510ustar00rootroot00000000000000setExpectedException('\JsonSchema\Exception\InvalidArgumentException'); $v->check(json_decode('{}'), json_decode('')); } } php-json-schema-5.2.9/tests/Constraints/TupleTypingTest.php000066400000000000000000000073701354400737700240520ustar00rootroot00000000000000 */ class TypeTest extends TestCase { /** * @see testIndefiniteArticleForTypeInTypeCheckErrorMessage * * @return array */ public function provideIndefiniteArticlesForTypes() { return array( array('integer', 'an integer'), array('number', 'a number'), array('boolean', 'a boolean'), array('object', 'an object'), array('array', 'an array'), array('string', 'a string'), array('null', 'a null', array(), 'array'), array(array('string', 'boolean', 'integer'), 'a string, a boolean or an integer'), array(array('string', 'boolean'), 'a string or a boolean'), array(array('string'), 'a string'), ); } /** * @dataProvider provideIndefiniteArticlesForTypes */ public function testIndefiniteArticleForTypeInTypeCheckErrorMessage($type, $wording, $value = null, $label = 'NULL') { $constraint = new TypeConstraint(); $constraint->check($value, (object) array('type' => $type)); $this->assertTypeConstraintError(ucwords($label) . " value found, but $wording is required", $constraint); } /** * Test uncovered areas of the loose type checker */ public function testLooseTypeChecking() { $v = new \stdClass(); $v->property = 'dataOne'; LooseTypeCheck::propertySet($v, 'property', 'dataTwo'); $this->assertEquals('dataTwo', $v->property); $this->assertEquals('dataTwo', LooseTypeCheck::propertyGet($v, 'property')); $this->assertEquals(1, LooseTypeCheck::propertyCount($v)); } /** * Helper to assert an error message * * @param string $expected * @param TypeConstraint $actual */ private function assertTypeConstraintError($expected, TypeConstraint $actual) { $actualErrors = $actual->getErrors(); $this->assertCount(1, $actualErrors, 'Failed to assert that Type has exactly one error to assert the error message against.'); $actualError = $actualErrors[0]; $this->assertInternalType('array', $actualError, sprintf('Failed to assert that Type error is an array, %s given', gettype($actualError))); $messageKey = 'message'; $this->assertArrayHasKey( $messageKey, $actualError, sprintf('Failed to assert that Type error has a message key %s.', var_export($messageKey, true)) ); $actualMessage = $actualError[$messageKey]; $this->assertEquals($expected, $actualMessage); // first equal for the diff $this->assertSame($expected, $actualMessage); // the same for the strictness } public function testValidateTypeNameWording() { $t = new TypeConstraint(); $r = new \ReflectionObject($t); $m = $r->getMethod('validateTypeNameWording'); $m->setAccessible(true); $this->setExpectedException( '\UnexpectedValueException', "No wording for 'notAValidTypeName' available, expected wordings are: [an integer, a number, a boolean, an object, an array, a string, a null]" ); $m->invoke($t, 'notAValidTypeName'); } public function testValidateTypeException() { $t = new TypeConstraint(); $data = new \stdClass(); $schema = json_decode('{"type": "notAValidTypeName"}'); $this->setExpectedException( 'JsonSchema\Exception\InvalidArgumentException', 'object is an invalid type for notAValidTypeName' ); $t->check($data, $schema); } } php-json-schema-5.2.9/tests/Constraints/UnionTypesTest.php000066400000000000000000000024221354400737700236740ustar00rootroot00000000000000assertInstanceOf('\JsonSchema\Exception\ValidationException', $exception); $checkValue = json_decode('{"propertyOne": "thisIsNotAnObject"}'); $schema = json_decode('{ "type": "object", "additionalProperties": false, "properties": { "propertyOne": { "type": "object" } } }'); $validator = new Validator(); try { $validator->validate($checkValue, $schema, Constraint::CHECK_MODE_EXCEPTIONS); } catch (\Exception $e) { $exception = $e; } $this->assertEquals( 'Error validating /propertyOne: String value found, but an object is required', $exception->getMessage() ); $this->setExpectedException('JsonSchema\Exception\ValidationException'); throw $exception; } } php-json-schema-5.2.9/tests/Constraints/VeryBaseTestCase.php000066400000000000000000000052651354400737700241030ustar00rootroot00000000000000getJsonSchemaDraft03(); $jsonSchemaDraft04 = $this->getJsonSchemaDraft04(); $uriRetriever = $this->prophesize('JsonSchema\UriRetrieverInterface'); $uriRetriever->retrieve('http://www.my-domain.com/schema.json') ->willReturn($schema) ->shouldBeCalled(); $uriRetriever->retrieve(Argument::any()) ->will(function ($args) use ($jsonSchemaDraft03, $jsonSchemaDraft04, $relativeTestsRoot) { if ('http://json-schema.org/draft-03/schema' === $args[0]) { return $jsonSchemaDraft03; } elseif ('http://json-schema.org/draft-04/schema' === $args[0]) { return $jsonSchemaDraft04; } elseif (0 === strpos($args[0], 'http://localhost:1234')) { $urlParts = parse_url($args[0]); return json_decode(file_get_contents($relativeTestsRoot . $urlParts['path'])); } elseif (0 === strpos($args[0], 'http://www.my-domain.com')) { $urlParts = parse_url($args[0]); return json_decode(file_get_contents($relativeTestsRoot . '/folder' . $urlParts['path'])); } }); return $uriRetriever->reveal(); } /** * @return object */ private function getJsonSchemaDraft03() { if (!$this->jsonSchemaDraft03) { $this->jsonSchemaDraft03 = json_decode( file_get_contents(__DIR__ . '/../../dist/schema/json-schema-draft-03.json') ); } return $this->jsonSchemaDraft03; } /** * @return object */ private function getJsonSchemaDraft04() { if (!$this->jsonSchemaDraft04) { $this->jsonSchemaDraft04 = json_decode( file_get_contents(__DIR__ . '/../../dist/schema/json-schema-draft-04.json') ); } return $this->jsonSchemaDraft04; } } php-json-schema-5.2.9/tests/Constraints/WrongMessagesFailingTestCaseTest.php000066400000000000000000000024531354400737700272750ustar00rootroot00000000000000getFilePaths(); $skippedTests = $this->getSkippedTests(); $tests = array(); foreach ($filePaths as $path) { foreach (glob($path . '/*.json') as $file) { $filename = basename($file); if (in_array($filename, $skippedTests)) { continue; } $suites = json_decode(file_get_contents($file)); foreach ($suites as $suite) { $suiteDescription = $suite->description; foreach ($suite->tests as $test) { $testCaseDescription = $test->description; if ($isValid === $test->valid) { $tests[ $this->createDataSetPath($filename, $suiteDescription, $testCaseDescription) ] = array(json_encode($test->data), json_encode($suite->schema)); } } } } } return $tests; } /** * {@inheritdoc} */ public function getInvalidTests() { return $this->setUpTests(false); } /** * {@inheritdoc} */ public function getValidTests() { return $this->setUpTests(true); } /** * @return string[] */ abstract protected function getFilePaths(); /** * @return string[] */ abstract protected function getSkippedTests(); /** * Generates a readable path to Json Schema Test Suite data set under test * * @param string $filename * @param string $suiteDesc * @param string $testCaseDesc * * @return string */ private function createDataSetPath($filename, $suiteDesc, $testCaseDesc) { $separator = ' / '; return $filename . $separator . $suiteDesc . $separator . $testCaseDesc; } } php-json-schema-5.2.9/tests/Drafts/Draft3Test.php000066400000000000000000000031541354400737700216210ustar00rootroot00000000000000relativeTestsRoot . '/draft3'), realpath(__DIR__ . $this->relativeTestsRoot . '/draft3/optional') ); } public function getInvalidForAssocTests() { $tests = parent::getInvalidForAssocTests(); unset( $tests['type.json / object type matches objects / an array is not an object'], $tests['type.json / array type matches arrays / an object is not an array'] ); return $tests; } public function getValidForAssocTests() { $tests = parent::getValidForAssocTests(); unset( $tests['type.json / object type matches objects / an array is not an object'], $tests['type.json / array type matches arrays / an object is not an array'] ); return $tests; } /** * {@inheritdoc} */ protected function getSkippedTests() { return array( // Optional 'bignum.json', 'format.json', 'jsregex.json', 'zeroTerminatedFloats.json' ); } } php-json-schema-5.2.9/tests/Drafts/Draft4Test.php000066400000000000000000000032361354400737700216230ustar00rootroot00000000000000relativeTestsRoot . '/draft4'), realpath(__DIR__ . $this->relativeTestsRoot . '/draft4/optional') ); } public function getInvalidForAssocTests() { $tests = parent::getInvalidForAssocTests(); unset( $tests['type.json / object type matches objects / an array is not an object'], $tests['type.json / array type matches arrays / an object is not an array'] ); return $tests; } public function getValidForAssocTests() { $tests = parent::getValidForAssocTests(); unset( $tests['type.json / object type matches objects / an array is not an object'], $tests['type.json / array type matches arrays / an object is not an array'] ); return $tests; } /** * {@inheritdoc} */ protected function getSkippedTests() { return array( // Optional 'bignum.json', 'format.json', 'zeroTerminatedFloats.json', // Required 'not.json' // only one test case failing ); } } php-json-schema-5.2.9/tests/Entity/000077500000000000000000000000001354400737700171535ustar00rootroot00000000000000php-json-schema-5.2.9/tests/Entity/JsonPointerTest.php000066400000000000000000000120271354400737700230000ustar00rootroot00000000000000 */ class JsonPointerTest extends TestCase { /** * @dataProvider getTestData * * @param string $testValue * @param string $expectedFileName * @param array $expectedPropertyPaths * @param string $expectedPropertyPathAsString * @param string $expectedToString */ public function testJsonPointer( $testValue, $expectedFileName, $expectedPropertyPaths, $expectedPropertyPathAsString, $expectedToString ) { $jsonPointer = new JsonPointer($testValue); $this->assertEquals($expectedFileName, $jsonPointer->getFilename()); $this->assertEquals($expectedPropertyPaths, $jsonPointer->getPropertyPaths()); $this->assertEquals($expectedPropertyPathAsString, $jsonPointer->getPropertyPathAsString()); $this->assertEquals($expectedToString, (string) $jsonPointer); } /** * @return array[] */ public function getTestData() { return array( 'testDataSet_01' => array( 'testValue' => '#/definitions/date', 'expectedFileName' => '', 'expectedPropertyPaths' => array('definitions', 'date'), 'expectedPropertyPathAsString' => '#/definitions/date', 'expectedToString' => '#/definitions/date' ), 'testDataSet_02' => array( 'testValue' => 'http://www.example.com/definitions.json#/definitions/date', 'expectedFileName' => 'http://www.example.com/definitions.json', 'expectedPropertyPaths' => array('definitions', 'date'), 'expectedPropertyPathAsString' => '#/definitions/date', 'expectedToString' => 'http://www.example.com/definitions.json#/definitions/date' ), 'testDataSet_03' => array( 'testValue' => '/tmp/schema.json#definitions/common/date/', 'expectedFileName' => '/tmp/schema.json', 'expectedPropertyPaths' => array('definitions', 'common', 'date'), 'expectedPropertyPathAsString' => '#/definitions/common/date', 'expectedToString' => '/tmp/schema.json#/definitions/common/date' ), 'testDataSet_04' => array( 'testValue' => './definitions.json#', 'expectedFileName' => './definitions.json', 'expectedPropertyPaths' => array(), 'expectedPropertyPathAsString' => '#', 'expectedToString' => './definitions.json#' ), 'testDataSet_05' => array( 'testValue' => '/schema.json#~0definitions~1general/%custom%25', 'expectedFileName' => '/schema.json', 'expectedPropertyPaths' => array('~definitions/general', '%custom%'), 'expectedPropertyPathAsString' => '#/~0definitions~1general/%25custom%25', 'expectedToString' => '/schema.json#/~0definitions~1general/%25custom%25' ), 'testDataSet_06' => array( 'testValue' => '#/items/0', 'expectedFileName' => '', 'expectedPropertyPaths' => array('items', '0'), 'expectedPropertyPathAsString' => '#/items/0', 'expectedToString' => '#/items/0' ) ); } public function testJsonPointerWithPropertyPaths() { $initial = new JsonPointer('#/definitions/date'); $this->assertEquals(array('definitions', 'date'), $initial->getPropertyPaths()); $this->assertEquals('#/definitions/date', $initial->getPropertyPathAsString()); $modified = $initial->withPropertyPaths(array('~definitions/general', '%custom%')); $this->assertNotSame($initial, $modified); $this->assertEquals(array('definitions', 'date'), $initial->getPropertyPaths()); $this->assertEquals('#/definitions/date', $initial->getPropertyPathAsString()); $this->assertEquals(array('~definitions/general', '%custom%'), $modified->getPropertyPaths()); $this->assertEquals('#/~0definitions~1general/%25custom%25', $modified->getPropertyPathAsString()); } public function testCreateWithInvalidValue() { $this->setExpectedException( '\JsonSchema\Exception\InvalidArgumentException', 'Ref value must be a string' ); new JsonPointer(null); } } php-json-schema-5.2.9/tests/Exception/000077500000000000000000000000001354400737700176355ustar00rootroot00000000000000php-json-schema-5.2.9/tests/Exception/InvalidArgumentExceptionTest.php000066400000000000000000000007001354400737700261530ustar00rootroot00000000000000getMessage()); } public function testErrorNoneMessage() { $exception = new JsonDecodingException(JSON_ERROR_NONE); self::assertNotEmpty($exception->getMessage()); } public function testErrorDepthMessage() { $exception = new JsonDecodingException(JSON_ERROR_DEPTH); self::assertNotEmpty($exception->getMessage()); } public function testErrorStateMismatchMessage() { $exception = new JsonDecodingException(JSON_ERROR_STATE_MISMATCH); self::assertNotEmpty($exception->getMessage()); } public function testErrorControlCharacterMessage() { $exception = new JsonDecodingException(JSON_ERROR_CTRL_CHAR); self::assertNotEmpty($exception->getMessage()); } public function testErrorUtf8Message() { $exception = new JsonDecodingException(JSON_ERROR_UTF8); self::assertNotEmpty($exception->getMessage()); } public function testErrorSyntaxMessage() { $exception = new JsonDecodingException(JSON_ERROR_SYNTAX); self::assertNotEmpty($exception->getMessage()); } public function testErrorInfiniteOrNotANumberMessage() { if (!defined('JSON_ERROR_INF_OR_NAN')) { self::markTestSkipped('JSON_ERROR_INF_OR_NAN is not defined until php55.'); } $exception = new JsonDecodingException(JSON_ERROR_INF_OR_NAN); self::assertNotEmpty($exception->getMessage()); } public function testErrorRecursionMessage() { if (!defined('JSON_ERROR_RECURSION')) { self::markTestSkipped('JSON_ERROR_RECURSION is not defined until php55.'); } $exception = new JsonDecodingException(JSON_ERROR_RECURSION); self::assertNotEmpty($exception->getMessage()); } public function testErrorUnsupportedTypeMessage() { if (!defined('JSON_ERROR_UNSUPPORTED_TYPE')) { self::markTestSkipped('JSON_ERROR_UNSUPPORTED_TYPE is not defined until php55.'); } $exception = new JsonDecodingException(JSON_ERROR_UNSUPPORTED_TYPE); self::assertNotEmpty($exception->getMessage()); } } php-json-schema-5.2.9/tests/Exception/ResourceNotFoundExceptionTest.php000066400000000000000000000010211354400737700263230ustar00rootroot00000000000000testObject = (object) array( 'subOne' => (object) array( 'propertyOne' => 'valueOne', 'propertyTwo' => 'valueTwo', 'propertyThree' => 'valueThree' ), 'subTwo' => (object) array( 'propertyFour' => 'valueFour', 'subThree' => (object) array( 'propertyFive' => 'valueFive', 'propertySix' => 'valueSix' ) ), 'propertySeven' => 'valueSeven' ); } public function testCreate() { $i = new ObjectIterator($this->testObject); $this->assertInstanceOf('\JsonSchema\Iterator\ObjectIterator', $i); } public function testInitialState() { $i = new ObjectIterator($this->testObject); $this->assertEquals($this->testObject, $i->current()); } public function testCount() { $i = new ObjectIterator($this->testObject); $this->assertEquals(4, $i->count()); } public function testKey() { $i = new ObjectIterator($this->testObject); while ($i->key() != 2) { $i->next(); } $this->assertEquals($this->testObject->subTwo->subThree, $i->current()); } public function testAlwaysObjects() { $i= new ObjectIterator($this->testObject); foreach ($i as $item) { $this->assertInstanceOf('\StdClass', $item); } } public function testReachesAllProperties() { $i = new ObjectIterator($this->testObject); $count = 0; foreach ($i as $item) { $count += count(get_object_vars($item)); } $this->assertEquals(10, $count); } } php-json-schema-5.2.9/tests/Rfc3339Test.php000066400000000000000000000047501354400737700203120ustar00rootroot00000000000000assertInstanceOf('DateTime', $actual); $this->assertEquals($expected->format('U.u'), $actual->format('U.u')); } /** * @param string $string * @dataProvider provideInvalidFormats */ public function testCreateFromInvalidString($string) { $this->assertNull(Rfc3339::createFromString($string), sprintf('String "%s" should not be converted to DateTime', $string)); } public function provideValidFormats() { return array( array( '2000-05-01T12:12:12Z', \DateTime::createFromFormat('Y-m-d\TH:i:s', '2000-05-01T12:12:12', new \DateTimeZone('UTC')) ), array( '2000-05-01T12:12:12+0100', \DateTime::createFromFormat('Y-m-d\TH:i:sP', '2000-05-01T12:12:12+01:00') ), array( '2000-05-01T12:12:12+01:00', \DateTime::createFromFormat('Y-m-d\TH:i:sP', '2000-05-01T12:12:12+01:00') ), array( '2000-05-01T12:12:12.123456Z', \DateTime::createFromFormat('Y-m-d\TH:i:s.u', '2000-05-01T12:12:12.123456', new \DateTimeZone('UTC')) ), array( '2000-05-01T12:12:12.123Z', \DateTime::createFromFormat('Y-m-d\TH:i:s.u', '2000-05-01T12:12:12.123000', new \DateTimeZone('UTC')) ), array( '2000-05-01 12:12:12.123Z', \DateTime::createFromFormat('Y-m-d H:i:s.u', '2000-05-01 12:12:12.123000', new \DateTimeZone('UTC')) ), array( '2000-05-01 12:12:12.123456Z', \DateTime::createFromFormat('Y-m-d H:i:s.u', '2000-05-01 12:12:12.123456', new \DateTimeZone('UTC')) ) ); } public function provideInvalidFormats() { return array( array('1999-1-11T00:00:00Z'), array('1999-01-11T00:00:00+100'), array('1999-01-11T00:00:00+1:00'), array('1999-01-01 00:00:00Z'), array('1999-1-11 00:00:00Z') ); } } php-json-schema-5.2.9/tests/SchemaStorageTest.php000066400000000000000000000265251354400737700220070ustar00rootroot00000000000000getMainSchema(); $mainSchemaPath = 'http://www.example.com/schema.json'; $uriRetriever = $this->prophesize('JsonSchema\UriRetrieverInterface'); $uriRetriever->retrieve($mainSchemaPath)->willReturn($mainSchema)->shouldBeCalled(); $schemaStorage = new SchemaStorage($uriRetriever->reveal()); $this->assertEquals( (object) array('type' => 'string'), $schemaStorage->resolveRef("$mainSchemaPath#/definitions/house/properties/door") ); } public function testResolveTopRef() { $input = json_decode('{"propertyOne":"notANumber"}'); $schema = json_decode('{"$ref":"#/definition","definition":{"properties":{"propertyOne":{"type":"number"}}}}'); $v = new Validator(); $v->validate($input, $schema); $this->assertFalse($v->isValid()); } /** * @depends testResolveRef */ public function testSchemaWithLocalAndExternalReferencesWithCircularReference() { $mainSchema = $this->getMainSchema(); $schema2 = $this->getSchema2(); $schema3 = $this->getSchema3(); $mainSchemaPath = 'http://www.example.com/schema.json'; $schema2Path = 'http://www.my-domain.com/schema2.json'; $schema3Path = 'http://www.my-domain.com/schema3.json'; /** @var UriRetriever $uriRetriever */ $uriRetriever = $this->prophesize('JsonSchema\UriRetrieverInterface'); $uriRetriever->retrieve($mainSchemaPath)->willReturn($mainSchema)->shouldBeCalled(); $uriRetriever->retrieve($schema2Path)->willReturn($schema2)->shouldBeCalled(); $uriRetriever->retrieve($schema3Path)->willReturn($schema3)->shouldBeCalled(); $schemaStorage = new SchemaStorage($uriRetriever->reveal()); // remote ref $this->assertEquals( $schemaStorage->resolveRef("$schema2Path#/definitions/car"), $schemaStorage->resolveRef("$mainSchemaPath#/properties/car") ); $this->assertEquals( $schemaStorage->resolveRef("$schema3Path#/wheel"), $schemaStorage->resolveRef("$mainSchemaPath#/properties/car/properties/wheel") ); // properties ref $this->assertEquals( $schemaStorage->resolveRef("$mainSchemaPath#/definitions/yardproperties"), $schemaStorage->resolveRef("$mainSchemaPath#/properties/yard/properties") ); // local ref with overriding $this->assertNotEquals( $schemaStorage->resolveRef("$mainSchemaPath#/definitions/house/additionalProperties"), $schemaStorage->resolveRef("$mainSchemaPath#/properties/house/additionalProperties") ); $this->assertEquals( $schemaStorage->resolveRef("$mainSchemaPath#/definitions/house/properties"), $schemaStorage->resolveRef("$mainSchemaPath#/properties/house/properties") ); // recursive ref $this->assertEquals( $schemaStorage->resolveRef("$mainSchemaPath#/definitions/house"), $schemaStorage->resolveRef("$mainSchemaPath#/properties/house/properties/house") ); $this->assertEquals( $schemaStorage->resolveRef("$mainSchemaPath#/definitions/house"), $schemaStorage->resolveRef("$mainSchemaPath#/properties/house/properties/house/properties/house") ); } public function testUnresolvableJsonPointExceptionShouldBeThrown() { $this->setExpectedException( 'JsonSchema\Exception\UnresolvableJsonPointerException', 'File: http://www.example.com/schema.json is found, but could not resolve fragment: #/definitions/car' ); $mainSchema = $this->getInvalidSchema(); $mainSchemaPath = 'http://www.example.com/schema.json'; $uriRetriever = $this->prophesize('JsonSchema\UriRetrieverInterface'); $uriRetriever->retrieve($mainSchemaPath) ->willReturn($mainSchema) ->shouldBeCalled(); $schemaStorage = new SchemaStorage($uriRetriever->reveal()); $schemaStorage->resolveRef("$mainSchemaPath#/definitions/car"); } public function testResolveRefWithNoAssociatedFileName() { $this->setExpectedException( 'JsonSchema\Exception\UnresolvableJsonPointerException', "Could not resolve fragment '#': no file is defined" ); $schemaStorage = new SchemaStorage(); $schemaStorage->resolveRef('#'); } /** * @return object */ private function getMainSchema() { return (object) array( 'version' => 'v1', '$schema' => 'http://json-schema.org/draft-04/schema#', 'id' => 'http://www.example.com/schema.json', 'type' => 'object', 'additionalProperties' => true, 'required' => array( 'car' ), 'properties' => (object) array( 'car' => (object) array( '$ref' => 'http://www.my-domain.com/schema2.json#/definitions/car' ), 'house' => (object) array( 'additionalProperties' => true, '$ref' => '#/definitions/house' ), 'yard' => (object) array( 'type' => 'object', 'additionalProperties' => false, 'properties' => (object) array( '$ref' => '#/definitions/yardproperties' ) ) ), 'definitions' => (object) array( 'house' => (object) array( 'type' => 'object', 'additionalProperties' => false, 'required' => array( 'door', 'window' ), 'properties' => (object) array( 'door' => (object) array( 'type' => 'string' ), 'window' => (object) array( 'type' => 'string' ), 'house' => (object) array( '$ref' => '#/definitions/house' ) ) ), 'yardproperties' => (object) array( 'tree'=>(object) array( 'type' => 'string' ), 'pool'=>(object) array( 'type' => 'string' ) ) ) ); } /** * @return object */ private function getSchema2() { return (object) array( 'version' => 'v1', '$schema' => 'http://json-schema.org/draft-04/schema#', 'id' => 'http://www.my-domain.com/schema2.json', 'definitions' => (object) array( 'car' => (object) array( 'type' => 'object', 'additionalProperties' => false, 'properties' => (object) array( 'id' => (object) array( 'type' => 'integer' ), 'name' => (object) array( 'type' => 'string', 'minLength' => 1 ), 'wheel' => (object) array( '$ref' => './schema3.json#/wheel' ) ) ) ) ); } /** * @return object */ private function getSchema3() { return (object) array( 'version' => 'v1', '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'wheel', 'wheel' => (object) array( 'properties' => (object) array( 'spokes' => (object) array( 'type' => 'integer' ), 'size' => (object) array( 'type' => 'integer' ), 'car' => (object) array( '$ref' => './schema2.json#/definitions/car' ) ) ) ); } /** * @return object */ private function getInvalidSchema() { return (object) array( 'version' => 'v1', '$schema' => 'http://json-schema.org/draft-04/schema#', 'type' => 'object', 'properties' => (object) array( 'spokes' => (object) array( 'type' => 'integer' ), 'size' => (object) array( 'type' => 'integer' ), 'car' => (object) array( '$ref' => '#/definitions/car' ) ), 'definitions' => (object) array( 'date' => (object) array( 'type' => 'string', 'pattern' => '^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$' ) ) ); } public function testGetUriRetriever() { $s = new SchemaStorage(); $s->addSchema('http://json-schema.org/draft-04/schema#'); $this->assertInstanceOf('\JsonSchema\Uri\UriRetriever', $s->getUriRetriever()); } public function testGetUriResolver() { $s = new SchemaStorage(); $s->addSchema('http://json-schema.org/draft-04/schema#'); $this->assertInstanceOf('\JsonSchema\Uri\UriResolver', $s->getUriResolver()); } public function testMetaSchemaFixes() { $s = new SchemaStorage(); $s->addSchema('http://json-schema.org/draft-03/schema#'); $s->addSchema('http://json-schema.org/draft-04/schema#'); $draft_03 = $s->getSchema('http://json-schema.org/draft-03/schema#'); $draft_04 = $s->getSchema('http://json-schema.org/draft-04/schema#'); $this->assertEquals('uri-reference', $draft_03->properties->id->format); $this->assertEquals('uri-reference', $draft_03->properties->{'$ref'}->format); $this->assertEquals('uri-reference', $draft_04->properties->id->format); } public function testNoDoubleResolve() { $schemaOne = json_decode('{"id": "test/schema", "$ref": "../test2/schema2"}'); $uriRetriever = $this->prophesize('JsonSchema\UriRetrieverInterface'); $uriRetriever->retrieve('test/schema')->willReturn($schemaOne)->shouldBeCalled(); $s = new SchemaStorage($uriRetriever->reveal()); $schema = $s->addSchema('test/schema'); $r = new \ReflectionObject($s); $p = $r->getProperty('schemas'); $p->setAccessible(true); $schemas = $p->getValue($s); $this->assertEquals( 'file://' . getcwd() . '/test2/schema2#', $schemas['test/schema']->{'$ref'} ); } } php-json-schema-5.2.9/tests/Uri/000077500000000000000000000000001354400737700164365ustar00rootroot00000000000000php-json-schema-5.2.9/tests/Uri/Fixture/000077500000000000000000000000001354400737700200645ustar00rootroot00000000000000php-json-schema-5.2.9/tests/Uri/Fixture/child.json000066400000000000000000000001671354400737700220460ustar00rootroot00000000000000{ "type":"object", "title":"parent", "properties": { "parentProp": { "type":"boolean" } } }php-json-schema-5.2.9/tests/Uri/Retrievers/000077500000000000000000000000001354400737700205705ustar00rootroot00000000000000php-json-schema-5.2.9/tests/Uri/Retrievers/CurlTest.php000066400000000000000000000031611354400737700230470ustar00rootroot00000000000000retrieve(realpath(__DIR__ . '/../../fixtures/foobar.json')); } public function testRetrieveNonexistantFile() { $c = new Curl(); $this->setExpectedException( '\JsonSchema\Exception\ResourceNotFoundException', 'JSON schema not found' ); $c->retrieve(__DIR__ . '/notARealFile'); } public function testNoContentType() { $c = new Curl(); $c->retrieve(realpath(__DIR__ . '/../../fixtures') . '/foobar-noheader.json'); } } } namespace JsonSchema\Uri\Retrievers { function curl_exec($curl) { $uri = curl_getinfo($curl, \CURLINFO_EFFECTIVE_URL); if ($uri === realpath(__DIR__ . '/../../fixtures/foobar.json')) { // return file with headers $headers = implode("\n", array( 'Content-Type: application/json' )); return sprintf("%s\r\n\r\n%s", $headers, file_get_contents($uri)); } elseif ($uri === realpath(__DIR__ . '/../../fixtures') . '/foobar-noheader.json') { // return file without headers $uri = realpath(__DIR__ . '/../../fixtures/foobar.json'); return "\r\n\r\n" . file_get_contents($uri); } // fallback to real curl_exec return \curl_exec($curl); } } php-json-schema-5.2.9/tests/Uri/Retrievers/FileGetContentsTest.php000066400000000000000000000043141354400737700252000ustar00rootroot00000000000000retrieve(__DIR__ . '/Fixture/missing.json'); } public function testFetchFile() { $res = new FileGetContents(); $result = $res->retrieve(__DIR__ . '/../Fixture/child.json'); $this->assertNotEmpty($result); } public function testFalseReturn() { $res = new FileGetContents(); $this->setExpectedException( '\JsonSchema\Exception\ResourceNotFoundException', 'JSON schema not found at http://example.com/false' ); $res->retrieve('http://example.com/false'); } public function testFetchDirectory() { $res = new FileGetContents(); $this->setExpectedException( '\JsonSchema\Exception\ResourceNotFoundException', 'JSON schema not found at file:///this/is/a/directory/' ); $res->retrieve('file:///this/is/a/directory/'); } public function testContentType() { $res = new FileGetContents(); $reflector = new \ReflectionObject($res); $fetchContentType = $reflector->getMethod('fetchContentType'); $fetchContentType->setAccessible(true); $this->assertTrue($fetchContentType->invoke($res, array('Content-Type: application/json'))); $this->assertFalse($fetchContentType->invoke($res, array('X-Some-Header: whateverValue'))); } } } namespace JsonSchema\Uri\Retrievers { function file_get_contents($uri) { switch ($uri) { case 'http://example.com/false': return false; case 'file:///this/is/a/directory/': return ''; default: return \file_get_contents($uri); } } } php-json-schema-5.2.9/tests/Uri/Retrievers/PredefinedArrayTest.php000066400000000000000000000022501354400737700252040ustar00rootroot00000000000000retriever = new PredefinedArray( array( 'http://acme.com/schemas/person#' => 'THE_PERSON_SCHEMA', 'http://acme.com/schemas/address#' => 'THE_ADDRESS_SCHEMA', ), 'THE_CONTENT_TYPE' ); } public function testRetrieve() { $this->assertEquals('THE_PERSON_SCHEMA', $this->retriever->retrieve('http://acme.com/schemas/person#')); $this->assertEquals('THE_ADDRESS_SCHEMA', $this->retriever->retrieve('http://acme.com/schemas/address#')); } /** * @expectedException \JsonSchema\Exception\ResourceNotFoundException */ public function testRetrieveNonExistsingSchema() { $this->retriever->retrieve('http://acme.com/schemas/plop#'); } public function testGetContentType() { $this->assertEquals('THE_CONTENT_TYPE', $this->retriever->getContentType()); } } php-json-schema-5.2.9/tests/Uri/UriResolverTest.php000066400000000000000000000133631354400737700222760ustar00rootroot00000000000000resolver = new UriResolver(); } public function testParse() { $this->assertEquals( array( 'scheme' => 'http', 'authority' => 'example.org', 'path' => '/path/to/file.json' ), $this->resolver->parse('http://example.org/path/to/file.json') ); } public function testParseAnchor() { $this->assertEquals( array( 'scheme' => 'http', 'authority' => 'example.org', 'path' => '/path/to/file.json', 'query' => '', 'fragment' => 'foo' ), $this->resolver->parse('http://example.org/path/to/file.json#foo') ); } public function testCombineRelativePathWithBasePath() { $this->assertEquals( '/foo/baz.json', UriResolver::combineRelativePathWithBasePath( 'baz.json', '/foo/bar.json' ) ); } public function testCombineRelativePathWithBasePathAbsolute() { $this->assertEquals( '/baz/data.json', UriResolver::combineRelativePathWithBasePath( '/baz/data.json', '/foo/bar.json' ) ); } public function testCombineRelativePathWithBasePathRelativeSub() { $this->assertEquals( '/foo/baz/data.json', UriResolver::combineRelativePathWithBasePath( 'baz/data.json', '/foo/bar.json' ) ); } public function testCombineRelativePathWithBasePathNoPath() { //needed for anchor-only urls $this->assertEquals( '/foo/bar.json', UriResolver::combineRelativePathWithBasePath( '', '/foo/bar.json' ) ); } public function testResolveAbsoluteUri() { $this->assertEquals( 'http://example.org/foo/bar.json', $this->resolver->resolve( 'http://example.org/foo/bar.json', null ) ); } /** * @expectedException \JsonSchema\Exception\UriResolverException */ public function testResolveRelativeUriNoBase() { $this->assertEquals( 'http://example.org/foo/bar.json', $this->resolver->resolve( 'bar.json', null ) ); } public function testResolveRelativeUriBaseDir() { $this->assertEquals( 'http://example.org/foo/bar.json', $this->resolver->resolve( 'bar.json', 'http://example.org/foo/' ) ); } public function testResolveRelativeUriBaseFile() { $this->assertEquals( 'http://example.org/foo/bar.json', $this->resolver->resolve( 'bar.json', 'http://example.org/foo/baz.json' ) ); } public function testResolveAnchor() { $this->assertEquals( 'http://example.org/foo/bar.json#baz', $this->resolver->resolve( '#baz', 'http://example.org/foo/bar.json' ) ); } public function testResolveAnchorWithFile() { $this->assertEquals( 'http://example.org/foo/baz.json#baz', $this->resolver->resolve( 'baz.json#baz', 'http://example.org/foo/bar.json' ) ); } public function testResolveAnchorAnchor() { $this->assertEquals( 'http://example.org/foo/bar.json#bazinga', $this->resolver->resolve( '#bazinga', 'http://example.org/foo/bar.json#baz' ) ); } public function testResolveEmpty() { $this->assertEquals( 'http://example.org/foo/bar.json', $this->resolver->resolve( '', 'http://example.org/foo/bar.json' ) ); } public function testReversable() { $uri = 'scheme://user:password@authority/path?query#fragment'; $split = $this->resolver->parse($uri); // check that the URI was split as expected $this->assertEquals(array( 'scheme' => 'scheme', 'authority' => 'user:password@authority', 'path' => '/path', 'query' => 'query', 'fragment' => 'fragment' ), $split); // check that the recombined URI matches the original input $this->assertEquals($uri, $this->resolver->generate($split)); } public function testRelativeFileAsRoot() { $this->assertEquals( 'file://' . getcwd() . '/src/JsonSchema/Validator.php', $this->resolver->resolve( 'Validator.php', 'src/JsonSchema/SchemaStorage.php' ) ); } public function testRelativeDirectoryAsRoot() { $this->assertEquals( 'file://' . getcwd() . '/src/JsonSchema/Validator.php', $this->resolver->resolve( 'Validator.php', 'src/JsonSchema' ) ); } public function testRelativeNonExistentFileAsRoot() { $this->assertEquals( 'file://' . getcwd() . '/resolved.file', $this->resolver->resolve( 'resolved.file', 'test.file' ) ); } } php-json-schema-5.2.9/tests/Uri/UriRetrieverTest.php000066400000000000000000000320701354400737700224400ustar00rootroot00000000000000validator = new Validator(); } private function getRetrieverMock($returnSchema) { $jsonSchema = json_decode($returnSchema); if (JSON_ERROR_NONE < $error = json_last_error()) { throw new JsonDecodingException($error); } $retriever = $this->getMock('JsonSchema\Uri\UriRetriever', array('retrieve')); $retriever->expects($this->at(0)) ->method('retrieve') ->with($this->equalTo(null), $this->equalTo('http://some.host.at/somewhere/parent')) ->will($this->returnValue($jsonSchema)); return $retriever; } /** * @dataProvider jsonProvider */ public function testChildExtendsParentValidTest($childSchema, $parentSchema) { $this->mockRetriever($parentSchema); $json = '{"childProp":"infant", "parentProp":false}'; $decodedJson = json_decode($json); $decodedJsonSchema = json_decode($childSchema); $this->validator->check($decodedJson, $decodedJsonSchema); $this->assertTrue($this->validator->isValid()); } /** * @dataProvider jsonProvider */ public function testChildExtendsParentInvalidChildTest($childSchema, $parentSchema) { $this->mockRetriever($parentSchema); $json = '{"childProp":1, "parentProp":false}'; $decodedJson = json_decode($json); $decodedJsonSchema = json_decode($childSchema); $this->validator->check($decodedJson, $decodedJsonSchema); $this->assertFalse($this->validator->isValid()); } /** * @dataProvider jsonProvider */ public function testChildExtendsParentInvalidParentTest($childSchema, $parentSchema) { $this->mockRetriever($parentSchema); $json = '{"childProp":"infant", "parentProp":1}'; $decodedJson = json_decode($json); $decodedJsonSchema = json_decode($childSchema); $this->validator->check($decodedJson, $decodedJsonSchema); $this->assertFalse($this->validator->isValid()); } /** * @dataProvider jsonProvider */ public function testResolveRelativeUri($childSchema, $parentSchema) { self::setParentSchemaExtendsValue($parentSchema, 'grandparent'); $this->mockRetriever($parentSchema); $json = '{"childProp":"infant", "parentProp":false}'; $decodedJson = json_decode($json); $decodedJsonSchema = json_decode($childSchema); $this->validator->check($decodedJson, $decodedJsonSchema); $this->assertTrue($this->validator->isValid()); } private static function setParentSchemaExtendsValue(&$parentSchema, $value) { $parentSchemaDecoded = json_decode($parentSchema, true); $parentSchemaDecoded['extends'] = $value; $parentSchema = json_encode($parentSchemaDecoded); } public function jsonProvider() { $childSchema = << 'schema' ); $retriever = new \JsonSchema\Uri\UriRetriever(); $this->assertEquals( $schema, $retriever->resolvePointer( $schema, 'http://example.org/schema.json' ) ); } public function testResolvePointerFragment() { $schema = (object) array( 'definitions' => (object) array( 'foo' => (object) array( 'title' => 'foo' ) ), 'title' => 'schema' ); $retriever = new \JsonSchema\Uri\UriRetriever(); $this->assertEquals( $schema->definitions->foo, $retriever->resolvePointer( $schema, 'http://example.org/schema.json#/definitions/foo' ) ); } /** * @expectedException \JsonSchema\Exception\ResourceNotFoundException */ public function testResolvePointerFragmentNotFound() { $schema = (object) array( 'definitions' => (object) array( 'foo' => (object) array( 'title' => 'foo' ) ), 'title' => 'schema' ); $retriever = new \JsonSchema\Uri\UriRetriever(); $retriever->resolvePointer( $schema, 'http://example.org/schema.json#/definitions/bar' ); } /** * @expectedException \JsonSchema\Exception\ResourceNotFoundException */ public function testResolvePointerFragmentNoArray() { $schema = (object) array( 'definitions' => (object) array( 'foo' => array( 'title' => 'foo' ) ), 'title' => 'schema' ); $retriever = new \JsonSchema\Uri\UriRetriever(); $retriever->resolvePointer( $schema, 'http://example.org/schema.json#/definitions/foo' ); } /** * @expectedException \JsonSchema\Exception\UriResolverException */ public function testResolveExcessLevelUp() { $retriever = new \JsonSchema\Uri\UriRetriever(); $retriever->resolve( '../schema.json#', 'http://example.org/schema.json#' ); } public function testConfirmMediaTypeAcceptsJsonSchemaType() { $retriever = $this->getMock('JsonSchema\Uri\UriRetriever', array('getContentType')); $retriever->expects($this->at(0)) ->method('getContentType') ->will($this->returnValue('application/schema+json')); $this->assertEquals(null, $retriever->confirmMediaType($retriever, null)); } public function testConfirmMediaTypeAcceptsJsonType() { $retriever = $this->getMock('JsonSchema\Uri\UriRetriever', array('getContentType')); $retriever->expects($this->at(0)) ->method('getContentType') ->will($this->returnValue('application/json')); $this->assertEquals(null, $retriever->confirmMediaType($retriever, null)); } /** * @expectedException \JsonSchema\Exception\InvalidSchemaMediaTypeException */ public function testConfirmMediaTypeThrowsExceptionForUnsupportedTypes() { $retriever = $this->getMock('JsonSchema\Uri\UriRetriever', array('getContentType')); $retriever->expects($this->at(0)) ->method('getContentType') ->will($this->returnValue('text/html')); $this->assertEquals(null, $retriever->confirmMediaType($retriever, null)); } private function mockRetriever($schema) { $retrieverMock = $this->getRetrieverMock($schema); $factory = new \ReflectionProperty('JsonSchema\Constraints\BaseConstraint', 'factory'); $factory->setAccessible(true); $factory = $factory->getValue($this->validator); $retriever = new \ReflectionProperty('JsonSchema\Constraints\Factory', 'uriRetriever'); $retriever->setAccessible(true); $retriever->setValue($factory, $retrieverMock); } public function testTranslations() { $retriever = new UriRetriever(); $uri = 'http://example.com/foo/bar'; $translated = 'file://another/bar'; $retriever->setTranslation('|^https?://example.com/foo/bar#?|', 'file://another/bar'); $this->assertEquals($translated, $retriever->translate($uri)); } public function testPackageURITranslation() { $retriever = new UriRetriever(); $root = sprintf('file://%s/', realpath(__DIR__ . '/../..')); $uri = $retriever->translate('package://foo/bar.json'); $this->assertEquals("${root}foo/bar.json", $uri); } public function testDefaultDistTranslations() { $retriever = new UriRetriever(); $root = sprintf('file://%s/dist/schema/', realpath(__DIR__ . '/../..')); $this->assertEquals( $root . 'json-schema-draft-03.json', $retriever->translate('http://json-schema.org/draft-03/schema#') ); $this->assertEquals( $root . 'json-schema-draft-04.json', $retriever->translate('http://json-schema.org/draft-04/schema#') ); } public function testRetrieveSchemaFromPackage() { $retriever = new UriRetriever(); // load schema from package $schema = $retriever->retrieve('package://tests/fixtures/foobar.json'); $this->assertNotFalse($schema); // check that the schema was loaded & processed correctly $this->assertEquals('454f423bd7edddf0bc77af4130ed9161', md5(json_encode($schema))); } public function testInvalidContentTypeEndpointsDefault() { $mock = $this->getMock('JsonSchema\Uri\UriRetriever', array('getContentType')); $mock->method('getContentType')->willReturn('Application/X-Fake-Type'); $retriever = new UriRetriever(); $this->assertTrue($retriever->confirmMediaType($mock, 'http://json-schema.org/')); $this->assertTrue($retriever->confirmMediaType($mock, 'https://json-schema.org/')); } /** * @expectedException \JsonSchema\Exception\InvalidSchemaMediaTypeException */ public function testInvalidContentTypeEndpointsUnknown() { $mock = $this->getMock('JsonSchema\Uri\UriRetriever', array('getContentType')); $mock->method('getContentType')->willReturn('Application/X-Fake-Type'); $retriever = new UriRetriever(); $retriever->confirmMediaType($mock, 'http://example.com'); } public function testInvalidContentTypeEndpointsAdded() { $mock = $this->getMock('JsonSchema\Uri\UriRetriever', array('getContentType')); $mock->method('getContentType')->willReturn('Application/X-Fake-Type'); $retriever = new UriRetriever(); $retriever->addInvalidContentTypeEndpoint('http://example.com'); $retriever->confirmMediaType($mock, 'http://example.com'); } public function testSchemaCache() { $retriever = new UriRetriever(); $reflector = new \ReflectionObject($retriever); // inject a schema cache value $schemaCache = $reflector->getProperty('schemaCache'); $schemaCache->setAccessible(true); $schemaCache->setValue($retriever, array('local://test/uri' => 'testSchemaValue')); // retrieve from schema cache $loadSchema = $reflector->getMethod('loadSchema'); $loadSchema->setAccessible(true); $this->assertEquals( 'testSchemaValue', $loadSchema->invoke($retriever, 'local://test/uri') ); } public function testLoadSchemaJSONDecodingException() { $retriever = new UriRetriever(); $this->setExpectedException( 'JsonSchema\Exception\JsonDecodingException', 'JSON syntax is malformed' ); $schema = $retriever->retrieve('package://tests/fixtures/bad-syntax.json'); } public function testGenerateURI() { $retriever = new UriRetriever(); $components = array( 'scheme' => 'scheme', 'authority' => 'authority', 'path' => '/path', 'query' => '?query', 'fragment' => '#fragment' ); $this->assertEquals('scheme://authority/path?query#fragment', $retriever->generate($components)); } public function testResolveHTTP() { $retriever = new UriRetriever(); $this->assertEquals( 'http://example.com/schema', $retriever->resolve('http://example.com/schema') ); } public function combinedURITests() { return array( array('blue', 'http://example.com/red', 'http://example.com/blue'), array('blue', 'http://example.com/', 'http://example.com/blue'), ); } /** * @dataProvider combinedURITests */ public function testResolveCombinedURI($uri, $baseURI, $combinedURI) { $retriever = new UriRetriever(); $this->assertEquals($combinedURI, $retriever->resolve($uri, $baseURI)); } public function testIsValidURI() { $retriever = new UriRetriever(); $this->assertTrue($retriever->isValid('http://example.com/schema')); } } php-json-schema-5.2.9/tests/ValidatorTest.php000066400000000000000000000034761354400737700212070ustar00rootroot00000000000000validate($data, $schema); $this->assertFalse($validator->isValid(), 'Validation succeeded, but should have failed.'); } public function testBadAssocSchemaInput() { if (version_compare(phpversion(), '5.5.0', '<')) { $this->markTestSkipped('PHP versions < 5.5.0 trigger an error on json_encode issues'); } if (defined('HHVM_VERSION')) { $this->markTestSkipped('HHVM has no problem with encoding resources'); } $schema = array('propertyOne' => fopen('php://stdout', 'w')); $data = json_decode('{"propertyOne":[42]}', true); $validator = new Validator(); $this->setExpectedException('\JsonSchema\Exception\InvalidArgumentException'); $validator->validate($data, $schema); } public function testCheck() { $schema = json_decode('{"type":"string"}'); $data = json_decode('42'); $validator = new Validator(); $validator->check($data, $schema); $this->assertFalse($validator->isValid(), 'Validation succeeded, but should have failed.'); } public function testCoerce() { $schema = json_decode('{"type":"integer"}'); $data = json_decode('"42"'); $validator = new Validator(); $validator->coerce($data, $schema); $this->assertTrue($validator->isValid(), 'Validation failed, but should have succeeded.'); } } php-json-schema-5.2.9/tests/fixtures/000077500000000000000000000000001354400737700175505ustar00rootroot00000000000000php-json-schema-5.2.9/tests/fixtures/bad-syntax.json000066400000000000000000000000021354400737700225050ustar00rootroot00000000000000{ php-json-schema-5.2.9/tests/fixtures/foobar.json000066400000000000000000000003601354400737700217120ustar00rootroot00000000000000{ "$id": "http://example.com/foo/bar#", "type": "object", "properties": { "foo": { "type": "string", "default": "bar" } }, "required": ["foo"], "additionalProperties": false }