diff --git a/FormValidator.php b/FormValidator.php deleted file mode 100644 index 35eec05..0000000 --- a/FormValidator.php +++ /dev/null @@ -1,743 +0,0 @@ - - * @see Based on idea: http://brettic.us/2010/06/18/form-validation-class-using-php-5-3/ - */ -class FormValidator { - - private $messages = array(); - private $errors = array(); - private $rules = array(); - private $fields = array(); - private $functions = array(); - private $arguments = array(); - private $data = null; - - /** - * Constructor. - * Define values to validate. - * - * @param array $data - */ - function __construct($data = null) { - $this->data = (is_null($data)) ? $_POST : $data; - } - - // ----------------- ADD NEW RULE FUNCTIONS BELOW THIS LINE ---------------- - - /** - * Field, if completed, has to be a valid email address. - * - * @param string $message - * @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); - return $this; - } - - /** - * Field must be filled in. - * - * @param string $message - * @return FormValidator - */ - public function required($message = null) { - $this->set_rule(__FUNCTION__, function($string) { - return (strlen(trim($string)) === 0) ? FALSE : TRUE; - }, $message); - return $this; - } - - /** - * Field must contain a valid float value. - * - * @param string $message - * @return FormValidator - */ - public function float($message = null) { - $this->set_rule(__FUNCTION__, function($string) { - return (filter_var($string, FILTER_VALIDATE_FLOAT) === FALSE) ? FALSE : TRUE; - }, $message); - return $this; - } - - /** - * Field must contain a valid integer value. - * - * @param string $message - * @return FormValidator - */ - public function integer($message = null) { - $this->set_rule(__FUNCTION__, function($string) { - return (filter_var($string, FILTER_VALIDATE_INT) === FALSE) ? FALSE : TRUE; - }, $message); - return $this; - } - - /** - * Every character in field, if completed, must be a digit. - * This is just like integer(), except there is no upper limit. - * - * @param string $message - * @return FormValidator - */ - public function digits($message = null) { - $this->set_rule(__FUNCTION__, function($value) { - return (strlen($value) === 0 || ctype_digit((string) $value)); - }, $message); - return $this; - } - - /** - * Field must be a number greater than [or equal to] X. - * - * @param numeric $limit - * @param bool $include Whether to include limit value. - * @param string $message - * @return FormValidator - */ - public function min($limit, $include = TRUE, $message = null) { - $this->set_rule(__FUNCTION__, function($value, $args) { - if (strlen($value) === 0) { - return TRUE; - } - - $value = (float) $value; - $limit = (float) $args[0]; - $inc = (bool) $args[1]; - - return ($value > $limit || ($inc === TRUE && $value === $limit)); - }, $message, array($limit, $include)); - return $this; - } - - /** - * Field must be a number greater than [or equal to] X. - * - * @param numeric $limit - * @param bool $include Whether to include limit value. - * @param string $message - * @return FormValidator - */ - public function max($limit, $include = TRUE, $message = null) { - $this->set_rule(__FUNCTION__, function($value, $args) { - if (strlen($value) === 0) { - return TRUE; - } - - $value = (float) $value; - $limit = (float) $args[0]; - $inc = (bool) $args[1]; - - return ($value < $limit || ($inc === TRUE && $value === $limit)); - }, $message, array($limit, $include)); - return $this; - } - - /** - * Field must be a number between X and Y. - * - * @param numeric $min - * @param numeric $max - * @param bool $include Whether to include limit value. - * @param string $message - * @return FormValidator - */ - public function between($min, $max, $include = TRUE, $message = null) { - $message = self::getDefaultMessage(__FUNCTION__, array($min, $max, $include)); - - $this->min($min, $include, $message)->max($max, $include, $message); - return $this; - } - - /** - * Field has to be greater than or equal to X characters long. - * - * @param int $len - * @param string $message - * @return FormValidator - */ - public function minlength($len, $message = null) { - $this->set_rule(__FUNCTION__, function($string, $args) { - return (strlen(trim($string)) < $args[0]) ? FALSE : TRUE; - }, $message, array($len)); - return $this; - } - - /** - * Field has to be less than or equal to X characters long. - * - * @param int $len - * @param string $message - * @return FormValidator - */ - public function maxlength($len, $message = null) { - $this->set_rule(__FUNCTION__, function($string, $args) { - return (strlen(trim($string)) > $args[0]) ? FALSE : TRUE; - }, $message, array($len)); - return $this; - } - - /** - * Field has to be X characters long. - * - * @param int $len - * @param string $message - * @return FormValidator - */ - public function length($len, $message = null) { - $this->set_rule(__FUNCTION__, function($string, $args) { - return (strlen(trim($string)) == $args[0]) ? TRUE : FALSE; - }, $message, array($len)); - return $this; - } - - /** - * Field is the same as another one (password comparison etc). - * - * @param string $field - * @param string $label - * @param string $message - * @return FormValidator - */ - public function matches($field, $label, $message = null) { - $this->set_rule(__FUNCTION__, function($string, $args) { - return ((string) $args[0] == (string) $string) ? TRUE : FALSE; - }, $message, array($this->getval($field), $label)); - return $this; - } - - /** - * Field is different from another one. - * - * @param string $field - * @param string $label - * @param string $message - * @return FormValidator - */ - public function notmatches($field, $label, $message = null) { - $this->set_rule(__FUNCTION__, function($string, $args) { - return ((string) $args[0] == (string) $string) ? FALSE : TRUE; - }, $message, array($this->getval($field), $label)); - return $this; - } - - /** - * Field must start with a specific substring. - * - * @param string $sub - * @param string $message - * @return FormValidator - */ - public function startsWith($sub, $message = null) { - $this->set_rule(__FUNCTION__, function($string, $args) { - $sub = $args[0]; - return (strlen($string) === 0 || substr($string, 0, strlen($sub)) === $sub); - }, $message, array($sub)); - return $this; - } - - /** - * Field must NOT start with a specific substring. - * - * @param string $sub - * @param string $message - * @return FormValidator - */ - public function notstartsWith($sub, $message = null) { - $this->set_rule(__FUNCTION__, function($string, $args) { - $sub = $args[0]; - return (strlen($string) === 0 || substr($string, 0, strlen($sub)) !== $sub); - }, $message, array($sub)); - return $this; - } - - /** - * Field must end with a specific substring. - * - * @param string $sub - * @param string $message - * @return FormValidator - */ - public function endsWith($sub, $message = null) { - $this->set_rule(__FUNCTION__, function($string, $args) { - $sub = $args[0]; - return (strlen($string) === 0 || substr($string, -strlen($sub)) === $sub); - }, $message, array($sub)); - return $this; - } - - /** - * Field must not end with a specific substring. - * - * @param string $sub - * @param string $message - * @return FormValidator - */ - public function notendsWith($sub, $message = null) { - $this->set_rule(__FUNCTION__, function($string, $args) { - $sub = $args[0]; - return (strlen($string) === 0 || substr($string, -strlen($sub)) !== $sub); - }, $message, array($sub)); - return $this; - } - - /** - * Field has to be valid IP address. - * - * @param string $message - * @return FormValidator - */ - public function ip($message = null) { - $this->set_rule(__FUNCTION__, function($string) { - return (strlen(trim($string)) === 0 || filter_var($string, FILTER_VALIDATE_IP)) ? TRUE : FALSE; - }, $message); - return $this; - } - - /** - * Field has to be valid internet address. - * - * @param string $message - * @return FormValidator - */ - public function url($message = null) { - $this->set_rule(__FUNCTION__, function($string) { - return (strlen(trim($string)) === 0 || filter_var($string, FILTER_VALIDATE_URL)) ? TRUE : FALSE; - }, $message); - return $this; - } - - /** - * Date format. - * - * @return string - */ - private static function getDefaultDateFormat() { - return 'd/m/Y'; - } - - /** - * Field has to be a valid date. - * - * @param string $message - * @return FormValidator - */ - public function date($format = null, $separator = null, $message = null) { - if (empty($format)) { - $format = self::getDefaultDateFormat(); - } - - $this->set_rule(__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; - } - } - - return (checkdate($dateToCheck[1], $dateToCheck[0], $dateToCheck[2]) === FALSE) ? FALSE : TRUE; - }, $message, array($format, $separator)); - return $this; - } - - /** - * Field has to be a date later than or equal to X. - * - * @param string $message - * @return FormValidator - */ - public function mindate($date = 0, $format = null, $message = null) { - if (empty($format)) { - $format = self::getDefaultDateFormat(); - } - if (is_numeric($date)) { - $date = new DateTime($date . ' days'); // Days difference from today - } else { - $fieldValue = $this->getval($date); - $date = ($fieldValue == FALSE) ? $date : $fieldValue; - - $date = DateTime::createFromFormat($format, $date); - } - - $this->set_rule(__FUNCTION__, function($string, $args) { - $format = $args[1]; - $limitDate = $args[0]; - - return ($limitDate > DateTime::createFromFormat($format, $string)) ? FALSE : TRUE; - }, $message, array($date, $format)); - return $this; - } - - /** - * Field has to be a date later than or equal to X. - * - * @param string|integer $date Limit date. - * @param string $format Date format. - * @param string $message - * @return FormValidator - */ - public function maxdate($date = 0, $format = null, $message = null) { - if (empty($format)) { - $format = self::getDefaultDateFormat(); - } - if (is_numeric($date)) { - $date = new DateTime($date . ' days'); // Days difference from today - } else { - $fieldValue = $this->getval($date); - $date = ($fieldValue == FALSE) ? $date : $fieldValue; - - $date = DateTime::createFromFormat($format, $date); - } - - $this->set_rule(__FUNCTION__, function($string, $args) { - $format = $args[1]; - $limitDate = $args[0]; - - return ($limitDate < DateTime::createFromFormat($format, $string)) ? FALSE : TRUE; - }, $message, array($date, $format)); - return $this; - } - - /** - * Field has to be a valid credit card number format. - * - * @see https://github.com/funkatron/inspekt/blob/master/Inspekt.php - * @param string $message - * @return FormValidator - */ - public function ccnum($message = null) { - $this->set_rule(__FUNCTION__, function($value) { - $value = str_replace(' ', '', $value); - $length = strlen($value); - - if ($length < 13 || $length > 19) { - return FALSE; - } - - $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; - } - - $mod = (10 - $sum % 10) % 10; - - return ($mod == $value[$length - 1]); - }, $message); - return $this; - } - - /** - * Field has to be one of the allowed ones. - * - * @param string|array $allowed Allowed values. - * @param string $message - * @return FormValidator - */ - public function oneof($allowed, $message = null) { - if (is_string($allowed)) { - $allowed = explode(',', $allowed); - } - - $this->set_rule(__FUNCTION__, function($string, $args) { - return in_array($string, $args[0]); - }, $message, array($allowed)); - return $this; - } - - // --------------- END [ADD NEW RULE FUNCTIONS ABOVE THIS LINE] ------------ - - /** - * callback - * @param string $name - * @param mixed $function - * @param string $message - * @return FormValidator - */ - public function callback($name, $function, $message='') { - if (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); - } 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); - } - return $this; - } - - /** - * validate - * @param string $key - * @param string $label - * @return bool - */ - 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 - - $valid = (empty($args)) ? $function($string) : $function($string, $args); - if ($valid === FALSE) { - $this->register_error($rule, $key); - - $this->rules = array(); // reset rules - return FALSE; - } - } - } - - // reset rules - $this->rules = array(); - return $string; - } - - /** - * Whether errors have been found. - * - * @return bool - */ - public function hasErrors() { - return (count($this->errors) > 0) ? TRUE : FALSE; - } - - /** - * Get specific error. - * - * @param string $field - * @return string - */ - public function getError($field) { - return $this->errors[$field]; - } - - /** - * Get all errors. - * - * @return array - */ - public function getAllErrors($keys = true) { - return ($keys == true) ? $this->errors : array_values($this->errors); - } - - /** - * getval - * @param string $key - * @return mixed - */ - private function getval($key) { - return (isset($this->data[$key])) ? $this->data[$key] : FALSE; - } - - /** - * Register error. - * - * @param string $rule - * @param string $key - * @param string $message - */ - private function register_error($rule, $key, $message = null) { - if (empty($message)) { - $message = $this->messages[$rule]; - } - - $this->errors[$key] = sprintf($message, $this->fields[$key]); - } - - /** - * Set rule. - * - * @param string $rule - * @param closure $function - * @param string $message - * @param array $args - */ - private function set_rule($rule, $function, $message = '', $args = array()) { - if (!array_key_exists($rule, $this->rules)) { - $this->rules[$rule] = TRUE; - if (!array_key_exists($rule, $this->functions)) { - if (!is_callable($function)) { - die('Invalid function for rule: ' . $rule); - } - $this->functions[$rule] = $function; - } - $this->arguments[$rule] = $args; // Specific arguments for rule - - $this->messages[$rule] = (empty($message)) ? self::getDefaultMessage($rule, $args) : $message; - } - } - - /** - * Get default error message. - * - * @param string $key - * @param array $args - * @return string - */ - private static function getDefaultMessage($rule, $args = null) { - switch ($rule) { - case 'email': - $message = '%s is an invalid email address.'; - break; - - case 'ip': - $message = '%s is an invalid IP address.'; - break; - - case 'url': - $message = '%s is an invalid url.'; - break; - - case 'required': - $message = '%s is required.'; - break; - - case 'float': - $message = '%s must consist of numbers only.'; - break; - - case 'integer': - $message = '%s must consist of integer value.'; - break; - - case 'digits': - $message = '%s must consist only of digits.'; - break; - - case 'min': - $message = '%s must be greater than '; - if ($args[1] == TRUE) { - $message .= 'or equal to '; - } - $message .= $args[0] . '.'; - break; - - case 'max': - $message = '%s must be less than '; - if ($args[1] == TRUE) { - $message .= 'or equal to '; - } - $message .= $args[0] . '.'; - break; - - case 'between': - $message = '%s must be between ' . $args[0] . ' and ' . $args[1] . '.'; - if ($args[2] == FALSE) { - $message .= '(Without limits)'; - } - break; - - case 'minlength': - $message = '%s must be at least ' . $args[0] . ' characters or longer.'; - break; - - case 'maxlength': - $message = '%s must be no longer than ' . $args[0] . ' characters.'; - break; - - case 'length': - $message = '%s must be exactly ' . $args[0] . ' characters in length.'; - break; - - case 'matches': - $message = '%s must match ' . $args[1] . '.'; - break; - - case 'notmatches': - $message = '%s must not match ' . $args[1] . '.'; - break; - - case 'startsWith': - $message = '%s must start with "' . $args[0] . '".'; - break; - - case 'notstartsWith': - $message = '%s must not start with "' . $args[0] . '".'; - break; - - case 'endsWith': - $message = '%s must end with "' . $args[0] . '".'; - break; - - case 'notendsWith': - $message = '%s must not end with "' . $args[0] . '".'; - break; - - case 'date': - $message = '%s is not valid date.'; - break; - - case 'mindate': - $message = '%s must be later than ' . $args[0]->format($args[1]) . '.'; - break; - - case 'maxdate': - $message = '%s must be before ' . $args[0]->format($args[1]) . '.'; - break; - - case 'oneof': - $message = '%s must be one of ' . implode(', ', $args[0]) . '.'; - break; - - case 'ccnum': - $message = '%s must be a valid credit card number.'; - break; - - default: - $message = '%s has an error.'; - break; - } - - return $message; - } - -} -?> - - diff --git a/README.md b/README.md new file mode 100644 index 0000000..cb4b978 --- /dev/null +++ b/README.md @@ -0,0 +1,193 @@ +# 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. + +## 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. +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. + +```php +_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'); + + // check for errors + if ($validator->hasErrors()) { + throw new Validator_Exception( + 'There were errors in your form.', + $validator->getAllErrors() + ); + } + + return $validator->getValidData(); + } + +} +``` + +## 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 = 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. + +### 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. + +### Validating Specific Indices + +To validate specific indices of an array, use dot notation, i.e. + +```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 +be custom defined so long as they pass an is_callable() check. + +* filter($callback) + +### Filter Examples + +```php + // 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'; + return $val; + }) + ->validate('field_to_be_formatted'); +``` + +# Credits + +* 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. + +# License + +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: + +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. diff --git a/README.txt b/README.txt index 0fc9d20..4488941 100644 --- a/README.txt +++ b/README.txt @@ -1,5 +1,106 @@ -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'; + return $val; + }) + ->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. + +### 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. + +### 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, 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 new file mode 100644 index 0000000..1325ddb --- /dev/null +++ b/Validator.php @@ -0,0 +1,932 @@ + + * @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 { + + protected $messages = array(); + protected $errors = array(); + protected $rules = array(); + protected $fields = array(); + protected $functions = array(); + protected $arguments = array(); + protected $filters = array(); + protected $data = null; + protected $validData = array(); + + /** + * Constructor. + * Define values to validate. + * + * @param array $data + */ + function __construct(array $data = null) { + if (!empty($data)) $this->setData($data); + } + + /** + * set the data to be validated + * + * @access public + * @param mixed $data + * @return FormValidator + */ + public function setData(array $data) { + $this->data = $data; + return $this; + } + + // ----------------- ADD NEW RULE FUNCTIONS BELOW THIS LINE ---------------- + + /** + * Field, if completed, has to be a valid email address. + * + * @param string $message + * @return FormValidator + */ + public function email($message = null) { + $this->setRule(__FUNCTION__, function($email) { + 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; + } + + /** + * Field must be filled in. + * + * @param string $message + * @return FormValidator + */ + public function required($message = null) { + $this->setRule(__FUNCTION__, function($val) { + if (is_scalar($val)) { + $val = trim($val); + } + return !empty($val); + }, $message); + return $this; + } + + /** + * Field must contain a valid float value. + * + * @param string $message + * @return FormValidator + */ + public function float($message = null) { + $this->setRule(__FUNCTION__, function($val) { + return !(filter_var($val, FILTER_VALIDATE_FLOAT) === FALSE); + }, $message); + return $this; + } + + /** + * Field must contain a valid integer value. + * + * @param string $message + * @return FormValidator + */ + public function integer($message = null) { + $this->setRule(__FUNCTION__, function($val) { + return !(filter_var($val, FILTER_VALIDATE_INT) === FALSE); + }, $message); + return $this; + } + + /** + * Every character in field, if completed, must be a digit. + * This is just like integer(), except there is no upper limit. + * + * @param string $message + * @return FormValidator + */ + public function digits($message = null) { + $this->setRule(__FUNCTION__, function($val) { + return (strlen($val) === 0 || ctype_digit((string) $val)); + }, $message); + return $this; + } + + /** + * Field must be a number greater than [or equal to] X. + * + * @param numeric $limit + * @param bool $include Whether to include limit value. + * @param string $message + * @return FormValidator + */ + public function min($limit, $include = TRUE, $message = null) { + $this->setRule(__FUNCTION__, function($val, $args) { + if (strlen($val) === 0) { + return TRUE; + } + + $val = (float) $val; + $limit = (float) $args[0]; + $inc = (bool) $args[1]; + + return ($val > $limit || ($inc === TRUE && $val === $limit)); + }, $message, array($limit, $include)); + return $this; + } + + /** + * Field must be a number greater than [or equal to] X. + * + * @param numeric $limit + * @param bool $include Whether to include limit value. + * @param string $message + * @return FormValidator + */ + public function max($limit, $include = TRUE, $message = null) { + $this->setRule(__FUNCTION__, function($val, $args) { + if (strlen($val) === 0) { + return TRUE; + } + + $val = (float) $val; + $limit = (float) $args[0]; + $inc = (bool) $args[1]; + + return ($val < $limit || ($inc === TRUE && $val === $limit)); + }, $message, array($limit, $include)); + return $this; + } + + /** + * Field must be a number between X and Y. + * + * @param numeric $min + * @param numeric $max + * @param bool $include Whether to include limit value. + * @param string $message + * @return FormValidator + */ + public function between($min, $max, $include = TRUE, $message = null) { + $message = $this->_getDefaultMessage(__FUNCTION__, array($min, $max, $include)); + + $this->min($min, $include, $message)->max($max, $include, $message); + return $this; + } + + /** + * Field has to be greater than or equal to X characters long. + * + * @param int $len + * @param string $message + * @return FormValidator + */ + public function minlength($len, $message = null) { + $this->setRule(__FUNCTION__, function($val, $args) { + return !(strlen(trim($val)) < $args[0]); + }, $message, array($len)); + return $this; + } + + /** + * Field has to be less than or equal to X characters long. + * + * @param int $len + * @param string $message + * @return FormValidator + */ + public function maxlength($len, $message = null) { + $this->setRule(__FUNCTION__, function($val, $args) { + return !(strlen(trim($val)) > $args[0]); + }, $message, array($len)); + 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. + * + * @param int $len + * @param string $message + * @return FormValidator + */ + public function length($len, $message = null) { + $this->setRule(__FUNCTION__, function($val, $args) { + return (strlen(trim($val)) == $args[0]); + }, $message, array($len)); + return $this; + } + + /** + * Field is the same as another one (password comparison etc). + * + * @param string $field + * @param string $label + * @param string $message + * @return FormValidator + */ + public function matches($field, $label, $message = null) { + $this->setRule(__FUNCTION__, function($val, $args) { + return ((string) $args[0] == (string) $val); + }, $message, array($this->_getVal($field), $label)); + return $this; + } + + /** + * Field is different from another one. + * + * @param string $field + * @param string $label + * @param string $message + * @return FormValidator + */ + public function notmatches($field, $label, $message = null) { + $this->setRule(__FUNCTION__, function($val, $args) { + return ((string) $args[0] != (string) $val); + }, $message, array($this->_getVal($field), $label)); + return $this; + } + + /** + * Field must start with a specific substring. + * + * @param string $sub + * @param string $message + * @return FormValidator + */ + public function startsWith($sub, $message = null) { + $this->setRule(__FUNCTION__, function($val, $args) { + $sub = $args[0]; + return (strlen($val) === 0 || substr($val, 0, strlen($sub)) === $sub); + }, $message, array($sub)); + return $this; + } + + /** + * Field must NOT start with a specific substring. + * + * @param string $sub + * @param string $message + * @return FormValidator + */ + public function notstartsWith($sub, $message = null) { + $this->setRule(__FUNCTION__, function($val, $args) { + $sub = $args[0]; + return (strlen($val) === 0 || substr($val, 0, strlen($sub)) !== $sub); + }, $message, array($sub)); + return $this; + } + + /** + * Field must end with a specific substring. + * + * @param string $sub + * @param string $message + * @return FormValidator + */ + public function endsWith($sub, $message = null) { + $this->setRule(__FUNCTION__, function($val, $args) { + $sub = $args[0]; + return (strlen($val) === 0 || substr($val, -strlen($sub)) === $sub); + }, $message, array($sub)); + return $this; + } + + /** + * Field must not end with a specific substring. + * + * @param string $sub + * @param string $message + * @return FormValidator + */ + public function notendsWith($sub, $message = null) { + $this->setRule(__FUNCTION__, function($val, $args) { + $sub = $args[0]; + return (strlen($val) === 0 || substr($val, -strlen($sub)) !== $sub); + }, $message, array($sub)); + return $this; + } + + /** + * Field has to be valid IP address. + * + * @param string $message + * @return FormValidator + */ + public function ip($message = null) { + $this->setRule(__FUNCTION__, function($val) { + return (strlen(trim($val)) === 0 || filter_var($val, FILTER_VALIDATE_IP) !== FALSE); + }, $message); + return $this; + } + + /** + * Field has to be valid internet address. + * + * @param string $message + * @return FormValidator + */ + public function url($message = null) { + $this->setRule(__FUNCTION__, function($val) { + return (strlen(trim($val)) === 0 || filter_var($val, FILTER_VALIDATE_URL) !== FALSE); + }, $message); + return $this; + } + + /** + * Date format. + * + * @return string + */ + protected function _getDefaultDateFormat() { + return 'd/m/Y'; + } + + /** + * Field has to be a valid date. + * + * @param string $message + * @return FormValidator + */ + public function date($message = null) { + $this->setRule(__FUNCTION__, function($val, $args) { + + if (strlen(trim($val)) === 0) { + return TRUE; + } + + try { + $dt = new DateTime($val, new DateTimeZone("UTC")); + return true; + } catch(Exception $e) { + return false; + } + + }, $message, array($format, $separator)); + return $this; + } + + /** + * Field has to be a date later than or equal to X. + * + * @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) { + if (empty($format)) { + $format = $this->_getDefaultDateFormat(); + } + if (is_numeric($date)) { + $date = new DateTime($date . ' days'); // Days difference from today + } else { + $fieldValue = $this->_getVal($date); + $date = ($fieldValue == FALSE) ? $date : $fieldValue; + + $date = DateTime::createFromFormat($format, $date); + } + + $this->setRule(__FUNCTION__, function($val, $args) { + $format = $args[1]; + $limitDate = $args[0]; + + return ($limitDate > DateTime::createFromFormat($format, $val)) ? FALSE : TRUE; + }, $message, array($date, $format)); + return $this; + } + + /** + * Field has to be a date later than or equal to X. + * + * @param string|integer $date Limit date. + * @param string $format Date format. + * @param string $message + * @return FormValidator + */ + public function maxDate($date = 0, $format = null, $message = null) { + if (empty($format)) { + $format = $this->_getDefaultDateFormat(); + } + if (is_numeric($date)) { + $date = new DateTime($date . ' days'); // Days difference from today + } else { + $fieldValue = $this->_getVal($date); + $date = ($fieldValue == FALSE) ? $date : $fieldValue; + + $date = DateTime::createFromFormat($format, $date); + } + + $this->setRule(__FUNCTION__, function($val, $args) { + $format = $args[1]; + $limitDate = $args[0]; + + return !($limitDate < DateTime::createFromFormat($format, $val)); + }, $message, array($date, $format)); + return $this; + } + + /** + * Field has to be a valid credit card number format. + * + * @see https://github.com/funkatron/inspekt/blob/master/Inspekt.php + * @param string $message + * @return FormValidator + */ + public function ccnum($message = null) { + $this->setRule(__FUNCTION__, function($value) { + $value = str_replace(' ', '', $value); + $length = strlen($value); + + if ($length < 13 || $length > 19) { + return FALSE; + } + + $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; + } + + $mod = (10 - $sum % 10) % 10; + + return ($mod == $value[$length - 1]); + }, $message); + return $this; + } + + /** + * Field has to be one of the allowed ones. + * + * @param string|array $allowed Allowed values. + * @param string $message + * @return FormValidator + */ + public function oneOf($allowed, $message = null) { + if (is_string($allowed)) { + $allowed = explode(',', $allowed); + } + + $this->setRule(__FUNCTION__, function($val, $args) { + return in_array($val, $args[0]); + }, $message, array($allowed)); + return $this; + } + + // --------------- END [ADD NEW RULE FUNCTIONS ABOVE THIS LINE] ------------ + + /** + * callback + * @param string $name + * @param mixed $function + * @param string $message + * @param mixed $params + * @return FormValidator + */ + public function callback($callback, $message = '', $params = array()) { + 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 (!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); + } + }, $message, $params); + } + + } else { + throw new Exception(sprintf('%s is not callable.', $function)); + } + + return $this; + } + + // ------------------ PRE VALIDATION FILTERING ------------------- + /** + * add a filter callback for the data + * + * @param mixed $callback + * @return FormValidator + */ + public function filter($callback) { + 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 + * @param string $label + * @return bool + */ + 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; + + // apply filters to the data + $this->_applyFilters($key); + + $val = $this->_getVal($key); + + // validate the piece of data + $this->_validate($key, $val, $recursive); + + // reset rules + $this->rules = array(); + $this->filters = array(); + return $val; + } + + /** + * recursively validates a value + * + * @access protected + * @param string $key + * @param mixed $val + * @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. + 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); + + $this->rules = array(); // reset rules + $this->filters = array(); + return FALSE; + } + } + } + + $this->validData[$key] = $val; + return TRUE; + } + } + + /** + * Whether errors have been found. + * + * @return bool + */ + public function hasErrors() { + return (count($this->errors) > 0); + } + + /** + * Get specific error. + * + * @param string $field + * @return string + */ + public function getError($field) { + return $this->errors[$field]; + } + + /** + * Get all errors. + * + * @return array + */ + 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 + * 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 + */ + 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; + } + } + + /** + * Register error. + * + * @param string $rule + * @param string $key + * @param string $message + */ + protected function registerError($rule, $key, $message = null) { + if (empty($message)) { + $message = $this->messages[$rule]; + } + + $this->errors[$key] = sprintf($message, $this->fields[$key]); + } + + /** + * Set rule. + * + * @param string $rule + * @param closure $function + * @param string $message + * @param array $args + */ + 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)) { + if (!is_callable($function)) { + die('Invalid function for rule: ' . $rule); + } + $this->functions[$rule] = $function; + } + $this->arguments[$rule] = $args; // Specific arguments for rule + + $this->messages[$rule] = (empty($message)) ? $this->_getDefaultMessage($rule, $args) : $message; + } + } + + /** + * Get default error message. + * + * @param string $key + * @param array $args + * @return string + */ + protected function _getDefaultMessage($rule, $args = null) { + + switch ($rule) { + case 'email': + $message = '%s is an invalid email address.'; + break; + + case 'ip': + $message = '%s is an invalid IP address.'; + break; + + case 'url': + $message = '%s is an invalid url.'; + break; + + case 'required': + $message = '%s is required.'; + break; + + case 'float': + $message = '%s must consist of numbers only.'; + break; + + case 'integer': + $message = '%s must consist of integer value.'; + break; + + case 'digits': + $message = '%s must consist only of digits.'; + break; + + case 'min': + $message = '%s must be greater than '; + if ($args[1] == TRUE) { + $message .= 'or equal to '; + } + $message .= $args[0] . '.'; + break; + + case 'max': + $message = '%s must be less than '; + if ($args[1] == TRUE) { + $message .= 'or equal to '; + } + $message .= $args[0] . '.'; + break; + + case 'between': + $message = '%s must be between ' . $args[0] . ' and ' . $args[1] . '.'; + if ($args[2] == FALSE) { + $message .= '(Without limits)'; + } + break; + + case 'minlength': + $message = '%s must be at least ' . $args[0] . ' characters or longer.'; + break; + + case 'maxlength': + $message = '%s must be no longer than ' . $args[0] . ' characters.'; + break; + + case 'length': + $message = '%s must be exactly ' . $args[0] . ' characters in length.'; + break; + + case 'matches': + $message = '%s must match ' . $args[1] . '.'; + break; + + case 'notmatches': + $message = '%s must not match ' . $args[1] . '.'; + break; + + case 'startsWith': + $message = '%s must start with "' . $args[0] . '".'; + break; + + case 'notstartsWith': + $message = '%s must not start with "' . $args[0] . '".'; + break; + + case 'endsWith': + $message = '%s must end with "' . $args[0] . '".'; + break; + + case 'notendsWith': + $message = '%s must not end with "' . $args[0] . '".'; + break; + + case 'date': + $message = '%s is not valid date.'; + break; + + case 'mindate': + $message = '%s must be later than ' . $args[0]->format($args[1]) . '.'; + break; + + case 'maxdate': + $message = '%s must be before ' . $args[0]->format($args[1]) . '.'; + break; + + case 'oneof': + $message = '%s must be one of ' . implode(', ', $args[0]) . '.'; + break; + + case 'ccnum': + $message = '%s must be a valid credit card number.'; + break; + + default: + $message = '%s has an error.'; + break; + } + + return $message; + } + +} diff --git a/Validator/Exception.php b/Validator/Exception.php new file mode 100644 index 0000000..5b001d5 --- /dev/null +++ b/Validator/Exception.php @@ -0,0 +1,14 @@ +_errors = $errors; + } + + public function getErrors() { + return $this->_errors; + } +} diff --git a/tests/ValidatorTest.php b/tests/ValidatorTest.php new file mode 100644 index 0000000..89e5d70 --- /dev/null +++ b/tests/ValidatorTest.php @@ -0,0 +1,480 @@ +_validator = new Validator(); + } + + /** + * test filter with string 'trim' as the callbacl + */ + public function testFilterTrimCallback() + { + $validator = $this->_validator; + + $data = array( + 'email' => ' test@emailwithwhitespace.com ', + ); + + $validator->setData($data); + $email = $validator->filter('trim')->validate('email'); + + $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()->validate('emails', true); + $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', true); + + $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', true); + + $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()); + + $data['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()); + + $data['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()); + + $data['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()); + + $validator->setData($data) + ->min(40) + ->validate('min'); + + $this->assertTrue($validator->hasErrors()); + } + + /** + * test the max rule + */ + public function testValidateMax() + { + $validator = $this->_validator; + + $data = array( + 'max' => 29 + ); + + $validator->setData($data) + ->max(30) + ->validate('max'); + + $this->assertFalse($validator->hasErrors()); + + $validator->setData($data) + ->max(20) + ->validate('max'); + + $this->assertTrue($validator->hasErrors()); + } + + /** + * test the between rule + */ + public function testValidateBetween() + { + $validator = $this->_validator; + + $data = array( + 'between' => 35 + ); + + $validator->setData($data) + ->between(30, 40) + ->validate('between'); + + $this->assertFalse($validator->hasErrors()); + + $validator->setData($data) + ->between(40, 50) + ->validate('between'); + + $this->assertTrue($validator->hasErrors()); + } + + /** + * test the minLength rule + */ + public function testValidateMinLength() + { + $validator = $this->_validator; + + $data = array( + 'minlength' => 'this is a string' + ); + + $validator->setData($data) + ->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 minLength rule + */ + public function testValidateLength() + { + $validator = $this->_validator; + + $data = array( + 'length' => 'this is a string' + ); + + $validator->setData($data) + ->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()); + } + + 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 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