From 9a69eb6198fe4415b4399f6c61d1087041c81a59 Mon Sep 17 00:00:00 2001 From: Vance Lucas Date: Mon, 28 Jan 2013 13:10:50 -0600 Subject: [PATCH] All initial validators added and tested --- src/Valitron/Validator.php | 338 +++++++++++++++++++++++++++--------- tests/Valitron/Validate.php | 306 +++++++++++++++++++++++++++++++- 2 files changed, 557 insertions(+), 87 deletions(-) diff --git a/src/Valitron/Validator.php b/src/Valitron/Validator.php index 8460b22..c98dbb2 100644 --- a/src/Valitron/Validator.php +++ b/src/Valitron/Validator.php @@ -32,56 +32,10 @@ class Validator } } - - /** - * Register new validation rule callback - */ - public static function addRule($name, $callback) - { - if(!is_callable($callback)) { - throw new \InvalidArgumentException("Second argument must be a valid callback. Given argument was not callable."); - } - - static::$_rules[$name] = $callback; - } - - - /** - * Convenience method to add validation rules - */ - public function rule($rule, $fields, $message = null) - { - if(!isset(static::$_rules[$rule])) { - throw new \InvalidArgumentException("Rule '" . $rule . "' has not been registered with " . __CLASS__ . "::addRule()."); - } - - $this->_validations[] = array( - 'rule' => $rule, - 'fields' => (array) $fields, - 'message' => $message - ); - return $this; - } - - - /** - * Convenience method to add validation rules - */ - public function __call($rule, array $args) - { - if(!isset(static::$_rules[$rule])) { - throw new \InvalidArgumentException("Method '" . $rule . "' does not exist, or rule '" . $rule . "' has not been registered with " . __CLASS__ . "::addRule()."); - } - - array_unshift($args, $rule); - call_user_func_array(array($this, 'rule'), $args); - return $this; - } - /** * Required field validator */ - public function validateRequired($field, $value, array $params = array()) + public function validateRequired($field, $value) { if(is_null($value)) { return false; @@ -92,49 +46,201 @@ class Validator } /** - * Validate that an attribute is a valid IP address + * Validate that two values match + * + * @param string $field + * @param mixed $value + * @param array $fields + * @return void + */ + protected function validateEquals($field, $value, array $params) + { + $field2 = $params[0]; + return isset($this->_fields[$field2]) && $value == $this->_fields[$field2]; + } + + /** + * Validate that a field is different from another field + * + * @param string $field + * @param mixed $value + * @param array $fields + * @return bool + */ + protected function validateDifferent($field, $value, array $params) + { + $field2 = $params[0]; + return isset($this->_fields[$field2]) && $value != $this->_fields[$field2]; + } + + /** + * Validate that a field was "accepted" (based on PHP's string evaluation rules) + * + * This validation rule implies the field is "required" * * @param string $field * @param mixed $value * @return bool */ - public static function validateIp($field, $value) + protected function validateAccepted($field, $value) + { + $acceptable = array('yes', 'on', 1, true); + return $this->validateRequired($field, $value) && in_array($value, $acceptable, true); + } + + /** + * Validate that a field is numeric + * + * @param string $field + * @param mixed $value + * @return bool + */ + protected function validateNumeric($field, $value) + { + return is_numeric($value); + } + + /** + * Validate that a field is an integer + * + * @param string $field + * @param mixed $value + * @return bool + */ + protected function validateInteger($field, $value) + { + return filter_var($value, FILTER_VALIDATE_INT) !== false; + } + + /** + * Validate the length of a string + * + * @param string $field + * @param mixed $value + * @param array $fields + * @return bool + */ + protected function validateLength($field, $value, $params) + { + $length = $this->stringLength($value); + // Length between + if(isset($params[1])) { + return $length >= $params[0] && $length <= $params[1]; + } + // Length same + return $length == $params[0]; + } + + /** + * Get the length of a string + * + * @param string $value + * @return int + */ + protected function stringLength($value) + { + if (function_exists('mb_strlen')) { + return mb_strlen($value); + } + return strlen($value); + } + + /** + * Validate the size of a field is greater than a minimum value. + * + * @param string $field + * @param mixed $value + * @param array $fields + * @return bool + */ + protected function validateMin($field, $value, $params) + { + return (int) $value >= $params[0]; + } + + /** + * Validate the size of a field is less than a maximum value + * + * @param string $field + * @param mixed $value + * @param array $fields + * @return bool + */ + protected function validateMax($field, $value, $params) + { + return (int) $value <= $params[0]; + } + + /** + * Validate a field is contained within a list of values + * + * @param string $field + * @param mixed $value + * @param array $fields + * @return bool + */ + protected function validateIn($field, $value, $params) + { + return in_array($value, $params[0]); + } + + /** + * Validate a field is not contained within a list of values + * + * @param string $field + * @param mixed $value + * @param array $fields + * @return bool + */ + protected function validateNotIn($field, $value, $params) + { + return !$this->validateIn($field, $value, $params); + } + + /** + * Validate that a field is a valid IP address + * + * @param string $field + * @param mixed $value + * @return bool + */ + protected function validateIp($field, $value) { return filter_var($value, FILTER_VALIDATE_IP) !== false; } /** - * Validate that an attribute is a valid e-mail address + * Validate that a field is a valid e-mail address * * @param string $field * @param mixed $value * @return bool */ - public static function validateEmail($field, $value) + protected function validateEmail($field, $value) { return filter_var($value, FILTER_VALIDATE_EMAIL) !== false; } /** - * Validate that an attribute is a valid URL by syntax + * Validate that a field is a valid URL by syntax * * @param string $field * @param mixed $value * @return bool */ - public static function validateUrl($field, $value) + protected function validateUrl($field, $value) { return filter_var($value, FILTER_VALIDATE_URL) !== false; } /** - * Validate that an attribute is an active URL by verifying DNS record + * Validate that a field is an active URL by verifying DNS record * * @param string $field * @param mixed $value * @return bool */ - public static function validateActiveUrl($field, $value) + protected function validateUrlActive($field, $value) { $url = str_replace(array('http://', 'https://', 'ftp://'), '', strtolower($value)); @@ -142,74 +248,74 @@ class Validator } /** - * Validate that an attribute contains only alphabetic characters + * Validate that a field contains only alphabetic characters * * @param string $field * @param mixed $value * @return bool */ - public static function validateAlpha($field, $value) + protected function validateAlpha($field, $value) { return preg_match('/^([a-z])+$/i', $value); } /** - * Validate that an attribute contains only alpha-numeric characters. + * Validate that a field contains only alpha-numeric characters * * @param string $field * @param mixed $value * @return bool */ - public static function validateAlphaNum($field, $value) + protected function validateAlphaNum($field, $value) { return preg_match('/^([a-z0-9])+$/i', $value); } /** - * Validate that an attribute contains only alpha-numeric characters, dashes, and underscores. + * Validate that a field contains only alpha-numeric characters, dashes, and underscores * * @param string $field * @param mixed $value * @return bool */ - public static function validateAlphaDash($field, $value) + protected function validateAlphaDash($field, $value) { return preg_match('/^([-a-z0-9_-])+$/i', $value); } /** - * Validate that an attribute passes a regular expression check. + * Validate that a field passes a regular expression check * * @param string $field * @param mixed $value * @return bool */ - public static function validateRegex($field, $value, $params) + protected function validateRegex($field, $value, $params) { return preg_match($params[0], $value); } /** - * Validate that an attribute is a valid date. + * Validate that a field is a valid date * * @param string $field * @param mixed $value * @return bool */ - public static function validateDate($field, $value) + protected function validateDate($field, $value) { return strtotime($value) !== false; } /** - * Validate that an attribute matches a date format. + * Validate that a field matches a date format * * @param string $field * @param mixed $value - * @param array $params + * @param array $fields * @return bool */ - public static function validateDateFormat($field, $value, $params) + protected function validateDateFormat($field, $value, $params) { $parsed = date_parse_from_format($params[0], $value); @@ -217,29 +323,33 @@ class Validator } /** - * Validate the date is before a given date. + * Validate the date is before a given date * * @param string $field * @param mixed $value - * @param array $params + * @param array $fields * @return bool */ - public static function validateBefore($field, $value, $params) + protected function validateDateBefore($field, $value, $params) { - return strtotime($value) < strtotime($params[0]); + $vtime = ($value instanceof \DateTime) ? $value->getTimestamp() : strtotime($value); + $ptime = ($params[0] instanceof \DateTime) ? $params[0]->getTimestamp() : strtotime($params[0]); + return $vtime < $ptime; } /** - * Validate the date is after a given date. + * Validate the date is after a given date * * @param string $field * @param mixed $value - * @param array $params + * @param array $fields * @return bool */ - public static function validateAfter($field, $value, $params) + protected function validateDateAfter($field, $value, $params) { - return strtotime($value) > strtotime($params[0]); + $vtime = ($value instanceof \DateTime) ? $value->getTimestamp() : strtotime($value); + $ptime = ($params[0] instanceof \DateTime) ? $params[0]->getTimestamp() : strtotime($params[0]); + return $vtime > $ptime; } /** @@ -270,6 +380,16 @@ class Validator $this->_errors[$field][] = sprintf($msg, $field); } + /** + * Reset object properties + */ + public function reset() + { + $this->_fields = array(); + $this->_errors = array(); + $this->_validations = array(); + } + /** * Run validations and return boolean result * @@ -280,19 +400,69 @@ class Validator foreach($this->_validations as $v) { foreach($v['fields'] as $field) { $value = isset($this->_fields[$field]) ? $this->_fields[$field] : null; - $result = call_user_func(static::$_rules[$v['rule']], $field, $value); + + // Callback is user-specified or assumed method on class + if(isset(static::$_rules[$v['rule']])) { + $callback = static::$_rules[$v['rule']]; + } else { + $callback = array($this, 'validate' . ucfirst($v['rule'])); + } + + $result = call_user_func($callback, $field, $value, $v['params']); if(!$result) { $this->error($field, $v['message']); } } } - return count($this->errors()) === 0; + $result = count($this->errors()) === 0; + $this->reset(); + return $result; + } + + /** + * Register new validation rule callback + */ + public static function addRule($name, $callback) + { + if(!is_callable($callback)) { + throw new \InvalidArgumentException("Second argument must be a valid callback. Given argument was not callable."); + } + + static::$_rules[$name] = $callback; + } + + /** + * Convenience method to add validation rules + */ + public function rule($rule, $fields) + { + if(!isset(static::$_rules[$rule])) { + $ruleMethod = 'validate' . ucfirst($rule); + if(!method_exists($this, $ruleMethod)) { + throw new \InvalidArgumentException("Rule '" . $rule . "' has not been registered with " . __CLASS__ . "::addRule()."); + } + } + + // Get any other arguments passed to function + $params = array_slice(func_get_args(), 2); + + $this->_validations[] = array( + 'rule' => $rule, + 'fields' => (array) $fields, + 'params' => (array) $params, + 'message' => null + ); + return $this; + } + + /** + * Convenience method to add validation rules + */ + public function __call($rule, array $args) + { + array_unshift($args, $rule); + call_user_func_array(array($this, 'rule'), $args); + return $this; } } -// Register default validations here so they can be overridden by user after include -$class = __NAMESPACE__ . '\Validator'; -$class::addRule('required', array($class, 'validateRequired')); -$class::addRule('email', array($class, 'validateEmail')); -$class::addRule('url', array($class, 'validateUrl')); - diff --git a/tests/Valitron/Validate.php b/tests/Valitron/Validate.php index 3c19e8a..4f4fb9f 100644 --- a/tests/Valitron/Validate.php +++ b/tests/Valitron/Validate.php @@ -15,14 +15,20 @@ class ValidateTest extends \PHPUnit_Framework_TestCase $this->assertEquals($v->data(), array('foo' => 'bar')); } - public function testRequired() + public function testAccurateErrorCount() { $v = new Validator(array('name' => 'Chester Tester')); $v->required('name'); - $this->assertTrue($v->validate()); $this->assertSame(1, count($v->errors('name'))); } + public function testRequiredValid() + { + $v = new Validator(array('name' => 'Chester Tester')); + $v->rule('required', 'name'); + $this->assertTrue($v->validate()); + } + public function testRequiredNonExistentField() { $v = new Validator(array('name' => 'Chester Tester')); @@ -30,6 +36,174 @@ class ValidateTest extends \PHPUnit_Framework_TestCase $this->assertFalse($v->validate()); } + public function testEqualsValid() + { + $v = new Validator(array('foo' => 'bar', 'bar' => 'bar')); + $v->equals('foo', 'bar'); + $this->assertTrue($v->validate()); + } + + public function testEqualsInvalid() + { + $v = new Validator(array('foo' => 'foo', 'bar' => 'bar')); + $v->equals('foo', 'bar'); + $this->assertFalse($v->validate()); + } + + public function testDifferentValid() + { + $v = new Validator(array('foo' => 'bar', 'bar' => 'baz')); + $v->rule('different', 'foo', 'bar'); + $this->assertTrue($v->validate()); + } + + public function testDifferentInvalid() + { + $v = new Validator(array('test1' => 'test', 'test2' => 'test')); + $v->different('test1', 'test2'); + $this->assertFalse($v->validate()); + } + + public function testAcceptedValid() + { + $v = new Validator(array('agree' => 'yes')); + $v->rule('accepted', 'agree'); + $this->assertTrue($v->validate()); + } + + public function testAcceptedInvalid() + { + $v = new Validator(array('agree' => 'no')); + $v->accepted('agree'); + $this->assertFalse($v->validate()); + } + + public function testNumericValid() + { + $v = new Validator(array('num' => '42.341569')); + $v->rule('numeric', 'num'); + $this->assertTrue($v->validate()); + } + + public function testNumericInvalid() + { + $v = new Validator(array('num' => 'nope')); + $v->numeric('num'); + $this->assertFalse($v->validate()); + } + + public function testIntegerValid() + { + $v = new Validator(array('num' => '41243')); + $v->rule('integer', 'num'); + $this->assertTrue($v->validate()); + } + + public function testIntegerInvalid() + { + $v = new Validator(array('num' => '42.341569')); + $v->integer('num'); + $this->assertFalse($v->validate()); + } + + public function testLengthValid() + { + $v = new Validator(array('str' => 'happy')); + $v->rule('length', 'str', 5); + $this->assertTrue($v->validate()); + } + + public function testLengthInvalid() + { + $v = new Validator(array('str' => 'sad')); + $v->length('str', 6); + $this->assertFalse($v->validate()); + } + + public function testLengthBetweenValid() + { + $v = new Validator(array('str' => 'happy')); + $v->rule('length', 'str', 2, 8); + $this->assertTrue($v->validate()); + } + + public function testLengthBetweenInvalid() + { + $v = new Validator(array('str' => 'sad')); + $v->length('str', 4, 10); + $this->assertFalse($v->validate()); + } + + public function testMinValid() + { + $v = new Validator(array('num' => 5)); + $v->rule('min', 'num', 2); + $this->assertTrue($v->validate()); + } + + public function testMinInvalid() + { + $v = new Validator(array('num' => 5)); + $v->min('num', 6); + $this->assertFalse($v->validate()); + } + + public function testMaxValid() + { + $v = new Validator(array('num' => 5)); + $v->rule('max', 'num', 6); + $this->assertTrue($v->validate()); + } + + public function testMaxInvalid() + { + $v = new Validator(array('num' => 5)); + $v->max('num', 4); + $this->assertFalse($v->validate()); + } + + public function testInValid() + { + $v = new Validator(array('color' => 'green')); + $v->rule('in', 'color', array('red', 'green', 'blue')); + $this->assertTrue($v->validate()); + } + + public function testInInvalid() + { + $v = new Validator(array('color' => 'yellow')); + $v->in('color', array('red', 'green', 'blue')); + $this->assertFalse($v->validate()); + } + + public function testNotInValid() + { + $v = new Validator(array('color' => 'yellow')); + $v->rule('notIn', 'color', array('red', 'green', 'blue')); + $this->assertTrue($v->validate()); + } + + public function testNotInInvalid() + { + $v = new Validator(array('color' => 'blue')); + $v->notIn('color', array('red', 'green', 'blue')); + $this->assertFalse($v->validate()); + } + + public function testIpValid() + { + $v = new Validator(array('ip' => '127.0.0.1')); + $v->rule('ip', 'ip'); + $this->assertTrue($v->validate()); + } + + public function testIpInvalid() + { + $v = new Validator(array('ip' => 'buy viagra now!')); + $v->ip('ip'); + $this->assertFalse($v->validate()); + } + public function testEmailValid() { $v = new Validator(array('name' => 'Chester Tester', 'email' => 'chester@tester.com')); @@ -47,7 +221,7 @@ class ValidateTest extends \PHPUnit_Framework_TestCase public function testUrlValid() { $v = new Validator(array('website' => 'http://google.com')); - $v->rule('url', 'website'); + $v->url('website'); $this->assertTrue($v->validate()); } @@ -57,5 +231,131 @@ class ValidateTest extends \PHPUnit_Framework_TestCase $v->rule('url', 'website'); $this->assertFalse($v->validate()); } + + public function testUrlActive() + { + $v = new Validator(array('website' => 'http://google.com')); + $v->urlActive('website'); + $this->assertTrue($v->validate()); + } + + public function testUrlInactive() + { + $v = new Validator(array('website' => 'http://sonotgoogleitsnotevenfunny.dev')); + $v->rule('urlActive', 'website'); + $this->assertFalse($v->validate()); + } + + public function testAlphaValid() + { + $v = new Validator(array('test' => 'abcDEF')); + $v->alpha('test'); + $this->assertTrue($v->validate()); + } + + public function testAlphaInvalid() + { + $v = new Validator(array('test' => 'abc123')); + $v->rule('alpha', 'test'); + $this->assertFalse($v->validate()); + } + + public function testAlphaNumValid() + { + $v = new Validator(array('test' => 'abc123')); + $v->alphaNum('test'); + $this->assertTrue($v->validate()); + } + + public function testAlphaNumInvalid() + { + $v = new Validator(array('test' => 'abc123$%^')); + $v->rule('alphaNum', 'test'); + $this->assertFalse($v->validate()); + } + + public function testAlphaDashValid() + { + $v = new Validator(array('test' => 'abc-123_DEF')); + $v->alphaDash('test'); + $this->assertTrue($v->validate()); + } + + public function testAlphaDashInvalid() + { + $v = new Validator(array('test' => 'abc-123_DEF $%^')); + $v->rule('alphaDash', 'test'); + $this->assertFalse($v->validate()); + } + + public function testRegexValid() + { + $v = new Validator(array('test' => '42')); + $v->regex('test', '/[\d]+/'); + $this->assertTrue($v->validate()); + } + + public function testRegexInvalid() + { + $v = new Validator(array('test' => 'istheanswer')); + $v->rule('regex', 'test', '/[\d]+/'); + $this->assertFalse($v->validate()); + } + + public function testDateValid() + { + $v = new Validator(array('date' => '2013-01-27')); + $v->date('date'); + $this->assertTrue($v->validate()); + } + + public function testDateInvalid() + { + $v = new Validator(array('date' => 'no thanks')); + $v->rule('date', 'date'); + $this->assertFalse($v->validate()); + } + + public function testDateFormatValid() + { + $v = new Validator(array('date' => '2013-01-27')); + $v->dateFormat('date', 'Y-m-d'); + $this->assertTrue($v->validate()); + } + + public function testDateFormatInvalid() + { + $v = new Validator(array('date' => 'no thanks')); + $v->rule('dateFormat', 'date', 'Y-m-d'); + $this->assertFalse($v->validate()); + } + + public function testDateBeforeValid() + { + $v = new Validator(array('date' => '2013-01-27')); + $v->dateBefore('date', new \DateTime('2013-01-28')); + $this->assertTrue($v->validate()); + } + + public function testDateBeforeInvalid() + { + $v = new Validator(array('date' => '2013-01-27')); + $v->rule('dateBefore', 'date', '2013-01-26'); + $this->assertFalse($v->validate()); + } + + public function testDateAfterValid() + { + $v = new Validator(array('date' => '2013-01-27')); + $v->dateAfter('date', new \DateTime('2013-01-26')); + $this->assertTrue($v->validate()); + } + + public function testDateAfterInvalid() + { + $v = new Validator(array('date' => '2013-01-27')); + $v->rule('dateAfter', 'date', '2013-01-28'); + $this->assertFalse($v->validate()); + } }