diff --git a/.php_cs.dist b/.php_cs.dist index c4e84b4e..0b5e2b2c 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -11,11 +11,9 @@ $config '@PSR2' => true, '@Symfony' => true, // additionally - 'align_multiline_comment' => array('comment_type' => 'phpdocs_like'), 'array_syntax' => array('syntax' => 'long'), 'binary_operator_spaces' => false, 'concat_space' => array('spacing' => 'one'), - 'increment_style' => false, 'no_useless_else' => true, 'no_useless_return' => true, 'ordered_imports' => true, @@ -25,7 +23,6 @@ $config 'pre_increment' => false, 'simplified_null_return' => false, 'trailing_comma_in_multiline_array' => false, - 'yoda_style' => null, )) ->setFinder($finder) ; diff --git a/.travis.yml b/.travis.yml index ebf29d24..3b4f5fc1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,7 @@ matrix: 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 diff --git a/bin/validate-json b/bin/validate-json index 421ebcde..aeb818f0 100755 --- a/bin/validate-json +++ b/bin/validate-json @@ -129,7 +129,7 @@ function parseHeaderValue($headerValue) /** * Send a string to the output stream, but only if --quiet is not enabled * - * @param $str A string output + * @param $str string A string output */ function output($str) { global $arOptions; diff --git a/composer.json b/composer.json index 6e72ab9b..20e85fc3 100644 --- a/composer.json +++ b/composer.json @@ -1,9 +1,12 @@ { "name": "justinrainbow/json-schema", + "type": "library", "description": "A library to validate a json schema.", - "keywords": ["json", "schema"], + "keywords": [ + "json", + "schema" + ], "homepage": "https://github.com/justinrainbow/json-schema", - "type": "library", "license": "MIT", "authors": [ { @@ -23,43 +26,51 @@ "email": "seroscho@googlemail.com" } ], - "repositories": [{ - "type": "package", - "package": { - "name": "json-schema/JSON-Schema-Test-Suite", - "version": "1.2.0", - "source": { - "url": "https://github.com/json-schema/JSON-Schema-Test-Suite", - "type": "git", - "reference": "1.2.0" - } - } - }], "require": { "php": ">=5.3.3" }, "require-dev": { + "friendsofphp/php-cs-fixer": "~2.2.20", "json-schema/JSON-Schema-Test-Suite": "1.2.0", - "friendsofphp/php-cs-fixer": "^2.1", "phpunit/phpunit": "^4.8.35" }, - "autoload": { - "psr-4": { "JsonSchema\\": "src/JsonSchema/" } - }, - "autoload-dev": { - "psr-4": { "JsonSchema\\Tests\\": "tests/" } - }, - "bin": ["bin/validate-json"], "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": { - "test" : "phpunit", - "testOnly" : "phpunit --colors --filter", - "coverage" : "phpunit --coverage-text", - "style-check" : "php-cs-fixer fix --dry-run --verbose --diff", - "style-fix" : "php-cs-fixer fix --verbose" + "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" } } diff --git a/src/JsonSchema/Constraints/CollectionConstraint.php b/src/JsonSchema/Constraints/CollectionConstraint.php index 49841a49..d1384b88 100644 --- a/src/JsonSchema/Constraints/CollectionConstraint.php +++ b/src/JsonSchema/Constraints/CollectionConstraint.php @@ -65,53 +65,27 @@ protected function validateItems(&$value, $schema = null, JsonPointer $path = nu { if (is_object($schema->items)) { // just one type definition for the whole array + foreach ($value as $k => &$v) { + $initErrors = $this->getErrors(); - if (isset($schema->items->type) - && ( - $schema->items->type == 'string' - || $schema->items->type == 'number' - || $schema->items->type == 'integer' - ) - && !isset($schema->additionalItems) - ) { - // performance optimization - $type = $schema->items->type; - $typeValidator = $this->factory->createInstanceFor('type'); - $validator = $this->factory->createInstanceFor($type === 'integer' ? 'number' : $type); - - foreach ($value as $k => &$v) { - $k_path = $this->incrementPath($path, $k); - $typeValidator->check($v, $schema->items, $k_path, $i); + // First check if its defined in "items" + $this->checkUndefined($v, $schema->items, $path, $k); - $validator->check($v, $schema->items, $k_path, $i); + // 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); } - unset($v); /* remove dangling reference to prevent any future bugs - * caused by accidentally using $v elsewhere */ - $this->addErrors($typeValidator->getErrors()); - $this->addErrors($validator->getErrors()); - } else { - 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; - } + // 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 */ } + 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) { diff --git a/src/JsonSchema/Constraints/NumberConstraint.php b/src/JsonSchema/Constraints/NumberConstraint.php index 5a809774..d4c31a46 100644 --- a/src/JsonSchema/Constraints/NumberConstraint.php +++ b/src/JsonSchema/Constraints/NumberConstraint.php @@ -69,18 +69,13 @@ public function check(&$element, $schema = null, JsonPointer $path = null, $i = private function fmod($number1, $number2) { - $number1 = abs($number1); - $modulus = fmod($number1, $number2); - $precision = abs(0.0000000001); - $diff = (float) ($modulus - $number2); + $modulus = ($number1 - round($number1 / $number2) * $number2); + $precision = 0.0000000001; - if (-$precision < $diff && $diff < $precision) { + if (-$precision < $modulus && $modulus < $precision) { return 0.0; } - $decimals1 = mb_strpos($number1, '.') ? mb_strlen($number1) - mb_strpos($number1, '.') - 1 : 0; - $decimals2 = mb_strpos($number2, '.') ? mb_strlen($number2) - mb_strpos($number2, '.') - 1 : 0; - - return (float) round($modulus, max($decimals1, $decimals2)); + return $modulus; } } diff --git a/src/JsonSchema/Constraints/TypeCheck/StrictTypeCheck.php b/src/JsonSchema/Constraints/TypeCheck/StrictTypeCheck.php index a6303a7a..59fa12be 100644 --- a/src/JsonSchema/Constraints/TypeCheck/StrictTypeCheck.php +++ b/src/JsonSchema/Constraints/TypeCheck/StrictTypeCheck.php @@ -31,6 +31,10 @@ public static function propertyExists($value, $property) public static function propertyCount($value) { + if (!is_object($value)) { + return 0; + } + return count(get_object_vars($value)); } } diff --git a/src/JsonSchema/Uri/UriRetriever.php b/src/JsonSchema/Uri/UriRetriever.php index 65452788..41147a2a 100644 --- a/src/JsonSchema/Uri/UriRetriever.php +++ b/src/JsonSchema/Uri/UriRetriever.php @@ -32,6 +32,14 @@ class UriRetriever implements BaseUriRetrieverInterface '|^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 */ @@ -44,6 +52,16 @@ class UriRetriever implements BaseUriRetrieverInterface */ 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 * @@ -65,9 +83,10 @@ public function confirmMediaType($uriRetriever, $uri) return; } - if (substr($uri, 0, 23) == 'http://json-schema.org/') { - //HACK; they deliver broken content types - return true; + foreach ($this->allowedInvalidContentTypeEndpoints as $endpoint) { + if (strpos($uri, $endpoint) === 0) { + return true; + } } throw new InvalidSchemaMediaTypeException(sprintf('Media type %s expected', Validator::SCHEMA_MEDIA_TYPE)); diff --git a/tests/Constraints/MinMaxPropertiesTest.php b/tests/Constraints/MinMaxPropertiesTest.php index 2063122c..90774b91 100644 --- a/tests/Constraints/MinMaxPropertiesTest.php +++ b/tests/Constraints/MinMaxPropertiesTest.php @@ -123,6 +123,16 @@ public function getInvalidTests() } }' ), + array( + '{ + "value": [] + }', + '{ + "properties": { + "value": {"minProperties": 1,"maxProperties": 2} + } + }' + ), ); } } diff --git a/tests/Uri/UriRetrieverTest.php b/tests/Uri/UriRetrieverTest.php index c95fc923..fb05cc18 100644 --- a/tests/Uri/UriRetrieverTest.php +++ b/tests/Uri/UriRetrieverTest.php @@ -330,13 +330,36 @@ public function testRetrieveSchemaFromPackage() $this->assertEquals('454f423bd7edddf0bc77af4130ed9161', md5(json_encode($schema))); } - public function testJsonSchemaOrgMediaTypeHack() + 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()