diff --git a/composer.json b/composer.json index c6b6936c..df1b806c 100644 --- a/composer.json +++ b/composer.json @@ -28,12 +28,13 @@ ], "require": { "php": ">=5.3.3", + "ext-json": "*", "marc-mabe/php-enum":"^2.0 || ^3.0 || ^4.0", "icecave/parity": "1.0.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "~2.2.20 || ~2.19.0", - "json-schema/json-schema-test-suite": "1.2.0", + "json-schema/json-schema-test-suite": "2.0.0", "phpunit/phpunit": "^4.8.35" }, "extra": { @@ -56,11 +57,11 @@ "type": "package", "package": { "name": "json-schema/json-schema-test-suite", - "version": "1.2.0", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/json-schema/JSON-Schema-Test-Suite", - "reference": "1.2.0" + "reference": "2.0.0" } } } diff --git a/src/JsonSchema/ConstraintError.php b/src/JsonSchema/ConstraintError.php index fd5ba8e6..de49cdd0 100644 --- a/src/JsonSchema/ConstraintError.php +++ b/src/JsonSchema/ConstraintError.php @@ -9,7 +9,11 @@ class ConstraintError extends Enum const ADDITIONAL_ITEMS = 'additionalItems'; const ADDITIONAL_PROPERTIES = 'additionalProp'; const ALL_OF = 'allOf'; + const ALWAYS_FAILS = 'alwaysFails'; const ANY_OF = 'anyOf'; + const CONDITIONAL_IF = 'if'; + const CONDITIONAL_THEN = 'then'; + const CONDITIONAL_ELSE = 'else'; const DEPENDENCIES = 'dependencies'; const DISALLOW = 'disallow'; const DIVISIBLE_BY = 'divisibleBy'; @@ -59,12 +63,16 @@ public function getMessage() self::ADDITIONAL_ITEMS => 'The item %s[%s] is not defined and the definition does not allow additional items', self::ADDITIONAL_PROPERTIES => 'The property %s is not defined and the definition does not allow additional properties', self::ALL_OF => 'Failed to match all schemas', + self::ALWAYS_FAILS => 'Schema always fails validation', self::ANY_OF => 'Failed to match at least one schema', self::DEPENDENCIES => '%s depends on %s, which is missing', self::DISALLOW => 'Disallowed value was matched', self::DIVISIBLE_BY => 'Is not divisible by %d', self::ENUM => 'Does not have a value in the enumeration %s', self::CONSTANT => 'Does not have a value equal to %s', + self::CONDITIONAL_IF => 'The keyword "if" must be a boolean or an object', + self::CONDITIONAL_THEN => 'The keyword "then" must be a boolean or an object', + self::CONDITIONAL_ELSE => 'The keyword "else" must be a boolean or an object', self::EXCLUSIVE_MINIMUM => 'Must have a minimum value greater than %d', self::EXCLUSIVE_MAXIMUM => 'Must have a maximum value less than %d', self::FORMAT_COLOR => 'Invalid color', diff --git a/src/JsonSchema/Constraints/ObjectConstraint.php b/src/JsonSchema/Constraints/ObjectConstraint.php index b4f85650..bd77c7ac 100644 --- a/src/JsonSchema/Constraints/ObjectConstraint.php +++ b/src/JsonSchema/Constraints/ObjectConstraint.php @@ -173,6 +173,11 @@ protected function &getProperty(&$element, $property, $fallback = null) */ protected function validateMinMaxConstraint($element, $objectDefinition, JsonPointer $path = null) { + // minProperties and maxProperties constraints only applies on objects elements. + if (!is_object($element)) { + return; + } + // Verify minimum number of properties if (isset($objectDefinition->minProperties) && !is_object($objectDefinition->minProperties)) { if ($this->getTypeCheck()->propertyCount($element) < $objectDefinition->minProperties) { diff --git a/src/JsonSchema/Constraints/UndefinedConstraint.php b/src/JsonSchema/Constraints/UndefinedConstraint.php index aa5e664f..bdb76a76 100644 --- a/src/JsonSchema/Constraints/UndefinedConstraint.php +++ b/src/JsonSchema/Constraints/UndefinedConstraint.php @@ -14,6 +14,7 @@ use JsonSchema\Entity\JsonPointer; use JsonSchema\Exception\ValidationException; use JsonSchema\Uri\UriResolver; +use JsonSchema\Validator; /** * The UndefinedConstraint Constraints @@ -46,7 +47,7 @@ public function check(&$value, $schema = null, JsonPointer $path = null, $i = nu // check special properties $this->validateCommonProperties($value, $schema, $path, $i); - // check allOf, anyOf, and oneOf properties + // check allOf, anyOf, oneOf, if, then, and else properties $this->validateOfProperties($value, $schema, $path, ''); // check known types @@ -372,6 +373,34 @@ protected function validateOfProperties(&$value, $schema, JsonPointer $path, $i $this->errors = $startErrors; } } + + if (isset($schema->if)) { + if (!is_bool($schema->if) && !is_object($schema->if)) { + $this->addError(ConstraintError::CONDITIONAL_IF(), $path); + } + $validator = new Validator(); + if ($schema->if !== false && Validator::ERROR_NONE === $validator->validate($value, $schema->if)) { + if (isset($schema->then)) { + if (!is_bool($schema->then) && !is_object($schema->then)) { + $this->addError(ConstraintError::CONDITIONAL_THEN(), $path); + } + if ($schema->then === false) { + $this->addError(ConstraintError::ALWAYS_FAILS(), $path); + } else { + $this->check($value, $schema->then); + } + } + } elseif (isset($schema->else)) { + if (!is_bool($schema->else) && !is_object($schema->else)) { + $this->addError(ConstraintError::CONDITIONAL_ELSE(), $path); + } + if ($schema->else === false) { + $this->addError(ConstraintError::ALWAYS_FAILS(), $path); + } else { + $this->check($value, $schema->else); + } + } + } } /** diff --git a/tests/Constraints/IfThenElseTest.php b/tests/Constraints/IfThenElseTest.php new file mode 100644 index 00000000..afb93ed1 --- /dev/null +++ b/tests/Constraints/IfThenElseTest.php @@ -0,0 +1,231 @@ +