From 5eb3ccf05b97484f171069b5e4ee573e9f8df75e Mon Sep 17 00:00:00 2001 From: Chris Gutierrez Date: Sat, 2 Apr 2011 19:41:19 -0400 Subject: [PATCH 01/32] started adding some unit tests. renamed FormValidator to Validator as this class can be used for validation beyond forms. added support for validating whole arrays. @cballou added support for dot notation in get val to reference array values. awesome library --- FormValidator.php => Validator.php | 275 +++++++++++++++++++++-------- tests/FormValidatorTest.php | 85 +++++++++ tests/phpunit.xml | 5 + 3 files changed, 291 insertions(+), 74 deletions(-) rename FormValidator.php => Validator.php (73%) create mode 100644 tests/FormValidatorTest.php create mode 100644 tests/phpunit.xml diff --git a/FormValidator.php b/Validator.php similarity index 73% rename from FormValidator.php rename to Validator.php index 35eec05..745828f 100644 --- a/FormValidator.php +++ b/Validator.php @@ -6,15 +6,16 @@ * @author Tasos Bekos * @see Based on idea: http://brettic.us/2010/06/18/form-validation-class-using-php-5-3/ */ -class FormValidator { +class Validator { - private $messages = array(); - private $errors = array(); - private $rules = array(); - private $fields = array(); - private $functions = array(); - private $arguments = array(); - private $data = null; + protected $messages = array(); + protected $errors = array(); + protected $rules = array(); + protected $fields = array(); + protected $functions = array(); + protected $arguments = array(); + protected $filters = array(); + protected $data = null; /** * Constructor. @@ -23,9 +24,21 @@ class FormValidator { * @param array $data */ function __construct($data = null) { - $this->data = (is_null($data)) ? $_POST : $data; + if (!empty($data)) $this->setData($data); } + /** + * set the data to be validated + * + * @access public + * @param mixed $data + * @return FormValidator + */ + public function setData($data) { + $this->data = $data; + return $this; + } + // ----------------- ADD NEW RULE FUNCTIONS BELOW THIS LINE ---------------- /** @@ -35,9 +48,8 @@ function __construct($data = null) { * @return FormValidator */ public function email($message = null) { - $this->set_rule(__FUNCTION__, function($email) { - return (strlen($email) === 0 || filter_var($email, FILTER_VALIDATE_EMAIL) === TRUE) ? TRUE : FALSE; - }, $message); + $this->setRule(__FUNCTION__, function($email) { + return (strlen($email) === 0 || filter_var($email, FILTER_VALIDATE_EMAIL) !== FALSE); }, $message); return $this; } @@ -48,8 +60,8 @@ public function email($message = null) { * @return FormValidator */ public function required($message = null) { - $this->set_rule(__FUNCTION__, function($string) { - return (strlen(trim($string)) === 0) ? FALSE : TRUE; + $this->setRule(__FUNCTION__, function($string) { + return !(strlen(trim($string)) === 0); }, $message); return $this; } @@ -61,8 +73,8 @@ public function required($message = null) { * @return FormValidator */ public function float($message = null) { - $this->set_rule(__FUNCTION__, function($string) { - return (filter_var($string, FILTER_VALIDATE_FLOAT) === FALSE) ? FALSE : TRUE; + $this->setRule(__FUNCTION__, function($string) { + return !(filter_var($string, FILTER_VALIDATE_FLOAT) === FALSE); }, $message); return $this; } @@ -74,8 +86,8 @@ public function float($message = null) { * @return FormValidator */ public function integer($message = null) { - $this->set_rule(__FUNCTION__, function($string) { - return (filter_var($string, FILTER_VALIDATE_INT) === FALSE) ? FALSE : TRUE; + $this->setRule(__FUNCTION__, function($string) { + return !(filter_var($string, FILTER_VALIDATE_INT) === FALSE); }, $message); return $this; } @@ -88,7 +100,7 @@ public function integer($message = null) { * @return FormValidator */ public function digits($message = null) { - $this->set_rule(__FUNCTION__, function($value) { + $this->setRule(__FUNCTION__, function($value) { return (strlen($value) === 0 || ctype_digit((string) $value)); }, $message); return $this; @@ -103,7 +115,7 @@ public function digits($message = null) { * @return FormValidator */ public function min($limit, $include = TRUE, $message = null) { - $this->set_rule(__FUNCTION__, function($value, $args) { + $this->setRule(__FUNCTION__, function($value, $args) { if (strlen($value) === 0) { return TRUE; } @@ -126,7 +138,7 @@ public function min($limit, $include = TRUE, $message = null) { * @return FormValidator */ public function max($limit, $include = TRUE, $message = null) { - $this->set_rule(__FUNCTION__, function($value, $args) { + $this->setRule(__FUNCTION__, function($value, $args) { if (strlen($value) === 0) { return TRUE; } @@ -164,7 +176,7 @@ public function between($min, $max, $include = TRUE, $message = null) { * @return FormValidator */ public function minlength($len, $message = null) { - $this->set_rule(__FUNCTION__, function($string, $args) { + $this->setRule(__FUNCTION__, function($string, $args) { return (strlen(trim($string)) < $args[0]) ? FALSE : TRUE; }, $message, array($len)); return $this; @@ -178,7 +190,7 @@ public function minlength($len, $message = null) { * @return FormValidator */ public function maxlength($len, $message = null) { - $this->set_rule(__FUNCTION__, function($string, $args) { + $this->setRule(__FUNCTION__, function($string, $args) { return (strlen(trim($string)) > $args[0]) ? FALSE : TRUE; }, $message, array($len)); return $this; @@ -192,7 +204,7 @@ public function maxlength($len, $message = null) { * @return FormValidator */ public function length($len, $message = null) { - $this->set_rule(__FUNCTION__, function($string, $args) { + $this->setRule(__FUNCTION__, function($string, $args) { return (strlen(trim($string)) == $args[0]) ? TRUE : FALSE; }, $message, array($len)); return $this; @@ -207,9 +219,9 @@ public function length($len, $message = null) { * @return FormValidator */ public function matches($field, $label, $message = null) { - $this->set_rule(__FUNCTION__, function($string, $args) { + $this->setRule(__FUNCTION__, function($string, $args) { return ((string) $args[0] == (string) $string) ? TRUE : FALSE; - }, $message, array($this->getval($field), $label)); + }, $message, array($this->_getVal($field), $label)); return $this; } @@ -222,9 +234,9 @@ public function matches($field, $label, $message = null) { * @return FormValidator */ public function notmatches($field, $label, $message = null) { - $this->set_rule(__FUNCTION__, function($string, $args) { + $this->setRule(__FUNCTION__, function($string, $args) { return ((string) $args[0] == (string) $string) ? FALSE : TRUE; - }, $message, array($this->getval($field), $label)); + }, $message, array($this->_getVal($field), $label)); return $this; } @@ -236,7 +248,7 @@ public function notmatches($field, $label, $message = null) { * @return FormValidator */ public function startsWith($sub, $message = null) { - $this->set_rule(__FUNCTION__, function($string, $args) { + $this->setRule(__FUNCTION__, function($string, $args) { $sub = $args[0]; return (strlen($string) === 0 || substr($string, 0, strlen($sub)) === $sub); }, $message, array($sub)); @@ -251,7 +263,7 @@ public function startsWith($sub, $message = null) { * @return FormValidator */ public function notstartsWith($sub, $message = null) { - $this->set_rule(__FUNCTION__, function($string, $args) { + $this->setRule(__FUNCTION__, function($string, $args) { $sub = $args[0]; return (strlen($string) === 0 || substr($string, 0, strlen($sub)) !== $sub); }, $message, array($sub)); @@ -266,7 +278,7 @@ public function notstartsWith($sub, $message = null) { * @return FormValidator */ public function endsWith($sub, $message = null) { - $this->set_rule(__FUNCTION__, function($string, $args) { + $this->setRule(__FUNCTION__, function($string, $args) { $sub = $args[0]; return (strlen($string) === 0 || substr($string, -strlen($sub)) === $sub); }, $message, array($sub)); @@ -281,7 +293,7 @@ public function endsWith($sub, $message = null) { * @return FormValidator */ public function notendsWith($sub, $message = null) { - $this->set_rule(__FUNCTION__, function($string, $args) { + $this->setRule(__FUNCTION__, function($string, $args) { $sub = $args[0]; return (strlen($string) === 0 || substr($string, -strlen($sub)) !== $sub); }, $message, array($sub)); @@ -295,7 +307,7 @@ public function notendsWith($sub, $message = null) { * @return FormValidator */ public function ip($message = null) { - $this->set_rule(__FUNCTION__, function($string) { + $this->setRule(__FUNCTION__, function($string) { return (strlen(trim($string)) === 0 || filter_var($string, FILTER_VALIDATE_IP)) ? TRUE : FALSE; }, $message); return $this; @@ -308,7 +320,7 @@ public function ip($message = null) { * @return FormValidator */ public function url($message = null) { - $this->set_rule(__FUNCTION__, function($string) { + $this->setRule(__FUNCTION__, function($string) { return (strlen(trim($string)) === 0 || filter_var($string, FILTER_VALIDATE_URL)) ? TRUE : FALSE; }, $message); return $this; @@ -319,7 +331,7 @@ public function url($message = null) { * * @return string */ - private static function getDefaultDateFormat() { + protected static function getDefaultDateFormat() { return 'd/m/Y'; } @@ -334,7 +346,7 @@ public function date($format = null, $separator = null, $message = null) { $format = self::getDefaultDateFormat(); } - $this->set_rule(__FUNCTION__, function($string, $args) { + $this->setRule(__FUNCTION__, function($string, $args) { if (strlen(trim($string)) === 0) { return TRUE; } @@ -382,13 +394,13 @@ public function mindate($date = 0, $format = null, $message = null) { if (is_numeric($date)) { $date = new DateTime($date . ' days'); // Days difference from today } else { - $fieldValue = $this->getval($date); + $fieldValue = $this->_getVal($date); $date = ($fieldValue == FALSE) ? $date : $fieldValue; $date = DateTime::createFromFormat($format, $date); } - $this->set_rule(__FUNCTION__, function($string, $args) { + $this->setRule(__FUNCTION__, function($string, $args) { $format = $args[1]; $limitDate = $args[0]; @@ -412,13 +424,13 @@ public function maxdate($date = 0, $format = null, $message = null) { if (is_numeric($date)) { $date = new DateTime($date . ' days'); // Days difference from today } else { - $fieldValue = $this->getval($date); + $fieldValue = $this->_getVal($date); $date = ($fieldValue == FALSE) ? $date : $fieldValue; $date = DateTime::createFromFormat($format, $date); } - $this->set_rule(__FUNCTION__, function($string, $args) { + $this->setRule(__FUNCTION__, function($string, $args) { $format = $args[1]; $limitDate = $args[0]; @@ -435,7 +447,7 @@ public function maxdate($date = 0, $format = null, $message = null) { * @return FormValidator */ public function ccnum($message = null) { - $this->set_rule(__FUNCTION__, function($value) { + $this->setRule(__FUNCTION__, function($value) { $value = str_replace(' ', '', $value); $length = strlen($value); @@ -471,7 +483,7 @@ public function oneof($allowed, $message = null) { $allowed = explode(',', $allowed); } - $this->set_rule(__FUNCTION__, function($string, $args) { + $this->setRule(__FUNCTION__, function($string, $args) { return in_array($string, $args[0]); }, $message, array($allowed)); return $this; @@ -489,21 +501,67 @@ public function oneof($allowed, $message = null) { public function callback($name, $function, $message='') { if (is_callable($function)) { // set rule and function - $this->set_rule($name, $function, $message); + $this->setRule($name, $function, $message); } elseif (is_string($function) && preg_match($function, 'callback') !== FALSE) { // we can parse this as a regexp. set rule function accordingly. - $this->set_rule($name, function($value) use ($function) { + $this->setRule($name, function($value) use ($function) { return ( preg_match($function, $value) ) ? TRUE : FALSE; }, $message); } else { // just set a rule function to check equality. - $this->set_rule($name, function($value) use ( $function) { + $this->setRule($name, function($value) use ( $function) { return ( (string) $value === (string) $function ) ? TRUE : FALSE; }, $message); } return $this; } + // ------------------ PRE VALIDATION FILTERING ------------------- + /** + * add a filter callback for the data + * + * @param mixed $callback + * @return FormValidator + */ + public function filter($callback, $global = false) { + if(is_callable($callback)) { + $this->filters[] = $callback; + } + + return $this; + } + + /** + * applies filters based on a data key + * + * @access protected + * @param string $key + * @return void + */ + protected function _applyFilters($key) { + $this->_applyFilter($this->data[$key]); + } + + /** + * recursively apply filters to a value + * + * @access protected + * @param mixed $val reference + * @return void + */ + protected function _applyFilter(&$val) + { + if (is_array($val)) { + foreach($val as $key => &$item) { + $this->_applyFilter($item); + } + } else { + foreach($this->filters as $filter) { + $val = $filter($val); + } + } + } + /** * validate * @param string $key @@ -514,28 +572,61 @@ public function validate($key, $label = '') { // set up field name for error message $this->fields[$key] = (empty($label)) ? 'Field with the name of "' . $key . '"' : $label; - // Keep value for use in each rule - $string = $this->getval($key); - - // try each rule function - foreach ($this->rules as $rule => $is_true) { - if ($is_true) { - $function = $this->functions[$rule]; - $args = $this->arguments[$rule]; // Arguments of rule + // apply filters to the data + $this->_applyFilters($key); + + // validate the piece of data + $this->_validate($key, $this->_getVal($key)); - $valid = (empty($args)) ? $function($string) : $function($string, $args); - if ($valid === FALSE) { - $this->register_error($rule, $key); + // reset rules + $this->rules = array(); + $this->filters = array(); + return $val; + } - $this->rules = array(); // reset rules - return FALSE; + /** + * recursively validates a value + * + * @access protected + * @param string $key + * @param mixed $val + * @return bool + */ + protected function _validate($key, $val) + { + if (is_array($val)) { + + // run validations on each element of the array + foreach($val as $index => $item) { + if (!$this->_validate($key, $item)) { + // halt validation for this value. + return false; } } - } - - // reset rules - $this->rules = array(); - return $string; + return TRUE; + + } else { + + // try each rule function + foreach ($this->rules as $rule => $is_true) { + if ($is_true) { + $function = $this->functions[$rule]; + $args = $this->arguments[$rule]; // Arguments of rule + + $valid = (empty($args)) ? $function($val) : $function($val, $args); + + if ($valid === FALSE) { + $this->registerError($rule, $key); + + $this->rules = array(); // reset rules + $this->filters = array(); + return FALSE; + } + } + } + + return TRUE; + } } /** @@ -544,7 +635,7 @@ public function validate($key, $label = '') { * @return bool */ public function hasErrors() { - return (count($this->errors) > 0) ? TRUE : FALSE; + return (count($this->errors) > 0); } /** @@ -567,12 +658,50 @@ public function getAllErrors($keys = true) { } /** - * getval + * _getVal with added support for retrieving values from numeric and + * associative multi-dimensional arrays. When doing so, use DOT notation + * to indicate a break in keys, i.e.: + * + * key = "one.two.three" + * + * would search the array: + * + * array('one' => array( + * 'two' => array( + * 'three' => 'RETURN THIS' + * ) + * ); + * * @param string $key * @return mixed */ - private function getval($key) { - return (isset($this->data[$key])) ? $this->data[$key] : FALSE; + protected function _getVal($key) { + // handle multi-dimensional arrays + if (strpos($key, '.') !== FALSE) { + $arrData = NULL; + $keys = explode('.', $key); + $keyLen = count($keys); + for ($i = 0; $i < $keyLen; ++$i) { + if (trim($keys[$i]) == '') { + return false; + } else { + if (is_null($arrData)) { + if (!isset($this->data[$keys[$i]])) { + return false; + } + $arrData = $this->data[$keys[$i]]; + } else { + if (!isset($arrData[$keys[$i]])) { + return false; + } + $arrData = $arrData[$keys[$i]]; + } + } + } + return $arrData; + } else { + return (isset($this->data[$key])) ? $this->data[$key] : FALSE; + } } /** @@ -582,7 +711,7 @@ private function getval($key) { * @param string $key * @param string $message */ - private function register_error($rule, $key, $message = null) { + protected function registerError($rule, $key, $message = null) { if (empty($message)) { $message = $this->messages[$rule]; } @@ -598,7 +727,7 @@ private function register_error($rule, $key, $message = null) { * @param string $message * @param array $args */ - private function set_rule($rule, $function, $message = '', $args = array()) { + protected function setRule($rule, $function, $message = '', $args = array()) { if (!array_key_exists($rule, $this->rules)) { $this->rules[$rule] = TRUE; if (!array_key_exists($rule, $this->functions)) { @@ -620,7 +749,8 @@ private function set_rule($rule, $function, $message = '', $args = array()) { * @param array $args * @return string */ - private static function getDefaultMessage($rule, $args = null) { + protected static function getDefaultMessage($rule, $args = null) { + switch ($rule) { case 'email': $message = '%s is an invalid email address.'; @@ -737,7 +867,4 @@ private static function getDefaultMessage($rule, $args = null) { return $message; } -} -?> - - +} \ No newline at end of file diff --git a/tests/FormValidatorTest.php b/tests/FormValidatorTest.php new file mode 100644 index 0000000..a0db0a1 --- /dev/null +++ b/tests/FormValidatorTest.php @@ -0,0 +1,85 @@ +_validator = new Validator(); + } + + public function testFilterStringCallback() + { + $validator = $this->_validator; + + $data = array( + 'email' => ' test@emailwithwhitespace.com ', + ); + + $validator->setData($data); + $email = $validator->filter('trim')->validate('email'); + echo $email; exit; + $this->assertEquals(strlen($email), 28); + } + + /** + * test the FormValidator email rule + */ + public function testValidateEmail() + { + $validator = $this->_validator; + + $data = array( + 'email' => 'test@test.com', + ); + + $validator->email()->validate('email'); + + $this->assertFalse($validator->hasErrors()); + } + + /** + * test the FormValidator email rule on an array of emails + */ + public function testValidateEmailArray() + { + $validator = $this->_validator; + + $data = array( + 'emails' => array( + 'test@test.com', + 'test2@test.com', + 'test3@test.com' + ) + ); + + $validator->setData($data); + $validator->email('contains an invalid email address')->validate('emails'); + $this->assertFalse($validator->hasErrors()); + } + + /** + * test the Validator + */ + public function testInvalidEmailArray() + { + $validator = $this->_validator; + + $data = array( + 'emails' => array( + 'test@test.com', + 'test2@test.com', + 'testtest.com' + ) + ); + + $validator->setData($data) + ->email() + ->validate('emails'); + + $this->assertTrue($validator->hasErrors()); + } +} \ No newline at end of file diff --git a/tests/phpunit.xml b/tests/phpunit.xml new file mode 100644 index 0000000..6569ee3 --- /dev/null +++ b/tests/phpunit.xml @@ -0,0 +1,5 @@ + + + ./ + + \ No newline at end of file From 6544638b8f9b1ee06781aa484aec48483b3d2bf5 Mon Sep 17 00:00:00 2001 From: Chris Gutierrez Date: Sat, 2 Apr 2011 19:42:16 -0400 Subject: [PATCH 02/32] renamed test case --- tests/{FormValidatorTest.php => ValidatorTest.php} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/{FormValidatorTest.php => ValidatorTest.php} (97%) diff --git a/tests/FormValidatorTest.php b/tests/ValidatorTest.php similarity index 97% rename from tests/FormValidatorTest.php rename to tests/ValidatorTest.php index a0db0a1..331c94b 100644 --- a/tests/FormValidatorTest.php +++ b/tests/ValidatorTest.php @@ -1,7 +1,7 @@ Date: Sat, 2 Apr 2011 20:26:22 -0400 Subject: [PATCH 03/32] added more tests for individual rules --- Validator.php | 45 ++++---- tests/ValidatorTest.php | 224 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 243 insertions(+), 26 deletions(-) diff --git a/Validator.php b/Validator.php index 745828f..c5c5a61 100644 --- a/Validator.php +++ b/Validator.php @@ -23,7 +23,7 @@ class Validator { * * @param array $data */ - function __construct($data = null) { + function __construct(array $data = null) { if (!empty($data)) $this->setData($data); } @@ -34,7 +34,7 @@ function __construct($data = null) { * @param mixed $data * @return FormValidator */ - public function setData($data) { + public function setData(array $data) { $this->data = $data; return $this; } @@ -162,7 +162,7 @@ public function max($limit, $include = TRUE, $message = null) { * @return FormValidator */ public function between($min, $max, $include = TRUE, $message = null) { - $message = self::getDefaultMessage(__FUNCTION__, array($min, $max, $include)); + $message = $this->_getDefaultMessage(__FUNCTION__, array($min, $max, $include)); $this->min($min, $include, $message)->max($max, $include, $message); return $this; @@ -177,7 +177,7 @@ public function between($min, $max, $include = TRUE, $message = null) { */ public function minlength($len, $message = null) { $this->setRule(__FUNCTION__, function($string, $args) { - return (strlen(trim($string)) < $args[0]) ? FALSE : TRUE; + return !(strlen(trim($string)) < $args[0]); }, $message, array($len)); return $this; } @@ -191,7 +191,7 @@ public function minlength($len, $message = null) { */ public function maxlength($len, $message = null) { $this->setRule(__FUNCTION__, function($string, $args) { - return (strlen(trim($string)) > $args[0]) ? FALSE : TRUE; + return !(strlen(trim($string)) > $args[0]); }, $message, array($len)); return $this; } @@ -205,7 +205,7 @@ public function maxlength($len, $message = null) { */ public function length($len, $message = null) { $this->setRule(__FUNCTION__, function($string, $args) { - return (strlen(trim($string)) == $args[0]) ? TRUE : FALSE; + return (strlen(trim($string)) == $args[0]); }, $message, array($len)); return $this; } @@ -220,7 +220,7 @@ public function length($len, $message = null) { */ public function matches($field, $label, $message = null) { $this->setRule(__FUNCTION__, function($string, $args) { - return ((string) $args[0] == (string) $string) ? TRUE : FALSE; + return ((string) $args[0] == (string) $string); }, $message, array($this->_getVal($field), $label)); return $this; } @@ -235,7 +235,7 @@ public function matches($field, $label, $message = null) { */ public function notmatches($field, $label, $message = null) { $this->setRule(__FUNCTION__, function($string, $args) { - return ((string) $args[0] == (string) $string) ? FALSE : TRUE; + return !((string) $args[0] == (string) $string); }, $message, array($this->_getVal($field), $label)); return $this; } @@ -308,7 +308,7 @@ public function notendsWith($sub, $message = null) { */ public function ip($message = null) { $this->setRule(__FUNCTION__, function($string) { - return (strlen(trim($string)) === 0 || filter_var($string, FILTER_VALIDATE_IP)) ? TRUE : FALSE; + return (strlen(trim($string)) === 0 || filter_var($string, FILTER_VALIDATE_IP) !== FALSE); }, $message); return $this; } @@ -321,7 +321,7 @@ public function ip($message = null) { */ public function url($message = null) { $this->setRule(__FUNCTION__, function($string) { - return (strlen(trim($string)) === 0 || filter_var($string, FILTER_VALIDATE_URL)) ? TRUE : FALSE; + return (strlen(trim($string)) === 0 || filter_var($string, FILTER_VALIDATE_URL) !== FALSE); }, $message); return $this; } @@ -331,7 +331,7 @@ public function url($message = null) { * * @return string */ - protected static function getDefaultDateFormat() { + protected function _getDefaultDateFormat() { return 'd/m/Y'; } @@ -343,7 +343,7 @@ protected static function getDefaultDateFormat() { */ public function date($format = null, $separator = null, $message = null) { if (empty($format)) { - $format = self::getDefaultDateFormat(); + $format = $this->_getDefaultDateFormat(); } $this->setRule(__FUNCTION__, function($string, $args) { @@ -389,7 +389,7 @@ public function date($format = null, $separator = null, $message = null) { */ public function mindate($date = 0, $format = null, $message = null) { if (empty($format)) { - $format = self::getDefaultDateFormat(); + $format = $this->_getDefaultDateFormat(); } if (is_numeric($date)) { $date = new DateTime($date . ' days'); // Days difference from today @@ -419,7 +419,7 @@ public function mindate($date = 0, $format = null, $message = null) { */ public function maxdate($date = 0, $format = null, $message = null) { if (empty($format)) { - $format = self::getDefaultDateFormat(); + $format = $this->_getDefaultDateFormat(); } if (is_numeric($date)) { $date = new DateTime($date . ' days'); // Days difference from today @@ -434,7 +434,7 @@ public function maxdate($date = 0, $format = null, $message = null) { $format = $args[1]; $limitDate = $args[0]; - return ($limitDate < DateTime::createFromFormat($format, $string)) ? FALSE : TRUE; + return !($limitDate < DateTime::createFromFormat($format, $string)); }, $message, array($date, $format)); return $this; } @@ -523,7 +523,7 @@ public function callback($name, $function, $message='') { * @param mixed $callback * @return FormValidator */ - public function filter($callback, $global = false) { + public function filter($callback) { if(is_callable($callback)) { $this->filters[] = $callback; } @@ -549,8 +549,7 @@ protected function _applyFilters($key) { * @param mixed $val reference * @return void */ - protected function _applyFilter(&$val) - { + protected function _applyFilter(&$val) { if (is_array($val)) { foreach($val as $key => &$item) { $this->_applyFilter($item); @@ -574,9 +573,11 @@ public function validate($key, $label = '') { // apply filters to the data $this->_applyFilters($key); + + $val = $this->_getVal($key); // validate the piece of data - $this->_validate($key, $this->_getVal($key)); + $this->_validate($key, $val); // reset rules $this->rules = array(); @@ -727,7 +728,7 @@ protected function registerError($rule, $key, $message = null) { * @param string $message * @param array $args */ - protected function setRule($rule, $function, $message = '', $args = array()) { + public function setRule($rule, $function, $message = '', $args = array()) { if (!array_key_exists($rule, $this->rules)) { $this->rules[$rule] = TRUE; if (!array_key_exists($rule, $this->functions)) { @@ -738,7 +739,7 @@ protected function setRule($rule, $function, $message = '', $args = array()) { } $this->arguments[$rule] = $args; // Specific arguments for rule - $this->messages[$rule] = (empty($message)) ? self::getDefaultMessage($rule, $args) : $message; + $this->messages[$rule] = (empty($message)) ? $this->_getDefaultMessage($rule, $args) : $message; } } @@ -749,7 +750,7 @@ protected function setRule($rule, $function, $message = '', $args = array()) { * @param array $args * @return string */ - protected static function getDefaultMessage($rule, $args = null) { + protected function _getDefaultMessage($rule, $args = null) { switch ($rule) { case 'email': diff --git a/tests/ValidatorTest.php b/tests/ValidatorTest.php index 331c94b..560e532 100644 --- a/tests/ValidatorTest.php +++ b/tests/ValidatorTest.php @@ -10,8 +10,11 @@ public function setUp() parent::setUp(); $this->_validator = new Validator(); } - - public function testFilterStringCallback() + + /** + * test filter with string 'trim' as the callbacl + */ + public function testFilterTrimCallback() { $validator = $this->_validator; @@ -21,7 +24,7 @@ public function testFilterStringCallback() $validator->setData($data); $email = $validator->filter('trim')->validate('email'); - echo $email; exit; + $this->assertEquals(strlen($email), 28); } @@ -57,7 +60,7 @@ public function testValidateEmailArray() ); $validator->setData($data); - $validator->email('contains an invalid email address')->validate('emails'); + $validator->email()->validate('emails'); $this->assertFalse($validator->hasErrors()); } @@ -82,4 +85,217 @@ public function testInvalidEmailArray() $this->assertTrue($validator->hasErrors()); } + + /** + * test the required rule with value present + */ + public function testValidateRequired() + { + $validator = $this->_validator; + + $data = array( + 'name' => 'Test Name' + ); + + $validator->setData($data) + ->required() + ->validate('name'); + + $this->assertFalse($validator->hasErrors()); + } + + /** + * test the required rule without value present + */ + public function testValidateRequiredEmptyVal() + { + $validator = $this->_validator; + + $data = array( + 'name' => '' + ); + + $validator->setData($data) + ->required() + ->validate('name'); + + $this->assertTrue($validator->hasErrors()); + } + + public function testValidateRequiredWithArray() + { + $validator = $this->_validator; + + $data = array( + 'names' => array('Test Name', 'Another Name', 'And Another Name') + ); + + $validator->setData($data) + ->required() + ->validate('names'); + + $this->assertFalse($validator->hasErrors()); + } + + /** + * test the required rule without value present + */ + public function testValidateRequiredWithArrayEmptyElement() + { + $validator = $this->_validator; + + $data = array( + 'names' => array('Test Name', '', 'And Another Name') + ); + + $validator->setData($data) + ->required() + ->validate('names'); + + $this->assertTrue($validator->hasErrors()); + } + + /** + * test the float rule + */ + public function testValidateFloat() + { + $validator = $this->_validator; + + $data = array( + 'float' => 2.5 + ); + + $validator->setData($data) + ->float() + ->validate('float'); + + $this->assertFalse($validator->hasErrors()); + } + + /** + * test the float rule with invalid value + */ + public function testValidateFloatInvalidStringValue() + { + $validator = $this->_validator; + + $data = array( + 'float' => 'test' + ); + + $validator->setData($data) + ->float() + ->validate('float'); + + $this->assertTrue($validator->hasErrors()); + } + + /** + * test the integer rule + */ + public function testValidateInteger() + { + $validator = $this->_validator; + + $data = array( + 'integer' => 20 + ); + + $validator->setData($data) + ->integer() + ->validate('integer'); + + $this->assertFalse($validator->hasErrors()); + } + + /** + * test the integer rule with invalid value + */ + public function testValidateIntegerInvalidStringValue() + { + $validator = $this->_validator; + + $data = array( + 'integer' => 'test' + ); + + $validator->setData($data) + ->integer() + ->validate('integer'); + + $this->assertTrue($validator->hasErrors()); + } + + /** + * test the digits rule + */ + public function testValidateDigits() + { + $validator = $this->_validator; + + $data = array( + 'digits' => 20 + ); + + $validator->setData($data) + ->digits() + ->validate('digits'); + + $this->assertFalse($validator->hasErrors()); + } + + /** + * test the digits rule with invalid value + */ + public function testValidateDigitsInvalidStringValue() + { + $validator = $this->_validator; + + $data = array( + 'digits' => 'test' + ); + + $validator->setData($data) + ->digits() + ->validate('digits'); + + $this->assertTrue($validator->hasErrors()); + } + + /** + * test the min rule + */ + public function testValidateMin() + { + $validator = $this->_validator; + + $data = array( + 'min' => 35 + ); + + $validator->setData($data) + ->min(30) + ->validate('min'); + + $this->assertFalse($validator->hasErrors()); + } + + /** + * test the min rule with invalid value + */ + public function testValidateMinInvalidValue() + { + $validator = $this->_validator; + + $data = array( + 'min' => 5 + ); + + $validator->setData($data) + ->min(30) + ->validate('min'); + + $this->assertTrue($validator->hasErrors()); + } } \ No newline at end of file From eb62020d1b0d85c8b1dbe680c14c4c1fb444fe71 Mon Sep 17 00:00:00 2001 From: Chris Gutierrez Date: Sat, 2 Apr 2011 22:27:39 -0400 Subject: [PATCH 04/32] simplified the date rule and added tests for it --- Validator.php | 32 ++---- tests/ValidatorTest.php | 228 ++++++++++++++++++++++++++++++++++------ 2 files changed, 200 insertions(+), 60 deletions(-) diff --git a/Validator.php b/Validator.php index c5c5a61..6e5d2d7 100644 --- a/Validator.php +++ b/Validator.php @@ -235,7 +235,7 @@ public function matches($field, $label, $message = null) { */ public function notmatches($field, $label, $message = null) { $this->setRule(__FUNCTION__, function($string, $args) { - return !((string) $args[0] == (string) $string); + return ((string) $args[0] != (string) $string); }, $message, array($this->_getVal($field), $label)); return $this; } @@ -351,32 +351,14 @@ public function date($format = null, $separator = null, $message = null) { return TRUE; } - $separator = $args[1]; - $dt = (is_null($separator)) ? preg_split('/[-\.\/ ]/', $string) : explode($separator, $string); - - if ((count($dt) != 3) || !is_numeric($dt[2]) || !is_numeric($dt[1]) || !is_numeric($dt[0])) { + $date = @strtotime($string); + + if($date === FALSE) { return FALSE; } - - $dateToCheck = array(); - $format = explode('/', $args[0]); - foreach ($format as $i => $f) { - switch ($f) { - case 'Y': - $dateToCheck[2] = $dt[$i]; - break; - - case 'm': - $dateToCheck[1] = $dt[$i]; - break; - - case 'd': - $dateToCheck[0] = $dt[$i]; - break; - } - } - - return (checkdate($dateToCheck[1], $dateToCheck[0], $dateToCheck[2]) === FALSE) ? FALSE : TRUE; + + list($m, $d, $y) = explode("/", @date("m/d/Y")); + return checkdate($m, $d, $y); }, $message, array($format, $separator)); return $this; } diff --git a/tests/ValidatorTest.php b/tests/ValidatorTest.php index 560e532..65895ee 100644 --- a/tests/ValidatorTest.php +++ b/tests/ValidatorTest.php @@ -171,131 +171,289 @@ public function testValidateFloat() ->validate('float'); $this->assertFalse($validator->hasErrors()); + + $data['float'] = 'test'; + + $validator->setData($data) + ->float() + ->validate('float'); + + $this->assertTrue($validator->hasErrors()); } /** - * test the float rule with invalid value + * test the integer rule */ - public function testValidateFloatInvalidStringValue() + public function testValidateInteger() { $validator = $this->_validator; $data = array( - 'float' => 'test' + 'integer' => 20 ); $validator->setData($data) - ->float() - ->validate('float'); + ->integer() + ->validate('integer'); + + $this->assertFalse($validator->hasErrors()); + + $data['integer'] = 'test'; + + $validator->setData($data) + ->integer() + ->validate('integer'); $this->assertTrue($validator->hasErrors()); } /** - * test the integer rule + * test the digits rule */ - public function testValidateInteger() + public function testValidateDigits() { $validator = $this->_validator; $data = array( - 'integer' => 20 + 'digits' => 20 ); $validator->setData($data) - ->integer() - ->validate('integer'); + ->digits() + ->validate('digits'); $this->assertFalse($validator->hasErrors()); + + $data['digits'] = 'test'; + + $validator->setData($data) + ->digits() + ->validate('digits'); + + $this->assertTrue($validator->hasErrors()); } /** - * test the integer rule with invalid value + * test the min rule */ - public function testValidateIntegerInvalidStringValue() + public function testValidateMin() { $validator = $this->_validator; $data = array( - 'integer' => 'test' + 'min' => 35 ); $validator->setData($data) - ->integer() - ->validate('integer'); + ->min(30) + ->validate('min'); + + $this->assertFalse($validator->hasErrors()); + + $validator->setData($data) + ->min(40) + ->validate('min'); $this->assertTrue($validator->hasErrors()); } /** - * test the digits rule + * test the max rule */ - public function testValidateDigits() + public function testValidateMax() { $validator = $this->_validator; $data = array( - 'digits' => 20 + 'max' => 29 ); $validator->setData($data) - ->digits() - ->validate('digits'); + ->max(30) + ->validate('max'); $this->assertFalse($validator->hasErrors()); + + $validator->setData($data) + ->max(20) + ->validate('max'); + + $this->assertTrue($validator->hasErrors()); } /** - * test the digits rule with invalid value + * test the between rule */ - public function testValidateDigitsInvalidStringValue() + public function testValidateBetween() { $validator = $this->_validator; $data = array( - 'digits' => 'test' + 'between' => 35 ); $validator->setData($data) - ->digits() - ->validate('digits'); + ->between(30, 40) + ->validate('between'); + + $this->assertFalse($validator->hasErrors()); + $validator->setData($data) + ->between(40, 50) + ->validate('between'); + $this->assertTrue($validator->hasErrors()); } /** - * test the min rule + * test the minLength rule */ - public function testValidateMin() + public function testValidateMinLength() { $validator = $this->_validator; $data = array( - 'min' => 35 + 'minlength' => 'this is a string' ); $validator->setData($data) - ->min(30) - ->validate('min'); + ->minlength(10) + ->validate('minlength'); + + $this->assertFalse($validator->hasErrors()); + + $validator->setData($data) + ->minlength(60) + ->validate('minlength'); + + $this->assertTrue($validator->hasErrors()); + } + + /** + * test the minLength rule + */ + public function testValidateMaxLength() + { + $validator = $this->_validator; + + $data = array( + 'maxlength' => 'this is a string' + ); + + $validator->setData($data) + ->maxlength(20) + ->validate('maxlength'); $this->assertFalse($validator->hasErrors()); + + $validator->setData($data) + ->maxlength(5) + ->validate('maxlength'); + + $this->assertTrue($validator->hasErrors()); } /** - * test the min rule with invalid value + * test the minLength rule */ - public function testValidateMinInvalidValue() + public function testValidateLength() { $validator = $this->_validator; $data = array( - 'min' => 5 + 'length' => 'this is a string' ); $validator->setData($data) - ->min(30) - ->validate('min'); + ->length(16) + ->validate('length'); + + $this->assertFalse($validator->hasErrors()); + + $validator->setData($data) + ->length(5) + ->validate('length'); + + $this->assertTrue($validator->hasErrors()); + } + + /** + * test the matches rule + */ + public function testValidateMatches() + { + $validator = $this->_validator; + + $data = array( + 'password' => 'testpass', + 'password_confirm' => 'testpass' + ); + + $validator->setData($data) + ->matches('password_confirm', 'Password Confirmation') + ->validate('password'); + + $this->assertFalse($validator->hasErrors()); + $data['password_confirm'] = 'Oh Noes I forgot what I types!'; + + $validator->setData($data) + ->matches('password_confirmaton', 'Password Confirmation') + ->validate('password'); + + $this->assertTrue($validator->hasErrors()); + } + + /** + * test the notmatches rule + */ + public function testValidateNotMatches() + { + $validator = $this->_validator; + + $data = array( + 'password' => 'test', + 'password_confirm' => 'another test' + ); + + $validator->setData($data) + ->notmatches('password_confirm', 'Password Confirmation') + ->validate('password'); + + $this->assertFalse($validator->hasErrors()); + + $data['password_confirm'] = 'test'; + + $validator->setData($data) + ->notmatches('password_confirm', 'Password Confirmation') + ->validate('password'); + + $this->assertTrue($validator->hasErrors()); + } + + /** + * test the date rule + */ + public function testValidateDate() + { + $validator = $this->_validator; + + $data = array( + 'date' => '10/20/2010', + ); + + $validator->setData($data) + ->date('m/d/Y') + ->validate('date'); + + $this->assertFalse($validator->hasErrors()); + + $data['date'] = 'test'; + + $validator->setData($data) + ->date('m/d/Y') + ->validate('date'); + $this->assertTrue($validator->hasErrors()); } } \ No newline at end of file From fe7c3cc0aae12b32c3af03064c65a1f13160cb11 Mon Sep 17 00:00:00 2001 From: Chris Gutierrez Date: Sat, 2 Apr 2011 22:47:17 -0400 Subject: [PATCH 05/32] messing with the date validator --- Validator.php | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/Validator.php b/Validator.php index 6e5d2d7..d8e90f3 100644 --- a/Validator.php +++ b/Validator.php @@ -351,14 +351,32 @@ public function date($format = null, $separator = null, $message = null) { return TRUE; } - $date = @strtotime($string); - - if($date === FALSE) { + $separator = $args[1]; + $dt = (is_null($separator)) ? preg_split('/[-\.\/ ]/', $string) : explode($separator, $string); + + if ((count($dt) != 3) || !is_numeric($dt[2]) || !is_numeric($dt[1]) || !is_numeric($dt[0])) { return FALSE; } - - list($m, $d, $y) = explode("/", @date("m/d/Y")); - return checkdate($m, $d, $y); + + $dateToCheck = array(); + $format = explode('/', $args[0]); + foreach ($format as $i => $f) { + switch ($f) { + case 'Y': + $dateToCheck[2] = $dt[$i]; + break; + + case 'm': + $dateToCheck[1] = $dt[$i]; + break; + + case 'd': + $dateToCheck[0] = $dt[$i]; + break; + } + } + + return checkdate($dateToCheck[1], $dateToCheck[0], $dateToCheck[2]); }, $message, array($format, $separator)); return $this; } From eb333cdb9ecea5a6091d6e84b2bb0466ce35aaaa Mon Sep 17 00:00:00 2001 From: Chris Gutierrez Date: Thu, 7 Apr 2011 17:53:02 -0400 Subject: [PATCH 06/32] starting to update rules to accept array validation --- Validator.php | 102 +++++++++++++++++----------------------- tests/ValidatorTest.php | 21 +++++++++ 2 files changed, 65 insertions(+), 58 deletions(-) diff --git a/Validator.php b/Validator.php index d8e90f3..1f3ed5c 100644 --- a/Validator.php +++ b/Validator.php @@ -49,7 +49,8 @@ public function setData(array $data) { */ public function email($message = null) { $this->setRule(__FUNCTION__, function($email) { - return (strlen($email) === 0 || filter_var($email, FILTER_VALIDATE_EMAIL) !== FALSE); }, $message); + return (strlen($email) === 0 || filter_var($email, FILTER_VALIDATE_EMAIL) !== FALSE); + }, $message); return $this; } @@ -60,9 +61,12 @@ public function email($message = null) { * @return FormValidator */ public function required($message = null) { - $this->setRule(__FUNCTION__, function($string) { - return !(strlen(trim($string)) === 0); - }, $message); + $this->setRule(__FUNCTION__, function($val) { + if (!is_array($val)) { + $val = trim($val); + } + return !empty($val); + }, $message); return $this; } @@ -341,43 +345,21 @@ protected function _getDefaultDateFormat() { * @param string $message * @return FormValidator */ - public function date($format = null, $separator = null, $message = null) { - if (empty($format)) { - $format = $this->_getDefaultDateFormat(); - } - + public function date($message = null) { $this->setRule(__FUNCTION__, function($string, $args) { - if (strlen(trim($string)) === 0) { - return TRUE; - } - - $separator = $args[1]; - $dt = (is_null($separator)) ? preg_split('/[-\.\/ ]/', $string) : explode($separator, $string); - - if ((count($dt) != 3) || !is_numeric($dt[2]) || !is_numeric($dt[1]) || !is_numeric($dt[0])) { - return FALSE; - } - $dateToCheck = array(); - $format = explode('/', $args[0]); - foreach ($format as $i => $f) { - switch ($f) { - case 'Y': - $dateToCheck[2] = $dt[$i]; - break; - - case 'm': - $dateToCheck[1] = $dt[$i]; - break; - - case 'd': - $dateToCheck[0] = $dt[$i]; - break; - } - } + if (strlen(trim($string)) === 0) { + return TRUE; + } - return checkdate($dateToCheck[1], $dateToCheck[0], $dateToCheck[2]); - }, $message, array($format, $separator)); + try { + $dt = new DateTime($string, new DateTimeZone("UTC")); + return true; + } catch(Exception $e) { + return false; + } + + }, $message, array($format, $separator)); return $this; } @@ -448,26 +430,26 @@ public function maxdate($date = 0, $format = null, $message = null) { */ public function ccnum($message = null) { $this->setRule(__FUNCTION__, function($value) { - $value = str_replace(' ', '', $value); - $length = strlen($value); + $value = str_replace(' ', '', $value); + $length = strlen($value); - if ($length < 13 || $length > 19) { - return FALSE; - } + if ($length < 13 || $length > 19) { + return FALSE; + } - $sum = 0; - $weight = 2; + $sum = 0; + $weight = 2; - for ($i = $length - 2; $i >= 0; $i--) { - $digit = $weight * $value[$i]; - $sum += floor($digit / 10) + $digit % 10; - $weight = $weight % 2 + 1; - } + for ($i = $length - 2; $i >= 0; $i--) { + $digit = $weight * $value[$i]; + $sum += floor($digit / 10) + $digit % 10; + $weight = $weight % 2 + 1; + } - $mod = (10 - $sum % 10) % 10; + $mod = (10 - $sum % 10) % 10; - return ($mod == $value[$length - 1]); - }, $message); + return ($mod == $value[$length - 1]); + }, $message); return $this; } @@ -498,20 +480,24 @@ public function oneof($allowed, $message = null) { * @param string $message * @return FormValidator */ - public function callback($name, $function, $message='') { + public function callback($function, $message = '') { + + // generate a random name for the callback + $name = sha1(uniqid()); + if (is_callable($function)) { // set rule and function $this->setRule($name, $function, $message); } elseif (is_string($function) && preg_match($function, 'callback') !== FALSE) { // we can parse this as a regexp. set rule function accordingly. $this->setRule($name, function($value) use ($function) { - return ( preg_match($function, $value) ) ? TRUE : FALSE; - }, $message); + return ( preg_match($function, $value) ) ? TRUE : FALSE; + }, $message); } else { // just set a rule function to check equality. $this->setRule($name, function($value) use ( $function) { - return ( (string) $value === (string) $function ) ? TRUE : FALSE; - }, $message); + return ( (string) $value === (string) $function ) ? TRUE : FALSE; + }, $message); } return $this; } diff --git a/tests/ValidatorTest.php b/tests/ValidatorTest.php index 65895ee..bfe341f 100644 --- a/tests/ValidatorTest.php +++ b/tests/ValidatorTest.php @@ -456,4 +456,25 @@ public function testValidateDate() $this->assertTrue($validator->hasErrors()); } + + public function testValidateCallback() + { + $validator = $this->_validator; + + $data = array( + 'test' => 'test' + ); + + $validator + ->setData($data) + ->callback(function($val) { + return true; + }) + ->validate('test'); + } + + public function testValidateFiles() + { + + } } \ No newline at end of file From b490997aaaaa6d5599d40117130d07f602a78a4f Mon Sep 17 00:00:00 2001 From: Corey Ballou Date: Mon, 11 Apr 2011 11:04:46 -0400 Subject: [PATCH 07/32] Updated the callback validator to include handling for passing a mixed value parameter (i.e. string/array) to the callback function. --- Validator.php | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/Validator.php b/Validator.php index 1f3ed5c..ad0ed69 100644 --- a/Validator.php +++ b/Validator.php @@ -475,29 +475,31 @@ public function oneof($allowed, $message = null) { /** * callback - * @param string $name - * @param mixed $function - * @param string $message - * @return FormValidator + * @param string $name + * @param mixed $function + * @param string $message + * @param mixed $params + * @return FormValidator */ - public function callback($function, $message = '') { - - // generate a random name for the callback - $name = sha1(uniqid()); + public function callback($name, $function, $message = '', $params = NULL) { - if (is_callable($function)) { + if (is_array($function)) { + $this->_set_rule($name, function($value) use($function, $params) { + return call_user_func($function, $value, $params); + }, $message); + } elseif (is_callable($function)) { // set rule and function - $this->setRule($name, $function, $message); + $this->_set_rule($name, $function, $message); } elseif (is_string($function) && preg_match($function, 'callback') !== FALSE) { // we can parse this as a regexp. set rule function accordingly. - $this->setRule($name, function($value) use ($function) { - return ( preg_match($function, $value) ) ? TRUE : FALSE; - }, $message); + $this->_set_rule($name, function($value) use ($function) { + return ( preg_match($function, $value) ) ? TRUE : FALSE; + }, $message); } else { // just set a rule function to check equality. - $this->setRule($name, function($value) use ( $function) { - return ( (string) $value === (string) $function ) ? TRUE : FALSE; - }, $message); + $this->_set_rule($name, function($value) use ( $function) { + return ( (string) $value === (string) $function ) ? TRUE : FALSE; + }, $message); } return $this; } From 5ed76a60bf2133cfe5848a65b49100581dc5184e Mon Sep 17 00:00:00 2001 From: Chris Gutierrez Date: Wed, 13 Apr 2011 22:41:37 -0400 Subject: [PATCH 08/32] started working with optionally recursing through arrays --- Validator.php | 179 ++++++++++++++++++++-------------------- tests/ValidatorTest.php | 6 +- 2 files changed, 92 insertions(+), 93 deletions(-) diff --git a/Validator.php b/Validator.php index 1f3ed5c..39357e7 100644 --- a/Validator.php +++ b/Validator.php @@ -62,7 +62,7 @@ public function email($message = null) { */ public function required($message = null) { $this->setRule(__FUNCTION__, function($val) { - if (!is_array($val)) { + if (is_scalar($val)) { $val = trim($val); } return !empty($val); @@ -77,9 +77,9 @@ public function required($message = null) { * @return FormValidator */ public function float($message = null) { - $this->setRule(__FUNCTION__, function($string) { - return !(filter_var($string, FILTER_VALIDATE_FLOAT) === FALSE); - }, $message); + $this->setRule(__FUNCTION__, function($val) { + return !(filter_var($val, FILTER_VALIDATE_FLOAT) === FALSE); + }, $message); return $this; } @@ -90,9 +90,9 @@ public function float($message = null) { * @return FormValidator */ public function integer($message = null) { - $this->setRule(__FUNCTION__, function($string) { - return !(filter_var($string, FILTER_VALIDATE_INT) === FALSE); - }, $message); + $this->setRule(__FUNCTION__, function($val) { + return !(filter_var($val, FILTER_VALIDATE_INT) === FALSE); + }, $message); return $this; } @@ -104,9 +104,9 @@ public function integer($message = null) { * @return FormValidator */ public function digits($message = null) { - $this->setRule(__FUNCTION__, function($value) { - return (strlen($value) === 0 || ctype_digit((string) $value)); - }, $message); + $this->setRule(__FUNCTION__, function($val) { + return (strlen($val) === 0 || ctype_digit((string) $val)); + }, $message); return $this; } @@ -119,17 +119,17 @@ public function digits($message = null) { * @return FormValidator */ public function min($limit, $include = TRUE, $message = null) { - $this->setRule(__FUNCTION__, function($value, $args) { - if (strlen($value) === 0) { - return TRUE; - } + $this->setRule(__FUNCTION__, function($val, $args) { + if (strlen($val) === 0) { + return TRUE; + } - $value = (float) $value; - $limit = (float) $args[0]; - $inc = (bool) $args[1]; + $val = (float) $val; + $limit = (float) $args[0]; + $inc = (bool) $args[1]; - return ($value > $limit || ($inc === TRUE && $value === $limit)); - }, $message, array($limit, $include)); + return ($val > $limit || ($inc === TRUE && $value === $limit)); + }, $message, array($limit, $include)); return $this; } @@ -142,17 +142,17 @@ public function min($limit, $include = TRUE, $message = null) { * @return FormValidator */ public function max($limit, $include = TRUE, $message = null) { - $this->setRule(__FUNCTION__, function($value, $args) { - if (strlen($value) === 0) { - return TRUE; - } + $this->setRule(__FUNCTION__, function($val, $args) { + if (strlen($val) === 0) { + return TRUE; + } - $value = (float) $value; - $limit = (float) $args[0]; - $inc = (bool) $args[1]; + $val = (float) $val; + $limit = (float) $args[0]; + $inc = (bool) $args[1]; - return ($value < $limit || ($inc === TRUE && $value === $limit)); - }, $message, array($limit, $include)); + return ($val < $limit || ($inc === TRUE && $value === $limit)); + }, $message, array($limit, $include)); return $this; } @@ -180,9 +180,9 @@ public function between($min, $max, $include = TRUE, $message = null) { * @return FormValidator */ public function minlength($len, $message = null) { - $this->setRule(__FUNCTION__, function($string, $args) { - return !(strlen(trim($string)) < $args[0]); - }, $message, array($len)); + $this->setRule(__FUNCTION__, function($val, $args) { + return !(strlen(trim($val)) < $args[0]); + }, $message, array($len)); return $this; } @@ -194,9 +194,9 @@ public function minlength($len, $message = null) { * @return FormValidator */ public function maxlength($len, $message = null) { - $this->setRule(__FUNCTION__, function($string, $args) { - return !(strlen(trim($string)) > $args[0]); - }, $message, array($len)); + $this->setRule(__FUNCTION__, function($val, $args) { + return !(strlen(trim($val)) > $args[0]); + }, $message, array($len)); return $this; } @@ -208,9 +208,9 @@ public function maxlength($len, $message = null) { * @return FormValidator */ public function length($len, $message = null) { - $this->setRule(__FUNCTION__, function($string, $args) { - return (strlen(trim($string)) == $args[0]); - }, $message, array($len)); + $this->setRule(__FUNCTION__, function($val, $args) { + return (strlen(trim($val)) == $args[0]); + }, $message, array($len)); return $this; } @@ -223,9 +223,9 @@ public function length($len, $message = null) { * @return FormValidator */ public function matches($field, $label, $message = null) { - $this->setRule(__FUNCTION__, function($string, $args) { - return ((string) $args[0] == (string) $string); - }, $message, array($this->_getVal($field), $label)); + $this->setRule(__FUNCTION__, function($val, $args) { + return ((string) $args[0] == (string) $val); + }, $message, array($this->_getVal($field), $label)); return $this; } @@ -238,9 +238,9 @@ public function matches($field, $label, $message = null) { * @return FormValidator */ public function notmatches($field, $label, $message = null) { - $this->setRule(__FUNCTION__, function($string, $args) { - return ((string) $args[0] != (string) $string); - }, $message, array($this->_getVal($field), $label)); + $this->setRule(__FUNCTION__, function($val, $args) { + return ((string) $args[0] != (string) $val); + }, $message, array($this->_getVal($field), $label)); return $this; } @@ -252,10 +252,10 @@ public function notmatches($field, $label, $message = null) { * @return FormValidator */ public function startsWith($sub, $message = null) { - $this->setRule(__FUNCTION__, function($string, $args) { - $sub = $args[0]; - return (strlen($string) === 0 || substr($string, 0, strlen($sub)) === $sub); - }, $message, array($sub)); + $this->setRule(__FUNCTION__, function($val, $args) { + $sub = $args[0]; + return (strlen($val) === 0 || substr($val, 0, strlen($sub)) === $sub); + }, $message, array($sub)); return $this; } @@ -267,10 +267,10 @@ public function startsWith($sub, $message = null) { * @return FormValidator */ public function notstartsWith($sub, $message = null) { - $this->setRule(__FUNCTION__, function($string, $args) { - $sub = $args[0]; - return (strlen($string) === 0 || substr($string, 0, strlen($sub)) !== $sub); - }, $message, array($sub)); + $this->setRule(__FUNCTION__, function($val, $args) { + $sub = $args[0]; + return (strlen($val) === 0 || substr($val, 0, strlen($sub)) !== $sub); + }, $message, array($sub)); return $this; } @@ -282,10 +282,10 @@ public function notstartsWith($sub, $message = null) { * @return FormValidator */ public function endsWith($sub, $message = null) { - $this->setRule(__FUNCTION__, function($string, $args) { - $sub = $args[0]; - return (strlen($string) === 0 || substr($string, -strlen($sub)) === $sub); - }, $message, array($sub)); + $this->setRule(__FUNCTION__, function($val, $args) { + $sub = $args[0]; + return (strlen($val) === 0 || substr($val, -strlen($sub)) === $sub); + }, $message, array($sub)); return $this; } @@ -297,10 +297,10 @@ public function endsWith($sub, $message = null) { * @return FormValidator */ public function notendsWith($sub, $message = null) { - $this->setRule(__FUNCTION__, function($string, $args) { - $sub = $args[0]; - return (strlen($string) === 0 || substr($string, -strlen($sub)) !== $sub); - }, $message, array($sub)); + $this->setRule(__FUNCTION__, function($val, $args) { + $sub = $args[0]; + return (strlen($val) === 0 || substr($val, -strlen($sub)) !== $sub); + }, $message, array($sub)); return $this; } @@ -311,9 +311,9 @@ public function notendsWith($sub, $message = null) { * @return FormValidator */ public function ip($message = null) { - $this->setRule(__FUNCTION__, function($string) { - return (strlen(trim($string)) === 0 || filter_var($string, FILTER_VALIDATE_IP) !== FALSE); - }, $message); + $this->setRule(__FUNCTION__, function($val) { + return (strlen(trim($val)) === 0 || filter_var($val, FILTER_VALIDATE_IP) !== FALSE); + }, $message); return $this; } @@ -324,9 +324,9 @@ public function ip($message = null) { * @return FormValidator */ public function url($message = null) { - $this->setRule(__FUNCTION__, function($string) { - return (strlen(trim($string)) === 0 || filter_var($string, FILTER_VALIDATE_URL) !== FALSE); - }, $message); + $this->setRule(__FUNCTION__, function($val) { + return (strlen(trim($val)) === 0 || filter_var($val, FILTER_VALIDATE_URL) !== FALSE); + }, $message); return $this; } @@ -346,14 +346,14 @@ protected function _getDefaultDateFormat() { * @return FormValidator */ public function date($message = null) { - $this->setRule(__FUNCTION__, function($string, $args) { + $this->setRule(__FUNCTION__, function($val, $args) { - if (strlen(trim($string)) === 0) { + if (strlen(trim($val)) === 0) { return TRUE; } try { - $dt = new DateTime($string, new DateTimeZone("UTC")); + $dt = new DateTime($val, new DateTimeZone("UTC")); return true; } catch(Exception $e) { return false; @@ -382,12 +382,12 @@ public function mindate($date = 0, $format = null, $message = null) { $date = DateTime::createFromFormat($format, $date); } - $this->setRule(__FUNCTION__, function($string, $args) { - $format = $args[1]; - $limitDate = $args[0]; + $this->setRule(__FUNCTION__, function($val, $args) { + $format = $args[1]; + $limitDate = $args[0]; - return ($limitDate > DateTime::createFromFormat($format, $string)) ? FALSE : TRUE; - }, $message, array($date, $format)); + return ($limitDate > DateTime::createFromFormat($format, $val)) ? FALSE : TRUE; + }, $message, array($date, $format)); return $this; } @@ -412,12 +412,12 @@ public function maxdate($date = 0, $format = null, $message = null) { $date = DateTime::createFromFormat($format, $date); } - $this->setRule(__FUNCTION__, function($string, $args) { - $format = $args[1]; - $limitDate = $args[0]; + $this->setRule(__FUNCTION__, function($val, $args) { + $format = $args[1]; + $limitDate = $args[0]; - return !($limitDate < DateTime::createFromFormat($format, $string)); - }, $message, array($date, $format)); + return !($limitDate < DateTime::createFromFormat($format, $val)); + }, $message, array($date, $format)); return $this; } @@ -465,9 +465,9 @@ public function oneof($allowed, $message = null) { $allowed = explode(',', $allowed); } - $this->setRule(__FUNCTION__, function($string, $args) { - return in_array($string, $args[0]); - }, $message, array($allowed)); + $this->setRule(__FUNCTION__, function($val, $args) { + return in_array($val, $args[0]); + }, $message, array($allowed)); return $this; } @@ -553,7 +553,7 @@ protected function _applyFilter(&$val) { * @param string $label * @return bool */ - public function validate($key, $label = '') { + public function validate($key, $recursive = false, $label = '') { // set up field name for error message $this->fields[$key] = (empty($label)) ? 'Field with the name of "' . $key . '"' : $label; @@ -561,9 +561,9 @@ public function validate($key, $label = '') { $this->_applyFilters($key); $val = $this->_getVal($key); - + // validate the piece of data - $this->_validate($key, $val); + $this->_validate($key, $val, $recursive); // reset rules $this->rules = array(); @@ -579,15 +579,14 @@ public function validate($key, $label = '') { * @param mixed $val * @return bool */ - protected function _validate($key, $val) - { - if (is_array($val)) { - + protected function _validate($key, $val, $recursive = false) + { + if ($recursive && is_array($val)) { // run validations on each element of the array foreach($val as $index => $item) { - if (!$this->_validate($key, $item)) { + if (!$this->_validate($key, $item, $recursive)) { // halt validation for this value. - return false; + return FALSE; } } return TRUE; diff --git a/tests/ValidatorTest.php b/tests/ValidatorTest.php index bfe341f..89e5d70 100644 --- a/tests/ValidatorTest.php +++ b/tests/ValidatorTest.php @@ -60,7 +60,7 @@ public function testValidateEmailArray() ); $validator->setData($data); - $validator->email()->validate('emails'); + $validator->email()->validate('emails', true); $this->assertFalse($validator->hasErrors()); } @@ -81,7 +81,7 @@ public function testInvalidEmailArray() $validator->setData($data) ->email() - ->validate('emails'); + ->validate('emails', true); $this->assertTrue($validator->hasErrors()); } @@ -150,7 +150,7 @@ public function testValidateRequiredWithArrayEmptyElement() $validator->setData($data) ->required() - ->validate('names'); + ->validate('names', true); $this->assertTrue($validator->hasErrors()); } From 9919922669df5f141ad35a45dd6e8313f8b9f706 Mon Sep 17 00:00:00 2001 From: Chris Gutierrez Date: Wed, 13 Apr 2011 23:47:27 -0400 Subject: [PATCH 09/32] changed callback to use reflection to allow for extra parameters --- Validator.php | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/Validator.php b/Validator.php index 5654718..3016c2e 100644 --- a/Validator.php +++ b/Validator.php @@ -481,26 +481,29 @@ public function oneof($allowed, $message = null) { * @param mixed $params * @return FormValidator */ - public function callback($name, $function, $message = '', $params = NULL) { + public function callback($callback, $message = '', $params = NULL) { - if (is_array($function)) { - $this->_set_rule($name, function($value) use($function, $params) { - return call_user_func($function, $value, $params); - }, $message); - } elseif (is_callable($function)) { - // set rule and function - $this->_set_rule($name, $function, $message); - } elseif (is_string($function) && preg_match($function, 'callback') !== FALSE) { - // we can parse this as a regexp. set rule function accordingly. - $this->_set_rule($name, function($value) use ($function) { - return ( preg_match($function, $value) ) ? TRUE : FALSE; - }, $message); + if (is_callable($callback)) { + + if (is_array($callback)) { + $func = new ReflectionMethod($callback[0], $callback[1]); + } else if (is_string($callback)) { + $func = new ReflectionFunction($callback); + } + + if (!empty($func)) { + // needs a unique name to avoild collisions in the rules array + $name = 'callback_' . sha1(uniqid()); + $this->_setRule($name, function($value) use ($func, $params, $callback) { + return is_array($callback) ? + $func->invokeArgs($callback[0], (array) $params) : $func->invokeArgs($callback); + }); + } + } else { - // just set a rule function to check equality. - $this->_set_rule($name, function($value) use ( $function) { - return ( (string) $value === (string) $function ) ? TRUE : FALSE; - }, $message); + throw new Exception(sprintf('%s is not callable.', $function)); } + return $this; } From 6fac0e38b4f41e54fced3bb1c89db21d616fecd6 Mon Sep 17 00:00:00 2001 From: Corey Ballou Date: Sat, 16 Apr 2011 08:11:57 -0400 Subject: [PATCH 10/32] Updated readme. --- README.txt | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 3 deletions(-) diff --git a/README.txt b/README.txt index 0fc9d20..01c9402 100644 --- a/README.txt +++ b/README.txt @@ -1,5 +1,87 @@ -PHP Class for Form Validation. +# A PHP 5.3 Class for Easy Form Validation -Tasos Bekos +## Available Pre-Validation Filtering -Based on idea of "Bretticus". See: http://brettic.us/2010/06/18/form-validation-class-using-php-5-3/ \ No newline at end of file +You can apply pre-validation filters to your data (i.e. trim, strip_tags, htmlentities). These filters can also +be custom defined so long as they pass an is_callable() check. + +* filter($callback) + +## Filter Examples + + +// standard php filter for valid user ids. +$validator + ->filter('intval') + ->min(1) + ->validate('user_id'); + +// custom filter +$validator + ->filter(function($val) { + // bogus formatting of the field + $val = rtrim($val, '/'); + $val .= '_custom_formatted'; + }) + ->validate('field_to_be_formatted'); + + +## Available Validation Methods + +* required($message = null) - The field value is required. +* email($message = null) - The field value must be a valid email address string. +* float($message = null) - The field value must be a float. +* integer($message = null) - The field value must be an integer. +* digits($message = null) - The field value must be a digit (integer with no upper bounds). +* min($limit, $include = TRUE, $message = null) - The field value must be greater than $limit (numeric). $include defines if the value can be equal to the limit. +* max($limit, $include = TRUE, $message = null) - The field value must be less than $limit (numeric). $include defines if the value can be equal to the limit. +* between($min, $max, $include = TRUE, $message = null) - The field value must be between $min and $max (numeric). $include defines if the value can be equal to $min and $max. +* minLength($length, $message = null) - The field value must be greater than or equal to $length characters. +* maxLength($length, $message = null) - The field value must be less than or equal to $length characters. +* length($length, $message = null) - The field must be $length characters long. +* matches($field, $label, $message = null) - One field matches another one (i.e. password matching) +* notMatches($field, $label, $message = null) - The field value must not match the value of $field. +* startsWith($sub, $message = null) - The field must start with $sub as a string. +* notStartsWith($sub, $message = null) - The field must not start with $sub as a string. +* endsWith($sub, $message = null) - THe field must end with $sub as a string. +* notEndsWith($sub, $message = null) - The field must not end with $sub as a string. +* ip($message = null) - The field value is a valid IP, determined using filter_var. +* url($message = null) - The field value is a valid URL, determined using filter_var. +* date($message = null) - The field value is a valid date, can be of any format accepted by DateTime() +* minDate($date, $format, $message = null) - The date must be greater than $date. $format must be of a format on the page http://php.net/manual/en/datetime.createfromformat.php +* maxDate($date, $format, $message = null) - The date must be less than $date. $format must be of a format on the page http://php.net/manual/en/datetime.createfromformat.php +* ccnum($message = null) - The field value must be a valid credit card number. +* oneOf($allowed, $message = null) - The field value must be one of the $allowed values. $allowed can be either an array or a comma-separated list of values. If comma separated, do not include spaces unless intended for matching. +* callback($callback, $message = '', $params = null) - Define your own custom callback validation function. $callback must pass an is_callable() check. $params can be any value, or an array if multiple parameters must be passed. + +## Validating Arrays and Array Indices + +This validation class has been extended to allow for validation of arrays as well as nested indices of a multi-dimensional array. + +### Validating Specific Indices + +To validate specific indices of an array, use dot notation, i.e. + + +// load the validator +$validator = new Blackbelt_Validator($_POST); + +// ensure $_POST['field']['nested'] exists +$validator + ->required('The nested field is required.') + ->validate('field.nested'); + +// ensure we have the first two numeric indices of $_POST['links'][] +$validator + ->required('This field is required') + ->validate('links.0'); +$validator + ->required('This field is required') + ->validate('links.1'); + + +## Credits + +* Modifications by Corey Ballou and Chris Gutierrez. +* Forked from Tasos Bekos which was based on the initial work of "Bretticus". +* See http://brettic.us/2010/06/18/form-validation-class-using-php-5-3/ for the original. From 2b7ed4e3afd59d509236b4f982a0979c96e726a5 Mon Sep 17 00:00:00 2001 From: Corey Ballou Date: Sat, 16 Apr 2011 08:12:51 -0400 Subject: [PATCH 11/32] Moving readme to markdown syntax. --- README.md | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..01c9402 --- /dev/null +++ b/README.md @@ -0,0 +1,87 @@ +# A PHP 5.3 Class for Easy Form Validation + +## Available Pre-Validation Filtering + +You can apply pre-validation filters to your data (i.e. trim, strip_tags, htmlentities). These filters can also +be custom defined so long as they pass an is_callable() check. + +* filter($callback) + +## Filter Examples + + +// standard php filter for valid user ids. +$validator + ->filter('intval') + ->min(1) + ->validate('user_id'); + +// custom filter +$validator + ->filter(function($val) { + // bogus formatting of the field + $val = rtrim($val, '/'); + $val .= '_custom_formatted'; + }) + ->validate('field_to_be_formatted'); + + +## Available Validation Methods + +* required($message = null) - The field value is required. +* email($message = null) - The field value must be a valid email address string. +* float($message = null) - The field value must be a float. +* integer($message = null) - The field value must be an integer. +* digits($message = null) - The field value must be a digit (integer with no upper bounds). +* min($limit, $include = TRUE, $message = null) - The field value must be greater than $limit (numeric). $include defines if the value can be equal to the limit. +* max($limit, $include = TRUE, $message = null) - The field value must be less than $limit (numeric). $include defines if the value can be equal to the limit. +* between($min, $max, $include = TRUE, $message = null) - The field value must be between $min and $max (numeric). $include defines if the value can be equal to $min and $max. +* minLength($length, $message = null) - The field value must be greater than or equal to $length characters. +* maxLength($length, $message = null) - The field value must be less than or equal to $length characters. +* length($length, $message = null) - The field must be $length characters long. +* matches($field, $label, $message = null) - One field matches another one (i.e. password matching) +* notMatches($field, $label, $message = null) - The field value must not match the value of $field. +* startsWith($sub, $message = null) - The field must start with $sub as a string. +* notStartsWith($sub, $message = null) - The field must not start with $sub as a string. +* endsWith($sub, $message = null) - THe field must end with $sub as a string. +* notEndsWith($sub, $message = null) - The field must not end with $sub as a string. +* ip($message = null) - The field value is a valid IP, determined using filter_var. +* url($message = null) - The field value is a valid URL, determined using filter_var. +* date($message = null) - The field value is a valid date, can be of any format accepted by DateTime() +* minDate($date, $format, $message = null) - The date must be greater than $date. $format must be of a format on the page http://php.net/manual/en/datetime.createfromformat.php +* maxDate($date, $format, $message = null) - The date must be less than $date. $format must be of a format on the page http://php.net/manual/en/datetime.createfromformat.php +* ccnum($message = null) - The field value must be a valid credit card number. +* oneOf($allowed, $message = null) - The field value must be one of the $allowed values. $allowed can be either an array or a comma-separated list of values. If comma separated, do not include spaces unless intended for matching. +* callback($callback, $message = '', $params = null) - Define your own custom callback validation function. $callback must pass an is_callable() check. $params can be any value, or an array if multiple parameters must be passed. + +## Validating Arrays and Array Indices + +This validation class has been extended to allow for validation of arrays as well as nested indices of a multi-dimensional array. + +### Validating Specific Indices + +To validate specific indices of an array, use dot notation, i.e. + + +// load the validator +$validator = new Blackbelt_Validator($_POST); + +// ensure $_POST['field']['nested'] exists +$validator + ->required('The nested field is required.') + ->validate('field.nested'); + +// ensure we have the first two numeric indices of $_POST['links'][] +$validator + ->required('This field is required') + ->validate('links.0'); +$validator + ->required('This field is required') + ->validate('links.1'); + + +## Credits + +* Modifications by Corey Ballou and Chris Gutierrez. +* Forked from Tasos Bekos which was based on the initial work of "Bretticus". +* See http://brettic.us/2010/06/18/form-validation-class-using-php-5-3/ for the original. From 9d17013175aaf77389489e5619e65eae855db08f Mon Sep 17 00:00:00 2001 From: Corey Ballou Date: Sat, 16 Apr 2011 08:13:38 -0400 Subject: [PATCH 12/32] Fixing headings of readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 01c9402..0ec1581 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ be custom defined so long as they pass an is_callable() check. * filter($callback) -## Filter Examples +### Filter Examples // standard php filter for valid user ids. From e6635dda1f33206a27c428e1de92b3cbf696f730 Mon Sep 17 00:00:00 2001 From: Corey Ballou Date: Sat, 16 Apr 2011 08:16:35 -0400 Subject: [PATCH 13/32] Code blocks. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0ec1581..3f2d9ef 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ be custom defined so long as they pass an is_callable() check. ### Filter Examples - +` // standard php filter for valid user ids. $validator ->filter('intval') @@ -24,7 +24,7 @@ $validator $val .= '_custom_formatted'; }) ->validate('field_to_be_formatted'); - +` ## Available Validation Methods @@ -62,7 +62,7 @@ This validation class has been extended to allow for validation of arrays as wel To validate specific indices of an array, use dot notation, i.e. - +` // load the validator $validator = new Blackbelt_Validator($_POST); @@ -78,7 +78,7 @@ $validator $validator ->required('This field is required') ->validate('links.1'); - +` ## Credits From 9fd3a54dfb935bdb532def3e081a73632c86e705 Mon Sep 17 00:00:00 2001 From: Corey Ballou Date: Sat, 16 Apr 2011 08:18:12 -0400 Subject: [PATCH 14/32] Code blocks. --- README.md | 61 ++++++++++++++++++++++++++----------------------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 3f2d9ef..f11aea7 100644 --- a/README.md +++ b/README.md @@ -9,22 +9,20 @@ be custom defined so long as they pass an is_callable() check. ### Filter Examples -` -// standard php filter for valid user ids. -$validator - ->filter('intval') - ->min(1) - ->validate('user_id'); - -// custom filter -$validator - ->filter(function($val) { - // bogus formatting of the field - $val = rtrim($val, '/'); - $val .= '_custom_formatted'; - }) - ->validate('field_to_be_formatted'); -` + // standard php filter for valid user ids. + $validator + ->filter('intval') + ->min(1) + ->validate('user_id'); + + // custom filter + $validator + ->filter(function($val) { + // bogus formatting of the field + $val = rtrim($val, '/'); + $val .= '_custom_formatted'; + }) + ->validate('field_to_be_formatted'); ## Available Validation Methods @@ -62,23 +60,22 @@ This validation class has been extended to allow for validation of arrays as wel To validate specific indices of an array, use dot notation, i.e. -` -// load the validator -$validator = new Blackbelt_Validator($_POST); - -// ensure $_POST['field']['nested'] exists -$validator - ->required('The nested field is required.') - ->validate('field.nested'); + // load the validator + $validator = new Blackbelt_Validator($_POST); + + // ensure $_POST['field']['nested'] exists + $validator + ->required('The nested field is required.') + ->validate('field.nested'); + + // ensure we have the first two numeric indices of $_POST['links'][] + $validator + ->required('This field is required') + ->validate('links.0'); + $validator + ->required('This field is required') + ->validate('links.1'); -// ensure we have the first two numeric indices of $_POST['links'][] -$validator - ->required('This field is required') - ->validate('links.0'); -$validator - ->required('This field is required') - ->validate('links.1'); -` ## Credits From af5824f2c1615c7aa401ca8adfe608567abbf037 Mon Sep 17 00:00:00 2001 From: Corey Ballou Date: Sat, 16 Apr 2011 08:22:20 -0400 Subject: [PATCH 15/32] Added a quick note to the top. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f11aea7..3353eff 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # A PHP 5.3 Class for Easy Form Validation +This class follows Zend Framework naming conventions for easy drop-in as a substitute to Zend_Validation. If you opt out of using the bulky Zend_Form on your projects, you might choose to use this for quick and painless form validation. + ## Available Pre-Validation Filtering You can apply pre-validation filters to your data (i.e. trim, strip_tags, htmlentities). These filters can also From a89e4aa745147c0a7cdea53a5209e28c853407e2 Mon Sep 17 00:00:00 2001 From: Corey Ballou Date: Sat, 16 Apr 2011 08:35:30 -0400 Subject: [PATCH 16/32] Added the custom exception handler. --- Validator/Exception.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 Validator/Exception.php diff --git a/Validator/Exception.php b/Validator/Exception.php new file mode 100644 index 0000000..6f8e158 --- /dev/null +++ b/Validator/Exception.php @@ -0,0 +1,14 @@ +_errors = $errors; + } + + public function getErrors() { + return $this->_errors; + } +} From c5c780cff95fa89354ea7573cc2415cc31bf38f8 Mon Sep 17 00:00:00 2001 From: Corey Ballou Date: Sat, 16 Apr 2011 08:35:40 -0400 Subject: [PATCH 17/32] Included a full example. --- README.md | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/README.md b/README.md index 3353eff..76e6aae 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,67 @@ To validate specific indices of an array, use dot notation, i.e. ->required('This field is required') ->validate('links.1'); +## A Full Example + +The example below shows how to throw validation exceptions with the custom +exception. You can then retrieve the error messages from the calling method. + + class Example { + + /** + * Your controller action that handles validation errors, as you would + * want these errors passed on to the view. + */ + public function indexAction() + { + try { + + // validate the data + $validData = $this->exampleValidate($_POST); + + // validation passed because no exception was thrown + // ... to something with the $validData ... + + } catch (Validator_Exception $e) { + // retrieve the overall error message to display + $message = $e->getMessage(); + + // retrieve all of the errors + $errors = $e->getErrors(); + + // the below code is specific to ZF + $this->_helper->FlashMessenger(array('error' => $message)); + $this->_helper->layout->getView()->errors = $errors; + } + } + + /** + * Your user-defined validation handling. The exception section is + * very important and should always be used. + */ + public function exampleValidate($post) + { + $validator = new Validator($post); + $validator + ->required('You must supply a name.') + ->validate('name', 'Name'); + $validator + ->required('You must supply an email address.') + ->email('You must supply a valid email address') + ->validate('email', 'Email'); + + // check for errors + if ($validator->hasErrors()) { + throw new Validator_Exception( + 'There were errors in your form.', + $validator->getAllErrors() + ); + } + + return $validator->getValidData(); + } + + } ## Credits From 60cdcbfb2a881f24ba9287ccdbbdc65b6bc8d72b Mon Sep 17 00:00:00 2001 From: Corey Ballou Date: Sat, 16 Apr 2011 08:39:08 -0400 Subject: [PATCH 18/32] Adding a few notes to the example. --- README.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 76e6aae..cd72e8c 100644 --- a/README.md +++ b/README.md @@ -82,19 +82,24 @@ To validate specific indices of an array, use dot notation, i.e. The example below shows how to throw validation exceptions with the custom exception. You can then retrieve the error messages from the calling method. +It is not good practice to validate your data in your controller, this should +be handled in your Model. This is just a quick example. - class Example { + class ExampleController extends Zend_Controller_Action { /** * Your controller action that handles validation errors, as you would * want these errors passed on to the view. + * + * @access public + * @return void */ public function indexAction() { try { // validate the data - $validData = $this->exampleValidate($_POST); + $validData = $this->_validate($_POST); // validation passed because no exception was thrown // ... to something with the $validData ... @@ -115,8 +120,12 @@ exception. You can then retrieve the error messages from the calling method. /** * Your user-defined validation handling. The exception section is * very important and should always be used. + * + * @access private + * @param array $post + * @return mixed */ - public function exampleValidate($post) + private function _validate(array $post = array()) { $validator = new Validator($post); $validator From 3a6b15b2aed7193eebe9d13e4c9510f0adb791ed Mon Sep 17 00:00:00 2001 From: Corey Ballou Date: Sat, 16 Apr 2011 08:41:01 -0400 Subject: [PATCH 19/32] Moved readme sections around. --- README.md | 154 +++++++++++++++++++++++++++--------------------------- 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index cd72e8c..aba75ec 100644 --- a/README.md +++ b/README.md @@ -2,83 +2,7 @@ This class follows Zend Framework naming conventions for easy drop-in as a substitute to Zend_Validation. If you opt out of using the bulky Zend_Form on your projects, you might choose to use this for quick and painless form validation. -## Available Pre-Validation Filtering - -You can apply pre-validation filters to your data (i.e. trim, strip_tags, htmlentities). These filters can also -be custom defined so long as they pass an is_callable() check. - -* filter($callback) - -### Filter Examples - - // standard php filter for valid user ids. - $validator - ->filter('intval') - ->min(1) - ->validate('user_id'); - - // custom filter - $validator - ->filter(function($val) { - // bogus formatting of the field - $val = rtrim($val, '/'); - $val .= '_custom_formatted'; - }) - ->validate('field_to_be_formatted'); - -## Available Validation Methods - -* required($message = null) - The field value is required. -* email($message = null) - The field value must be a valid email address string. -* float($message = null) - The field value must be a float. -* integer($message = null) - The field value must be an integer. -* digits($message = null) - The field value must be a digit (integer with no upper bounds). -* min($limit, $include = TRUE, $message = null) - The field value must be greater than $limit (numeric). $include defines if the value can be equal to the limit. -* max($limit, $include = TRUE, $message = null) - The field value must be less than $limit (numeric). $include defines if the value can be equal to the limit. -* between($min, $max, $include = TRUE, $message = null) - The field value must be between $min and $max (numeric). $include defines if the value can be equal to $min and $max. -* minLength($length, $message = null) - The field value must be greater than or equal to $length characters. -* maxLength($length, $message = null) - The field value must be less than or equal to $length characters. -* length($length, $message = null) - The field must be $length characters long. -* matches($field, $label, $message = null) - One field matches another one (i.e. password matching) -* notMatches($field, $label, $message = null) - The field value must not match the value of $field. -* startsWith($sub, $message = null) - The field must start with $sub as a string. -* notStartsWith($sub, $message = null) - The field must not start with $sub as a string. -* endsWith($sub, $message = null) - THe field must end with $sub as a string. -* notEndsWith($sub, $message = null) - The field must not end with $sub as a string. -* ip($message = null) - The field value is a valid IP, determined using filter_var. -* url($message = null) - The field value is a valid URL, determined using filter_var. -* date($message = null) - The field value is a valid date, can be of any format accepted by DateTime() -* minDate($date, $format, $message = null) - The date must be greater than $date. $format must be of a format on the page http://php.net/manual/en/datetime.createfromformat.php -* maxDate($date, $format, $message = null) - The date must be less than $date. $format must be of a format on the page http://php.net/manual/en/datetime.createfromformat.php -* ccnum($message = null) - The field value must be a valid credit card number. -* oneOf($allowed, $message = null) - The field value must be one of the $allowed values. $allowed can be either an array or a comma-separated list of values. If comma separated, do not include spaces unless intended for matching. -* callback($callback, $message = '', $params = null) - Define your own custom callback validation function. $callback must pass an is_callable() check. $params can be any value, or an array if multiple parameters must be passed. - -## Validating Arrays and Array Indices - -This validation class has been extended to allow for validation of arrays as well as nested indices of a multi-dimensional array. - -### Validating Specific Indices - -To validate specific indices of an array, use dot notation, i.e. - - // load the validator - $validator = new Blackbelt_Validator($_POST); - - // ensure $_POST['field']['nested'] exists - $validator - ->required('The nested field is required.') - ->validate('field.nested'); - - // ensure we have the first two numeric indices of $_POST['links'][] - $validator - ->required('This field is required') - ->validate('links.0'); - $validator - ->required('This field is required') - ->validate('links.1'); - -## A Full Example +## A Quick Example The example below shows how to throw validation exceptions with the custom exception. You can then retrieve the error messages from the calling method. @@ -149,6 +73,82 @@ be handled in your Model. This is just a quick example. } +## Available Validation Methods + +* required($message = null) - The field value is required. +* email($message = null) - The field value must be a valid email address string. +* float($message = null) - The field value must be a float. +* integer($message = null) - The field value must be an integer. +* digits($message = null) - The field value must be a digit (integer with no upper bounds). +* min($limit, $include = TRUE, $message = null) - The field value must be greater than $limit (numeric). $include defines if the value can be equal to the limit. +* max($limit, $include = TRUE, $message = null) - The field value must be less than $limit (numeric). $include defines if the value can be equal to the limit. +* between($min, $max, $include = TRUE, $message = null) - The field value must be between $min and $max (numeric). $include defines if the value can be equal to $min and $max. +* minLength($length, $message = null) - The field value must be greater than or equal to $length characters. +* maxLength($length, $message = null) - The field value must be less than or equal to $length characters. +* length($length, $message = null) - The field must be $length characters long. +* matches($field, $label, $message = null) - One field matches another one (i.e. password matching) +* notMatches($field, $label, $message = null) - The field value must not match the value of $field. +* startsWith($sub, $message = null) - The field must start with $sub as a string. +* notStartsWith($sub, $message = null) - The field must not start with $sub as a string. +* endsWith($sub, $message = null) - THe field must end with $sub as a string. +* notEndsWith($sub, $message = null) - The field must not end with $sub as a string. +* ip($message = null) - The field value is a valid IP, determined using filter_var. +* url($message = null) - The field value is a valid URL, determined using filter_var. +* date($message = null) - The field value is a valid date, can be of any format accepted by DateTime() +* minDate($date, $format, $message = null) - The date must be greater than $date. $format must be of a format on the page http://php.net/manual/en/datetime.createfromformat.php +* maxDate($date, $format, $message = null) - The date must be less than $date. $format must be of a format on the page http://php.net/manual/en/datetime.createfromformat.php +* ccnum($message = null) - The field value must be a valid credit card number. +* oneOf($allowed, $message = null) - The field value must be one of the $allowed values. $allowed can be either an array or a comma-separated list of values. If comma separated, do not include spaces unless intended for matching. +* callback($callback, $message = '', $params = null) - Define your own custom callback validation function. $callback must pass an is_callable() check. $params can be any value, or an array if multiple parameters must be passed. + +## Validating Arrays and Array Indices + +This validation class has been extended to allow for validation of arrays as well as nested indices of a multi-dimensional array. + +### Validating Specific Indices + +To validate specific indices of an array, use dot notation, i.e. + + // load the validator + $validator = new Validator($_POST); + + // ensure $_POST['field']['nested'] exists + $validator + ->required('The nested field is required.') + ->validate('field.nested'); + + // ensure we have the first two numeric indices of $_POST['links'][] + $validator + ->required('This field is required') + ->validate('links.0'); + $validator + ->required('This field is required') + ->validate('links.1'); + +## Available Pre-Validation Filtering + +You can apply pre-validation filters to your data (i.e. trim, strip_tags, htmlentities). These filters can also +be custom defined so long as they pass an is_callable() check. + +* filter($callback) + +### Filter Examples + + // standard php filter for valid user ids. + $validator + ->filter('intval') + ->min(1) + ->validate('user_id'); + + // custom filter + $validator + ->filter(function($val) { + // bogus formatting of the field + $val = rtrim($val, '/'); + $val .= '_custom_formatted'; + }) + ->validate('field_to_be_formatted'); + ## Credits * Modifications by Corey Ballou and Chris Gutierrez. From eb337f5b7edb1b8ca6513f25cc4d05f57827d39f Mon Sep 17 00:00:00 2001 From: Corey Ballou Date: Sat, 16 Apr 2011 08:41:33 -0400 Subject: [PATCH 20/32] Fixed headers. --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index aba75ec..6cc7ad2 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This class follows Zend Framework naming conventions for easy drop-in as a substitute to Zend_Validation. If you opt out of using the bulky Zend_Form on your projects, you might choose to use this for quick and painless form validation. -## A Quick Example +# A Quick Example The example below shows how to throw validation exceptions with the custom exception. You can then retrieve the error messages from the calling method. @@ -73,7 +73,7 @@ be handled in your Model. This is just a quick example. } -## Available Validation Methods +# Available Validation Methods * required($message = null) - The field value is required. * email($message = null) - The field value must be a valid email address string. @@ -101,7 +101,7 @@ be handled in your Model. This is just a quick example. * oneOf($allowed, $message = null) - The field value must be one of the $allowed values. $allowed can be either an array or a comma-separated list of values. If comma separated, do not include spaces unless intended for matching. * callback($callback, $message = '', $params = null) - Define your own custom callback validation function. $callback must pass an is_callable() check. $params can be any value, or an array if multiple parameters must be passed. -## Validating Arrays and Array Indices +# Validating Arrays and Array Indices This validation class has been extended to allow for validation of arrays as well as nested indices of a multi-dimensional array. @@ -125,7 +125,7 @@ To validate specific indices of an array, use dot notation, i.e. ->required('This field is required') ->validate('links.1'); -## Available Pre-Validation Filtering +# Available Pre-Validation Filtering You can apply pre-validation filters to your data (i.e. trim, strip_tags, htmlentities). These filters can also be custom defined so long as they pass an is_callable() check. @@ -149,7 +149,7 @@ be custom defined so long as they pass an is_callable() check. }) ->validate('field_to_be_formatted'); -## Credits +# Credits * Modifications by Corey Ballou and Chris Gutierrez. * Forked from Tasos Bekos which was based on the initial work of "Bretticus". From dcf2a9b16f08f7925451a1a0769744d16806d17a Mon Sep 17 00:00:00 2001 From: Chris Gutierrez Date: Thu, 28 Apr 2011 21:16:24 -0400 Subject: [PATCH 21/32] small undefined variable fix --- Validator.php | 66 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/Validator.php b/Validator.php index 3016c2e..e058c8d 100644 --- a/Validator.php +++ b/Validator.php @@ -4,6 +4,9 @@ * Form validation library. * * @author Tasos Bekos + * @author Chris Gutierrez + * @author Corey Ballou + * @see https://github.com/blackbelt/php-validation * @see Based on idea: http://brettic.us/2010/06/18/form-validation-class-using-php-5-3/ */ class Validator { @@ -16,6 +19,7 @@ class Validator { protected $arguments = array(); protected $filters = array(); protected $data = null; + protected $validData = array(); /** * Constructor. @@ -38,7 +42,7 @@ public function setData(array $data) { $this->data = $data; return $this; } - + // ----------------- ADD NEW RULE FUNCTIONS BELOW THIS LINE ---------------- /** @@ -128,7 +132,7 @@ public function min($limit, $include = TRUE, $message = null) { $limit = (float) $args[0]; $inc = (bool) $args[1]; - return ($val > $limit || ($inc === TRUE && $value === $limit)); + return ($val > $limit || ($inc === TRUE && $val === $limit)); }, $message, array($limit, $include)); return $this; } @@ -151,7 +155,7 @@ public function max($limit, $include = TRUE, $message = null) { $limit = (float) $args[0]; $inc = (bool) $args[1]; - return ($val < $limit || ($inc === TRUE && $value === $limit)); + return ($val < $limit || ($inc === TRUE && $val === $limit)); }, $message, array($limit, $include)); return $this; } @@ -358,7 +362,7 @@ public function date($message = null) { } catch(Exception $e) { return false; } - + }, $message, array($format, $separator)); return $this; } @@ -366,10 +370,12 @@ public function date($message = null) { /** * Field has to be a date later than or equal to X. * - * @param string $message + * @param string|int $date Limit date + * @param string $format Date format + * @param string $message * @return FormValidator */ - public function mindate($date = 0, $format = null, $message = null) { + public function minDate($date = 0, $format = null, $message = null) { if (empty($format)) { $format = $this->_getDefaultDateFormat(); } @@ -399,7 +405,7 @@ public function mindate($date = 0, $format = null, $message = null) { * @param string $message * @return FormValidator */ - public function maxdate($date = 0, $format = null, $message = null) { + public function maxDate($date = 0, $format = null, $message = null) { if (empty($format)) { $format = $this->_getDefaultDateFormat(); } @@ -460,7 +466,7 @@ public function ccnum($message = null) { * @param string $message * @return FormValidator */ - public function oneof($allowed, $message = null) { + public function oneOf($allowed, $message = null) { if (is_string($allowed)) { $allowed = explode(',', $allowed); } @@ -478,32 +484,32 @@ public function oneof($allowed, $message = null) { * @param string $name * @param mixed $function * @param string $message - * @param mixed $params + * @param mixed $params * @return FormValidator */ public function callback($callback, $message = '', $params = NULL) { - + if (is_callable($callback)) { - + if (is_array($callback)) { $func = new ReflectionMethod($callback[0], $callback[1]); } else if (is_string($callback)) { $func = new ReflectionFunction($callback); } - + if (!empty($func)) { // needs a unique name to avoild collisions in the rules array $name = 'callback_' . sha1(uniqid()); $this->_setRule($name, function($value) use ($func, $params, $callback) { - return is_array($callback) ? + return is_array($callback) ? $func->invokeArgs($callback[0], (array) $params) : $func->invokeArgs($callback); }); } - + } else { throw new Exception(sprintf('%s is not callable.', $function)); } - + return $this; } @@ -528,7 +534,7 @@ public function filter($callback) { * @access protected * @param string $key * @return void - */ + */ protected function _applyFilters($key) { $this->_applyFilter($this->data[$key]); } @@ -548,7 +554,7 @@ protected function _applyFilter(&$val) { } else { foreach($this->filters as $filter) { $val = $filter($val); - } + } } } @@ -585,27 +591,27 @@ public function validate($key, $recursive = false, $label = '') { * @return bool */ protected function _validate($key, $val, $recursive = false) - { + { if ($recursive && is_array($val)) { // run validations on each element of the array foreach($val as $index => $item) { if (!$this->_validate($key, $item, $recursive)) { - // halt validation for this value. + // halt validation for this value. return FALSE; } } return TRUE; - + } else { - + // try each rule function foreach ($this->rules as $rule => $is_true) { if ($is_true) { $function = $this->functions[$rule]; $args = $this->arguments[$rule]; // Arguments of rule - + $valid = (empty($args)) ? $function($val) : $function($val, $args); - + if ($valid === FALSE) { $this->registerError($rule, $key); @@ -615,9 +621,10 @@ protected function _validate($key, $val, $recursive = false) } } } - + + $this->validData[$key] = $val; return TRUE; - } + } } /** @@ -648,6 +655,11 @@ public function getAllErrors($keys = true) { return ($keys == true) ? $this->errors : array_values($this->errors); } + public function getValidData() + { + return $this->validData; + } + /** * _getVal with added support for retrieving values from numeric and * associative multi-dimensional arrays. When doing so, use DOT notation @@ -662,7 +674,7 @@ public function getAllErrors($keys = true) { * 'three' => 'RETURN THIS' * ) * ); - * + * * @param string $key * @return mixed */ @@ -741,7 +753,7 @@ public function setRule($rule, $function, $message = '', $args = array()) { * @return string */ protected function _getDefaultMessage($rule, $args = null) { - + switch ($rule) { case 'email': $message = '%s is an invalid email address.'; From ba97f7d1d18d594293edf728470b64c69655b84c Mon Sep 17 00:00:00 2001 From: Chris Gutierrez Date: Sun, 22 May 2011 18:32:28 -0400 Subject: [PATCH 22/32] posting updates from @cballou --- Validator.php | 67 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/Validator.php b/Validator.php index e058c8d..e199ad6 100644 --- a/Validator.php +++ b/Validator.php @@ -53,7 +53,46 @@ public function setData(array $data) { */ public function email($message = null) { $this->setRule(__FUNCTION__, function($email) { - return (strlen($email) === 0 || filter_var($email, FILTER_VALIDATE_EMAIL) !== FALSE); + if (strlen($email) == 0) return true; + $isValid = true; + $atIndex = strrpos($email, '@'); + if (is_bool($atIndex) && !$atIndex) { + $isValid = false; + } else { + $domain = substr($email, $atIndex+1); + $local = substr($email, 0, $atIndex); + $localLen = strlen($local); + $domainLen = strlen($domain); + if ($localLen < 1 || $localLen > 64) { + $isValid = false; + } else if ($domainLen < 1 || $domainLen > 255) { + // domain part length exceeded + $isValid = false; + } else if ($local[0] == '.' || $local[$localLen-1] == '.') { + // local part starts or ends with '.' + $isValid = false; + } else if (preg_match('/\\.\\./', $local)) { + // local part has two consecutive dots + $isValid = false; + } else if (!preg_match('/^[A-Za-z0-9\\-\\.]+$/', $domain)) { + // character not valid in domain part + $isValid = false; + } else if (preg_match('/\\.\\./', $domain)) { + // domain part has two consecutive dots + $isValid = false; + } else if (!preg_match('/^(\\\\.|[A-Za-z0-9!#%&`_=\\/$\'*+?^{}|~.-])+$/', str_replace("\\\\","",$local))) { + // character not valid in local part unless + // local part is quoted + if (!preg_match('/^"(\\\\"|[^"])+"$/', str_replace("\\\\","",$local))) { + $isValid = false; + } + } + // check DNS + if ($isValid && !(checkdnsrr($domain,"MX") || checkdnsrr($domain,"A"))) { + $isValid = false; + } + } + return $isValid; }, $message); return $this; } @@ -204,6 +243,20 @@ public function maxlength($len, $message = null) { return $this; } + /** + * Field has to be between minlength and maxlength characters long. + * + * @param int $minlength + * @param int $maxlength + * @ + */ + public function betweenlength($minlength, $maxlength, $message = null) { + $message = empty($message) ? self::getDefaultMessage(__FUNCTION__, array($minlength, $maxlength)) : NULL; + + $this->minlength($minlength, $message)->max($maxlength, $message); + return $this; + } + /** * Field has to be X characters long. * @@ -500,9 +553,17 @@ public function callback($callback, $message = '', $params = NULL) { if (!empty($func)) { // needs a unique name to avoild collisions in the rules array $name = 'callback_' . sha1(uniqid()); - $this->_setRule($name, function($value) use ($func, $params, $callback) { + $this->setRule($name, function($value) use ($func, $params, $callback) { + if (is_array($callback)) { + return $func->invoke($callback[0], $value, (array) $params); + } else { + return $func->invoke($callback, $value); + } + + /* return is_array($callback) ? - $func->invokeArgs($callback[0], (array) $params) : $func->invokeArgs($callback); + $func->invokeArgs($callback[0], (array) $params) : $func->invokeArgs($callback); + */ }); } From 79f60c64225d207fdfa60801976a5f19041ff932 Mon Sep 17 00:00:00 2001 From: rfruchtm Date: Fri, 10 Feb 2012 15:57:31 -0700 Subject: [PATCH 23/32] Fixed syntax error in declaration of Validator_Exception class. --- Validator/Exception.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Validator/Exception.php b/Validator/Exception.php index 6f8e158..17020a2 100644 --- a/Validator/Exception.php +++ b/Validator/Exception.php @@ -1,5 +1,5 @@ Date: Fri, 10 Feb 2012 16:00:44 -0700 Subject: [PATCH 24/32] Fixed Validator_Exception constructor to call the Exception constructor with the right arguments. --- Validator/Exception.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Validator/Exception.php b/Validator/Exception.php index 17020a2..5b001d5 100644 --- a/Validator/Exception.php +++ b/Validator/Exception.php @@ -4,7 +4,7 @@ class Validator_Exception extends Exception protected $_errors = array(); public function __construct($message, array $errors = array()) { - parent::__construct($message, $errors); + parent::__construct($message); $this->_errors = $errors; } From 2655d4d2a097c332f14d0442db72eb0a4c90a122 Mon Sep 17 00:00:00 2001 From: rfruchtm Date: Fri, 10 Feb 2012 16:21:49 -0700 Subject: [PATCH 25/32] Overhauled callback function. It will now call setRule and the input callback function with the correct arguments in both cases. --- Validator.php | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/Validator.php b/Validator.php index e199ad6..75580fd 100644 --- a/Validator.php +++ b/Validator.php @@ -541,30 +541,28 @@ public function oneOf($allowed, $message = null) { * @return FormValidator */ public function callback($callback, $message = '', $params = NULL) { - if (is_callable($callback)) { - - if (is_array($callback)) { - $func = new ReflectionMethod($callback[0], $callback[1]); - } else if (is_string($callback)) { - $func = new ReflectionFunction($callback); - } + + // If an array is callable, it is a method + if (is_array($callback)) { + $func = new ReflectionMethod($callback[0], $callback[1]); + } else { + $func = new ReflectionFunction($callback); + } if (!empty($func)) { // needs a unique name to avoild collisions in the rules array $name = 'callback_' . sha1(uniqid()); $this->setRule($name, function($value) use ($func, $params, $callback) { + // Creates merged arguments array with validation target as first argument + $args = array_merge(array($value), (is_array($params) ? $params : array($params))); if (is_array($callback)) { - return $func->invoke($callback[0], $value, (array) $params); + // If callback is a method, the object must be the first argument + return $func->invokeArgs($callback[0], $args); } else { - return $func->invoke($callback, $value); + return $func->invokeArgs($args); } - - /* - return is_array($callback) ? - $func->invokeArgs($callback[0], (array) $params) : $func->invokeArgs($callback); - */ - }); + }, $message, $params); } } else { From 6c39c3f57b34a91bca5996061d873007b8ef2712 Mon Sep 17 00:00:00 2001 From: rfruchtm Date: Fri, 10 Feb 2012 16:27:27 -0700 Subject: [PATCH 26/32] Adjusted formatting in Validator. --- Validator.php | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Validator.php b/Validator.php index 75580fd..cb9b5ee 100644 --- a/Validator.php +++ b/Validator.php @@ -19,7 +19,7 @@ class Validator { protected $arguments = array(); protected $filters = array(); protected $data = null; - protected $validData = array(); + protected $validData = array(); /** * Constructor. @@ -543,25 +543,25 @@ public function oneOf($allowed, $message = null) { public function callback($callback, $message = '', $params = NULL) { if (is_callable($callback)) { - // If an array is callable, it is a method - if (is_array($callback)) { - $func = new ReflectionMethod($callback[0], $callback[1]); - } else { - $func = new ReflectionFunction($callback); - } + // If an array is callable, it is a method + if (is_array($callback)) { + $func = new ReflectionMethod($callback[0], $callback[1]); + } else { + $func = new ReflectionFunction($callback); + } if (!empty($func)) { // needs a unique name to avoild collisions in the rules array $name = 'callback_' . sha1(uniqid()); $this->setRule($name, function($value) use ($func, $params, $callback) { - // Creates merged arguments array with validation target as first argument - $args = array_merge(array($value), (is_array($params) ? $params : array($params))); - if (is_array($callback)) { - // If callback is a method, the object must be the first argument - return $func->invokeArgs($callback[0], $args); - } else { - return $func->invokeArgs($args); - } + // Creates merged arguments array with validation target as first argument + $args = array_merge(array($value), (is_array($params) ? $params : array($params))); + if (is_array($callback)) { + // If callback is a method, the object must be the first argument + return $func->invokeArgs($callback[0], $args); + } else { + return $func->invokeArgs($args); + } }, $message, $params); } @@ -929,4 +929,4 @@ protected function _getDefaultMessage($rule, $args = null) { return $message; } -} \ No newline at end of file +} From b9dfb0ed3815e737fec68728798bb11ad7ba9187 Mon Sep 17 00:00:00 2001 From: rfruchtm Date: Fri, 10 Feb 2012 16:33:19 -0700 Subject: [PATCH 27/32] Fixed formatting again in Validator. I need to work on tabs vs spaces. Also, fixed minor mistake in readme. --- README.md | 3 ++- README.txt | 3 ++- Validator.php | 12 ++++++------ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 6cc7ad2..a904b61 100644 --- a/README.md +++ b/README.md @@ -146,11 +146,12 @@ be custom defined so long as they pass an is_callable() check. // bogus formatting of the field $val = rtrim($val, '/'); $val .= '_custom_formatted'; + return $val; }) ->validate('field_to_be_formatted'); # Credits -* Modifications by Corey Ballou and Chris Gutierrez. +* Modifications by Corey Ballou, Chris Gutierrez, and Robert Fruchtman. * Forked from Tasos Bekos which was based on the initial work of "Bretticus". * See http://brettic.us/2010/06/18/form-validation-class-using-php-5-3/ for the original. diff --git a/README.txt b/README.txt index 01c9402..7d399f0 100644 --- a/README.txt +++ b/README.txt @@ -22,6 +22,7 @@ $validator // bogus formatting of the field $val = rtrim($val, '/'); $val .= '_custom_formatted'; + return $val; }) ->validate('field_to_be_formatted'); @@ -82,6 +83,6 @@ $validator ## Credits -* Modifications by Corey Ballou and Chris Gutierrez. +* Modifications by Corey Ballou, Chris Gutierrez, and Robert Fruchtman. * Forked from Tasos Bekos which was based on the initial work of "Bretticus". * See http://brettic.us/2010/06/18/form-validation-class-using-php-5-3/ for the original. diff --git a/Validator.php b/Validator.php index cb9b5ee..0ac1946 100644 --- a/Validator.php +++ b/Validator.php @@ -556,12 +556,12 @@ public function callback($callback, $message = '', $params = NULL) { $this->setRule($name, function($value) use ($func, $params, $callback) { // Creates merged arguments array with validation target as first argument $args = array_merge(array($value), (is_array($params) ? $params : array($params))); - if (is_array($callback)) { - // If callback is a method, the object must be the first argument - return $func->invokeArgs($callback[0], $args); - } else { - return $func->invokeArgs($args); - } + if (is_array($callback)) { + // If callback is a method, the object must be the first argument + return $func->invokeArgs($callback[0], $args); + } else { + return $func->invokeArgs($args); + } }, $message, $params); } From f4c01eb1fa5d4bf86948b9e478a8f69696155360 Mon Sep 17 00:00:00 2001 From: rfruchtm Date: Fri, 10 Feb 2012 17:53:45 -0700 Subject: [PATCH 28/32] Fixed issue where callback functions would receive an extra argument. Also tweaked documentation and added a section on example callbacks. --- README.md | 24 ++++++++++++++++++++---- README.txt | 18 ++++++++++++++++++ Validator.php | 2 +- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a904b61..070e281 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This class follows Zend Framework naming conventions for easy drop-in as a substitute to Zend_Validation. If you opt out of using the bulky Zend_Form on your projects, you might choose to use this for quick and painless form validation. -# A Quick Example +## A Quick Example The example below shows how to throw validation exceptions with the custom exception. You can then retrieve the error messages from the calling method. @@ -73,7 +73,7 @@ be handled in your Model. This is just a quick example. } -# Available Validation Methods +## Available Validation Methods * required($message = null) - The field value is required. * email($message = null) - The field value must be a valid email address string. @@ -99,9 +99,25 @@ be handled in your Model. This is just a quick example. * maxDate($date, $format, $message = null) - The date must be less than $date. $format must be of a format on the page http://php.net/manual/en/datetime.createfromformat.php * ccnum($message = null) - The field value must be a valid credit card number. * oneOf($allowed, $message = null) - The field value must be one of the $allowed values. $allowed can be either an array or a comma-separated list of values. If comma separated, do not include spaces unless intended for matching. -* callback($callback, $message = '', $params = null) - Define your own custom callback validation function. $callback must pass an is_callable() check. $params can be any value, or an array if multiple parameters must be passed. +* callback($callback, $message = '', $params = array()) - Define your own custom callback validation function. $callback must pass an is_callable() check. $params can be any value, or an array if multiple parameters must be passed. -# Validating Arrays and Array Indices +### Callback Examples + +Callback functions can be passed as strings or closures. + + // numeric example + $validadator + ->callback('is_numeric', 'Field is not numeric.') + ->validate('number_field'); + + // closure example + $validator + ->callback(function($val) { + return $val < -1 || $val > 1; + }, 'Number must be less than -1 or greater than 1.') + ->validate('number_field_2'); + +## Validating Arrays and Array Indices This validation class has been extended to allow for validation of arrays as well as nested indices of a multi-dimensional array. diff --git a/README.txt b/README.txt index 7d399f0..4488941 100644 --- a/README.txt +++ b/README.txt @@ -55,6 +55,24 @@ $validator * oneOf($allowed, $message = null) - The field value must be one of the $allowed values. $allowed can be either an array or a comma-separated list of values. If comma separated, do not include spaces unless intended for matching. * callback($callback, $message = '', $params = null) - Define your own custom callback validation function. $callback must pass an is_callable() check. $params can be any value, or an array if multiple parameters must be passed. +### Callback Examples + +Callback functions can be passed as strings or closures. + + +// numeric example +$validadator + ->callback('is_numeric', 'Field is not numeric.') + ->validate('number_field'); + +// closure example +$validator + ->callback(function($val) { + return $val < -1 || $val > 1; + }, 'Number must be less than -1 or greater than 1.') + ->validate('number_field_2'); + + ## Validating Arrays and Array Indices This validation class has been extended to allow for validation of arrays as well as nested indices of a multi-dimensional array. diff --git a/Validator.php b/Validator.php index 0ac1946..1325ddb 100644 --- a/Validator.php +++ b/Validator.php @@ -540,7 +540,7 @@ public function oneOf($allowed, $message = null) { * @param mixed $params * @return FormValidator */ - public function callback($callback, $message = '', $params = NULL) { + public function callback($callback, $message = '', $params = array()) { if (is_callable($callback)) { // If an array is callable, it is a method From ef5b0ffb2e9a062ac7e6a912f6cc475607e3c9c6 Mon Sep 17 00:00:00 2001 From: Corey Ballou Date: Mon, 22 Oct 2012 11:18:58 -0300 Subject: [PATCH 29/32] Adding some color to the README --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6cc7ad2..0024f17 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # A PHP 5.3 Class for Easy Form Validation -This class follows Zend Framework naming conventions for easy drop-in as a substitute to Zend_Validation. If you opt out of using the bulky Zend_Form on your projects, you might choose to use this for quick and painless form validation. +This class follows Zend Framework naming conventions for easy drop-in as a substitute to Zend_Validation. +If you opt out of using the bulky Zend_Form on your projects, you might choose to use this for quick and painless +form validation. # A Quick Example @@ -9,6 +11,7 @@ exception. You can then retrieve the error messages from the calling method. It is not good practice to validate your data in your controller, this should be handled in your Model. This is just a quick example. + is_callable() check. ### Filter Examples + filter('intval') From 568f67fe2c23ba8b448bc6d64413861f315303a4 Mon Sep 17 00:00:00 2001 From: Corey Ballou Date: Mon, 22 Oct 2012 11:21:09 -0300 Subject: [PATCH 30/32] Update README.md --- README.md | 191 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 99 insertions(+), 92 deletions(-) diff --git a/README.md b/README.md index 0024f17..cef3556 100644 --- a/README.md +++ b/README.md @@ -11,70 +11,72 @@ exception. You can then retrieve the error messages from the calling method. It is not good practice to validate your data in your controller, this should be handled in your Model. This is just a quick example. - _validate($_POST); - // validate the data - $validData = $this->_validate($_POST); - - // validation passed because no exception was thrown - // ... to something with the $validData ... - - } catch (Validator_Exception $e) { - // retrieve the overall error message to display - $message = $e->getMessage(); - - // retrieve all of the errors - $errors = $e->getErrors(); - - // the below code is specific to ZF - $this->_helper->FlashMessenger(array('error' => $message)); - $this->_helper->layout->getView()->errors = $errors; - } - } - - /** - * Your user-defined validation handling. The exception section is - * very important and should always be used. - * - * @access private - * @param array $post - * @return mixed - */ - private function _validate(array $post = array()) - { - $validator = new Validator($post); - $validator - ->required('You must supply a name.') - ->validate('name', 'Name'); - $validator - ->required('You must supply an email address.') - ->email('You must supply a valid email address') - ->validate('email', 'Email'); + // validation passed because no exception was thrown + // ... to something with the $validData ... - // check for errors - if ($validator->hasErrors()) { - throw new Validator_Exception( - 'There were errors in your form.', - $validator->getAllErrors() - ); - } - - return $validator->getValidData(); + } catch (Validator_Exception $e) { + // retrieve the overall error message to display + $message = $e->getMessage(); + + // retrieve all of the errors + $errors = $e->getErrors(); + + // the below code is specific to ZF + $this->_helper->FlashMessenger(array('error' => $message)); + $this->_helper->layout->getView()->errors = $errors; } + } + + /** + * Your user-defined validation handling. The exception section is + * very important and should always be used. + * + * @access private + * @param array $post + * @return mixed + */ + private function _validate(array $post = array()) + { + $validator = new Validator($post); + $validator + ->required('You must supply a name.') + ->validate('name', 'Name'); + $validator + ->required('You must supply an email address.') + ->email('You must supply a valid email address') + ->validate('email', 'Email'); + // check for errors + if ($validator->hasErrors()) { + throw new Validator_Exception( + 'There were errors in your form.', + $validator->getAllErrors() + ); + } + + return $validator->getValidData(); } + +} +``` # Available Validation Methods @@ -112,23 +114,25 @@ This validation class has been extended to allow for validation of arrays as wel To validate specific indices of an array, use dot notation, i.e. - required('The nested field is required.') - ->validate('field.nested'); - - // ensure we have the first two numeric indices of $_POST['links'][] - $validator - ->required('This field is required') - ->validate('links.0'); - $validator - ->required('This field is required') - ->validate('links.1'); - +```php +required('The nested field is required.') + ->validate('field.nested'); + +// ensure we have the first two numeric indices of $_POST['links'][] +$validator + ->required('This field is required') + ->validate('links.0'); +$validator + ->required('This field is required') + ->validate('links.1'); +``` + # Available Pre-Validation Filtering You can apply pre-validation filters to your data (i.e. trim, strip_tags, htmlentities). These filters can also @@ -138,24 +142,27 @@ be custom defined so long as they pass an is_callable() check. ### Filter Examples - filter('intval') - ->min(1) - ->validate('user_id'); - - // custom filter - $validator - ->filter(function($val) { - // bogus formatting of the field - $val = rtrim($val, '/'); - $val .= '_custom_formatted'; - }) - ->validate('field_to_be_formatted'); +```php +filter('intval') + ->min(1) + ->validate('user_id'); + +// custom filter +$validator + ->filter(function($val) { + // bogus formatting of the field + $val = rtrim($val, '/'); + $val .= '_custom_formatted'; + }) + ->validate('field_to_be_formatted'); +``` # Credits -* Modifications by Corey Ballou and Chris Gutierrez. + +* Modifications by Corey Ballou and Chris Gutierrez * Forked from Tasos Bekos which was based on the initial work of "Bretticus". * See http://brettic.us/2010/06/18/form-validation-class-using-php-5-3/ for the original. From 286c7ebe61e3d25c1557cb9ebc1578aedb43006a Mon Sep 17 00:00:00 2001 From: Chris Gutierrez Date: Wed, 24 Oct 2012 17:27:44 -0300 Subject: [PATCH 31/32] Added MIT license --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index cef3556..7ccb4e8 100644 --- a/README.md +++ b/README.md @@ -166,3 +166,13 @@ $validator * Modifications by Corey Ballou and Chris Gutierrez * Forked from Tasos Bekos which was based on the initial work of "Bretticus". * See http://brettic.us/2010/06/18/form-validation-class-using-php-5-3/ for the original. + +# License + +Copyright (c) <2012> + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From df8823663ac76dd4ef6af8ceec0d494bcffae806 Mon Sep 17 00:00:00 2001 From: Chris Gutierrez Date: Wed, 24 Oct 2012 17:28:18 -0300 Subject: [PATCH 32/32] small license update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7ccb4e8..e2c049e 100644 --- a/README.md +++ b/README.md @@ -169,7 +169,7 @@ $validator # License -Copyright (c) <2012> +Copyright (c) 2012 http://github.com/bekos, http://github.com/blackbe.lt Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: