diff --git a/src/JsonSchema/ConstraintError.php b/src/JsonSchema/ConstraintError.php index 25f4c6e5..fd5ba8e6 100644 --- a/src/JsonSchema/ConstraintError.php +++ b/src/JsonSchema/ConstraintError.php @@ -14,6 +14,7 @@ class ConstraintError extends Enum const DISALLOW = 'disallow'; const DIVISIBLE_BY = 'divisibleBy'; const ENUM = 'enum'; + const CONSTANT = 'const'; const EXCLUSIVE_MINIMUM = 'exclusiveMinimum'; const EXCLUSIVE_MAXIMUM = 'exclusiveMaximum'; const FORMAT_COLOR = 'colorFormat'; @@ -63,6 +64,7 @@ public function getMessage() 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::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/ConstConstraint.php b/src/JsonSchema/Constraints/ConstConstraint.php new file mode 100644 index 00000000..a3ca1068 --- /dev/null +++ b/src/JsonSchema/Constraints/ConstConstraint.php @@ -0,0 +1,54 @@ + + */ +class ConstConstraint extends Constraint +{ + /** + * {@inheritdoc} + */ + public function check(&$element, $schema = null, JsonPointer $path = null, $i = null) + { + // Only validate const if the attribute exists + if ($element instanceof UndefinedConstraint && (!isset($schema->required) || !$schema->required)) { + return; + } + $const = $schema->const; + + $type = gettype($element); + $constType = gettype($const); + + if ($this->factory->getConfig(self::CHECK_MODE_TYPE_CAST) && $type == 'array' && $constType == 'object') { + if ((object) $element == $const) { + return; + } + } + + if ($type === gettype($const)) { + if ($type == 'object') { + if ($element == $const) { + return; + } + } elseif ($element === $const) { + return; + } + } + + $this->addError(ConstraintError::CONSTANT(), $path, array('const' => $schema->const)); + } +} diff --git a/src/JsonSchema/Constraints/Constraint.php b/src/JsonSchema/Constraints/Constraint.php index 51b5a98a..389e0f53 100644 --- a/src/JsonSchema/Constraints/Constraint.php +++ b/src/JsonSchema/Constraints/Constraint.php @@ -169,6 +169,22 @@ protected function checkEnum($value, $schema = null, JsonPointer $path = null, $ $this->addErrors($validator->getErrors()); } + /** + * Checks a const element + * + * @param mixed $value + * @param mixed $schema + * @param JsonPointer|null $path + * @param mixed $i + */ + protected function checkConst($value, $schema = null, JsonPointer $path = null, $i = null) + { + $validator = $this->factory->createInstanceFor('const'); + $validator->check($value, $schema, $path, $i); + + $this->addErrors($validator->getErrors()); + } + /** * Checks format of an element * diff --git a/src/JsonSchema/Constraints/Factory.php b/src/JsonSchema/Constraints/Factory.php index 4e771c19..9c0f4f21 100644 --- a/src/JsonSchema/Constraints/Factory.php +++ b/src/JsonSchema/Constraints/Factory.php @@ -58,6 +58,7 @@ class Factory 'string' => 'JsonSchema\Constraints\StringConstraint', 'number' => 'JsonSchema\Constraints\NumberConstraint', 'enum' => 'JsonSchema\Constraints\EnumConstraint', + 'const' => 'JsonSchema\Constraints\ConstConstraint', 'format' => 'JsonSchema\Constraints\FormatConstraint', 'schema' => 'JsonSchema\Constraints\SchemaConstraint', 'validator' => 'JsonSchema\Validator' diff --git a/src/JsonSchema/Constraints/UndefinedConstraint.php b/src/JsonSchema/Constraints/UndefinedConstraint.php index 3b0c8eb0..02f14c0b 100644 --- a/src/JsonSchema/Constraints/UndefinedConstraint.php +++ b/src/JsonSchema/Constraints/UndefinedConstraint.php @@ -96,6 +96,11 @@ public function validateTypes(&$value, $schema = null, JsonPointer $path, $i = n if (isset($schema->enum)) { $this->checkEnum($value, $schema, $path, $i); } + + // check const + if (isset($schema->const)) { + $this->checkConst($value, $schema, $path, $i); + } } /** diff --git a/tests/Constraints/CoerciveTest.php b/tests/Constraints/CoerciveTest.php index 8d58a58d..587f1f99 100644 --- a/tests/Constraints/CoerciveTest.php +++ b/tests/Constraints/CoerciveTest.php @@ -130,7 +130,48 @@ public function dataCoerceCases() 'string', 'integer', 42, true ); - $tests = array(); + // #46 check coercion with "const" + $tests[] = array( + '{"properties":{"propertyOne":{"type":"string","const":"42"}}}', + '{"propertyOne":42}', + 'integer', 'string', '42', true + ); + + // #47 check coercion with "const" + $tests[] = array( + '{"properties":{"propertyOne":{"type":"number","const":42}}}', + '{"propertyOne":"42"}', + 'string', 'integer', 42, true + ); + + // #48 check boolean coercion with "const" + $tests[] = array( + '{"properties":{"propertyOne":{"type":"boolean","const":false}}}', + '{"propertyOne":"false"}', + 'string', 'boolean', false, true + ); + + // #49 check boolean coercion with "const" + $tests[] = array( + '{"properties":{"propertyOne":{"type":"boolean","const":true}}}', + '{"propertyOne":"true"}', + 'string', 'boolean', true, true + ); + + // #50 check boolean coercion with "const" + $tests[] = array( + '{"properties":{"propertyOne":{"type":"boolean","const":true}}}', + '{"propertyOne":1}', + 'integer', 'boolean', true, true + ); + + // #51 check boolean coercion with "const" + $tests[] = array( + '{"properties":{"propertyOne":{"type":"boolean","const":false}}}', + '{"propertyOne":"false"}', + 'string', 'boolean', false, true + ); + foreach ($types as $toType => $testCases) { foreach ($testCases as $testCase) { $tests[] = array( diff --git a/tests/Constraints/ConstTest.php b/tests/Constraints/ConstTest.php new file mode 100644 index 00000000..cdd0409a --- /dev/null +++ b/tests/Constraints/ConstTest.php @@ -0,0 +1,98 @@ +