From 5ccf3d1de60119247f668ec3a03f606278b78381 Mon Sep 17 00:00:00 2001 From: Aubrey Hewes Date: Wed, 11 Nov 2015 20:30:23 +0100 Subject: [PATCH 1/8] added custom constraint possibility --- src/JsonSchema/Constraints/Factory.php | 37 +++++++++- src/JsonSchema/Validator.php | 15 ++++ .../Constraints/CustomConstraintTest.php | 73 +++++++++++++++++++ .../Constraints/Fixtures/CustomConstraint.php | 12 +++ 4 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 tests/JsonSchema/Tests/Constraints/CustomConstraintTest.php create mode 100644 tests/JsonSchema/Tests/Constraints/Fixtures/CustomConstraint.php diff --git a/src/JsonSchema/Constraints/Factory.php b/src/JsonSchema/Constraints/Factory.php index a4570f61..8b33aaad 100644 --- a/src/JsonSchema/Constraints/Factory.php +++ b/src/JsonSchema/Constraints/Factory.php @@ -23,12 +23,17 @@ class Factory */ protected $uriRetriever; + /** + * @var array + */ + private $constraints = []; + /** * @param UriRetriever $uriRetriever */ public function __construct(UriRetriever $uriRetriever = null) { - if (!$uriRetriever) { + if ( ! $uriRetriever) { $uriRetriever = new UriRetriever(); } @@ -43,10 +48,35 @@ public function getUriRetriever() return $this->uriRetriever; } + /** + * Add a custom constraint + * + * $factory->addConstraint('name', new MyCustomConstraint(...)); + * + * @param string $name + * @param ConstraintInterface $constraint + */ + public function addConstraint($name, $constraint) + { + $this->constraints[$name] = $constraint; + } + + /** + * @param $constraintName + * + * @return bool + */ + public function hasConstraint($constraintName) + { + return ! empty($this->constraints[$constraintName]) + && $this->constraints[$constraintName] instanceof ConstraintInterface; + } + /** * Create a constraint instance for the given constraint name. * * @param string $constraintName + * * @return ConstraintInterface|ObjectConstraint * @throws InvalidArgumentException if is not possible create the constraint instance. */ @@ -76,6 +106,11 @@ public function createInstanceFor($constraintName) return new Validator(Constraint::CHECK_MODE_NORMAL, $this->uriRetriever, $this); } + if ($this->hasConstraint($constraintName)) { + return $this->constraints[$constraintName]; + } + throw new InvalidArgumentException('Unknown constraint ' . $constraintName); } + } diff --git a/src/JsonSchema/Validator.php b/src/JsonSchema/Validator.php index 14dbb609..a76eb9c2 100644 --- a/src/JsonSchema/Validator.php +++ b/src/JsonSchema/Validator.php @@ -9,6 +9,7 @@ namespace JsonSchema; +use JsonSchema\Constraints\ConstraintInterface; use JsonSchema\Constraints\SchemaConstraint; use JsonSchema\Constraints\Constraint; @@ -37,4 +38,18 @@ public function check($value, $schema = null, $path = null, $i = null) $this->addErrors(array_unique($validator->getErrors(), SORT_REGULAR)); } + + /** + * Add a custom constraint + * + * // extends Constraint or implements ConstraintInterface + * $factory->addConstraint('name', new MyCustomConstraint(...)); + * + * @param string $name + * @param ConstraintInterface $constraint + */ + public function addConstraint($name, $constraint) + { + $this->getFactory()->addConstraint($name, $constraint); + } } diff --git a/tests/JsonSchema/Tests/Constraints/CustomConstraintTest.php b/tests/JsonSchema/Tests/Constraints/CustomConstraintTest.php new file mode 100644 index 00000000..2c2bad96 --- /dev/null +++ b/tests/JsonSchema/Tests/Constraints/CustomConstraintTest.php @@ -0,0 +1,73 @@ +factory = new Factory(); + } + + /** + * @dataProvider constraintNameProvider + * + * @param string $constraintName + * @param string $expectedClass + */ + public function testConstraintInstanceWithoutCtrParams($constraintName, $expectedClass) + { + $this->factory->addConstraint($constraintName, new $expectedClass()); + $constraint = $this->factory->createInstanceFor($constraintName); + + $this->assertInstanceOf($expectedClass, $constraint); + $this->assertInstanceOf('JsonSchema\Constraints\ConstraintInterface', $constraint); + $this->assertNotSame($this->factory->getUriRetriever(), $constraint->getUriRetriever()); + } + + /** + * @dataProvider constraintNameProvider + * + * @param string $constraintName + * @param string $expectedClass + */ + public function testConstraintInstanceWithCtrParams($constraintName, $expectedClass) + { + $this->factory->addConstraint($constraintName, + new $expectedClass(Constraint::CHECK_MODE_NORMAL, $this->factory->getUriRetriever(), $this->factory)); + $constraint = $this->factory->createInstanceFor($constraintName); + + $this->assertInstanceOf($expectedClass, $constraint); + $this->assertInstanceOf('JsonSchema\Constraints\ConstraintInterface', $constraint); + $this->assertSame($this->factory->getUriRetriever(), $constraint->getUriRetriever()); + $this->assertSame($this->factory, $constraint->getFactory()); + } + + /** + * @return array + */ + public function constraintNameProvider() + { + return array( + array('exists', 'JsonSchema\Constraints\StringConstraint'), + array('custom', 'JsonSchema\Tests\Constraints\Fixtures\CustomConstraint'), + ); + } + +} diff --git a/tests/JsonSchema/Tests/Constraints/Fixtures/CustomConstraint.php b/tests/JsonSchema/Tests/Constraints/Fixtures/CustomConstraint.php new file mode 100644 index 00000000..2c5f25ba --- /dev/null +++ b/tests/JsonSchema/Tests/Constraints/Fixtures/CustomConstraint.php @@ -0,0 +1,12 @@ + Date: Wed, 11 Nov 2015 21:06:58 +0100 Subject: [PATCH 2/8] allow constraint as classname; inheriting current env: else requires setting env via ctr --- src/JsonSchema/Constraints/Factory.php | 19 +++- src/JsonSchema/Validator.php | 12 ++- .../Constraints/CustomConstraintTest.php | 89 ++++++++++++++----- 3 files changed, 91 insertions(+), 29 deletions(-) diff --git a/src/JsonSchema/Constraints/Factory.php b/src/JsonSchema/Constraints/Factory.php index 8b33aaad..e2826dd7 100644 --- a/src/JsonSchema/Constraints/Factory.php +++ b/src/JsonSchema/Constraints/Factory.php @@ -51,13 +51,28 @@ public function getUriRetriever() /** * Add a custom constraint * - * $factory->addConstraint('name', new MyCustomConstraint(...)); + * By instance: + * $factory->addConstraint('name', new \FQCN(...)); // need to provide own ctr params + * + * By class name: + * $factory->addConstraint('name', '\FQCN'); // inherits ctr params from current instance * * @param string $name - * @param ConstraintInterface $constraint + * @param ConstraintInterface|string $constraint + * + * @throws InvalidArgumentException if the $constraint is either not a class or not a ConstraintInterface */ public function addConstraint($name, $constraint) { + if (is_string($constraint)) { + if (!class_exists($constraint)) { + throw new InvalidArgumentException('Constraint class "' . $constraint . '" is not a Class'); + } + $constraint = new $constraint(Constraint::CHECK_MODE_NORMAL, $this->uriRetriever, $this); + if (!$constraint instanceof ConstraintInterface) { + throw new InvalidArgumentException('Constraint class "' . get_class($constraint) . '" is not an instance of ConstraintInterface'); + } + } $this->constraints[$name] = $constraint; } diff --git a/src/JsonSchema/Validator.php b/src/JsonSchema/Validator.php index a76eb9c2..f757a5d8 100644 --- a/src/JsonSchema/Validator.php +++ b/src/JsonSchema/Validator.php @@ -12,6 +12,7 @@ use JsonSchema\Constraints\ConstraintInterface; use JsonSchema\Constraints\SchemaConstraint; use JsonSchema\Constraints\Constraint; +use JsonSchema\Exception\InvalidArgumentException; /** * A JsonSchema Constraint @@ -42,11 +43,16 @@ public function check($value, $schema = null, $path = null, $i = null) /** * Add a custom constraint * - * // extends Constraint or implements ConstraintInterface - * $factory->addConstraint('name', new MyCustomConstraint(...)); + * By instance: + * $factory->addConstraint('name', new \FQCN(...)); // need to provide own ctr params + * + * By class name: + * $factory->addConstraint('name', '\FQCN'); // inherits ctr params from current instance * * @param string $name - * @param ConstraintInterface $constraint + * @param ConstraintInterface|string $constraint + * + * @throws InvalidArgumentException if the $constraint is either not a class or not a ConstraintInterface */ public function addConstraint($name, $constraint) { diff --git a/tests/JsonSchema/Tests/Constraints/CustomConstraintTest.php b/tests/JsonSchema/Tests/Constraints/CustomConstraintTest.php index 2c2bad96..84c28412 100644 --- a/tests/JsonSchema/Tests/Constraints/CustomConstraintTest.php +++ b/tests/JsonSchema/Tests/Constraints/CustomConstraintTest.php @@ -15,59 +15,100 @@ class CustomConstraintTest extends TestCase { + /** - * @var Factory + * @return array */ - protected $factory; + public function constraintNameProvider() + { + return array( + array('exists', 'JsonSchema\Constraints\StringConstraint'), + array('custom', 'JsonSchema\Tests\Constraints\Fixtures\CustomConstraint'), + ); + } - protected function setUp() + /** + * @dataProvider constraintNameProvider + * + * @param string $constraintName + * @param string $className + */ + public function testConstraintInstanceWithoutCtrParams($constraintName, $className) { - $this->factory = new Factory(); + $factory = new Factory(); + + // NOTE the uriRetriever and factory will be new instances; this is due to the Constraint class.. + $factory->addConstraint($constraintName, new $className()); + + $constraint = $factory->createInstanceFor($constraintName); + + $this->assertInstanceOf($className, $constraint); + $this->assertInstanceOf('JsonSchema\Constraints\ConstraintInterface', $constraint); + + $this->assertNotSame($factory->getUriRetriever(), $constraint->getUriRetriever()); } /** * @dataProvider constraintNameProvider * * @param string $constraintName - * @param string $expectedClass + * @param string $className */ - public function testConstraintInstanceWithoutCtrParams($constraintName, $expectedClass) + public function testConstraintInstanceWithCtrParams($constraintName, $className) { - $this->factory->addConstraint($constraintName, new $expectedClass()); - $constraint = $this->factory->createInstanceFor($constraintName); + $factory = new Factory(); + $factory->addConstraint($constraintName, + new $className(Constraint::CHECK_MODE_NORMAL, $factory->getUriRetriever(), $factory)); + $constraint = $factory->createInstanceFor($constraintName); - $this->assertInstanceOf($expectedClass, $constraint); + $this->assertInstanceOf($className, $constraint); $this->assertInstanceOf('JsonSchema\Constraints\ConstraintInterface', $constraint); - $this->assertNotSame($this->factory->getUriRetriever(), $constraint->getUriRetriever()); + $this->assertSame($factory->getUriRetriever(), $constraint->getUriRetriever()); + $this->assertSame($factory, $constraint->getFactory()); } /** * @dataProvider constraintNameProvider * * @param string $constraintName - * @param string $expectedClass + * @param string $className */ - public function testConstraintInstanceWithCtrParams($constraintName, $expectedClass) + public function testConstraintClassNameStringInjectingCtrParamsHasSame($constraintName, $className) { - $this->factory->addConstraint($constraintName, - new $expectedClass(Constraint::CHECK_MODE_NORMAL, $this->factory->getUriRetriever(), $this->factory)); - $constraint = $this->factory->createInstanceFor($constraintName); + $factory = new Factory(); + $factory->addConstraint($constraintName, $className); + $constraint = $factory->createInstanceFor($constraintName); - $this->assertInstanceOf($expectedClass, $constraint); + $this->assertInstanceOf($className, $constraint); $this->assertInstanceOf('JsonSchema\Constraints\ConstraintInterface', $constraint); - $this->assertSame($this->factory->getUriRetriever(), $constraint->getUriRetriever()); - $this->assertSame($this->factory, $constraint->getFactory()); + $this->assertSame($factory->getUriRetriever(), $constraint->getUriRetriever()); + $this->assertSame($factory, $constraint->getFactory()); } /** - * @return array + * @todo possible own exception? + * @expectedException \JsonSchema\Exception\InvalidArgumentException */ - public function constraintNameProvider() + public function testConstraintClassNameStringIsNotAClass() { - return array( - array('exists', 'JsonSchema\Constraints\StringConstraint'), - array('custom', 'JsonSchema\Tests\Constraints\Fixtures\CustomConstraint'), - ); + $name = 'NotAClass'; + + $factory = new Factory(); + $factory->addConstraint($name, $name); + $factory->createInstanceFor($name); + } + + /** + * @todo possible own exception? + * @expectedException \JsonSchema\Exception\InvalidArgumentException + */ + public function testConstraintClassNameStringIsAClassButNotAConstraint() + { + $name = 'JsonSchema\RefResolver'; // Class but not ConstraintInterface + + $factory = new Factory(); + $factory->addConstraint($name, $name); + $factory->createInstanceFor($name); } } From 14e48ff73d26021e027deb901339c943858bf125 Mon Sep 17 00:00:00 2001 From: Aubrey Hewes Date: Wed, 11 Nov 2015 21:19:44 +0100 Subject: [PATCH 3/8] allow constraint as callable --- .../Constraints/CallableConstraint.php | 59 +++++++++++++++++++ src/JsonSchema/Constraints/Factory.php | 22 +++++-- src/JsonSchema/Validator.php | 9 ++- .../Constraints/CustomConstraintTest.php | 10 ++++ 4 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 src/JsonSchema/Constraints/CallableConstraint.php diff --git a/src/JsonSchema/Constraints/CallableConstraint.php b/src/JsonSchema/Constraints/CallableConstraint.php new file mode 100644 index 00000000..ba1f6397 --- /dev/null +++ b/src/JsonSchema/Constraints/CallableConstraint.php @@ -0,0 +1,59 @@ +addConstraint('name', \Callable) + * + */ +class CallableConstraint extends Constraint +{ + + /** + * @var \Callable + */ + private $callable; + + /** + * @param int $checkMode + * @param UriRetriever $uriRetriever + * @param Factory $factory + * @param Callable $callable + */ + public function __construct( + $checkMode = self::CHECK_MODE_NORMAL, + UriRetriever $uriRetriever = null, + Factory $factory = null, + $callable + ) { + $this->callable = $callable; + parent::__construct($checkMode, $uriRetriever, $factory); + } + + /** + * {@inheritDoc} + */ + public function check($element, $schema = null, $path = null, $i = null) + { + if ( ! is_callable($this->callable)) { + return; + } + + $result = call_user_func($this->callable, $element, $schema, $path, $i); + + if ($result) { + $this->addError($path, $result); + } + } +} \ No newline at end of file diff --git a/src/JsonSchema/Constraints/Factory.php b/src/JsonSchema/Constraints/Factory.php index e2826dd7..739e3380 100644 --- a/src/JsonSchema/Constraints/Factory.php +++ b/src/JsonSchema/Constraints/Factory.php @@ -55,24 +55,38 @@ public function getUriRetriever() * $factory->addConstraint('name', new \FQCN(...)); // need to provide own ctr params * * By class name: - * $factory->addConstraint('name', '\FQCN'); // inherits ctr params from current instance + * $factory->addConstraint('name', '\FQCN'); // inherits ctr params from current + * + * As a \Callable (the Constraint::checks() method): + * $factory->addConstraint('name', \Callable); // inherits ctr params from current + * + * NOTE: By class-name or as a Callable will inherit the current configuration (uriRetriever, factory) * * @param string $name - * @param ConstraintInterface|string $constraint + * @param ConstraintInterface|string|\Callable $constraint * * @throws InvalidArgumentException if the $constraint is either not a class or not a ConstraintInterface */ public function addConstraint($name, $constraint) { + + if (is_callable($constraint)) { + $this->constraints[$name] = new CallableConstraint(Constraint::CHECK_MODE_NORMAL, $this->uriRetriever, + $this, $constraint); + + return; + } + if (is_string($constraint)) { - if (!class_exists($constraint)) { + if ( ! class_exists($constraint)) { throw new InvalidArgumentException('Constraint class "' . $constraint . '" is not a Class'); } $constraint = new $constraint(Constraint::CHECK_MODE_NORMAL, $this->uriRetriever, $this); - if (!$constraint instanceof ConstraintInterface) { + if ( ! $constraint instanceof ConstraintInterface) { throw new InvalidArgumentException('Constraint class "' . get_class($constraint) . '" is not an instance of ConstraintInterface'); } } + $this->constraints[$name] = $constraint; } diff --git a/src/JsonSchema/Validator.php b/src/JsonSchema/Validator.php index f757a5d8..39defa46 100644 --- a/src/JsonSchema/Validator.php +++ b/src/JsonSchema/Validator.php @@ -47,10 +47,15 @@ public function check($value, $schema = null, $path = null, $i = null) * $factory->addConstraint('name', new \FQCN(...)); // need to provide own ctr params * * By class name: - * $factory->addConstraint('name', '\FQCN'); // inherits ctr params from current instance + * $factory->addConstraint('name', '\FQCN'); // inherits ctr params from current + * + * As a \Callable (the Constraint::checks() method): + * $factory->addConstraint('name', \Callable); // inherits ctr params from current + * + * NOTE: By class-name or as a Callable will inherit the current configuration (uriRetriever, factory) * * @param string $name - * @param ConstraintInterface|string $constraint + * @param ConstraintInterface|string|\Callable $constraint * * @throws InvalidArgumentException if the $constraint is either not a class or not a ConstraintInterface */ diff --git a/tests/JsonSchema/Tests/Constraints/CustomConstraintTest.php b/tests/JsonSchema/Tests/Constraints/CustomConstraintTest.php index 84c28412..b6270a7a 100644 --- a/tests/JsonSchema/Tests/Constraints/CustomConstraintTest.php +++ b/tests/JsonSchema/Tests/Constraints/CustomConstraintTest.php @@ -111,4 +111,14 @@ public function testConstraintClassNameStringIsAClassButNotAConstraint() $factory->createInstanceFor($name); } + /** + * + */ + public function testConstraintCallable() + { + $factory = new Factory(); + $factory->addConstraint('callable', function () {}); + $this->assertInstanceOf('JsonSchema\Constraints\CallableConstraint', $factory->createInstanceFor('callable')); + } + } From 3200b0aabd6ea77f410010b5916e017c5c6322b0 Mon Sep 17 00:00:00 2001 From: Aubrey Hewes Date: Wed, 11 Nov 2015 21:20:45 +0100 Subject: [PATCH 4/8] superfluous --- src/JsonSchema/Validator.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/JsonSchema/Validator.php b/src/JsonSchema/Validator.php index 39defa46..85dbbbab 100644 --- a/src/JsonSchema/Validator.php +++ b/src/JsonSchema/Validator.php @@ -10,7 +10,6 @@ namespace JsonSchema; use JsonSchema\Constraints\ConstraintInterface; -use JsonSchema\Constraints\SchemaConstraint; use JsonSchema\Constraints\Constraint; use JsonSchema\Exception\InvalidArgumentException; From 0f28d3bb2e3711632e8335ff608fa052c783c8f5 Mon Sep 17 00:00:00 2001 From: Aubrey Hewes Date: Wed, 11 Nov 2015 21:23:03 +0100 Subject: [PATCH 5/8] doc --- src/JsonSchema/Constraints/Factory.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/JsonSchema/Constraints/Factory.php b/src/JsonSchema/Constraints/Factory.php index 739e3380..e50194c3 100644 --- a/src/JsonSchema/Constraints/Factory.php +++ b/src/JsonSchema/Constraints/Factory.php @@ -65,6 +65,8 @@ public function getUriRetriever() * @param string $name * @param ConstraintInterface|string|\Callable $constraint * + * @todo possible own exception? + * * @throws InvalidArgumentException if the $constraint is either not a class or not a ConstraintInterface */ public function addConstraint($name, $constraint) @@ -79,10 +81,12 @@ public function addConstraint($name, $constraint) if (is_string($constraint)) { if ( ! class_exists($constraint)) { + // @todo possible own exception? throw new InvalidArgumentException('Constraint class "' . $constraint . '" is not a Class'); } $constraint = new $constraint(Constraint::CHECK_MODE_NORMAL, $this->uriRetriever, $this); if ( ! $constraint instanceof ConstraintInterface) { + // @todo possible own exception? throw new InvalidArgumentException('Constraint class "' . get_class($constraint) . '" is not an instance of ConstraintInterface'); } } From 67331ea81e32f9a0da41d8e8d0d7fba21c17a293 Mon Sep 17 00:00:00 2001 From: Aubrey Hewes Date: Wed, 11 Nov 2015 21:44:36 +0100 Subject: [PATCH 6/8] updated readme --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index 3f9c58c3..c8b2c85b 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,32 @@ if ($validator->isValid()) { } ``` +## Custom Constraints +Add custom constraints via `$validator->addConstraint($name, $constraint)`; + +The given `$constraint` is applied when `$name` is found within the current evaluated schema path. + +### Add a callable constraint + + $validator->addConstraint('test', \Callable); + + * Inherits _current_ ctr params (uriRetriever, factory) + * The callable is the `ConstraintInterface->check` signature `function check($value, $schema = null, $path = null, $i = null)` + +### Add by custom Constraint instance + + $validator->addConstraint('test', new MyCustomConstraint(...)); + + * Requires adding the correct ctr params (uriRetriever, factory et al) + * `MyCustomConstraint` must be of type `JsonSchema\Constraints\ConstraintInterface` + +### Add by custom Constraint class-name + + $validator->addConstraint('test', 'FQCN'); + + * Inherits _current_ ctr params (uriRetriever, factory) + * `FQCN` must be of type `JsonSchema\Constraints\ConstraintInterface` + ## Running the tests $ vendor/bin/phpunit From 32801873ee4ec60a0ddd670fcc60217936980644 Mon Sep 17 00:00:00 2001 From: Aubrey Hewes Date: Wed, 11 Nov 2015 22:10:59 +0100 Subject: [PATCH 7/8] bc --- src/JsonSchema/Constraints/Factory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/JsonSchema/Constraints/Factory.php b/src/JsonSchema/Constraints/Factory.php index e50194c3..9876149b 100644 --- a/src/JsonSchema/Constraints/Factory.php +++ b/src/JsonSchema/Constraints/Factory.php @@ -26,7 +26,7 @@ class Factory /** * @var array */ - private $constraints = []; + private $constraints = array(); /** * @param UriRetriever $uriRetriever From 3eb3ef33d81a5d5044aaf0b158a94edb84b15776 Mon Sep 17 00:00:00 2001 From: Aubrey Hewes Date: Thu, 12 Nov 2015 17:52:46 +0100 Subject: [PATCH 8/8] doh; now also executes custom constraints;-) --- src/JsonSchema/Constraints/Constraint.php | 8 ++++++++ src/JsonSchema/Constraints/Factory.php | 9 +++++---- .../Constraints/UndefinedConstraint.php | 18 ++++++++++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/JsonSchema/Constraints/Constraint.php b/src/JsonSchema/Constraints/Constraint.php index cb3ee809..4c2604cc 100644 --- a/src/JsonSchema/Constraints/Constraint.php +++ b/src/JsonSchema/Constraints/Constraint.php @@ -275,6 +275,14 @@ protected function checkFormat($value, $schema = null, $path = null, $i = null) $this->addErrors($validator->getErrors()); } + protected function checkCustom($constraint, $value, $schema = null, $path = null, $i = null) + { + $validator = $this->getFactory()->createInstanceFor($constraint); + $validator->check($value, $schema, $path, $i); + + $this->addErrors($validator->getErrors()); + } + /** * @param string $uri JSON Schema URI * @return string JSON Schema contents diff --git a/src/JsonSchema/Constraints/Factory.php b/src/JsonSchema/Constraints/Factory.php index 9876149b..d6ddca2f 100644 --- a/src/JsonSchema/Constraints/Factory.php +++ b/src/JsonSchema/Constraints/Factory.php @@ -85,10 +85,11 @@ public function addConstraint($name, $constraint) throw new InvalidArgumentException('Constraint class "' . $constraint . '" is not a Class'); } $constraint = new $constraint(Constraint::CHECK_MODE_NORMAL, $this->uriRetriever, $this); - if ( ! $constraint instanceof ConstraintInterface) { - // @todo possible own exception? - throw new InvalidArgumentException('Constraint class "' . get_class($constraint) . '" is not an instance of ConstraintInterface'); - } + } + + if ( ! $constraint instanceof ConstraintInterface) { + // @todo possible own exception? + throw new InvalidArgumentException('Constraint class "' . get_class($constraint) . '" is not an instance of ConstraintInterface'); } $this->constraints[$name] = $constraint; diff --git a/src/JsonSchema/Constraints/UndefinedConstraint.php b/src/JsonSchema/Constraints/UndefinedConstraint.php index 658b1b7a..6318569f 100644 --- a/src/JsonSchema/Constraints/UndefinedConstraint.php +++ b/src/JsonSchema/Constraints/UndefinedConstraint.php @@ -47,6 +47,24 @@ public function check($value, $schema = null, $path = null, $i = null) // check known types $this->validateTypes($value, $schema, $path, $i); + + // check custom + $this->validateCustom($value, $schema, $path, $i); + } + + /** + * @param $value + * @param null $schema + * @param null $path + * @param null $i + */ + public function validateCustom($value, $schema = null, $path = null, $i = null) + { + foreach (array_keys((array)$schema) as $check) { + if ($this->getFactory()->hasConstraint($check)) { + $this->checkCustom($check, $value, $check, $path, $i); + } + } } /**