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/UndefinedConstraint.php b/src/JsonSchema/Constraints/UndefinedConstraint.php index aa5e664f..0e1903d8 100644 --- a/src/JsonSchema/Constraints/UndefinedConstraint.php +++ b/src/JsonSchema/Constraints/UndefinedConstraint.php @@ -46,7 +46,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 +372,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 @@ +