From 34f9ae1d8b46fc623d996596a8c73aa8217fc9aa Mon Sep 17 00:00:00 2001 From: Maks3w Date: Sun, 5 Apr 2015 10:33:28 +0200 Subject: [PATCH] Add constraint factory This factory can be injected and allow extend built-in constraint types with custom ones. --- src/JsonSchema/Constraints/Constraint.php | 37 ++++++--- src/JsonSchema/Constraints/Factory.php | 81 +++++++++++++++++++ src/JsonSchema/Validator.php | 2 +- .../Tests/Constraints/FactoryTest.php | 78 ++++++++++++++++++ 4 files changed, 188 insertions(+), 10 deletions(-) create mode 100644 src/JsonSchema/Constraints/Factory.php create mode 100644 tests/JsonSchema/Tests/Constraints/FactoryTest.php diff --git a/src/JsonSchema/Constraints/Constraint.php b/src/JsonSchema/Constraints/Constraint.php index b7781bc9..5dddc45e 100644 --- a/src/JsonSchema/Constraints/Constraint.php +++ b/src/JsonSchema/Constraints/Constraint.php @@ -27,14 +27,21 @@ abstract class Constraint implements ConstraintInterface const CHECK_MODE_NORMAL = 1; const CHECK_MODE_TYPE_CAST = 2; + /** + * @var null|Factory + */ + private $factory; + /** * @param int $checkMode * @param UriRetriever $uriRetriever + * @param Factory $factory */ - public function __construct($checkMode = self::CHECK_MODE_NORMAL, UriRetriever $uriRetriever = null) + public function __construct($checkMode = self::CHECK_MODE_NORMAL, UriRetriever $uriRetriever = null, Factory $factory = null) { $this->checkMode = $checkMode; $this->uriRetriever = $uriRetriever; + $this->factory = $factory; } /** @@ -50,6 +57,18 @@ public function getUriRetriever() return $this->uriRetriever; } + /** + * @return Factory + */ + public function getFactory() + { + if (!$this->factory) { + $this->factory = new Factory($this->getUriRetriever()); + } + + return $this->factory; + } + /** * @param UriRetriever $uriRetriever */ @@ -137,7 +156,7 @@ protected function incrementPath($path, $i) */ protected function checkArray($value, $schema = null, $path = null, $i = null) { - $validator = new CollectionConstraint($this->checkMode, $this->uriRetriever); + $validator = $this->getFactory()->createInstanceFor('collection'); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); @@ -154,7 +173,7 @@ protected function checkArray($value, $schema = null, $path = null, $i = null) */ protected function checkObject($value, $schema = null, $path = null, $i = null, $patternProperties = null) { - $validator = new ObjectConstraint($this->checkMode, $this->uriRetriever); + $validator = $this->getFactory()->createInstanceFor('object'); $validator->check($value, $schema, $path, $i, $patternProperties); $this->addErrors($validator->getErrors()); @@ -170,7 +189,7 @@ protected function checkObject($value, $schema = null, $path = null, $i = null, */ protected function checkType($value, $schema = null, $path = null, $i = null) { - $validator = new TypeConstraint($this->checkMode, $this->uriRetriever); + $validator = $this->getFactory()->createInstanceFor('type'); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); @@ -186,7 +205,7 @@ protected function checkType($value, $schema = null, $path = null, $i = null) */ protected function checkUndefined($value, $schema = null, $path = null, $i = null) { - $validator = new UndefinedConstraint($this->checkMode, $this->uriRetriever); + $validator = $this->getFactory()->createInstanceFor('undefined'); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); @@ -202,7 +221,7 @@ protected function checkUndefined($value, $schema = null, $path = null, $i = nul */ protected function checkString($value, $schema = null, $path = null, $i = null) { - $validator = new StringConstraint($this->checkMode, $this->uriRetriever); + $validator = $this->getFactory()->createInstanceFor('string'); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); @@ -218,7 +237,7 @@ protected function checkString($value, $schema = null, $path = null, $i = null) */ protected function checkNumber($value, $schema = null, $path = null, $i = null) { - $validator = new NumberConstraint($this->checkMode, $this->uriRetriever); + $validator = $this->getFactory()->createInstanceFor('number'); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); @@ -234,7 +253,7 @@ protected function checkNumber($value, $schema = null, $path = null, $i = null) */ protected function checkEnum($value, $schema = null, $path = null, $i = null) { - $validator = new EnumConstraint($this->checkMode, $this->uriRetriever); + $validator = $this->getFactory()->createInstanceFor('enum'); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); @@ -242,7 +261,7 @@ protected function checkEnum($value, $schema = null, $path = null, $i = null) protected function checkFormat($value, $schema = null, $path = null, $i = null) { - $validator = new FormatConstraint($this->checkMode, $this->uriRetriever); + $validator = $this->getFactory()->createInstanceFor('format'); $validator->check($value, $schema, $path, $i); $this->addErrors($validator->getErrors()); diff --git a/src/JsonSchema/Constraints/Factory.php b/src/JsonSchema/Constraints/Factory.php new file mode 100644 index 00000000..a4570f61 --- /dev/null +++ b/src/JsonSchema/Constraints/Factory.php @@ -0,0 +1,81 @@ +uriRetriever = $uriRetriever; + } + + /** + * @return UriRetriever + */ + public function getUriRetriever() + { + return $this->uriRetriever; + } + + /** + * 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. + */ + public function createInstanceFor($constraintName) + { + switch ($constraintName) { + case 'array': + case 'collection': + return new CollectionConstraint(Constraint::CHECK_MODE_NORMAL, $this->uriRetriever, $this); + case 'object': + return new ObjectConstraint(Constraint::CHECK_MODE_NORMAL, $this->uriRetriever, $this); + case 'type': + return new TypeConstraint(Constraint::CHECK_MODE_NORMAL, $this->uriRetriever, $this); + case 'undefined': + return new UndefinedConstraint(Constraint::CHECK_MODE_NORMAL, $this->uriRetriever, $this); + case 'string': + return new StringConstraint(Constraint::CHECK_MODE_NORMAL, $this->uriRetriever, $this); + case 'number': + return new NumberConstraint(Constraint::CHECK_MODE_NORMAL, $this->uriRetriever, $this); + case 'enum': + return new EnumConstraint(Constraint::CHECK_MODE_NORMAL, $this->uriRetriever, $this); + case 'format': + return new FormatConstraint(Constraint::CHECK_MODE_NORMAL, $this->uriRetriever, $this); + case 'schema': + return new SchemaConstraint(Constraint::CHECK_MODE_NORMAL, $this->uriRetriever, $this); + case 'validator': + return new Validator(Constraint::CHECK_MODE_NORMAL, $this->uriRetriever, $this); + } + + throw new InvalidArgumentException('Unknown constraint ' . $constraintName); + } +} diff --git a/src/JsonSchema/Validator.php b/src/JsonSchema/Validator.php index 935311bb..33f7aadf 100644 --- a/src/JsonSchema/Validator.php +++ b/src/JsonSchema/Validator.php @@ -37,7 +37,7 @@ class Validator extends Constraint */ public function check($value, $schema = null, $path = null, $i = null) { - $validator = new SchemaConstraint($this->checkMode, $this->uriRetriever); + $validator = $this->getFactory()->createInstanceFor('schema'); $validator->check($value, $schema); $this->addErrors(array_unique($validator->getErrors(), SORT_REGULAR)); diff --git a/tests/JsonSchema/Tests/Constraints/FactoryTest.php b/tests/JsonSchema/Tests/Constraints/FactoryTest.php new file mode 100644 index 00000000..b7c6cbe5 --- /dev/null +++ b/tests/JsonSchema/Tests/Constraints/FactoryTest.php @@ -0,0 +1,78 @@ +factory = new Factory(); + } + + /** + * @dataProvider constraintNameProvider + * + * @param string $constraintName + * @param string $expectedClass + * @return void + */ + public function testCreateInstanceForConstraintName($constraintName, $expectedClass) + { + $constraint = $this->factory->createInstanceFor($constraintName); + + $this->assertInstanceOf($expectedClass, $constraint); + $this->assertInstanceOf('JsonSchema\Constraints\ConstraintInterface', $constraint); + $this->assertSame($this->factory->getUriRetriever(), $constraint->getUriRetriever()); + } + + public function constraintNameProvider() + { + return array( + array('array', 'JsonSchema\Constraints\CollectionConstraint'), + array('collection', 'JsonSchema\Constraints\CollectionConstraint'), + array('object', 'JsonSchema\Constraints\ObjectConstraint'), + array('type', 'JsonSchema\Constraints\TypeConstraint'), + array('undefined', 'JsonSchema\Constraints\UndefinedConstraint'), + array('string', 'JsonSchema\Constraints\StringConstraint'), + array('number', 'JsonSchema\Constraints\NumberConstraint'), + array('enum', 'JsonSchema\Constraints\EnumConstraint'), + array('format', 'JsonSchema\Constraints\FormatConstraint'), + array('schema', 'JsonSchema\Constraints\SchemaConstraint'), + array('validator', 'JsonSchema\Validator'), + ); + } + + /** + * @dataProvider invalidConstraintNameProvider + * + * @param string $constraintName + * @return void + */ + public function testExceptionWhenCreateInstanceForInvalidConstraintName($constraintName) + { + $this->setExpectedException('JsonSchema\Exception\InvalidArgumentException'); + $this->factory->createInstanceFor($constraintName); + } + + public function invalidConstraintNameProvider() + { + return array( + array('invalidConstraintName'), + ); + } +}