From 9aee2ea16df7e58f0a3ec76bf1d59042fb7b4c5d Mon Sep 17 00:00:00 2001 From: Alexandre Gomes Gaigalas Date: Fri, 24 Sep 2010 00:32:00 -0300 Subject: [PATCH] Name refactorings, interface for simple validator calls, date improvements --- ...AbstractValidator.php => AbstractNode.php} | 2 +- library/Respect/Validation/Chain.php | 2 +- .../Respect/Validation/CompositeValidator.php | 119 ++++++++++ .../Validation/Date/AbstractDateValidator.php | 4 +- library/Respect/Validation/Date/Between.php | 2 + library/Respect/Validation/Validator.php | 159 ++++---------- .../Validation/CompositeValidatorTest.php | 204 ++++++++++++++++++ .../Respect/Validation/ValidatorTest.php | 196 ++--------------- .../Respect/Validation/ValidatorTestCase.php | 6 +- 9 files changed, 386 insertions(+), 308 deletions(-) rename library/Respect/Validation/{AbstractValidator.php => AbstractNode.php} (93%) create mode 100644 library/Respect/Validation/CompositeValidator.php create mode 100644 tests/library/Respect/Validation/CompositeValidatorTest.php diff --git a/library/Respect/Validation/AbstractValidator.php b/library/Respect/Validation/AbstractNode.php similarity index 93% rename from library/Respect/Validation/AbstractValidator.php rename to library/Respect/Validation/AbstractNode.php index b4d7f1e6..b4425f0c 100644 --- a/library/Respect/Validation/AbstractValidator.php +++ b/library/Respect/Validation/AbstractNode.php @@ -2,7 +2,7 @@ namespace Respect\Validation; -abstract class AbstractValidator +abstract class AbstractNode { protected $messages = array(); diff --git a/library/Respect/Validation/Chain.php b/library/Respect/Validation/Chain.php index 3907852a..ad43ecbd 100644 --- a/library/Respect/Validation/Chain.php +++ b/library/Respect/Validation/Chain.php @@ -4,7 +4,7 @@ namespace Respect\Validation; use LogicException; -class Chain extends Validator +class Chain extends CompositeValidator { protected $input; diff --git a/library/Respect/Validation/CompositeValidator.php b/library/Respect/Validation/CompositeValidator.php new file mode 100644 index 00000000..80c90f14 --- /dev/null +++ b/library/Respect/Validation/CompositeValidator.php @@ -0,0 +1,119 @@ +validators[spl_object_hash($validator)] = $validator; + $this->messages = array_merge( + $this->messages, $validator->getMessages() + ); + } + + public function addValidator($validator, $arguments=array()) + { + $this->appendValidator(Validator::buildValidator($validator, $arguments)); + } + + public function hasValidator($validator) + { + if (empty($this->validators)) + return false; + if ($validator instanceof Valitatable) + return isset($this->validators[spl_object_hash($validator)]); + else + return (boolean) array_filter( + $this->validators, + function($v) use ($validator) { + return (integer) ($v instanceof $validator); + }); + } + + public function addValidators(array $validators, $prefix='') + { + foreach ($validators as $k => $v) { + if (is_object($v)) { + $this->addValidator($v); + continue; + } elseif (is_numeric($k)) { + $validatorName = $v; + $validatorArgs = array(); + } else { + $validatorName = $k; + if (!empty($v) && !is_array($v)) + throw new ComponentException( + sprintf( + 'Arguments for array-specified validators must be an array, you provided %s', + $v + ) + ); + $validatorArgs = empty($v) ? array() : $v; + } + if (!empty($prefix)) + $validatorName = $prefix . '\\' . $validatorName; + $this->addValidator($validatorName, $validatorArgs); + } + } + + public function getValidators() + { + return $this->validators; + } + + protected function iterateValidation($input) + { + $validators = $this->getValidators(); + $exceptions = array(); + foreach ($validators as $v) + try { + $v->validate($input); + } catch (InvalidException $e) { + $exceptions[] = $e; + } + return $exceptions; + } + + public function validate($input) + { + $exceptions = $this->iterateValidation($input); + if (!empty($exceptions)) + throw new InvalidException($exceptions); + return true; + } + + public function validateOne($input) + { + $validators = $this->getValidators(); + $exceptions = $this->iterateValidation($input); + if (count($exceptions) === count($validators)) + throw new InvalidException($exceptions); + return true; + } + + public function isValid($input) + { + $validators = $this->getValidators(); + return count($validators) === count(array_filter( + $validators, + function($v) use($input) { + return $v->isValid($input); + } + )); + } + + public function isOneValid($input) + { + return (boolean) array_filter( + $this->getValidators(), + function($v) use($input) { + return $v->isValid($input); + } + ); + } + +} \ No newline at end of file diff --git a/library/Respect/Validation/Date/AbstractDateValidator.php b/library/Respect/Validation/Date/AbstractDateValidator.php index afbdf6ba..a57014ea 100644 --- a/library/Respect/Validation/Date/AbstractDateValidator.php +++ b/library/Respect/Validation/Date/AbstractDateValidator.php @@ -2,10 +2,10 @@ namespace Respect\Validation\Date; -use Respect\Validation\AbstractValidator; +use Respect\Validation\AbstractNode; use DateTime; -abstract class AbstractDateValidator extends AbstractValidator +abstract class AbstractDateValidator extends AbstractNode { protected $format = DateTime::RFC1036; diff --git a/library/Respect/Validation/Date/Between.php b/library/Respect/Validation/Date/Between.php index 6b10f859..ac0311b2 100644 --- a/library/Respect/Validation/Date/Between.php +++ b/library/Respect/Validation/Date/Between.php @@ -15,6 +15,8 @@ class Between extends AbstractDateValidator implements Validatable self::MSG_OUT_OF_BOUNDS => '%s is not between %s and %s.' ); + + public function __construct($min, $max, $format=null) { diff --git a/library/Respect/Validation/Validator.php b/library/Respect/Validation/Validator.php index 86f2555e..1a39256b 100644 --- a/library/Respect/Validation/Validator.php +++ b/library/Respect/Validation/Validator.php @@ -4,25 +4,47 @@ namespace Respect\Validation; use ReflectionClass; -class Validator extends AbstractValidator implements Validatable +abstract class Validator { - protected $validators = array(); - protected $messages = array(); - - protected function appendValidator(Validatable $validator) + public static function validate($input, $validatorName) { - $this->validators[spl_object_hash($validator)] = $validator; - $this->messages = array_merge( - $this->messages, $validator->getMessages() - ); + $arguments = func_get_args(); + $arguments = array_slice($arguments, 2); + return static::buildValidator($validatorName, $arguments)->validate($input); } - public function addValidator($validator, $arguments=array()) + public static function is($input, $validatorName) + { + $arguments = func_get_args(); + $arguments = array_slice($arguments, 2); + return static::buildValidator($validatorName, $arguments)->isValid($input); + } + + public static function __callStatic($method, $arguments) + { + if (2 > count($arguments)) + return false; + if (0 === strpos($method, 'valid')) { + $arguments[1] = substr($method, 5) . '\\' . $arguments[1]; + $arguments[1] = trim($arguments[1], '\\'); + return call_user_func_array( + array(get_called_class(), 'validate'), $arguments + ); + } elseif (0 === strpos($method, 'is')) { + $arguments[1] = substr($method, 2) . '\\' . $arguments[1]; + $arguments[1] = trim($arguments[1], '\\'); + return call_user_func_array( + array(get_called_class(), 'is'), $arguments + ); + } + return false; + } + + public static function buildValidator($validator, $arguments=array()) { if ($validator instanceof Validatable) { - $this->appendValidator($validator); - return; + return $validator; } if (is_object($validator)) throw new ComponentException( @@ -32,6 +54,7 @@ class Validator extends AbstractValidator implements Validatable $validatorFqn = explode('\\', get_called_class()); array_pop($validatorFqn); $validatorFqn = array_merge($validatorFqn, explode('\\', $validator)); + $validatorFqn = array_map('ucfirst', $validatorFqn); $validatorFqn = implode('\\', $validatorFqn); $validatorClass = new ReflectionClass($validatorFqn); $implementedInterface = $validatorClass->implementsInterface( @@ -49,117 +72,7 @@ class Validator extends AbstractValidator implements Validatable } else { $validatorInstance = new $validatorFqn; } - $this->appendValidator($validatorInstance); + return $validatorInstance; } - public function hasValidator($validator) - { - if (empty($this->validators)) - return false; - if ($validator instanceof Valitatable) - return isset($this->validators[spl_object_hash($validator)]); - else - return (boolean) array_filter( - $this->validators, - function($v) use ($validator) { - return (integer) ($v instanceof $validator); - }); - } - - public function addValidators(array $validators, $prefix='') - { - foreach ($validators as $k => $v) { - if (is_object($v)) { - $this->addValidator($v); - continue; - } elseif (is_numeric($k)) { - $validatorName = $v; - $validatorArgs = array(); - } else { - $validatorName = $k; - if (!empty($v) && !is_array($v)) - throw new ComponentException( - sprintf( - 'Arguments for array-specified validators must be an array, you provided %s', - $v - ) - ); - $validatorArgs = empty($v) ? array() : $v; - } - if (!empty($prefix)) - $validatorName = $prefix . '\\' . $validatorName; - $this->addValidator($validatorName, $validatorArgs); - } - } - - public function getValidators() - { - return $this->validators; - } - - protected function iterateValidation($input) - { - $validators = $this->getValidators(); - $exceptions = array(); - foreach ($validators as $v) - try { - $v->validate($input); - } catch (InvalidException $e) { - $exceptions[] = $e; - } - return $exceptions; - } - - public function validate($input) - { - $exceptions = $this->iterateValidation($input); - if (!empty($exceptions)) - throw new InvalidException($exceptions); - return true; - } - - public function validateOne($input) - { - $validators = $this->getValidators(); - $exceptions = $this->iterateValidation($input); - if (count($exceptions) === count($validators)) - throw new InvalidException($exceptions); - return true; - } - - public function isValid($input) - { - $validators = $this->getValidators(); - return count($validators) === count(array_filter( - $validators, - function($v) use($input) { - return $v->isValid($input); - } - )); - } - - public function isOneValid($input) - { - return (boolean) array_filter( - $this->getValidators(), - function($v) use($input) { - return $v->isValid($input); - } - ); - } - - /*public function getMessages() - { - return $this->messages; - } - - public function setMessages(array $messages) - { - if (count($this->messages) != count($messages)) - throw new ComponentException( - 'You must set exactly the same amount of messages currently present in the validator' - ); - $this->messages = $messages; - }*/ - } \ No newline at end of file diff --git a/tests/library/Respect/Validation/CompositeValidatorTest.php b/tests/library/Respect/Validation/CompositeValidatorTest.php new file mode 100644 index 00000000..8214440a --- /dev/null +++ b/tests/library/Respect/Validation/CompositeValidatorTest.php @@ -0,0 +1,204 @@ +object = new CompositeValidator; + } + + protected function tearDown() + { + unset($this->object); + } + + /** + * @dataProvider providerForMockImpossibleValidators + */ + public function testAddExistentValidator($validator) + { + $this->object->addValidator($validator); + $this->assertContains($validator, $this->object->getValidators()); + } + + /** + * @expectedException Respect\Validation\ComponentException + */ + public function testAddNonValidator() + { + $this->object->addValidator(new \stdClass); + } + + /** + * @expectedException Respect\Validation\ComponentException + */ + public function testAddNonValidator2() + { + if (!class_exists('Respect\Validation\Foo\Freak', false)) { + eval(" + namespace Respect\Validation\Foo; + class Freak{} + "); + } + $this->object->addValidator('Foo\Freak'); + } + + /** + * @dataProvider providerForMockImpossibleValidators + */ + public function testAddValidatorsMessages($a, $b) + { + $messagesA = $a->getMessages(); + $messagesB = $b->getMessages(); + $this->object->addValidators(func_get_args()); + $messagesObject = $this->object->getMessages(); + foreach ($messagesA as $m) { + $this->assertContains($m, $messagesObject); + } + foreach ($messagesB as $m) { + $this->assertContains($m, $messagesObject); + } + } + + public function testBuildValidators() + { + $this->providerForMockImpossibleValidators(); + $this->object->addValidators(array( + 'Foo\Bar', 'Foo\Baz', 'Foo\Bat' => array(1, 2, 3) + )); + $this->assertValidatorPresence($this->object, 'Bar', 'Baz', 'Bat'); + } + + /** + * @expectedException Respect\Validation\ComponentException + */ + public function testBuildValidatorsInvalid() + { + $this->providerForMockImpossibleValidators(); + $this->object->addValidators(array( + 'Foo\Bar', 'Foo\Baz', 'Foo\Bat' => 'balkbal' + )); + } + + /** + * @dataProvider providerForMockValidators + */ + public function testValidateManyValid($a, $b, $c) + { + $this->object->addValidators(func_get_args()); + $this->assertTrue($this->object->validate('any')); + } + + /** + * @dataProvider providerForMockValidators + */ + public function testManyIsValid($a, $b, $c) + { + $this->object->addValidators(func_get_args()); + $this->assertTrue($this->object->isValid('any')); + } + + /** + * @dataProvider providerForMockImpossibleValidators + */ + public function testManyIsInvalid($a, $b, $c) + { + $this->object->addValidators(func_get_args()); + $this->assertFalse($this->object->isValid('any')); + } + + /** + * @dataProvider providerForMockImpossibleValidators + */ + public function testManyIsInvalid2($a, $b, $c) + { + $this->object->addValidators(func_get_args()); + $this->object->addValidator( + $this->buildMockValidator('Aids', array('Aids_1' => 'aesfg')) + ); + $this->assertFalse($this->object->isValid('any')); + } + + /** + * @dataProvider providerForMockImpossibleValidators + */ + public function testValidateAllInvalid($a, $b, $c) + { + $this->object->addValidators(func_get_args()); + try { + $this->object->validate('any'); + } catch (InvalidException $e) { + $this->assertEquals(3, count($e->getExceptions())); + } + } + + /** + * @dataProvider providerForMockImpossibleValidators + */ + public function testValidateOneValid($invalidA, $invalidB, $invalidC) + { + $valid = $this->buildMockValidator('Darth', array('Darth_1' => 'o54n')); + $this->object->addValidators(func_get_args()); + $this->object->addValidator($valid); + $this->assertTrue($this->object->validateOne('any')); + } + + /** + * @dataProvider providerForMockImpossibleValidators + */ + public function testValidateOneAllRotten($invalidA, $invalidB, $invalidC) + { + $this->object->addValidators(func_get_args()); + try { + $this->object->validateOne('any'); + } catch (InvalidException $e) { + $this->assertEquals(3, count($e->getExceptions())); + } + } + + /** + * @dataProvider providerForMockImpossibleValidators + */ + public function testIsValidOneValid($invalidA, $invalidB, $invalidC) + { + $valid = $this->buildMockValidator('Darth', array('Darth_1' => 'o54n')); + $this->object->addValidators(func_get_args()); + $this->object->addValidator($valid); + $this->assertTrue($this->object->isOneValid('any')); + } + + /** + * @dataProvider providerForMockImpossibleValidators + */ + public function testIsValidOneAllRotten($invalidA, $invalidB, $invalidC) + { + $this->object->addValidators(func_get_args()); + $this->assertFalse($this->object->isOneValid('any')); + } + + /** + * @dataProvider providerForMockImpossibleValidators + */ + public function testSetMessages($a, $b) + { + $messagesA = $a->getMessages(); + $messagesB = $b->getMessages(); + $this->object->addValidators(func_get_args()); + $this->object->setMessages( + array_map('strrev', $this->object->getMessages()) + ); + $messagesObject = $this->object->getMessages(); + foreach ($messagesA as $m) { + $this->assertContains(strrev($m), $messagesObject); + } + foreach ($messagesB as $m) { + $this->assertContains(strrev($m), $messagesObject); + } + } + +} \ No newline at end of file diff --git a/tests/library/Respect/Validation/ValidatorTest.php b/tests/library/Respect/Validation/ValidatorTest.php index c0e5410e..068e5197 100644 --- a/tests/library/Respect/Validation/ValidatorTest.php +++ b/tests/library/Respect/Validation/ValidatorTest.php @@ -5,200 +5,40 @@ namespace Respect\Validation; class ValidatorTest extends ValidatorTestCase { - protected $object; - - protected function setUp() + public function testSimpleValidateCall() { - $this->object = new Validator; + $valid = Validator::valid('now', 'Date\Between', 'yesterday', 'tomorrow'); + $this->assertTrue($valid); } - protected function tearDown() + public function testDynamicValidateCall() { - unset($this->object); + $valid = Validator::validDate('now', 'Between', 'yesterday', 'tomorrow'); + $this->assertTrue($valid); } - /** - * @dataProvider providerForMockImpossibleValidators - */ - public function testAddExistentValidator($validator) + public function testSimpleIsValidCall() { - $this->object->addValidator($validator); - $this->assertContains($validator, $this->object->getValidators()); + $valid = Validator::is('now', 'Date\Between', 'yesterday', 'tomorrow'); + $this->assertTrue($valid); } - /** - * @expectedException Respect\Validation\ComponentException - */ - public function testAddNonValidator() + public function testDynamicIsValidCall() { - $this->object->addValidator(new \stdClass); + $valid = Validator::isDate('now', 'Between', 'yesterday', 'tomorrow'); + $this->assertTrue($valid); } - /** - * @expectedException Respect\Validation\ComponentException - */ - public function testAddNonValidator2() + public function testNodeName() { - if (!class_exists('Respect\Validation\Foo\Freak', false)) { - eval(" - namespace Respect\Validation\Foo; - class Freak{} - "); - } - $this->object->addValidator('Foo\Freak'); + $valid = Validator::isDate('now', 'between', 'yesterday', 'tomorrow'); + $this->assertTrue($valid); } - /** - * @dataProvider providerForMockImpossibleValidators - */ - public function testAddValidatorsMessages($a, $b) + public function testNodeName2() { - $messagesA = $a->getMessages(); - $messagesB = $b->getMessages(); - $this->object->addValidators(func_get_args()); - $messagesObject = $this->object->getMessages(); - foreach ($messagesA as $m) { - $this->assertContains($m, $messagesObject); - } - foreach ($messagesB as $m) { - $this->assertContains($m, $messagesObject); - } - } - - public function testBuildValidators() - { - $this->providerForMockImpossibleValidators(); - $this->object->addValidators(array( - 'Foo\Bar', 'Foo\Baz', 'Foo\Bat' => array(1, 2, 3) - )); - $this->assertValidatorPresence($this->object, 'Bar', 'Baz', 'Bat'); - } - - /** - * @expectedException Respect\Validation\ComponentException - */ - public function testBuildValidatorsInvalid() - { - $this->providerForMockImpossibleValidators(); - $this->object->addValidators(array( - 'Foo\Bar', 'Foo\Baz', 'Foo\Bat' => 'balkbal' - )); - } - - /** - * @dataProvider providerForMockValidators - */ - public function testValidateManyValid($a, $b, $c) - { - $this->object->addValidators(func_get_args()); - $this->assertTrue($this->object->validate('any')); - } - - /** - * @dataProvider providerForMockValidators - */ - public function testManyIsValid($a, $b, $c) - { - $this->object->addValidators(func_get_args()); - $this->assertTrue($this->object->isValid('any')); - } - - /** - * @dataProvider providerForMockImpossibleValidators - */ - public function testManyIsInvalid($a, $b, $c) - { - $this->object->addValidators(func_get_args()); - $this->assertFalse($this->object->isValid('any')); - } - - /** - * @dataProvider providerForMockImpossibleValidators - */ - public function testManyIsInvalid2($a, $b, $c) - { - $this->object->addValidators(func_get_args()); - $this->object->addValidator( - $this->buildMockValidator('Aids', array('Aids_1' => 'aesfg')) - ); - $this->assertFalse($this->object->isValid('any')); - } - - /** - * @dataProvider providerForMockImpossibleValidators - */ - public function testValidateAllInvalid($a, $b, $c) - { - $this->object->addValidators(func_get_args()); - try { - $this->object->validate('any'); - } catch (InvalidException $e) { - $this->assertEquals(3, count($e->getExceptions())); - } - } - - /** - * @dataProvider providerForMockImpossibleValidators - */ - public function testValidateOneValid($invalidA, $invalidB, $invalidC) - { - $valid = $this->buildMockValidator('Darth', array('Darth_1' => 'o54n')); - $this->object->addValidators(func_get_args()); - $this->object->addValidator($valid); - $this->assertTrue($this->object->validateOne('any')); - } - - /** - * @dataProvider providerForMockImpossibleValidators - */ - public function testValidateOneAllRotten($invalidA, $invalidB, $invalidC) - { - $this->object->addValidators(func_get_args()); - try { - $this->object->validateOne('any'); - } catch (InvalidException $e) { - $this->assertEquals(3, count($e->getExceptions())); - } - } - - /** - * @dataProvider providerForMockImpossibleValidators - */ - public function testIsValidOneValid($invalidA, $invalidB, $invalidC) - { - $valid = $this->buildMockValidator('Darth', array('Darth_1' => 'o54n')); - $this->object->addValidators(func_get_args()); - $this->object->addValidator($valid); - $this->assertTrue($this->object->isOneValid('any')); - } - - /** - * @dataProvider providerForMockImpossibleValidators - */ - public function testIsValidOneAllRotten($invalidA, $invalidB, $invalidC) - { - $this->object->addValidators(func_get_args()); - $this->assertFalse($this->object->isOneValid('any')); - } - - /** - * @dataProvider providerForMockImpossibleValidators - */ - public function testSetMessages($a, $b) - { - $messagesA = $a->getMessages(); - $messagesB = $b->getMessages(); - $this->object->addValidators(func_get_args()); - $this->object->setMessages( - array_map('strrev', $this->object->getMessages()) - ); - $messagesObject = $this->object->getMessages(); - foreach ($messagesA as $m) { - $this->assertContains(strrev($m), $messagesObject); - } - foreach ($messagesB as $m) { - $this->assertContains(strrev($m), $messagesObject); - } + $valid = Validator::is('now', 'date\between', 'yesterday', 'tomorrow'); + $this->assertTrue($valid); } } \ No newline at end of file diff --git a/tests/library/Respect/Validation/ValidatorTestCase.php b/tests/library/Respect/Validation/ValidatorTestCase.php index b081360a..6285a201 100644 --- a/tests/library/Respect/Validation/ValidatorTestCase.php +++ b/tests/library/Respect/Validation/ValidatorTestCase.php @@ -73,13 +73,13 @@ abstract class ValidatorTestCase extends \PHPUnit_Framework_TestCase public function providerForMockValidators() { $firstValidator = $this->buildMockValidator( - 'Bar', array('Bar_1' => 'fga', 'Bar_2' => 'dfgb'), false + 'Bara', array('Bara_1' => 'fga', 'Bara_2' => 'dfgb'), false ); $secondValidator = $this->buildMockValidator( - 'Baz', array('Baz_1' => 'gedg', 'Baz_2' => 'rihg49'), false + 'Baza', array('Baza_1' => 'gedg', 'Baza_2' => 'rihg49'), false ); $thirdValidator = $this->buildMockValidator( - 'Bat', array('Bat_1' => 'dfdsgdgfgb'), false + 'Bata', array('Bata_1' => 'dfdsgdgfgb'), false ); return array( array($firstValidator, $secondValidator, $thirdValidator),