/3.0/obsolete/web_client/system/libraries/Validation.php
PHP | 817 lines | 458 code | 105 blank | 254 comment | 43 complexity | 0d1628f7205857050d640f1baac55ab1 MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0, LGPL-2.1
- <?php defined('SYSPATH') OR die('No direct access allowed.');
- /**
- * Validation library.
- *
- * $Id: Validation.php 4713 2009-12-10 22:25:38Z isaiah $
- *
- * @package Validation
- * @author Kohana Team
- * @copyright (c) 2007-2009 Kohana Team
- * @license http://kohanaphp.com/license
- */
- class Validation_Core extends ArrayObject {
- // Filters
- protected $pre_filters = array();
- protected $post_filters = array();
- // Rules and callbacks
- protected $rules = array();
- protected $callbacks = array();
- // Rules that are allowed to run on empty fields
- protected $empty_rules = array('required', 'matches');
- // Errors
- protected $errors = array();
- protected $messages = array();
- // Field labels
- protected $labels = array();
- // Fields that are expected to be arrays
- protected $array_fields = array();
- /**
- * Creates a new Validation instance.
- *
- * @param array array to use for validation
- * @return object
- */
- public static function factory(array $array)
- {
- return new Validation($array);
- }
- /**
- * Sets the unique "any field" key and creates an ArrayObject from the
- * passed array.
- *
- * @param array array to validate
- * @return void
- */
- public function __construct(array $array)
- {
- parent::__construct($array, ArrayObject::ARRAY_AS_PROPS | ArrayObject::STD_PROP_LIST);
- }
- /**
- * Magic clone method, clears errors and messages.
- *
- * @return void
- */
- public function __clone()
- {
- $this->errors = array();
- $this->messages = array();
- }
- /**
- * Create a copy of the current validation rules and change the array.
- *
- * @chainable
- * @param array new array to validate
- * @return Validation
- */
- public function copy(array $array)
- {
- $copy = clone $this;
- $copy->exchangeArray($array);
- return $copy;
- }
- /**
- * Returns an array of all the field names that have filters, rules, or callbacks.
- *
- * @return array
- */
- public function field_names()
- {
- // All the fields that are being validated
- $fields = array_keys(array_merge
- (
- $this->pre_filters,
- $this->rules,
- $this->callbacks,
- $this->post_filters
- ));
- // Remove wildcard fields
- $fields = array_diff($fields, array('*'));
- return $fields;
- }
- /**
- * Returns the array values of the current object.
- *
- * @return array
- */
- public function as_array()
- {
- return $this->getArrayCopy();
- }
- /**
- * Returns the ArrayObject values, removing all inputs without rules.
- * To choose specific inputs, list the field name as arguments.
- *
- * @param boolean return only fields with filters, rules, and callbacks
- * @return array
- */
- public function safe_array()
- {
- // Load choices
- $choices = func_get_args();
- $choices = empty($choices) ? NULL : array_combine($choices, $choices);
- // Get field names
- $fields = $this->field_names();
- $safe = array();
- foreach ($fields as $field)
- {
- if ($choices === NULL OR isset($choices[$field]))
- {
- if (isset($this[$field]))
- {
- $value = $this[$field];
- if (is_object($value))
- {
- // Convert the value back into an array
- $value = $value->getArrayCopy();
- }
- }
- else
- {
- // Even if the field is not in this array, it must be set
- $value = NULL;
- }
- // Add the field to the array
- $safe[$field] = $value;
- }
- }
- return $safe;
- }
- /**
- * Add additional rules that will forced, even for empty fields. All arguments
- * passed will be appended to the list.
- *
- * @chainable
- * @param string rule name
- * @return object
- */
- public function allow_empty_rules($rules)
- {
- // Any number of args are supported
- $rules = func_get_args();
- // Merge the allowed rules
- $this->empty_rules = array_merge($this->empty_rules, $rules);
- return $this;
- }
- /**
- * Sets or overwrites the label name for a field.
- *
- * @param string field name
- * @param string label
- * @return $this
- */
- public function label($field, $label = NULL)
- {
- if ($label === NULL AND ($field !== TRUE OR $field !== '*') AND ! isset($this->labels[$field]))
- {
- // Set the field label to the field name
- $this->labels[$field] = ucfirst(preg_replace('/[^\pL]+/u', ' ', $field));
- }
- elseif ($label !== NULL)
- {
- // Set the label for this field
- $this->labels[$field] = $label;
- }
- return $this;
- }
- /**
- * Sets labels using an array.
- *
- * @param array list of field => label names
- * @return $this
- */
- public function labels(array $labels)
- {
- $this->labels = $labels + $this->labels;
- return $this;
- }
- /**
- * Converts a filter, rule, or callback into a fully-qualified callback array.
- *
- * @return mixed
- */
- protected function callback($callback)
- {
- if (is_string($callback))
- {
- if (strpos($callback, '::') !== FALSE)
- {
- $callback = explode('::', $callback);
- }
- elseif (function_exists($callback))
- {
- // No need to check if the callback is a method
- $callback = $callback;
- }
- elseif (method_exists($this, $callback))
- {
- // The callback exists in Validation
- $callback = array($this, $callback);
- }
- elseif (method_exists('valid', $callback))
- {
- // The callback exists in valid::
- $callback = array('valid', $callback);
- }
- }
- if ( ! is_callable($callback, FALSE))
- {
- if (is_array($callback))
- {
- if (is_object($callback[0]))
- {
- // Object instance syntax
- $name = get_class($callback[0]).'->'.$callback[1];
- }
- else
- {
- // Static class syntax
- $name = $callback[0].'::'.$callback[1];
- }
- }
- else
- {
- // Function syntax
- $name = $callback;
- }
- throw new Kohana_Exception('Callback %name% used for Validation is not callable', array('%name%' => $name));
- }
- return $callback;
- }
- /**
- * Add a pre-filter to one or more inputs. Pre-filters are applied before
- * rules or callbacks are executed.
- *
- * @chainable
- * @param callback filter
- * @param string fields to apply filter to, use TRUE for all fields
- * @return object
- */
- public function pre_filter($filter, $field = TRUE)
- {
- if ($field === TRUE OR $field === '*')
- {
- // Use wildcard
- $fields = array('*');
- }
- else
- {
- // Add the filter to specific inputs
- $fields = func_get_args();
- $fields = array_slice($fields, 1);
- }
- // Convert to a proper callback
- $filter = $this->callback($filter);
- foreach ($fields as $field)
- {
- // Add the filter to specified field
- $this->pre_filters[$field][] = $filter;
- }
- return $this;
- }
- /**
- * Add a post-filter to one or more inputs. Post-filters are applied after
- * rules and callbacks have been executed.
- *
- * @chainable
- * @param callback filter
- * @param string fields to apply filter to, use TRUE for all fields
- * @return object
- */
- public function post_filter($filter, $field = TRUE)
- {
- if ($field === TRUE)
- {
- // Use wildcard
- $fields = array('*');
- }
- else
- {
- // Add the filter to specific inputs
- $fields = func_get_args();
- $fields = array_slice($fields, 1);
- }
- // Convert to a proper callback
- $filter = $this->callback($filter);
- foreach ($fields as $field)
- {
- // Add the filter to specified field
- $this->post_filters[$field][] = $filter;
- }
- return $this;
- }
- /**
- * Add rules to a field. Validation rules may only return TRUE or FALSE and
- * can not manipulate the value of a field.
- *
- * @chainable
- * @param string field name
- * @param callback rules (one or more arguments)
- * @return object
- */
- public function add_rules($field, $rules)
- {
- // Get the rules
- $rules = func_get_args();
- $rules = array_slice($rules, 1);
- // Set a default field label
- $this->label($field);
- if ($field === TRUE)
- {
- // Use wildcard
- $field = '*';
- }
- foreach ($rules as $rule)
- {
- // Arguments for rule
- $args = NULL;
- // False rule
- $false_rule = FALSE;
- $rule_tmp = trim(is_string($rule) ? $rule : $rule[1]);
- // Should the rule return false?
- if ($rule_tmp !== ($rule_name = ltrim($rule_tmp, '! ')))
- {
- $false_rule = TRUE;
- }
- if (is_string($rule))
- {
- // Use the updated rule name
- $rule = $rule_name;
- // Have arguments?
- if (preg_match('/^([^\[]++)\[(.+)\]$/', $rule, $matches))
- {
- // Split the rule into the function and args
- $rule = $matches[1];
- $args = preg_split('/(?<!\\\\),\s*/', $matches[2]);
- // Replace escaped comma with comma
- $args = str_replace('\,', ',', $args);
- }
- }
- else
- {
- $rule[1] = $rule_name;
- }
- if ($rule === 'is_array')
- {
- // This field is expected to be an array
- $this->array_fields[$field] = $field;
- }
- // Convert to a proper callback
- $rule = $this->callback($rule);
- // Add the rule, with args, to the field
- $this->rules[$field][] = array($rule, $args, $false_rule);
- }
- return $this;
- }
- /**
- * Add callbacks to a field. Callbacks must accept the Validation object
- * and the input name. Callback returns are not processed.
- *
- * @chainable
- * @param string field name
- * @param callbacks callbacks (unlimited number)
- * @return object
- */
- public function add_callbacks($field, $callbacks)
- {
- // Get all callbacks as an array
- $callbacks = func_get_args();
- $callbacks = array_slice($callbacks, 1);
- // Set a default field label
- $this->label($field);
- if ($field === TRUE)
- {
- // Use wildcard
- $field = '*';
- }
- foreach ($callbacks as $callback)
- {
- // Convert to a proper callback
- $callback = $this->callback($callback);
- // Add the callback to specified field
- $this->callbacks[$field][] = $callback;
- }
- return $this;
- }
- /**
- * Validate by processing pre-filters, rules, callbacks, and post-filters.
- * All fields that have filters, rules, or callbacks will be initialized if
- * they are undefined. Validation will only be run if there is data already
- * in the array.
- *
- * @param object Validation object, used only for recursion
- * @param object name of field for errors
- * @return bool
- */
- public function validate($object = NULL, $field_name = NULL)
- {
- if ($object === NULL)
- {
- // Use the current object
- $object = $this;
- }
- $array = $this->safe_array();
- // Get all defined field names
- $fields = array_keys($array);
- foreach ($this->pre_filters as $field => $callbacks)
- {
- foreach ($callbacks as $callback)
- {
- if ($field === '*')
- {
- foreach ($fields as $f)
- {
- $array[$f] = is_array($array[$f]) ? array_map($callback, $array[$f]) : call_user_func($callback, $array[$f]);
- }
- }
- else
- {
- $array[$field] = is_array($array[$field]) ? array_map($callback, $array[$field]) : call_user_func($callback, $array[$field]);
- }
- }
- }
- foreach ($this->rules as $field => $callbacks)
- {
- foreach ($callbacks as $callback)
- {
- // Separate the callback, arguments and is false bool
- list ($callback, $args, $is_false) = $callback;
- // Function or method name of the rule
- $rule = is_array($callback) ? $callback[1] : $callback;
- if ($field === '*')
- {
- foreach ($fields as $f)
- {
- // Note that continue, instead of break, is used when
- // applying rules using a wildcard, so that all fields
- // will be validated.
- if (isset($this->errors[$f]))
- {
- // Prevent other rules from being evaluated if an error has occurred
- continue;
- }
- if (empty($array[$f]) AND ! in_array($rule, $this->empty_rules))
- {
- // This rule does not need to be processed on empty fields
- continue;
- }
- $result = ($args === NULL) ? call_user_func($callback, $array[$f]) : call_user_func($callback, $array[$f], $args);
- if (($result == $is_false))
- {
- $this->add_error($f, $rule, $args);
- // Stop validating this field when an error is found
- continue;
- }
- }
- }
- else
- {
- if (isset($this->errors[$field]))
- {
- // Prevent other rules from being evaluated if an error has occurred
- break;
- }
- if ( ! in_array($rule, $this->empty_rules) AND ! $this->required($array[$field]))
- {
- // This rule does not need to be processed on empty fields
- continue;
- }
- // Results of our test
- $result = ($args === NULL) ? call_user_func($callback, $array[$field]) : call_user_func($callback, $array[$field], $args);
- if (($result == $is_false))
- {
- $rule = $is_false ? '!'.$rule : $rule;
- $this->add_error($field, $rule, $args);
- // Stop validating this field when an error is found
- break;
- }
- }
- }
- }
- foreach ($this->callbacks as $field => $callbacks)
- {
- foreach ($callbacks as $callback)
- {
- if ($field === '*')
- {
- foreach ($fields as $f)
- {
- // Note that continue, instead of break, is used when
- // applying rules using a wildcard, so that all fields
- // will be validated.
- if (isset($this->errors[$f]))
- {
- // Stop validating this field when an error is found
- continue;
- }
- call_user_func($callback, $this, $f);
- }
- }
- else
- {
- if (isset($this->errors[$field]))
- {
- // Stop validating this field when an error is found
- break;
- }
- call_user_func($callback, $this, $field);
- }
- }
- }
- foreach ($this->post_filters as $field => $callbacks)
- {
- foreach ($callbacks as $callback)
- {
- if ($field === '*')
- {
- foreach ($fields as $f)
- {
- $array[$f] = is_array($array[$f]) ? array_map($callback, $array[$f]) : call_user_func($callback, $array[$f]);
- }
- }
- else
- {
- $array[$field] = is_array($array[$field]) ? array_map($callback, $array[$field]) : call_user_func($callback, $array[$field]);
- }
- }
- }
- // Swap the array back into the object
- $this->exchangeArray($array);
- // Return TRUE if there are no errors
- return $this->errors === array();
- }
- /**
- * Add an error to an input.
- *
- * @chainable
- * @param string input name
- * @param string unique error name
- * @param string arguments to pass to lang file
- * @return object
- */
- public function add_error($field, $name, $args = NULL)
- {
- $this->errors[$field] = array($name, $args);
- return $this;
- }
- /**
- * Return the errors array.
- *
- * @param boolean load errors from a message file
- * @return array
- */
- public function errors($file = NULL)
- {
- if ($file === NULL)
- {
- $errors = array();
- foreach($this->errors as $field => $error)
- {
- $errors[$field] = $error[0];
- }
- return $errors;
- }
- else
- {
- $errors = array();
- foreach ($this->errors as $input => $error)
- {
- // Locations to check for error messages
- $error_locations = array
- (
- "validation/{$file}.{$input}.{$error[0]}",
- "validation/{$file}.{$input}.default",
- "validation/default.{$error[0]}"
- );
- if (($message = Kohana::message($error_locations[0])) !== $error_locations[0])
- {
- // Found a message for this field and error
- }
- elseif (($message = Kohana::message($error_locations[1])) !== $error_locations[1])
- {
- // Found a default message for this field
- }
- elseif (($message = Kohana::message($error_locations[2])) !== $error_locations[2])
- {
- // Found a default message for this error
- }
- else
- {
- // No message exists, display the path expected
- $message = "validation/{$file}.{$input}.{$error[0]}";
- }
- // Start the translation values list
- $values = array(':field' => __($this->labels[$input]));
- if ( ! empty($error[1]))
- {
- foreach ($error[1] as $key => $value)
- {
- // Add each parameter as a numbered value, starting from 1
- $values[':param'.($key + 1)] = __($value);
- }
- }
- // Translate the message using the default language
- $errors[$input] = __($message, $values);
- }
- return $errors;
- }
- }
- /**
- * Rule: required. Generates an error if the field has an empty value.
- *
- * @param mixed input value
- * @return bool
- */
- public function required($str)
- {
- if (is_object($str) AND $str instanceof ArrayObject)
- {
- // Get the array from the ArrayObject
- $str = $str->getArrayCopy();
- }
- if (is_array($str))
- {
- return ! empty($str);
- }
- else
- {
- return ! ($str === '' OR $str === NULL OR $str === FALSE);
- }
- }
- /**
- * Rule: matches. Generates an error if the field does not match one or more
- * other fields.
- *
- * @param mixed input value
- * @param array input names to match against
- * @return bool
- */
- public function matches($str, array $inputs)
- {
- foreach ($inputs as $key)
- {
- if ($str !== (isset($this[$key]) ? $this[$key] : NULL))
- return FALSE;
- }
- return TRUE;
- }
- /**
- * Rule: length. Generates an error if the field is too long or too short.
- *
- * @param mixed input value
- * @param array minimum, maximum, or exact length to match
- * @return bool
- */
- public function length($str, array $length)
- {
- if ( ! is_string($str))
- return FALSE;
- $size = mb_strlen($str);
- $status = FALSE;
- if (count($length) > 1)
- {
- list ($min, $max) = $length;
- if ($size >= $min AND $size <= $max)
- {
- $status = TRUE;
- }
- }
- else
- {
- $status = ($size === (int) $length[0]);
- }
- return $status;
- }
- /**
- * Rule: depends_on. Generates an error if the field does not depend on one
- * or more other fields.
- *
- * @param mixed field name
- * @param array field names to check dependency
- * @return bool
- */
- public function depends_on($field, array $fields)
- {
- foreach ($fields as $depends_on)
- {
- if ( ! isset($this[$depends_on]) OR $this[$depends_on] == NULL)
- return FALSE;
- }
- return TRUE;
- }
- /**
- * Rule: chars. Generates an error if the field contains characters outside of the list.
- *
- * @param string field value
- * @param array allowed characters
- * @return bool
- */
- public function chars($value, array $chars)
- {
- return ! preg_match('![^'.implode('', $chars).']!u', $value);
- }
- } // End Validation