1 <?php
defined('SYSPATH') or die('No direct script access.');
3 * Array and variable validation.
8 * @copyright (c) 2008-2010 Kohana Team
9 * @license http://kohanaframework.org/license
11 class Kohana_Validation
extends ArrayObject
{
14 * Creates a new Validation instance.
16 * @param array array to use for validation
19 public static function factory(array $array)
21 return new Validation($array);
25 protected $_bound = array();
28 protected $_rules = array();
31 protected $_labels = array();
33 // Rules that are executed even when the value is empty
34 protected $_empty_rules = array('not_empty', 'matches');
36 // Error list, field => rule
37 protected $_errors = array();
40 * Sets the unique "any field" key and creates an ArrayObject from the
43 * @param array array to validate
46 public function __construct(array $array)
48 parent
::__construct($array, ArrayObject
::STD_PROP_LIST
);
52 * Copies the current rule to a new array.
54 * $copy = $array->copy($new_data);
56 * @param array new data set
60 public function copy(array $array)
62 // Create a copy of the current validation set
65 // Replace the data set
66 $copy->exchangeArray($array);
72 * Returns the array representation of the current object.
76 public function as_array()
78 return $this->getArrayCopy();
82 * Sets or overwrites the label name for a field.
84 * @param string field name
88 public function label($field, $label)
90 // Set the label for this field
91 $this->_labels
[$field] = $label;
97 * Sets labels using an array.
99 * @param array list of field => label names
102 public function labels(array $labels)
104 $this->_labels
= $labels +
$this->_labels
;
110 * Overwrites or appends rules to a field. Each rule will be executed once.
111 * All rules must be string names of functions method names.
113 * // The "username" must not be empty and have a minimum length of 4
114 * $validation->rule('username', 'not_empty')
115 * ->rule('username', 'min_length', array(4));
117 * @param string field name
118 * @param callback valid PHP callback
119 * @param array extra parameters for the rule
122 public function rule($field, $rule, array $params = NULL)
124 if ($params === NULL)
126 // Default to array(':value')
127 $params = array(':value');
130 if ($field !== TRUE AND ! isset($this->_labels
[$field]))
132 // Set the field label to the field name
133 $this->_labels
[$field] = preg_replace('/[^\pL]+/u', ' ', $field);
136 // Store the rule and params for this rule
137 $this->_rules
[$field][] = array($rule, $params);
143 * Add rules using an array.
145 * @param string field name
146 * @param array list of callbacks
149 public function rules($field, array $rules)
151 foreach ($rules as $rule)
153 $this->rule($field, $rule[0], Arr
::get($rule, 1));
160 * Bind a value to a parameter definition.
162 * // This allows you to use :model in the parameter definition of rules
163 * $validation->bind(':model', $model)
164 * ->rule('status', 'valid_status', array(':model'));
166 * @param string variable name or an array of variables
170 public function bind($key, $value = NULL)
174 foreach ($key as $name => $value)
176 $this->_bound
[$name] = $value;
181 $this->_bound
[$key] = $value;
188 * Executes all validation rules. This should
189 * typically be called within an if/else block.
191 * if ($validation->check())
193 * // The data is valid, do something here
196 * @param boolean allow empty array?
199 public function check()
201 if (Kohana
::$profiling === TRUE)
203 // Start a new benchmark
204 $benchmark = Profiler
::start('Validation', __FUNCTION__
);
208 $data = $this->_errors
= array();
210 // Get a list of the expected fields
211 $expected = array_keys($this->_labels
);
213 // Import the rules locally
214 $rules = $this->_rules
;
216 foreach ($expected as $field)
218 if (isset($this[$field]))
220 // Use the submitted value
221 $data[$field] = $this[$field];
225 // No data exists for this field
226 $data[$field] = NULL;
229 if (isset($rules[TRUE]))
231 if ( ! isset($rules[$field]))
233 // Initialize the rules for this field
234 $rules[$field] = array();
238 $rules[$field] = array_merge($rules[$field], $rules[TRUE]);
242 // Overload the current array with the new one
243 $this->exchangeArray($data);
245 // Remove the rules that apply to every field
248 // Bind the validation object to :validation
249 $this->bind(':validation', $this);
253 foreach ($rules as $field => $set)
255 // Get the field value
256 $value = $this[$field];
258 // Bind the field name and value to :field and :value respectively
265 foreach ($set as $array)
267 // Rules are defined as array($rule, $params)
268 list($rule, $params) = $array;
270 if ( ! in_array($rule, $this->_empty_rules
) AND ! Valid
::not_empty($value))
272 // Skip this rule for empty fields
276 foreach ($params as $key => $param)
278 if (is_string($param) AND array_key_exists($param, $this->_bound
))
280 // Replace with bound value
281 $params[$key] = $this->_bound
[$param];
285 if (is_array($rule) OR ! is_string($rule))
287 // This is either a callback as an array or a lambda
288 $passed = call_user_func_array($rule, $params);
290 elseif (method_exists('Valid', $rule))
292 // Use a method in this object
293 $method = new ReflectionMethod('Valid', $rule);
295 // Call static::$rule($this[$field], $param, ...) with Reflection
296 $passed = $method->invokeArgs(NULL, $params);
298 elseif (strpos($rule, '::') === FALSE)
300 // Use a function call
301 $function = new ReflectionFunction($rule);
303 // Call $function($this[$field], $param, ...) with Reflection
304 $passed = $function->invokeArgs($params);
308 // Split the class and method of the rule
309 list($class, $method) = explode('::', $rule, 2);
311 // Use a static method call
312 $method = new ReflectionMethod($class, $method);
314 // Call $Class::$method($this[$field], $param, ...) with Reflection
315 $passed = $method->invokeArgs(NULL, $params);
318 if ($passed === FALSE)
320 // Add the rule to the errors
321 $this->error($field, $rule, $params);
323 // This field has an error, stop executing rules
329 if (isset($benchmark))
332 Profiler
::stop($benchmark);
335 return empty($this->_errors
);
339 * Add an error to a field.
341 * @param string field name
342 * @param string error message
345 public function error($field, $error, array $params = NULL)
347 $this->_errors
[$field] = array($error, $params);
353 * Returns the error messages. If no file is specified, the error message
354 * will be the name of the rule that failed. When a file is specified, the
355 * message will be loaded from "field/rule", or if no rule-specific message
356 * exists, "field/default" will be used. If neither is set, the returned
357 * message will be "file/field/rule".
359 * By default all messages are translated using the default language.
360 * A string can be used as the second parameter to specified the language
361 * that the message was written in.
363 * // Get errors from messages/forms/login.php
364 * $errors = $Validation->errors('forms/login');
366 * @uses Kohana::message
367 * @param string file to load error messages from
368 * @param mixed translate the message
371 public function errors($file = NULL, $translate = TRUE)
375 // Return the error list
376 return $this->_errors
;
379 // Create a new message list
382 foreach ($this->_errors
as $field => $set)
384 list($error, $params) = $set;
386 // Get the label for this field
387 $label = $this->_labels
[$field];
391 if (is_string($translate))
393 // Translate the label using the specified language
394 $label = __($label, NULL, $translate);
398 // Translate the label
403 // Start the translation values list
406 ':value' => $this[$field],
409 if (is_array($values[':value']))
411 // All values must be strings
412 $values[':value'] = implode(', ', Arr
::flatten($values[':value']));
417 foreach ($params as $key => $value)
419 if (is_array($value))
421 // All values must be strings
422 $value = implode(', ', Arr
::flatten($value));
425 // Check if a label for this parameter exists
426 if (isset($this->_labels
[$value]))
428 // Use the label as the value, eg: related field name for "matches"
429 $value = $this->_labels
[$value];
433 if (is_string($translate))
435 // Translate the value using the specified language
436 $value = __($value, NULL, $translate);
440 // Translate the value
446 // Add each parameter as a numbered value, starting from 1
447 $values[':param'.($key +
1)] = $value;
451 if ($message = Kohana
::message($file, "{$field}.{$error}"))
453 // Found a message for this field and error
455 elseif ($message = Kohana
::message($file, "{$field}.default"))
457 // Found a default message for this field
459 elseif ($message = Kohana
::message($file, $error))
461 // Found a default message for this error
463 elseif ($message = Kohana
::message('validation', $error))
465 // Found a default message for this error
469 // No message exists, display the path expected
470 $message = "{$file}.{$field}.{$error}";
475 if (is_string($translate))
477 // Translate the message using specified language
478 $message = __($message, $values, $translate);
482 // Translate the message using the default language
483 $message = __($message, $values);
488 // Do not translate, just replace the values
489 $message = strtr($message, $values);
492 // Set the message for this field
493 $messages[$field] = $message;