'/^[A-z]+$/', 'num' => '/^-?[0-9]+$/', 'numNonNeg' => '/^[0-9]+$/', 'alnum' => '/^[A-z0-9]+$/', 'email_simple' => '#^[^@]+@[a-z0-9_-]+\.[a-z0-9_.-]{2,}$#', 'email_default' => '#^[a-z0-9!\\#$%&\'*+/=?^_`{|}~-]+(?:\.[a-z0-9!\\#$%&\'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+(?:[A-Z]{2}|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|asia|jobs|museum)$#i', 'email_rfc5322' => '#(?:[a-z0-9!\\#$%&\'*+/=?^_`{|}~-]+(?:\.[a-z0-9!\\#$%&\'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])#', 'ipv4' => '#^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$#', 'ipv6' => '#^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$#', ); /** * @var Object of rules */ private $rules = null; /** * @var Object of conditions */ private $globalConditions = null; /** * @var Array (associative) of data being validated - must include data for conditions. */ private $fields = null; /** * @param $options Array of options to pass to the constructor */ public function __construct($options = Array()) { $default_options = Array( ); if (!empty($options['rules'])) { $this->setRules($options['rules']); $options['rules'] = null; } if (!empty($options['fields'])) { $this->fields = $options['fields']; $options['fields'] = null; } elseif (!empty($options['data'])) { $this->fields = $options['data']; $options['data'] = null; } if (!empty($options['rules_file'])) { $this->setRulesFromFile($options['rules_file']); $options['rules_file'] = null; } $this->options = $options + $default_options; } /** * Set the rules for the validator to use. * @param Object $rules */ public function setRules($rules) { if (!empty($rules->_rules)) { $this->rules = $rules->_rules; $this->globalConditions = $rules->_globalConditions ? $rules->_globalConditions : new \stdClass(); } else { $this->rules = $rules; $this->globalConditions = new \stdClass(); } } /** * Set the rules for the validator to use directly from the JSON config * @param String $file */ public function setRulesFromFile($file) { $this->setRules(json_decode(file_get_contents($file))); } /** * Get the rules... * @param String $file */ public function getRules() { return $this->rules; } /** * Set & Validate Data * @param Array $fields Associative array of the fields * @return Array Array of errors */ public function validateFields($fields) { $this->fields = $fields; return $this->validateAll(); } /** * Validate Data already passed to Valid8r (like via constructor options.) * @return Array Array of errors */ public function validateAll() { $results = Array(); foreach($this->rules as $field=>$options) { if ($err = $this->validate($field, @$this->fields[$field])) { $results[$field] = $err; } } return $results; } /** * Generates an error string * @param $field * @param $rule * @param string $defaultString * @param array $args * @return string */ private function errStr($field, $rule, $defaultString = 'Field Error', $args = array()) { if (@$rule->errStr) { $defaultString = $rule->errStr; } if (!empty($args)) return vsprintf($defaultString, $args); return $defaultString; } /** * Validate a single field/value * @param String $field * @param String $value * @return string */ public function validate($field, $value) { if (!empty($this->rules->$field->rules)) { foreach((array)$this->rules->$field->rules as $sel=>$rule) { if (@$rule->when && !$this->satisfiesCondition(@$this->rules->$field->conditions->{$rule->when}, $rule)) continue; switch($rule->rule) { case 'required': $err = $this->validRequired($field, $value, $rule); break; case 'len': $err = $this->validLen($field, $value, $rule); break; case 'isAlpha': $err = $this->validIsAlpha($field, $value, $rule); break; case "isNum" : $err = $this->validIsNum($field, $value, $rule); break; case "isAlnum" : $err = $this->validIsAlnum($field, $value, $rule); break; case "formattedAs": $err = $this->validFormat($field, $value, $rule); break; case "regex": $err = $this->validRegex($field, $value, $rule); break; case "val": $err = $this->validVal($field, $value, $rule); break; case "email": $err = $this->validEmail($field, $value, $rule); break; case "url": $err = $this->validUrl($field, $value, $rule); break; case "checks": $err = $this->validChecks($field, $value, $rule); break; case "radios": $err = $this->validRadios($field, $value, $rule); break; case "custom": $err = $this->validCustom($field, $value, $rule); break; case "ip": $err = $this->validIp($field, $value, $rule); break; default: $err = "Invalid rule: $rule->rule"; break; } if ($err) return $err; } } return ''; } public function satisfiesCondition($condition, $rule) { if (!$condition) { $condition = $this->globalConditions->{$rule->when}; } $value = @$this->fields[$condition->field]; return ($condition->is == $value); } /** * Validate the length of a string. * @param String $field * @param String $value * @param Object $rule * @return string */ public function validRequired($field, $value, $rule) { if ($value === null || $value === '') { return $this->errStr($field, $rule, 'This field is required.'); } return ''; } public function validCustom($field, $value, $rule) { $callable = $rule->func; if (@$rule->php_static_class) { $callable = $rule->php_static_class . '::' .$callable; if (@$rule->php_namespace) { $callable = $rule->php_namespace . '\\' . $callable; } $err = call_user_func($callable, $field, $value); } elseif (@$rule->php_class) { $class = $rule->php_class; if (@$rule->php_namespace) { $class = $rule->php_namespace . '\\' . $class; } $o = new $class; $err = call_user_func(array($o, $callable), $field, $value); } else { $err = call_user_func($callable, $field, $value); } return $err; } public function validLen($field, $value, $rule) { $len = strlen($value); if (@$rule->min && @$rule->max) { if ($len < $rule->min || $len > $rule->max) { return $this->errStr($field, $rule, 'Between %d and %d characters required.', array($rule->min, $rule->max)); } } if (@$rule->min) { if ($len < $rule->min) { return $this->errStr($field, $rule, 'At least %d characters are required.', Array($rule->min)); } } else if (@$rule->max) { if ($len > $rule->max) { return $this->errStr($field, $rule, 'At least %d characters are required.', Array($rule->max)); } } return ''; } public function validIsAlpha ($field, $value, $rule) { if ($value != '' && !preg_match( self::$res['alpha'], $value)) { return $this->errStr($field, $rule, 'Please enter alphabetic characters only (a-z).'); } return ''; } public function validIsNum ($field, $value, $rule) { if ($value != '') { if (@$rule->nonNeg) { if (!preg_match(self::$res['numNonNeg'], $value)) { return $this->errStr($field, $rule, 'Please enter numeric characters only (0-9).'); } } if (!preg_match(self::$res['num'], $value)) { return $this->errStr($field, $rule, 'Please enter numeric characters only (0-9).'); } } return ''; } public function validIsAlnum($field, $value, $rule) { if ($value != '' && !preg_match(self::$res['alnum'],$value)) { return $this->errStr($field, $rule, 'Please enter alphanumeric characters only (a-z, 0-9).'); } return ''; } public function validFormat($field, $value, $rule) { if ($value != '') { $format_re = str_replace('D','\\d', str_replace('A','[A-Z]', preg_quote($rule->format, '#'))); if (!preg_match("#$format_re#i", $value)) { return $this->errStr($field, $rule, 'Does not match required format of: ' . $rule->format); } } return ''; } public function validRegex($field, $value, $rule) { if ($value != '') { if (!preg_match('#'.$rule->pattern.'#'.@$rule->modifiers, $value)) { return $this->errStr($field, $rule, 'Does not match required pattern: ' . $rule->pattern); } } } public function validVal ($field, $value, $rule) { if ($value != '') { $v = (int)$value; if (!is_numeric($value)) return $this->errStr($field, $rule, 'Please enter a number.'); if (@$rule->is) { if ($rule->is != $v) { return $this->errStr($field, $rule, 'Please enter ' . $rule->is); } } elseif (@$rule->min) { if ($v < $rule->min) { return $this->errStr($field, $rule, 'Please enter a number greater than or equal to ' . $rule->min); } } elseif (@$rule->max ) { if ($v > $rule->max) { return $this->errStr($field, $rule, 'Please enter a number less than or equal to ' . $rule->max); } } elseif (@$rule->outside) { if ($v >= $rule->outside[0] || $v <= $rule->outside[1]) { return $this->errStr($field, $rule, 'Please enter a number outside of %d-%d', Array($rule->outside[0],$rule->outside[1])); } } } return ''; } public function validEmail($field, $value, $rule) { if ($value != '') { if (@$rule->validator) $em_re = $rule->validator; else $em_re = 'default'; if (empty(self::$res['email_'.$em_re])) return 'INVALID EMAIL VALIDATOR: ' . $em_re; if (!preg_match(self::$res['email_'.$em_re], $value)) return $this->errStr($field, $rule, 'Please enter a valid email address.'); } return ''; } public function validIp($field, $value, $rule) { if ($value != '') { if (@$rule->v) { if (empty(self::$res['ipv'.$rule->v])) return 'INVALID IP VALIDATOR: ' . $rule->v; if (!preg_match(self::$res['ipv'.$rule->v], $value)) return $this->errStr($field, $rule, 'Invalid IPv'.$rule->v.' Address'); } elseif (!preg_match(self::$res['ipv4'], $value) && !preg_match(self::$res['ipv6'], $value)) { return $this->errStr($field, $rule, 'Invalid IP Address'); } } return ''; } public function validUrl($field, $value, $rule) { if ($value != '') { $parts = parse_url($value); if (empty($parts['host'])) { return $this->errStr($field, $rule, 'Please enter a valid URL.'); } if (isset($rule->protocols)) { if (is_array($rule->protocols)) { $protos = $rule->protocols; } else { $protos = explode(',', $rule->protocols); } if (!in_array($parts['scheme'], $protos)) return $this->errStr($field, $rule, 'Please enter a valid URL.'); } elseif (isset($rule->noProtocols) && !empty($parts['scheme'])) { return $this->errStr($field, $rule, 'Please enter a URL without the protocol (eg, http://, https://, etc...)'); } } return ''; } public function validChecks($field, $value, $rule) { $num_checked = is_array($value) ? count($value) : 0; if (@$rule->min && @$rule->max) { if ($rule->min > $num_checked || $rule->max < $num_checked) { return $this->errStr($field, $rule, 'Please check between %d and %d options.', Array($rule->min,$rule->max)); } } elseif (@$rule->min) { if ($rule->min > $num_checked) { return $this->errStr($field, $rule, 'Please check at least %d options.', Array($rule->min)); } } elseif (@$rule->max) { if ($rule->max < $num_checked) { return $this->errStr($field, $rule, 'Please check no more than %d options.', Array($rule->max)); } } return ''; } public function validRadios($field, $value, $rule) { if (empty($value)) return $this->errStr($field, $rule, 'Please choose one.'); return ''; } }