PageRenderTime 53ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/brainfart/bffront/system/classes/kohana/validation.php

https://github.com/o1iver/Code-Backup
PHP | 516 lines | 258 code | 67 blank | 191 comment | 24 complexity | 88a01c69b3687773bfae50e00397adf3 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-2011 Kohana Team
  9. * @license http://kohanaframework.org/license
  10. */
  11. class Kohana_Validation extends ArrayObject {
  12. /**
  13. * Creates a new Validation instance.
  14. *
  15. * @param 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. /**
  33. * Sets the unique "any field" key and creates an ArrayObject from the
  34. * passed array.
  35. *
  36. * @param array array to validate
  37. * @return void
  38. */
  39. public function __construct(array $array)
  40. {
  41. parent::__construct($array, ArrayObject::STD_PROP_LIST);
  42. }
  43. /**
  44. * Copies the current rule to a new array.
  45. *
  46. * $copy = $array->copy($new_data);
  47. *
  48. * @param array new data set
  49. * @return Validation
  50. * @since 3.0.5
  51. */
  52. public function copy(array $array)
  53. {
  54. // Create a copy of the current validation set
  55. $copy = clone $this;
  56. // Replace the data set
  57. $copy->exchangeArray($array);
  58. return $copy;
  59. }
  60. /**
  61. * Returns the array representation of the current object.
  62. *
  63. * @return array
  64. */
  65. public function as_array()
  66. {
  67. return $this->getArrayCopy();
  68. }
  69. /**
  70. * Sets or overwrites the label name for a field.
  71. *
  72. * @param string field name
  73. * @param string label
  74. * @return $this
  75. */
  76. public function label($field, $label)
  77. {
  78. // Set the label for this field
  79. $this->_labels[$field] = $label;
  80. return $this;
  81. }
  82. /**
  83. * Sets labels using an array.
  84. *
  85. * @param array list of field => label names
  86. * @return $this
  87. */
  88. public function labels(array $labels)
  89. {
  90. $this->_labels = $labels + $this->_labels;
  91. return $this;
  92. }
  93. /**
  94. * Overwrites or appends rules to a field. Each rule will be executed once.
  95. * All rules must be string names of functions method names. Parameters must
  96. * match the parameters of the callback function exactly
  97. *
  98. * Aliases you can use in callback parameters:
  99. * - :validation - the validation object
  100. * - :field - the field name
  101. * - :value - the value of the field
  102. *
  103. * // The "username" must not be empty and have a minimum length of 4
  104. * $validation->rule('username', 'not_empty')
  105. * ->rule('username', 'min_length', array('username', 4));
  106. *
  107. * // The "password" field must match the "password_repeat" field
  108. * $validation->rule('password', 'matches', array(':validation', 'password', 'password_repeat'));
  109. *
  110. * @param string field name
  111. * @param callback valid PHP callback
  112. * @param array extra parameters for the rule
  113. * @return $this
  114. */
  115. public function rule($field, $rule, array $params = NULL)
  116. {
  117. if ($params === NULL)
  118. {
  119. // Default to array(':value')
  120. $params = array(':value');
  121. }
  122. if ($field !== TRUE AND ! isset($this->_labels[$field]))
  123. {
  124. // Set the field label to the field name
  125. $this->_labels[$field] = preg_replace('/[^\pL]+/u', ' ', $field);
  126. }
  127. // Store the rule and params for this rule
  128. $this->_rules[$field][] = array($rule, $params);
  129. return $this;
  130. }
  131. /**
  132. * Add rules using an array.
  133. *
  134. * @param string field name
  135. * @param array list of callbacks
  136. * @return $this
  137. */
  138. public function rules($field, array $rules)
  139. {
  140. foreach ($rules as $rule)
  141. {
  142. $this->rule($field, $rule[0], Arr::get($rule, 1));
  143. }
  144. return $this;
  145. }
  146. /**
  147. * Bind a value to a parameter definition.
  148. *
  149. * // This allows you to use :model in the parameter definition of rules
  150. * $validation->bind(':model', $model)
  151. * ->rule('status', 'valid_status', array(':model'));
  152. *
  153. * @param string variable name or an array of variables
  154. * @param mixed value
  155. * @return $this
  156. */
  157. public function bind($key, $value = NULL)
  158. {
  159. if (is_array($key))
  160. {
  161. foreach ($key as $name => $value)
  162. {
  163. $this->_bound[$name] = $value;
  164. }
  165. }
  166. else
  167. {
  168. $this->_bound[$key] = $value;
  169. }
  170. return $this;
  171. }
  172. /**
  173. * Executes all validation rules. This should
  174. * typically be called within an if/else block.
  175. *
  176. * if ($validation->check())
  177. * {
  178. * // The data is valid, do something here
  179. * }
  180. *
  181. * @param boolean allow empty array?
  182. * @return boolean
  183. */
  184. public function check()
  185. {
  186. if (Kohana::$profiling === TRUE)
  187. {
  188. // Start a new benchmark
  189. $benchmark = Profiler::start('Validation', __FUNCTION__);
  190. }
  191. // New data set
  192. $data = $this->_errors = array();
  193. // Store the original data because this class should not modify it post-validation
  194. $original = $this->getArrayCopy();
  195. // Get a list of the expected fields
  196. $expected = Arr::merge(array_keys($original), array_keys($this->_labels));
  197. // Import the rules locally
  198. $rules = $this->_rules;
  199. foreach ($expected as $field)
  200. {
  201. if (isset($this[$field]))
  202. {
  203. // Use the submitted value
  204. $data[$field] = $this[$field];
  205. }
  206. else
  207. {
  208. // No data exists for this field
  209. $data[$field] = NULL;
  210. }
  211. if (isset($rules[TRUE]))
  212. {
  213. if ( ! isset($rules[$field]))
  214. {
  215. // Initialize the rules for this field
  216. $rules[$field] = array();
  217. }
  218. // Append the rules
  219. $rules[$field] = array_merge($rules[$field], $rules[TRUE]);
  220. }
  221. }
  222. // Overload the current array with the new one
  223. $this->exchangeArray($data);
  224. // Remove the rules that apply to every field
  225. unset($rules[TRUE]);
  226. // Bind the validation object to :validation
  227. $this->bind(':validation', $this);
  228. // Execute the rules
  229. foreach ($rules as $field => $set)
  230. {
  231. // Get the field value
  232. $value = $this[$field];
  233. // Bind the field name and value to :field and :value respectively
  234. $this->bind(array
  235. (
  236. ':field' => $field,
  237. ':value' => $value,
  238. ));
  239. foreach ($set as $array)
  240. {
  241. // Rules are defined as array($rule, $params)
  242. list($rule, $params) = $array;
  243. foreach ($params as $key => $param)
  244. {
  245. if (is_string($param) AND array_key_exists($param, $this->_bound))
  246. {
  247. // Replace with bound value
  248. $params[$key] = $this->_bound[$param];
  249. }
  250. }
  251. if (is_array($rule) OR ! is_string($rule))
  252. {
  253. // This is either a callback as an array or a lambda
  254. $passed = call_user_func_array($rule, $params);
  255. }
  256. elseif (method_exists('Valid', $rule))
  257. {
  258. // Use a method in this object
  259. $method = new ReflectionMethod('Valid', $rule);
  260. // Call static::$rule($this[$field], $param, ...) with Reflection
  261. $passed = $method->invokeArgs(NULL, $params);
  262. }
  263. elseif (strpos($rule, '::') === FALSE)
  264. {
  265. // Use a function call
  266. $function = new ReflectionFunction($rule);
  267. // Call $function($this[$field], $param, ...) with Reflection
  268. $passed = $function->invokeArgs($params);
  269. }
  270. else
  271. {
  272. // Split the class and method of the rule
  273. list($class, $method) = explode('::', $rule, 2);
  274. // Use a static method call
  275. $method = new ReflectionMethod($class, $method);
  276. // Call $Class::$method($this[$field], $param, ...) with Reflection
  277. $passed = $method->invokeArgs(NULL, $params);
  278. }
  279. // Ignore return values from rules when the field is empty
  280. if ( ! in_array($rule, $this->_empty_rules) AND ! Valid::not_empty($value))
  281. continue;
  282. if ($passed === FALSE)
  283. {
  284. // Add the rule to the errors
  285. $this->error($field, $rule, $params);
  286. // This field has an error, stop executing rules
  287. break;
  288. }
  289. }
  290. }
  291. // Restore the data to its original form
  292. $this->exchangeArray($original);
  293. if (isset($benchmark))
  294. {
  295. // Stop benchmarking
  296. Profiler::stop($benchmark);
  297. }
  298. return empty($this->_errors);
  299. }
  300. /**
  301. * Add an error to a field.
  302. *
  303. * @param string field name
  304. * @param string error message
  305. * @return $this
  306. */
  307. public function error($field, $error, array $params = NULL)
  308. {
  309. $this->_errors[$field] = array($error, $params);
  310. return $this;
  311. }
  312. /**
  313. * Returns the error messages. If no file is specified, the error message
  314. * will be the name of the rule that failed. When a file is specified, the
  315. * message will be loaded from "field/rule", or if no rule-specific message
  316. * exists, "field/default" will be used. If neither is set, the returned
  317. * message will be "file/field/rule".
  318. *
  319. * By default all messages are translated using the default language.
  320. * A string can be used as the second parameter to specified the language
  321. * that the message was written in.
  322. *
  323. * // Get errors from messages/forms/login.php
  324. * $errors = $Validation->errors('forms/login');
  325. *
  326. * @uses Kohana::message
  327. * @param string file to load error messages from
  328. * @param mixed translate the message
  329. * @return array
  330. */
  331. public function errors($file = NULL, $translate = TRUE)
  332. {
  333. if ($file === NULL)
  334. {
  335. // Return the error list
  336. return $this->_errors;
  337. }
  338. // Create a new message list
  339. $messages = array();
  340. foreach ($this->_errors as $field => $set)
  341. {
  342. list($error, $params) = $set;
  343. // Get the label for this field
  344. $label = $this->_labels[$field];
  345. if ($translate)
  346. {
  347. if (is_string($translate))
  348. {
  349. // Translate the label using the specified language
  350. $label = __($label, NULL, $translate);
  351. }
  352. else
  353. {
  354. // Translate the label
  355. $label = __($label);
  356. }
  357. }
  358. // Start the translation values list
  359. $values = array(
  360. ':field' => $label,
  361. ':value' => Arr::get($this, $field),
  362. );
  363. if (is_array($values[':value']))
  364. {
  365. // All values must be strings
  366. $values[':value'] = implode(', ', Arr::flatten($values[':value']));
  367. }
  368. if ($params)
  369. {
  370. foreach ($params as $key => $value)
  371. {
  372. if (is_array($value))
  373. {
  374. // All values must be strings
  375. $value = implode(', ', Arr::flatten($value));
  376. }
  377. elseif (is_object($value))
  378. {
  379. // Objects cannot be used in message files
  380. continue;
  381. }
  382. // Check if a label for this parameter exists
  383. if (isset($this->_labels[$value]))
  384. {
  385. // Use the label as the value, eg: related field name for "matches"
  386. $value = $this->_labels[$value];
  387. if ($translate)
  388. {
  389. if (is_string($translate))
  390. {
  391. // Translate the value using the specified language
  392. $value = __($value, NULL, $translate);
  393. }
  394. else
  395. {
  396. // Translate the value
  397. $value = __($value);
  398. }
  399. }
  400. }
  401. // Add each parameter as a numbered value, starting from 1
  402. $values[':param'.($key + 1)] = $value;
  403. }
  404. }
  405. if ($message = Kohana::message($file, "{$field}.{$error}"))
  406. {
  407. // Found a message for this field and error
  408. }
  409. elseif ($message = Kohana::message($file, "{$field}.default"))
  410. {
  411. // Found a default message for this field
  412. }
  413. elseif ($message = Kohana::message($file, $error))
  414. {
  415. // Found a default message for this error
  416. }
  417. elseif ($message = Kohana::message('validation', $error))
  418. {
  419. // Found a default message for this error
  420. }
  421. else
  422. {
  423. // No message exists, display the path expected
  424. $message = "{$file}.{$field}.{$error}";
  425. }
  426. if ($translate)
  427. {
  428. if (is_string($translate))
  429. {
  430. // Translate the message using specified language
  431. $message = __($message, $values, $translate);
  432. }
  433. else
  434. {
  435. // Translate the message using the default language
  436. $message = __($message, $values);
  437. }
  438. }
  439. else
  440. {
  441. // Do not translate, just replace the values
  442. $message = strtr($message, $values);
  443. }
  444. // Set the message for this field
  445. $messages[$field] = $message;
  446. }
  447. return $messages;
  448. }
  449. } // End Validation