PageRenderTime 46ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/public_html/new_koh/system/classes/Kohana/Validation.php

https://bitbucket.org/rybadour/todo_list_site
PHP | 612 lines | 288 code | 75 blank | 249 comment | 24 complexity | 4d1e7e7cf76dfa76dd085e4bef8f9b3f MD5 | raw file
  1. <?php defined('SYSPATH') OR die('No direct script access.');
  2. /**
  3. * Array and variable validation.
  4. *
  5. * @package Kohana
  6. * @category Security
  7. * @author Kohana Team
  8. * @copyright (c) 2008-2012 Kohana Team
  9. * @license http://kohanaframework.org/license
  10. */
  11. class Kohana_Validation implements ArrayAccess {
  12. /**
  13. * Creates a new Validation instance.
  14. *
  15. * @param array $array array to use for validation
  16. * @return Validation
  17. */
  18. public static function factory(array $array)
  19. {
  20. return new Validation($array);
  21. }
  22. // Bound values
  23. protected $_bound = array();
  24. // Field rules
  25. protected $_rules = array();
  26. // Field labels
  27. protected $_labels = array();
  28. // Rules that are executed even when the value is empty
  29. protected $_empty_rules = array('not_empty', 'matches');
  30. // Error list, field => rule
  31. protected $_errors = array();
  32. // Array to validate
  33. protected $_data = array();
  34. /**
  35. * Sets the unique "any field" key and creates an ArrayObject from the
  36. * passed array.
  37. *
  38. * @param array $array array to validate
  39. * @return void
  40. */
  41. public function __construct(array $array)
  42. {
  43. $this->_data = $array;
  44. }
  45. /**
  46. * Throws an exception because Validation is read-only.
  47. * Implements ArrayAccess method.
  48. *
  49. * @throws Kohana_Exception
  50. * @param string $offset key to set
  51. * @param mixed $value value to set
  52. * @return void
  53. */
  54. public function offsetSet($offset, $value)
  55. {
  56. throw new Kohana_Exception('Validation objects are read-only.');
  57. }
  58. /**
  59. * Checks if key is set in array data.
  60. * Implements ArrayAccess method.
  61. *
  62. * @param string $offset key to check
  63. * @return bool whether the key is set
  64. */
  65. public function offsetExists($offset)
  66. {
  67. return isset($this->_data[$offset]);
  68. }
  69. /**
  70. * Throws an exception because Validation is read-only.
  71. * Implements ArrayAccess method.
  72. *
  73. * @throws Kohana_Exception
  74. * @param string $offset key to unset
  75. * @return void
  76. */
  77. public function offsetUnset($offset)
  78. {
  79. throw new Kohana_Exception('Validation objects are read-only.');
  80. }
  81. /**
  82. * Gets a value from the array data.
  83. * Implements ArrayAccess method.
  84. *
  85. * @param string $offset key to return
  86. * @return mixed value from array
  87. */
  88. public function offsetGet($offset)
  89. {
  90. return $this->_data[$offset];
  91. }
  92. /**
  93. * Copies the current rules to a new array.
  94. *
  95. * $copy = $array->copy($new_data);
  96. *
  97. * @param array $array new data set
  98. * @return Validation
  99. * @since 3.0.5
  100. */
  101. public function copy(array $array)
  102. {
  103. // Create a copy of the current validation set
  104. $copy = clone $this;
  105. // Replace the data set
  106. $copy->_data = $array;
  107. return $copy;
  108. }
  109. /**
  110. * Returns the array representation of the current object.
  111. * Deprecated in favor of [Validation::data]
  112. *
  113. * @deprecated
  114. * @return array
  115. */
  116. public function as_array()
  117. {
  118. return $this->_data;
  119. }
  120. /**
  121. * Returns the array of data to be validated.
  122. *
  123. * @return array
  124. */
  125. public function data()
  126. {
  127. return $this->_data;
  128. }
  129. /**
  130. * Sets or overwrites the label name for a field.
  131. *
  132. * @param string $field field name
  133. * @param string $label label
  134. * @return $this
  135. */
  136. public function label($field, $label)
  137. {
  138. // Set the label for this field
  139. $this->_labels[$field] = $label;
  140. return $this;
  141. }
  142. /**
  143. * Sets labels using an array.
  144. *
  145. * @param array $labels list of field => label names
  146. * @return $this
  147. */
  148. public function labels(array $labels)
  149. {
  150. $this->_labels = $labels + $this->_labels;
  151. return $this;
  152. }
  153. /**
  154. * Overwrites or appends rules to a field. Each rule will be executed once.
  155. * All rules must be string names of functions method names. Parameters must
  156. * match the parameters of the callback function exactly
  157. *
  158. * Aliases you can use in callback parameters:
  159. * - :validation - the validation object
  160. * - :field - the field name
  161. * - :value - the value of the field
  162. *
  163. * // The "username" must not be empty and have a minimum length of 4
  164. * $validation->rule('username', 'not_empty')
  165. * ->rule('username', 'min_length', array(':value', 4));
  166. *
  167. * // The "password" field must match the "password_repeat" field
  168. * $validation->rule('password', 'matches', array(':validation', 'password', 'password_repeat'));
  169. *
  170. * // Using closure (anonymous function)
  171. * $validation->rule('index',
  172. * function(Validation $array, $field, $value)
  173. * {
  174. * if ($value > 6 AND $value < 10)
  175. * {
  176. * $array->error($field, 'custom');
  177. * }
  178. * }
  179. * , array(':validation', ':field', ':value')
  180. * );
  181. *
  182. * [!!] Errors must be added manually when using closures!
  183. *
  184. * @param string $field field name
  185. * @param callback $rule valid PHP callback or closure
  186. * @param array $params extra parameters for the rule
  187. * @return $this
  188. */
  189. public function rule($field, $rule, array $params = NULL)
  190. {
  191. if ($params === NULL)
  192. {
  193. // Default to array(':value')
  194. $params = array(':value');
  195. }
  196. if ($field !== TRUE AND ! isset($this->_labels[$field]))
  197. {
  198. // Set the field label to the field name
  199. $this->_labels[$field] = preg_replace('/[^\pL]+/u', ' ', $field);
  200. }
  201. // Store the rule and params for this rule
  202. $this->_rules[$field][] = array($rule, $params);
  203. return $this;
  204. }
  205. /**
  206. * Add rules using an array.
  207. *
  208. * @param string $field field name
  209. * @param array $rules list of callbacks
  210. * @return $this
  211. */
  212. public function rules($field, array $rules)
  213. {
  214. foreach ($rules as $rule)
  215. {
  216. $this->rule($field, $rule[0], Arr::get($rule, 1));
  217. }
  218. return $this;
  219. }
  220. /**
  221. * Bind a value to a parameter definition.
  222. *
  223. * // This allows you to use :model in the parameter definition of rules
  224. * $validation->bind(':model', $model)
  225. * ->rule('status', 'valid_status', array(':model'));
  226. *
  227. * @param string $key variable name or an array of variables
  228. * @param mixed $value value
  229. * @return $this
  230. */
  231. public function bind($key, $value = NULL)
  232. {
  233. if (is_array($key))
  234. {
  235. foreach ($key as $name => $value)
  236. {
  237. $this->_bound[$name] = $value;
  238. }
  239. }
  240. else
  241. {
  242. $this->_bound[$key] = $value;
  243. }
  244. return $this;
  245. }
  246. /**
  247. * Executes all validation rules. This should
  248. * typically be called within an if/else block.
  249. *
  250. * if ($validation->check())
  251. * {
  252. * // The data is valid, do something here
  253. * }
  254. *
  255. * @return boolean
  256. */
  257. public function check()
  258. {
  259. if (Kohana::$profiling === TRUE)
  260. {
  261. // Start a new benchmark
  262. $benchmark = Profiler::start('Validation', __FUNCTION__);
  263. }
  264. // New data set
  265. $data = $this->_errors = array();
  266. // Store the original data because this class should not modify it post-validation
  267. $original = $this->_data;
  268. // Get a list of the expected fields
  269. $expected = Arr::merge(array_keys($original), array_keys($this->_labels));
  270. // Import the rules locally
  271. $rules = $this->_rules;
  272. foreach ($expected as $field)
  273. {
  274. // Use the submitted value or NULL if no data exists
  275. $data[$field] = Arr::get($this, $field);
  276. if (isset($rules[TRUE]))
  277. {
  278. if ( ! isset($rules[$field]))
  279. {
  280. // Initialize the rules for this field
  281. $rules[$field] = array();
  282. }
  283. // Append the rules
  284. $rules[$field] = array_merge($rules[$field], $rules[TRUE]);
  285. }
  286. }
  287. // Overload the current array with the new one
  288. $this->_data = $data;
  289. // Remove the rules that apply to every field
  290. unset($rules[TRUE]);
  291. // Bind the validation object to :validation
  292. $this->bind(':validation', $this);
  293. // Bind the data to :data
  294. $this->bind(':data', $this->_data);
  295. // Execute the rules
  296. foreach ($rules as $field => $set)
  297. {
  298. // Get the field value
  299. $value = $this[$field];
  300. // Bind the field name and value to :field and :value respectively
  301. $this->bind(array
  302. (
  303. ':field' => $field,
  304. ':value' => $value,
  305. ));
  306. foreach ($set as $array)
  307. {
  308. // Rules are defined as array($rule, $params)
  309. list($rule, $params) = $array;
  310. foreach ($params as $key => $param)
  311. {
  312. if (is_string($param) AND array_key_exists($param, $this->_bound))
  313. {
  314. // Replace with bound value
  315. $params[$key] = $this->_bound[$param];
  316. }
  317. }
  318. // Default the error name to be the rule (except array and lambda rules)
  319. $error_name = $rule;
  320. if (is_array($rule))
  321. {
  322. // Allows rule('field', array(':model', 'some_rule'));
  323. if (is_string($rule[0]) AND array_key_exists($rule[0], $this->_bound))
  324. {
  325. // Replace with bound value
  326. $rule[0] = $this->_bound[$rule[0]];
  327. }
  328. // This is an array callback, the method name is the error name
  329. $error_name = $rule[1];
  330. $passed = call_user_func_array($rule, $params);
  331. }
  332. elseif ( ! is_string($rule))
  333. {
  334. // This is a lambda function, there is no error name (errors must be added manually)
  335. $error_name = FALSE;
  336. $passed = call_user_func_array($rule, $params);
  337. }
  338. elseif (method_exists('Valid', $rule))
  339. {
  340. // Use a method in this object
  341. $method = new ReflectionMethod('Valid', $rule);
  342. // Call static::$rule($this[$field], $param, ...) with Reflection
  343. $passed = $method->invokeArgs(NULL, $params);
  344. }
  345. elseif (strpos($rule, '::') === FALSE)
  346. {
  347. // Use a function call
  348. $function = new ReflectionFunction($rule);
  349. // Call $function($this[$field], $param, ...) with Reflection
  350. $passed = $function->invokeArgs($params);
  351. }
  352. else
  353. {
  354. // Split the class and method of the rule
  355. list($class, $method) = explode('::', $rule, 2);
  356. // Use a static method call
  357. $method = new ReflectionMethod($class, $method);
  358. // Call $Class::$method($this[$field], $param, ...) with Reflection
  359. $passed = $method->invokeArgs(NULL, $params);
  360. }
  361. // Ignore return values from rules when the field is empty
  362. if ( ! in_array($rule, $this->_empty_rules) AND ! Valid::not_empty($value))
  363. continue;
  364. if ($passed === FALSE AND $error_name !== FALSE)
  365. {
  366. // Add the rule to the errors
  367. $this->error($field, $error_name, $params);
  368. // This field has an error, stop executing rules
  369. break;
  370. }
  371. elseif (isset($this->_errors[$field]))
  372. {
  373. // The callback added the error manually, stop checking rules
  374. break;
  375. }
  376. }
  377. }
  378. // Restore the data to its original form
  379. $this->_data = $original;
  380. if (isset($benchmark))
  381. {
  382. // Stop benchmarking
  383. Profiler::stop($benchmark);
  384. }
  385. return empty($this->_errors);
  386. }
  387. /**
  388. * Add an error to a field.
  389. *
  390. * @param string $field field name
  391. * @param string $error error message
  392. * @param array $params
  393. * @return $this
  394. */
  395. public function error($field, $error, array $params = NULL)
  396. {
  397. $this->_errors[$field] = array($error, $params);
  398. return $this;
  399. }
  400. /**
  401. * Returns the error messages. If no file is specified, the error message
  402. * will be the name of the rule that failed. When a file is specified, the
  403. * message will be loaded from "field/rule", or if no rule-specific message
  404. * exists, "field/default" will be used. If neither is set, the returned
  405. * message will be "file/field/rule".
  406. *
  407. * By default all messages are translated using the default language.
  408. * A string can be used as the second parameter to specified the language
  409. * that the message was written in.
  410. *
  411. * // Get errors from messages/forms/login.php
  412. * $errors = $Validation->errors('forms/login');
  413. *
  414. * @uses Kohana::message
  415. * @param string $file file to load error messages from
  416. * @param mixed $translate translate the message
  417. * @return array
  418. */
  419. public function errors($file = NULL, $translate = TRUE)
  420. {
  421. if ($file === NULL)
  422. {
  423. // Return the error list
  424. return $this->_errors;
  425. }
  426. // Create a new message list
  427. $messages = array();
  428. foreach ($this->_errors as $field => $set)
  429. {
  430. list($error, $params) = $set;
  431. // Get the label for this field
  432. $label = $this->_labels[$field];
  433. if ($translate)
  434. {
  435. if (is_string($translate))
  436. {
  437. // Translate the label using the specified language
  438. $label = __($label, NULL, $translate);
  439. }
  440. else
  441. {
  442. // Translate the label
  443. $label = __($label);
  444. }
  445. }
  446. // Start the translation values list
  447. $values = array(
  448. ':field' => $label,
  449. ':value' => Arr::get($this, $field),
  450. );
  451. if (is_array($values[':value']))
  452. {
  453. // All values must be strings
  454. $values[':value'] = implode(', ', Arr::flatten($values[':value']));
  455. }
  456. if ($params)
  457. {
  458. foreach ($params as $key => $value)
  459. {
  460. if (is_array($value))
  461. {
  462. // All values must be strings
  463. $value = implode(', ', Arr::flatten($value));
  464. }
  465. elseif (is_object($value))
  466. {
  467. // Objects cannot be used in message files
  468. continue;
  469. }
  470. // Check if a label for this parameter exists
  471. if (isset($this->_labels[$value]))
  472. {
  473. // Use the label as the value, eg: related field name for "matches"
  474. $value = $this->_labels[$value];
  475. if ($translate)
  476. {
  477. if (is_string($translate))
  478. {
  479. // Translate the value using the specified language
  480. $value = __($value, NULL, $translate);
  481. }
  482. else
  483. {
  484. // Translate the value
  485. $value = __($value);
  486. }
  487. }
  488. }
  489. // Add each parameter as a numbered value, starting from 1
  490. $values[':param'.($key + 1)] = $value;
  491. }
  492. }
  493. if ($message = Kohana::message($file, "{$field}.{$error}") AND is_string($message))
  494. {
  495. // Found a message for this field and error
  496. }
  497. elseif ($message = Kohana::message($file, "{$field}.default") AND is_string($message))
  498. {
  499. // Found a default message for this field
  500. }
  501. elseif ($message = Kohana::message($file, $error) AND is_string($message))
  502. {
  503. // Found a default message for this error
  504. }
  505. elseif ($message = Kohana::message('validation', $error) AND is_string($message))
  506. {
  507. // Found a default message for this error
  508. }
  509. else
  510. {
  511. // No message exists, display the path expected
  512. $message = "{$file}.{$field}.{$error}";
  513. }
  514. if ($translate)
  515. {
  516. if (is_string($translate))
  517. {
  518. // Translate the message using specified language
  519. $message = __($message, $values, $translate);
  520. }
  521. else
  522. {
  523. // Translate the message using the default language
  524. $message = __($message, $values);
  525. }
  526. }
  527. else
  528. {
  529. // Do not translate, just replace the values
  530. $message = strtr($message, $values);
  531. }
  532. // Set the message for this field
  533. $messages[$field] = $message;
  534. }
  535. return $messages;
  536. }
  537. }