New Validators

This commit is contained in:
Alexandre Gomes Gaigalas 2010-09-27 18:02:30 -03:00
parent 8651990e17
commit 82e26fa159
32 changed files with 947 additions and 7 deletions

View file

@ -0,0 +1,10 @@
<?php
namespace Respect\Validation\Exceptions;
use InvalidArgumentException;
class AttributeNotPresentException extends InvalidArgumentException
{
}

View file

@ -0,0 +1,10 @@
<?php
namespace Respect\Validation\Exceptions;
use InvalidArgumentException;
class InvalidDate extends InvalidArgumentException
{
}

View file

@ -0,0 +1,10 @@
<?php
namespace Respect\Validation\Exceptions;
use InvalidArgumentException;
class NotAlphanumericException extends InvalidArgumentException
{
}

View file

@ -0,0 +1,10 @@
<?php
namespace Respect\Validation\Exceptions;
use InvalidArgumentException;
class NotNullException extends InvalidArgumentException
{
}

View file

@ -0,0 +1,10 @@
<?php
namespace Respect\Validation\Exceptions;
use InvalidArgumentException;
class NotNumericException extends InvalidArgumentException
{
}

View file

@ -0,0 +1,10 @@
<?php
namespace Respect\Validation\Exceptions;
use LengthException;
class StringLengthException extends LengthException
{
}

View file

@ -0,0 +1,10 @@
<?php
namespace Respect\Validation\Exceptions;
use InvalidArgumentException;
class WhitespaceFoundException extends InvalidArgumentException
{
}

View file

@ -7,10 +7,10 @@ use Respect\Validation\Rules\AbstractRule;
abstract class AbstractDate extends AbstractRule
{
const FORMAT_DEFAULT = DateTime::RFC1036;
protected $format = self::FORMAT_DEFAULT;
protected $format = DateTime::RFC1036;
protected function setFormat($format=DateTime::RFC1036)
protected function setFormat($format=self::FORMAT_DEFAULT)
{
$this->format = $format;
}

View file

@ -0,0 +1,49 @@
<?php
namespace Respect\Validation\Rules;
use Respect\Validation\Validatable;
use Respect\Validation\Rules\AbstractRule;
use Respect\Validation\Exceptions\NotAlphanumericException;
class Alnum extends AbstractRule implements Validatable
{
const MSG_NOT_ALPHANUMERIC = 'Alnum_1';
const MSG_NOT_ALPHANUMERIC_ADDITIONAL = 'Alnum_2';
protected $messages = array(
self::MSG_NOT_ALPHANUMERIC => '%s does not contains only letters and digits',
self::MSG_NOT_ALPHANUMERIC_ADDITIONAL => '%s does not contains only letters and digits (including %s)'
);
protected $additionalChars = '';
public function __construct($additionalChars='')
{
$this->additionalChars = $additionalChars;
}
public function validate($input)
{
return (boolean) preg_match(
"#^[a-zA-Z0-9\s{$this->additionalChars}]+$#", $input
);
}
public function assert($input)
{
if (!$this->validate($input))
if (empty($this->additionalChars))
throw new NotAlphanumericException(
sprintf($this->getMessage(self::MSG_NOT_ALPHANUMERIC),
$input)
);
else
throw new NotAlphanumericException(
sprintf(
$this->getMessage(self::MSG_NOT_ALPHANUMERIC_ADDITIONAL),
$input, $this->additionalChars
)
);
return true;
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\InvalidException;
use Respect\Validation\Validatable;
class AtLeast extends AbstractComposite implements Validatable
{
protected $howMany = 1;
public function __construct($howMany, $rules=array())
{
$this->howMany = $howMany;
call_user_func_array(array($this, 'parent::__construct'), $rules);
}
public function assert($input)
{
$validators = $this->getRules();
$exceptions = $this->iterateRules($input);
if ($this->howMany > (count($validators) - count($exceptions)))
throw new InvalidException($exceptions);
return true;
}
public function validate($input)
{
$validators = $this->getRules();
$exceptions = $this->iterateRules($input);
return $this->howMany <= (count($validators) - count($exceptions));
}
}

View file

@ -0,0 +1,50 @@
<?php
namespace Respect\Validation\Rules;
use Respect\Validation\Rules\AbstractDate;
use Respect\Validation\Exceptions\InvalidDate;
use Respect\Validation\Validatable;
class Date extends AbstractDate implements Validatable
{
const MSG_INVALID_DATE = 'Date_1';
const MSG_INVALID_FORMAT = 'Date_2';
protected $messages = array(
self::MSG_INVALID_DATE => '%s is not a valid date reference',
self::MSG_INVALID_FORMAT => '%s is not a valid date in the %s format',
);
public function __construct($format=null)
{
$this->format = $format;
}
public function validate($input)
{
if (is_null($this->format))
return (boolean) strtotime($input);
else
return date($this->format, strtotime($input)) == $input;
}
public function assert($input)
{
if (!$this->validate($input))
if (is_null($this->format))
throw new InvalidDate(
sprintf($this->getMessage(static::MSG_INVALID_DATE), $input)
);
else
throw new InvalidDate(
sprintf(
$this->getMessage(static::MSG_INVALID_FORMAT), $input,
$this->format
)
);
return true;
}
}

View file

@ -6,10 +6,12 @@ use Respect\Validation\Validatable;
use Respect\Validation\Rules\AbstractDate;
use Respect\Validation\Exceptions\ComponentException;
use Respect\Validation\Exceptions\DateOutOfBoundsException;
use Respect\Validation\Exceptions\InvalidDate;
use Respect\Validation\Validator;
class DateBetween extends AbstractDate implements Validatable
{
const MSG_OUT_OF_BOUNDS = 'Date_Between_1';
const MSG_OUT_OF_BOUNDS = 'DateBetween_1';
protected $min;
protected $max;
@ -19,9 +21,18 @@ class DateBetween extends AbstractDate implements Validatable
public function __construct($min, $max, $format=null)
{
$this->min = $this->getDateObject($min);
$this->max = $this->getDateObject($max);
try {
Validator::date($min);
} catch (InvalidDate $e) {
throw new ComponentException(sprintf('Invalid Date: %s', $min));
}
try {
Validator::date($max);
} catch (InvalidDate $e) {
throw new ComponentException(sprintf('Invalid Date: %s', $max));
}
if ($this->min > $this->max)
throw new ComponentException(
sprintf('%s cannot be less than %s for validation',

View file

@ -0,0 +1,43 @@
<?php
namespace Respect\Validation\Rules;
use Respect\Validation\Rules\AbstractRule;
use Respect\Validation\Validatable;
use Respect\Validation\Exceptions\AttributeNotPresentException;
use Respect\Validation\Rules\All;
class HasAttribute extends All implements Validatable
{
const MSG_ATTRIBUTE_NOT_PRESENT = 'NullValue_1';
protected $messages = array(
self::MSG_ATTRIBUTE_NOT_PRESENT => 'Object does not have the attribute %s'
);
protected $attribute = '';
public function __construct($attribute, $attributeValidator=null)
{
$this->attribute = $attribute;
if (!is_null($attributeValidator))
$this->addRule($attributeValidator);
}
public function validate($input)
{
return @property_exists($input, $this->attribute)
&& parent::validate($input->{$this->attribute});
}
public function assert($input)
{
if (!$this->validate($input))
throw new AttributeNotPresentException(
sprintf(
$this->getMessage(self::MSG_ATTRIBUTE_NOT_PRESENT),
$this->attribute
)
);
return parent::validate($input->{$this->attribute});
}
}

View file

@ -0,0 +1,27 @@
<?php
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\InvalidException;
use Respect\Validation\Validatable;
class Most extends AbstractComposite implements Validatable
{
public function assert($input)
{
$validators = $this->getRules();
$exceptions = $this->iterateRules($input);
if ((count($validators) / 2) > (count($validators) - count($exceptions)))
throw new InvalidException($exceptions);
return true;
}
public function validate($input)
{
$validators = $this->getRules();
$exceptions = $this->iterateRules($input);
return (count($validators) / 2) < (count($validators) - count($exceptions));
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace Respect\Validation\Rules;
use Respect\Validation\Validatable;
use Respect\Validation\Rules\AbstractRule;
use Respect\Validation\Exceptions\WhitespaceFoundException;
class NoWhitespace extends AbstractRule implements Validatable
{
const MSG_WHITESPACE_FOUND = 'NoWhitespace_1';
protected $messages = array(
self::MSG_WHITESPACE_FOUND => '%s contains spaces, tabs, line breaks or other not allowed charaters.'
);
public function validate($input)
{
return preg_match('#^\S+$#', $input);
}
public function assert($input)
{
if (!$this->validate($input))
throw new WhitespaceFoundException(
sprintf($this->getMessage(self::MSG_WHITESPACE_FOUND), $input)
);
return true;
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace Respect\Validation\Rules;
use Respect\Validation\Exceptions\InvalidException;
use Respect\Validation\Validatable;
class None extends AbstractComposite implements Validatable
{
public function validate($input)
{
$validators = $this->getRules();
return count($validators) === count(array_filter(
$validators,
function($v) use($input) {
return!$v->validate($input);
}
));
}
public function assert($input)
{
$exceptions = $this->iterateRules($input);
if (count($this->getRules()) !== count($exceptions))
throw new InvalidException($exceptions);
return true;
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace Respect\Validation\Rules;
use Respect\Validation\Rules\AbstractRule;
use Respect\Validation\Validatable;
use Respect\Validation\Exceptions\NotNullException;
class NullValue extends AbstractRule implements Validatable
{
const MSG_NOT_NULL = 'NullValue_1';
protected $messages = array(
self::MSG_NOT_NULL => '%s is not null'
);
public function validate($input)
{
return is_null($input);
}
public function assert($input)
{
if (!$this->validate($input))
throw new NotNullException(
sprintf($this->getMessage(self::MSG_NOT_NULL), $input)
);
return true;
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace Respect\Validation\Rules;
use Respect\Validation\Validatable;
use Respect\Validation\Rules\AbstractRule;
use Respect\Validation\Exceptions\NotNumericException;
class Numeric extends AbstractRule implements Validatable
{
const MSG_NOT_NUMERIC = 'Numeric_1';
protected $messages = array(
self::MSG_NOT_NUMERIC => '%s is not a numeric value'
);
public function validate($input)
{
return is_numeric($input);
}
public function assert($input)
{
if (!$this->validate($input))
throw new NotNumericException(
sprintf($this->getMessage(self::MSG_NOT_NUMERIC), $input)
);
return true;
}
}

View file

@ -0,0 +1,72 @@
<?php
namespace Respect\Validation\Rules;
use Respect\Validation\Rules\AbstractRule;
use Respect\Validation\Exceptions\StringLengthException;
use Respect\Validation\Validatable;
use Respect\Validation\Validator;
use Respect\Validation\Exceptions\NotNumericException;
use Respect\Validation\Exceptions\ComponentException;
class StringLength extends AbstractRule implements Validatable
{
const MSG_LENGTH_MIN = 'StringLength_1';
const MSG_LENGTH_MAX = 'StringLength_2';
protected $messages = array(
self::MSG_LENGTH_MIN => '%s does not have at least %s characters',
self::MSG_LENGTH_MAX => '%s exceeds the maximum of %s characters'
);
protected $min;
protected $max;
public function __construct($min=null, $max=null)
{
$this->min = $min;
$this->max = $max;
try {
Validator::one(Validator::numeric($min), Validator::NullValue());
} catch (NotNumericException $e) {
throw new ComponentException(
sprintf('%s is not a valid numeric length', $min)
);
}
try {
Validator::one(Validator::numeric($max), Validator::NullValue());
} catch (NotNumericException $e) {
throw new ComponentException(
sprintf('%s is not a valid numeric length', $max)
);
}
}
public function validateMin($input)
{
return is_null($this->min) || mb_strlen($input) >= $this->min;
}
public function validateMax($input)
{
return is_null($this->max) || mb_strlen($input) <= $this->max;
}
public function validate($input)
{
return $this->validateMin($input) && $this->validateMax($input);
}
public function assert($input)
{
if (!$this->validateMin($input))
throw new StringLengthException(
$this->getMessage(self::MSG_LENGTH_MIN, $input, $this->min)
);
if (!$this->validateMax($input))
throw new StringLengthException(
$this->getMessage(self::MSG_LENGTH_MAX, $input, $this->max)
);
return true;
}
}

View file

@ -8,7 +8,7 @@ use Respect\Validation\Rules\AbstractRule;
class StringNotEmpty extends AbstractRule implements Validatable
{
const MSG_EMPTY_STRING = 'String_NotEmpty_1';
const MSG_EMPTY_STRING = 'StringNotEmpty_1';
protected $messages = array(
self::MSG_EMPTY_STRING => 'You provided an empty string'
@ -26,6 +26,7 @@ class StringNotEmpty extends AbstractRule implements Validatable
throw new EmptyStringException(
$this->getMessage(self::MSG_EMPTY_STRING)
);
return true;
}
}

View file

@ -4,7 +4,7 @@ namespace Respect\Validation;
use Respect\Validation\Rules\All;
use ReflectionClass;
use Respect\Validation\ComponentException;
use Respect\Validation\Exceptions\ComponentException;
class Validator extends All
{

View file

@ -0,0 +1,49 @@
<?php
namespace Respect\Validation\Rules;
class AlnumTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider providerForValidAlnum
*/
public function testAlnumValid($validAlnum, $aditional)
{
$validator = new Alnum($aditional);
$this->assertTrue($validator->validate($validAlnum));
}
/**
* @dataProvider providerForInvalidAlnum
* @expectedException Respect\Validation\Exceptions\NotAlphanumericException
*/
public function testAlnumInvalid($invalidAlnum, $aditional)
{
$validator = new Alnum($aditional);
$validator->assert($invalidAlnum);
}
public function providerForValidAlnum()
{
return array(
array('alganet', ''),
array('1', ''),
array('a', ''),
array('foobar', ''),
array('rubinho_', '_'),
array('google.com', '.'),
);
}
public function providerForInvalidAlnum()
{
return array(
array('@#$', ''),
array('_', ''),
array('', ''),
array('dgç', ''),
);
}
}

View file

@ -0,0 +1,53 @@
<?php
namespace Respect\Validation\Rules;
use Respect\Validation\ValidatorTestCase;
use Respect\Validation\Exceptions\InvalidException;
class AtLeastTest extends ValidatorTestCase
{
protected $object;
protected function setUp()
{
$this->object = new AtLeast(2);
}
/**
* @dataProvider providerForMockImpossibleValidators
*/
public function testValidateOneValid($invalidA, $invalidB, $invalidC)
{
$this->object->addRules(func_get_args());
$valid = $this->buildMockValidator('Darth', array('Darth_1' => 'o54n'));
$this->object->addRule($valid);
$valid = $this->buildMockValidator('Dart2', array('Dart2_1' => 'sfg44'));
$this->object->addRule($valid);
$this->assertTrue($this->object->assert('any'));
}
/**
* @dataProvider providerForMockImpossibleValidators
*/
public function testIsValidOneValid($invalidA, $invalidB, $invalidC)
{
$this->object->addRules(func_get_args());
$valid = $this->buildMockValidator('Darth', array('Darth_1' => 'o54n'));
$this->object->addRule($valid);
$valid = $this->buildMockValidator('Dart2', array('Dart2_1' => 'sfg44'));
$this->object->addRule($valid);
$this->assertTrue($this->object->validate('any'));
}
/**
* @dataProvider providerForMockImpossibleValidators
*/
public function testIsValidOneAllRotten($invalidA, $invalidB, $invalidC)
{
$this->object->addRules(func_get_args());
$this->assertFalse($this->object->validate('any'));
}
}

View file

@ -0,0 +1,48 @@
<?php
namespace Respect\Validation\Rules;
class DateTest extends \PHPUnit_Framework_TestCase
{
protected $object;
protected function setUp()
{
$this->object = new Date;
}
protected function tearDown()
{
unset($this->object);
}
public function testDateWithoutFormat()
{
$this->assertTrue($this->object->validate('today'));
}
public function testInvalidDateWithoutFormat()
{
$this->assertFalse($this->object->validate('aids'));
}
/**
* @expectedException Respect\Validation\Exceptions\InvalidDate
*/
public function testDateFormat()
{
$this->object = new Date('Y-m-');
$this->assertTrue($this->object->assert('2009-09-09'));
}
/**
* @expectedException Respect\Validation\Exceptions\InvalidDate
*/
public function testInvalidDateFormat()
{
$this->object = new Date('y-m-d');
$this->assertFalse($this->object->assert('2009-09-09'));
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace Respect\Validation\Rules;
class HasAttributeTest extends \PHPUnit_Framework_TestCase
{
public function testHasAttribute()
{
$validator = new HasAttribute('bar');
$obj = new \stdClass;
$obj->bar = 'foo';
$this->assertTrue($validator->assert($obj));
}
/**
* @expectedException Respect\Validation\Exceptions\AttributeNotPresentException
*/
public function testNotNull()
{
$validator = new HasAttribute('bar');
$obj = new \stdClass;
$obj->baraaaaa = 'foo';
$this->assertTrue($validator->assert($obj));
}
public function testValidatorAttribute()
{
$subValidator = new StringLength(1, 3);
$validator = new HasAttribute('bar', $subValidator);
$obj = new \stdClass;
$obj->bar = 'foo';
$this->assertTrue($validator->assert($obj));
}
}

View file

@ -0,0 +1,61 @@
<?php
namespace Respect\Validation\Rules;
use Respect\Validation\ValidatorTestCase;
use Respect\Validation\Exceptions\InvalidException;
class MostTest extends ValidatorTestCase
{
protected $object;
protected function setUp()
{
$this->object = new Most;
}
/**
* @dataProvider providerForMockImpossibleValidators
*/
public function testValidateOneValid($invalidA, $invalidB, $invalidC)
{
$this->object->addRules(func_get_args());
$valid = $this->buildMockValidator('Darth', array('Darth_1' => 'o54n'));
$this->object->addRule($valid);
$valid = $this->buildMockValidator('Dart2', array('Dart2_1' => 'sfg44'));
$this->object->addRule($valid);
$valid = $this->buildMockValidator('Dart3', array('Dart3_1' => 'sfg44'));
$this->object->addRule($valid);
$valid = $this->buildMockValidator('Dart4', array('Dart4_1' => 'sfg44'));
$this->object->addRule($valid);
$this->assertTrue($this->object->assert('any'));
}
/**
* @dataProvider providerForMockImpossibleValidators
*/
public function testIsValidOneValid($invalidA, $invalidB, $invalidC)
{
$this->object->addRules(func_get_args());
$valid = $this->buildMockValidator('Darth', array('Darth_1' => 'o54n'));
$this->object->addRule($valid);
$valid = $this->buildMockValidator('Dart2', array('Dart2_1' => 'sfg44'));
$this->object->addRule($valid);
$valid = $this->buildMockValidator('Dart2', array('Dart3_1' => 'sfg44'));
$this->object->addRule($valid);
$valid = $this->buildMockValidator('Dart4', array('Dart4_1' => 'sfg44'));
$this->object->addRule($valid);
$this->assertTrue($this->object->validate('any'));
}
/**
* @dataProvider providerForMockImpossibleValidators
*/
public function testIsValidOneAllRotten($invalidA, $invalidB, $invalidC)
{
$this->object->addRules(func_get_args());
$this->assertFalse($this->object->validate('any'));
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace Respect\Validation\Rules;
class NoWhitespaceTest extends \PHPUnit_Framework_TestCase
{
protected $object;
protected function setUp()
{
$this->object = new NoWhitespace;
}
public function testNoWhitespace()
{
$this->assertTrue($this->object->assert('wpoiur'));
}
/**
* @expectedException Respect\Validation\Exceptions\WhitespaceFoundException
*/
public function testWhitespace()
{
$this->assertTrue($this->object->assert('w poiur'));
}
}

View file

@ -0,0 +1,62 @@
<?php
namespace Respect\Validation\Rules;
use Respect\Validation\ValidatorTestCase;
use Respect\Validation\Exceptions\InvalidException;
class NoneTest extends ValidatorTestCase
{
protected $object;
protected function setUp()
{
$this->object = new None;
}
/**
* @dataProvider providerForMockImpossibleValidators
*/
public function testValidateOneValid($invalidA, $invalidB, $invalidC)
{
$this->object->addRules(func_get_args());
$this->assertTrue($this->object->assert('any'));
}
/**
* @dataProvider providerForMockImpossibleValidators
*/
public function testValidateOneAllRotten($invalidA, $invalidB, $invalidC)
{
$valid = $this->buildMockValidator('Darth', array('Darth_1' => 'o54n'));
$this->object->addRule($valid);
$this->object->addRules(func_get_args());
try {
$this->object->assert('any');
} catch (InvalidException $e) {
$this->assertEquals(3, count($e->getExceptions()));
}
}
/**
* @dataProvider providerForMockImpossibleValidators
*/
public function testIsValidOneValid($invalidA, $invalidB, $invalidC)
{
$this->object->addRules(func_get_args());
$this->assertTrue($this->object->validate('any'));
}
/**
* @dataProvider providerForMockImpossibleValidators
*/
public function testIsValidOneAllRotten($invalidA, $invalidB, $invalidC)
{
$valid = $this->buildMockValidator('Darth', array('Darth_1' => 'o54n'));
$this->object->addRule($valid);
$this->object->addRules(func_get_args());
$this->assertFalse($this->object->validate('any'));
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace Respect\Validation\Rules;
class NullValueTest extends \PHPUnit_Framework_TestCase
{
protected $object;
protected function setUp()
{
$this->object = new NullValue;
}
public function testNullValue()
{
$this->assertTrue($this->object->assert(null));
}
/**
* @expectedException Respect\Validation\Exceptions\NotNullException
*/
public function testNotNull()
{
$this->assertTrue($this->object->assert('w poiur'));
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace Respect\Validation\Rules;
class NumericTest extends \PHPUnit_Framework_TestCase
{
protected $object;
protected function setUp()
{
$this->object = new Numeric;
}
public function testNumeric()
{
$this->assertTrue($this->object->assert(165));
}
/**
* @expectedException Respect\Validation\Exceptions\NotNumericException
*/
public function testNotNumeric()
{
$this->assertTrue($this->object->assert('w poiur'));
}
}

View file

@ -0,0 +1,41 @@
<?php
namespace Respect\Validation\Rules;
class StringLengthTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider providerForValidLenght
*/
public function testStringLengthValid($string, $min, $max)
{
$validator = new StringLength($min, $max);
$this->assertTrue($validator->assert($string));
}
/**
* @dataProvider providerForInvalidLenght
* @expectedException Respect\Validation\Exceptions\StringLengthException
*/
public function testStringLengthInvalid($string, $min, $max)
{
$validator = new StringLength($min, $max);
$this->assertFalse($validator->assert($string));
}
public function providerForValidLenght()
{
return array(
array('alganet', 1, 15)
);
}
public function providerForInvalidLenght()
{
return array(
array('alganet', 1, 3)
);
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace Respect\Validation\Rules;
class StringNotEmptyTest extends \PHPUnit_Framework_TestCase
{
protected $object;
protected function setUp()
{
$this->object = new StringNotEmpty;
}
public function testStringNotEmpty()
{
$this->assertTrue($this->object->assert('xsdfgf'));
}
/**
* @expectedException Respect\Validation\Exceptions\EmptyStringException
*/
public function testStringEmpty()
{
$this->assertTrue($this->object->assert(' '));
}
}