Refactored "validator" name to "rule" in many places, fluent interface improvements, validatior chaining for fluent interfaces and small fixes

This commit is contained in:
Alexandre Gomes Gaigalas 2010-09-25 01:13:26 -03:00
parent 47f0543ecf
commit 9bcdb56189
15 changed files with 166 additions and 226 deletions

View file

@ -2,46 +2,45 @@
namespace Respect\Validation;
abstract class AbstractCompositeValidator extends AbstractNode
implements Validatable
abstract class AbstractCompositeRule extends AbstractRule implements Validatable
{
protected $validators = array();
protected $rules = array();
protected function appendValidator(Validatable $validator)
protected function appendRule(Validatable $validator)
{
$this->validators[spl_object_hash($validator)] = $validator;
$this->rules[spl_object_hash($validator)] = $validator;
$this->messages = array_merge(
$this->messages, $validator->getMessages()
);
}
public function addValidator($validator, $arguments=array())
public function addRule($validator, $arguments=array())
{
$this->appendValidator(
Validator::buildValidator($validator, $arguments)
$this->appendRule(
Validator::buildRule($validator, $arguments)
);
}
public function hasValidator($validator)
public function hasRule($validator)
{
if (empty($this->validators))
if (empty($this->rules))
return false;
if ($validator instanceof Valitatable)
return isset($this->validators[spl_object_hash($validator)]);
return isset($this->rules[spl_object_hash($validator)]);
else
return (boolean) array_filter(
$this->validators,
$this->rules,
function($v) use ($validator) {
return (integer) ($v instanceof $validator);
});
}
public function addValidators(array $validators, $prefix='')
public function addRules(array $validators, $prefix='')
{
foreach ($validators as $k => $v) {
if (is_object($v)) {
$this->addValidator($v);
$this->addRule($v);
continue;
} elseif (is_numeric($k)) {
$validatorName = $v;
@ -59,18 +58,18 @@ abstract class AbstractCompositeValidator extends AbstractNode
}
if (!empty($prefix))
$validatorName = $prefix . '\\' . $validatorName;
$this->addValidator($validatorName, $validatorArgs);
$this->addRule($validatorName, $validatorArgs);
}
}
public function getValidators()
public function getRules()
{
return $this->validators;
return $this->rules;
}
protected function iterateValidation($input)
protected function iterateRules($input)
{
$validators = $this->getValidators();
$validators = $this->getRules();
$exceptions = array();
foreach ($validators as $v)
try {

View file

@ -2,7 +2,7 @@
namespace Respect\Validation;
abstract class AbstractNode
abstract class AbstractRule
{
protected $messages = array();

View file

@ -2,26 +2,26 @@
namespace Respect\Validation\Composite;
use Respect\Validation\AbstractCompositeValidator;
use Respect\Validation\AbstractCompositeRule;
use Respect\Validation\InvalidException;
class All extends AbstractCompositeValidator
class All extends AbstractCompositeRule
{
public function is($input)
public function validate($input)
{
$validators = $this->getValidators();
$validators = $this->getRules();
return count($validators) === count(array_filter(
$validators,
function($v) use($input) {
return $v->is($input);
return $v->validate($input);
}
));
}
public function assert($input)
{
$exceptions = $this->iterateValidation($input);
$exceptions = $this->iterateRules($input);
if (!empty($exceptions))
throw new InvalidException($exceptions);
return true;

View file

@ -2,27 +2,27 @@
namespace Respect\Validation\Composite;
use Respect\Validation\AbstractCompositeValidator;
use Respect\Validation\AbstractCompositeRule;
use Respect\Validation\InvalidException;
class One extends AbstractCompositeValidator
class One extends AbstractCompositeRule
{
public function assert($input)
{
$validators = $this->getValidators();
$exceptions = $this->iterateValidation($input);
$validators = $this->getRules();
$exceptions = $this->iterateRules($input);
if (count($exceptions) === count($validators))
throw new InvalidException($exceptions);
return true;
}
public function is($input)
public function validate($input)
{
return (boolean) array_filter(
$this->getValidators(),
$this->getRules(),
function($v) use($input) {
return $v->is($input);
return $v->validate($input);
}
);
}

View file

@ -2,10 +2,10 @@
namespace Respect\Validation\Date;
use Respect\Validation\AbstractNode;
use Respect\Validation\AbstractRule;
use DateTime;
abstract class AbstractDateValidator extends AbstractNode
abstract class AbstractDateValidator extends AbstractRule
{
protected $format = DateTime::RFC1036;

View file

@ -32,7 +32,7 @@ class Between extends AbstractDateValidator implements Validatable
public function assert($input)
{
$target = $this->getDateObject($input);
if (!$this->is($target))
if (!$this->validate($target))
throw new OutOfBoundsException(
sprintf(
$this->getMessage(self::MSG_OUT_OF_BOUNDS),
@ -43,7 +43,7 @@ class Between extends AbstractDateValidator implements Validatable
return true;
}
public function is($input)
public function validate($input)
{
$target = $this->getDateObject($input);
return $target >= $this->min and $target <= $this->max;

View file

@ -3,12 +3,12 @@
namespace Respect\Validation\String;
use Respect\Validation\Validatable;
use Respect\Validation\AbstractNode;
use Respect\Validation\AbstractRule;
class NotEmpty extends AbstractNode implements Validatable
class NotEmpty extends AbstractRule implements Validatable
{
public function is($input)
public function validate($input)
{
$trimmed = trim($input);
return!empty($trimmed);

View file

@ -7,7 +7,7 @@ interface Validatable
public function assert($input);
public function is($input);
public function validate($input);
public function getMessages();

View file

@ -7,30 +7,19 @@ use ReflectionClass;
class Validator
{
protected $input;
protected $subject;
protected $validator;
protected $arguments;
protected $operation;
public function getOperation()
{
return $this->operation;
}
public function getInput()
{
return $this->input;
}
protected $rule;
protected $arguments = array();
protected $validators = array();
public function getSubject()
{
return $this->subject;
}
public function getValidator()
public function getRule()
{
return $this->validator;
return $this->rule;
}
public function getArguments()
@ -38,19 +27,14 @@ class Validator
return $this->arguments;
}
public function setInput($input)
{
$this->input = $input;
}
public function setSubject($subject)
{
$this->subject = $subject;
}
public function setValidator($validator)
public function setRule($rule)
{
$this->validator = $validator;
$this->rule = $rule;
}
public function setArguments(array $arguments)
@ -63,106 +47,81 @@ class Validator
$this->arguments[] = $argument;
}
public function setOperation($operation)
public function addValidator(Validatable $validator)
{
$this->operation = $operation;
$this->validators[spl_object_hash($validator)] = $validator;
}
public function isComplete()
public function getValidators()
{
return isset($this->operation, $this->input, $this->subject,
$this->validator);
}
protected function applySteps(array $stepData)
{
foreach ($stepData as $p) {
if (!isset($this->operation)) {
$this->setOperation($p);
continue;
}
if (!isset($this->input)) {
$this->setInput($p);
continue;
}
if (!isset($this->subject)) {
$this->setSubject($p);
continue;
}
if (!isset($this->validator)) {
$this->setValidator($p);
continue;
}
$this->addArgument($p);
}
if ($this->isComplete())
return $this->execute();
else
return $this;
return $this->validators;
}
public function __call($method, $arguments)
{
array_unshift($arguments, $method);
return $this->applySteps($arguments);
}
public function __get($property)
{
return $this->applySteps(array($property));
}
public function execute()
{
if (!$this->isComplete())
throw new ComponentException('Validator not complete');
$validatorSpec = array($this->subject, $this->validator);
$validatorInstance = static::buildValidator($validatorSpec,
$this->arguments);
$operationSpec = array($validatorInstance, $this->operation);
return call_user_func($operationSpec, $this->input);
}
public function __construct($operation=null, $input=null, $subject=null,
$validator=null, $arguments=array())
{
$this->operation = $operation;
$this->input = $input;
$this->subject = $subject;
$this->validator = $validator;
$this->arguments = $arguments;
}
public static function __callStatic($method, $arguments)
{
preg_match('#^(is|assert)([[:alnum:]]+)?$#', $method, $matches);
array_shift($matches);
$input = array_shift($arguments);
$operation = array_shift($matches);
$subject = array_shift($matches) ? : array_shift($arguments);
$validator = array_shift($arguments);
$v = new static($operation, $input, $subject, $validator, $arguments);
if ($v->isComplete())
return $v->execute();
else
return $v;
}
public static function buildValidator($validator, $arguments=array())
{
if ($validator instanceof Validatable) {
return $validator;
foreach ($arguments as $a) {
if (!isset($this->subject)) {
$this->setSubject($a);
continue;
}
if (!isset($this->rule)) {
$this->setRule($a);
continue;
}
$this->addArgument($a);
}
if (is_object($validator))
$this->checkForCompleteRule();
return $this;
}
public function validates($input)
{
$v = new Composite\All();
$v->addRules($this->validators);
return $v->validate($input);
}
public function checkForCompleteRule()
{
if (!isset($this->subject, $this->rule))
return;
$this->addValidator(
static::buildRule(
array($this->subject, $this->rule), $this->arguments
)
);
$this->subject = null;
$this->rule = null;
$this->arguments = array();
}
public static function __callStatic($subject, $arguments)
{
$rule = array_shift($arguments);
$validator = new static;
$validator->setSubject($subject);
$validator->setRule($rule);
$validator->setArguments($arguments);
$validator->checkForCompleteRule();
return $validator;
}
public static function buildRule($ruleSpec, $arguments=array())
{
if ($ruleSpec instanceof Validatable) {
return $ruleSpec;
}
if (is_object($ruleSpec))
throw new ComponentException(
sprintf('%s does not implement the Respect\Validator\Validatable interface required for validators',
get_class($validator))
get_class($ruleSpec))
);
$validatorFqn = explode('\\', get_called_class());
array_pop($validatorFqn);
if (!is_array($validator))
$validator = explode('\\', $validator);
$validatorFqn = array_merge($validatorFqn, $validator);
if (!is_array($ruleSpec))
$ruleSpec = explode('\\', $ruleSpec);
$validatorFqn = array_merge($validatorFqn, $ruleSpec);
$validatorFqn = array_map('ucfirst', $validatorFqn);
$validatorFqn = implode('\\', $validatorFqn);
$validatorClass = new ReflectionClass($validatorFqn);

View file

@ -2,7 +2,7 @@
namespace Respect\Validation;
class AbstractCompositeValidatorTest extends ValidatorTestCase
class AbstractCompositeRuleTest extends ValidatorTestCase
{
protected $object;
@ -10,7 +10,7 @@ class AbstractCompositeValidatorTest extends ValidatorTestCase
protected function setUp()
{
$this->object = $this->getMockForAbstractClass(
'Respect\Validation\AbstractCompositeValidator'
'Respect\Validation\AbstractCompositeRule'
);
}
@ -24,8 +24,8 @@ class AbstractCompositeValidatorTest extends ValidatorTestCase
*/
public function testAddExistentValidator($validator)
{
$this->object->addValidator($validator);
$this->assertContains($validator, $this->object->getValidators());
$this->object->addRule($validator);
$this->assertContains($validator, $this->object->getRules());
}
/**
@ -33,7 +33,7 @@ class AbstractCompositeValidatorTest extends ValidatorTestCase
*/
public function testAddNonValidator()
{
$this->object->addValidator(new \stdClass);
$this->object->addRule(new \stdClass);
}
/**
@ -47,7 +47,7 @@ class AbstractCompositeValidatorTest extends ValidatorTestCase
class Freak{}
");
}
$this->object->addValidator('Foo\Freak');
$this->object->addRule('Foo\Freak');
}
/**
@ -57,7 +57,7 @@ class AbstractCompositeValidatorTest extends ValidatorTestCase
{
$messagesA = $a->getMessages();
$messagesB = $b->getMessages();
$this->object->addValidators(func_get_args());
$this->object->addRules(func_get_args());
$messagesObject = $this->object->getMessages();
foreach ($messagesA as $m) {
$this->assertContains($m, $messagesObject);
@ -70,7 +70,7 @@ class AbstractCompositeValidatorTest extends ValidatorTestCase
public function testBuildValidators()
{
$this->providerForMockImpossibleValidators();
$this->object->addValidators(array(
$this->object->addRules(array(
'Foo\Bar', 'Foo\Baz', 'Foo\Bat' => array(1, 2, 3)
));
$this->assertValidatorPresence($this->object, 'Bar', 'Baz', 'Bat');
@ -82,7 +82,7 @@ class AbstractCompositeValidatorTest extends ValidatorTestCase
public function testBuildValidatorsInvalid()
{
$this->providerForMockImpossibleValidators();
$this->object->addValidators(array(
$this->object->addRules(array(
'Foo\Bar', 'Foo\Baz', 'Foo\Bat' => 'balkbal'
));
}
@ -94,7 +94,7 @@ class AbstractCompositeValidatorTest extends ValidatorTestCase
{
$messagesA = $a->getMessages();
$messagesB = $b->getMessages();
$this->object->addValidators(func_get_args());
$this->object->addRules(func_get_args());
$this->object->setMessages(
array_map('strrev', $this->object->getMessages())
);

View file

@ -20,7 +20,7 @@ class AllTest extends ValidatorTestCase
*/
public function testValidateManyValid($a, $b, $c)
{
$this->object->addValidators(func_get_args());
$this->object->addRules(func_get_args());
$this->assertTrue($this->object->assert('any'));
}
@ -29,8 +29,8 @@ class AllTest extends ValidatorTestCase
*/
public function testManyIsValid($a, $b, $c)
{
$this->object->addValidators(func_get_args());
$this->assertTrue($this->object->is('any'));
$this->object->addRules(func_get_args());
$this->assertTrue($this->object->validate('any'));
}
/**
@ -38,8 +38,8 @@ class AllTest extends ValidatorTestCase
*/
public function testManyIsInvalid($a, $b, $c)
{
$this->object->addValidators(func_get_args());
$this->assertFalse($this->object->is('any'));
$this->object->addRules(func_get_args());
$this->assertFalse($this->object->validate('any'));
}
/**
@ -47,11 +47,11 @@ class AllTest extends ValidatorTestCase
*/
public function testManyIsInvalid2($a, $b, $c)
{
$this->object->addValidators(func_get_args());
$this->object->addValidator(
$this->object->addRules(func_get_args());
$this->object->addRule(
$this->buildMockValidator('Aids', array('Aids_1' => 'aesfg'))
);
$this->assertFalse($this->object->is('any'));
$this->assertFalse($this->object->validate('any'));
}
/**
@ -59,7 +59,7 @@ class AllTest extends ValidatorTestCase
*/
public function testValidateAllInvalid($a, $b, $c)
{
$this->object->addValidators(func_get_args());
$this->object->addRules(func_get_args());
try {
$this->object->assert('any');
} catch (InvalidException $e) {

View file

@ -21,8 +21,8 @@ class OneTest extends ValidatorTestCase
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->object->addRules(func_get_args());
$this->object->addRule($valid);
$this->assertTrue($this->object->assert('any'));
}
@ -31,7 +31,7 @@ class OneTest extends ValidatorTestCase
*/
public function testValidateOneAllRotten($invalidA, $invalidB, $invalidC)
{
$this->object->addValidators(func_get_args());
$this->object->addRules(func_get_args());
try {
$this->object->assert('any');
} catch (InvalidException $e) {
@ -45,9 +45,9 @@ class OneTest extends ValidatorTestCase
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->is('any'));
$this->object->addRules(func_get_args());
$this->object->addRule($valid);
$this->assertTrue($this->object->validate('any'));
}
/**
@ -55,8 +55,8 @@ class OneTest extends ValidatorTestCase
*/
public function testIsValidOneAllRotten($invalidA, $invalidB, $invalidC)
{
$this->object->addValidators(func_get_args());
$this->assertFalse($this->object->is('any'));
$this->object->addRules(func_get_args());
$this->assertFalse($this->object->validate('any'));
}
}

View file

@ -13,7 +13,7 @@ class BetweenTest extends \PHPUnit_Framework_TestCase
public function testBetweenValid($input, $min, $max)
{
$between = new Between($min, $max);
$this->assertTrue($between->is($input));
$this->assertTrue($between->validate($input));
$this->assertTrue($between->assert($input));
}
@ -25,7 +25,7 @@ class BetweenTest extends \PHPUnit_Framework_TestCase
{
$between = new Between($min, $max);
$this->assertFalse($between->is($input));
$this->assertFalse($between->validate($input));
$this->assertFalse($between->assert($input));
}

View file

@ -7,73 +7,55 @@ class ValidatorTest extends ValidatorTestCase
public function testPartialEmpty()
{
$v = Validator::is();
$v = Validator::date();
$this->assertType('Respect\Validation\Validator', $v);
$this->assertSame('is', $v->getOperation());
}
public function testPartialInputOnly()
{
$v = Validator::is('something');
$this->assertType('Respect\Validation\Validator', $v);
$this->assertSame('is', $v->getOperation());
$this->assertSame('something', $v->getInput());
$this->assertSame('date', $v->getSubject());
}
public function testPartialInputSubject()
{
$v = Validator::is('now', 'date');
$v = Validator::date('between', 'yesterday', 'tomorrow');
$this->assertType('Respect\Validation\Validator', $v);
$this->assertSame('is', $v->getOperation());
$this->assertSame('now', $v->getInput());
$this->assertSame('date', $v->getSubject());
$this->assertEquals(1, count($v->getValidators()));
$this->assertNull($v->getSubject());
$this->assertNull($v->getRule());
$this->assertEquals(array(), $v->getArguments());
}
public function testPartialInputChain()
{
$v = Validator::date()->between('yesterday', 'tomorrow')->string('notEmpty');
$this->assertType('Respect\Validation\Validator', $v);
$this->assertEquals(2, count($v->getValidators()));
$this->assertNull($v->getSubject());
$this->assertNull($v->getRule());
$this->assertEquals(array(), $v->getArguments());
}
public function testValidateSimple()
{
$v = Validator::is('foo', 'string', 'notEmpty');
$v = Validator::string('notEmpty')->validates('foo');
$this->assertTrue($v);
}
public function testValidateArguments()
{
$v = Validator::is('now', 'date', 'between', 'yesterday', 'tomorrow');
$v = Validator::date('between', 'yesterday', 'tomorrow')->validates('now');
$this->assertTrue($v);
}
public function testFluentSubjectGetter()
public function testValidateFluent()
{
$v = Validator::is('now')->date;
$this->assertType('Respect\Validation\Validator', $v);
$this->assertSame('is', $v->getOperation());
$this->assertSame('now', $v->getInput());
$this->assertSame('date', $v->getSubject());
}
public function testFluentSubjectCall()
{
$v = Validator::is('now')->date();
$this->assertType('Respect\Validation\Validator', $v);
$this->assertSame('is', $v->getOperation());
$this->assertSame('now', $v->getInput());
$this->assertSame('date', $v->getSubject());
}
public function testFluentSubjectGetterMixed()
{
$v = Validator::is('now')->date->between('yesterday', 'tomorrow');
$v = Validator::date()->between('yesterday', 'tomorrow')->validates('now');
$this->assertTrue($v);
}
public function testFluentSubjectCallMixed()
public function testValidateFluentChain()
{
$v = Validator::is('now', 'date')->between('yesterday', 'tomorrow');
$this->assertTrue($v);
}
public function testFluentSubjectCallMixed2()
{
$v = Validator::is('now')->date('between', 'yesterday', 'tomorrow');
$v = Validator::date()
->between('yesterday', 'tomorrow')
->string('notEmpty')
->validates('now');
$this->assertTrue($v);
}

View file

@ -13,7 +13,7 @@ abstract class ValidatorTestCase extends \PHPUnit_Framework_TestCase
array_shift($args);
foreach ($args as $req) {
$this->assertTrue(
$validator->hasValidator('Respect\Validation\Foo\\' . $req)
$validator->hasRule('Respect\Validation\Foo\\' . $req)
);
}
}
@ -25,9 +25,9 @@ abstract class ValidatorTestCase extends \PHPUnit_Framework_TestCase
$validator->shouldReceive('assert')->andThrow(
new InvalidException('Always invalid, man.')
);
$validator->shouldReceive('is')->andReturn(false);
$validator->shouldReceive('validate')->andReturn(false);
} else {
$validator->shouldReceive('is')->andReturn(true);
$validator->shouldReceive('validate')->andReturn(true);
$validator->shouldReceive('assert')->andReturn(true);
}
$validator->shouldReceive('getMessages')->andReturn(
@ -39,7 +39,7 @@ abstract class ValidatorTestCase extends \PHPUnit_Framework_TestCase
namespace Respect\Validation\Foo;
class $name implements \Respect\Validation\Validatable {
public function assert(\$input) {}
public function is(\$input) {}
public function validate(\$input) {}
public function setMessages(array \$messages) {}
public function getMessages() {
return " . var_export($messages,