PageRenderTime 49ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/3.0/obsolete/web_client/system/libraries/Validation.php

http://github.com/gallery/gallery3-contrib
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
  1. <?php defined('SYSPATH') OR die('No direct access allowed.');
  2. /**
  3. * Validation library.
  4. *
  5. * $Id: Validation.php 4713 2009-12-10 22:25:38Z isaiah $
  6. *
  7. * @package Validation
  8. * @author Kohana Team
  9. * @copyright (c) 2007-2009 Kohana Team
  10. * @license http://kohanaphp.com/license
  11. */
  12. class Validation_Core extends ArrayObject {
  13. // Filters
  14. protected $pre_filters = array();
  15. protected $post_filters = array();
  16. // Rules and callbacks
  17. protected $rules = array();
  18. protected $callbacks = array();
  19. // Rules that are allowed to run on empty fields
  20. protected $empty_rules = array('required', 'matches');
  21. // Errors
  22. protected $errors = array();
  23. protected $messages = array();
  24. // Field labels
  25. protected $labels = array();
  26. // Fields that are expected to be arrays
  27. protected $array_fields = array();
  28. /**
  29. * Creates a new Validation instance.
  30. *
  31. * @param array array to use for validation
  32. * @return object
  33. */
  34. public static function factory(array $array)
  35. {
  36. return new Validation($array);
  37. }
  38. /**
  39. * Sets the unique "any field" key and creates an ArrayObject from the
  40. * passed array.
  41. *
  42. * @param array array to validate
  43. * @return void
  44. */
  45. public function __construct(array $array)
  46. {
  47. parent::__construct($array, ArrayObject::ARRAY_AS_PROPS | ArrayObject::STD_PROP_LIST);
  48. }
  49. /**
  50. * Magic clone method, clears errors and messages.
  51. *
  52. * @return void
  53. */
  54. public function __clone()
  55. {
  56. $this->errors = array();
  57. $this->messages = array();
  58. }
  59. /**
  60. * Create a copy of the current validation rules and change the array.
  61. *
  62. * @chainable
  63. * @param array new array to validate
  64. * @return Validation
  65. */
  66. public function copy(array $array)
  67. {
  68. $copy = clone $this;
  69. $copy->exchangeArray($array);
  70. return $copy;
  71. }
  72. /**
  73. * Returns an array of all the field names that have filters, rules, or callbacks.
  74. *
  75. * @return array
  76. */
  77. public function field_names()
  78. {
  79. // All the fields that are being validated
  80. $fields = array_keys(array_merge
  81. (
  82. $this->pre_filters,
  83. $this->rules,
  84. $this->callbacks,
  85. $this->post_filters
  86. ));
  87. // Remove wildcard fields
  88. $fields = array_diff($fields, array('*'));
  89. return $fields;
  90. }
  91. /**
  92. * Returns the array values of the current object.
  93. *
  94. * @return array
  95. */
  96. public function as_array()
  97. {
  98. return $this->getArrayCopy();
  99. }
  100. /**
  101. * Returns the ArrayObject values, removing all inputs without rules.
  102. * To choose specific inputs, list the field name as arguments.
  103. *
  104. * @param boolean return only fields with filters, rules, and callbacks
  105. * @return array
  106. */
  107. public function safe_array()
  108. {
  109. // Load choices
  110. $choices = func_get_args();
  111. $choices = empty($choices) ? NULL : array_combine($choices, $choices);
  112. // Get field names
  113. $fields = $this->field_names();
  114. $safe = array();
  115. foreach ($fields as $field)
  116. {
  117. if ($choices === NULL OR isset($choices[$field]))
  118. {
  119. if (isset($this[$field]))
  120. {
  121. $value = $this[$field];
  122. if (is_object($value))
  123. {
  124. // Convert the value back into an array
  125. $value = $value->getArrayCopy();
  126. }
  127. }
  128. else
  129. {
  130. // Even if the field is not in this array, it must be set
  131. $value = NULL;
  132. }
  133. // Add the field to the array
  134. $safe[$field] = $value;
  135. }
  136. }
  137. return $safe;
  138. }
  139. /**
  140. * Add additional rules that will forced, even for empty fields. All arguments
  141. * passed will be appended to the list.
  142. *
  143. * @chainable
  144. * @param string rule name
  145. * @return object
  146. */
  147. public function allow_empty_rules($rules)
  148. {
  149. // Any number of args are supported
  150. $rules = func_get_args();
  151. // Merge the allowed rules
  152. $this->empty_rules = array_merge($this->empty_rules, $rules);
  153. return $this;
  154. }
  155. /**
  156. * Sets or overwrites the label name for a field.
  157. *
  158. * @param string field name
  159. * @param string label
  160. * @return $this
  161. */
  162. public function label($field, $label = NULL)
  163. {
  164. if ($label === NULL AND ($field !== TRUE OR $field !== '*') AND ! isset($this->labels[$field]))
  165. {
  166. // Set the field label to the field name
  167. $this->labels[$field] = ucfirst(preg_replace('/[^\pL]+/u', ' ', $field));
  168. }
  169. elseif ($label !== NULL)
  170. {
  171. // Set the label for this field
  172. $this->labels[$field] = $label;
  173. }
  174. return $this;
  175. }
  176. /**
  177. * Sets labels using an array.
  178. *
  179. * @param array list of field => label names
  180. * @return $this
  181. */
  182. public function labels(array $labels)
  183. {
  184. $this->labels = $labels + $this->labels;
  185. return $this;
  186. }
  187. /**
  188. * Converts a filter, rule, or callback into a fully-qualified callback array.
  189. *
  190. * @return mixed
  191. */
  192. protected function callback($callback)
  193. {
  194. if (is_string($callback))
  195. {
  196. if (strpos($callback, '::') !== FALSE)
  197. {
  198. $callback = explode('::', $callback);
  199. }
  200. elseif (function_exists($callback))
  201. {
  202. // No need to check if the callback is a method
  203. $callback = $callback;
  204. }
  205. elseif (method_exists($this, $callback))
  206. {
  207. // The callback exists in Validation
  208. $callback = array($this, $callback);
  209. }
  210. elseif (method_exists('valid', $callback))
  211. {
  212. // The callback exists in valid::
  213. $callback = array('valid', $callback);
  214. }
  215. }
  216. if ( ! is_callable($callback, FALSE))
  217. {
  218. if (is_array($callback))
  219. {
  220. if (is_object($callback[0]))
  221. {
  222. // Object instance syntax
  223. $name = get_class($callback[0]).'->'.$callback[1];
  224. }
  225. else
  226. {
  227. // Static class syntax
  228. $name = $callback[0].'::'.$callback[1];
  229. }
  230. }
  231. else
  232. {
  233. // Function syntax
  234. $name = $callback;
  235. }
  236. throw new Kohana_Exception('Callback %name% used for Validation is not callable', array('%name%' => $name));
  237. }
  238. return $callback;
  239. }
  240. /**
  241. * Add a pre-filter to one or more inputs. Pre-filters are applied before
  242. * rules or callbacks are executed.
  243. *
  244. * @chainable
  245. * @param callback filter
  246. * @param string fields to apply filter to, use TRUE for all fields
  247. * @return object
  248. */
  249. public function pre_filter($filter, $field = TRUE)
  250. {
  251. if ($field === TRUE OR $field === '*')
  252. {
  253. // Use wildcard
  254. $fields = array('*');
  255. }
  256. else
  257. {
  258. // Add the filter to specific inputs
  259. $fields = func_get_args();
  260. $fields = array_slice($fields, 1);
  261. }
  262. // Convert to a proper callback
  263. $filter = $this->callback($filter);
  264. foreach ($fields as $field)
  265. {
  266. // Add the filter to specified field
  267. $this->pre_filters[$field][] = $filter;
  268. }
  269. return $this;
  270. }
  271. /**
  272. * Add a post-filter to one or more inputs. Post-filters are applied after
  273. * rules and callbacks have been executed.
  274. *
  275. * @chainable
  276. * @param callback filter
  277. * @param string fields to apply filter to, use TRUE for all fields
  278. * @return object
  279. */
  280. public function post_filter($filter, $field = TRUE)
  281. {
  282. if ($field === TRUE)
  283. {
  284. // Use wildcard
  285. $fields = array('*');
  286. }
  287. else
  288. {
  289. // Add the filter to specific inputs
  290. $fields = func_get_args();
  291. $fields = array_slice($fields, 1);
  292. }
  293. // Convert to a proper callback
  294. $filter = $this->callback($filter);
  295. foreach ($fields as $field)
  296. {
  297. // Add the filter to specified field
  298. $this->post_filters[$field][] = $filter;
  299. }
  300. return $this;
  301. }
  302. /**
  303. * Add rules to a field. Validation rules may only return TRUE or FALSE and
  304. * can not manipulate the value of a field.
  305. *
  306. * @chainable
  307. * @param string field name
  308. * @param callback rules (one or more arguments)
  309. * @return object
  310. */
  311. public function add_rules($field, $rules)
  312. {
  313. // Get the rules
  314. $rules = func_get_args();
  315. $rules = array_slice($rules, 1);
  316. // Set a default field label
  317. $this->label($field);
  318. if ($field === TRUE)
  319. {
  320. // Use wildcard
  321. $field = '*';
  322. }
  323. foreach ($rules as $rule)
  324. {
  325. // Arguments for rule
  326. $args = NULL;
  327. // False rule
  328. $false_rule = FALSE;
  329. $rule_tmp = trim(is_string($rule) ? $rule : $rule[1]);
  330. // Should the rule return false?
  331. if ($rule_tmp !== ($rule_name = ltrim($rule_tmp, '! ')))
  332. {
  333. $false_rule = TRUE;
  334. }
  335. if (is_string($rule))
  336. {
  337. // Use the updated rule name
  338. $rule = $rule_name;
  339. // Have arguments?
  340. if (preg_match('/^([^\[]++)\[(.+)\]$/', $rule, $matches))
  341. {
  342. // Split the rule into the function and args
  343. $rule = $matches[1];
  344. $args = preg_split('/(?<!\\\\),\s*/', $matches[2]);
  345. // Replace escaped comma with comma
  346. $args = str_replace('\,', ',', $args);
  347. }
  348. }
  349. else
  350. {
  351. $rule[1] = $rule_name;
  352. }
  353. if ($rule === 'is_array')
  354. {
  355. // This field is expected to be an array
  356. $this->array_fields[$field] = $field;
  357. }
  358. // Convert to a proper callback
  359. $rule = $this->callback($rule);
  360. // Add the rule, with args, to the field
  361. $this->rules[$field][] = array($rule, $args, $false_rule);
  362. }
  363. return $this;
  364. }
  365. /**
  366. * Add callbacks to a field. Callbacks must accept the Validation object
  367. * and the input name. Callback returns are not processed.
  368. *
  369. * @chainable
  370. * @param string field name
  371. * @param callbacks callbacks (unlimited number)
  372. * @return object
  373. */
  374. public function add_callbacks($field, $callbacks)
  375. {
  376. // Get all callbacks as an array
  377. $callbacks = func_get_args();
  378. $callbacks = array_slice($callbacks, 1);
  379. // Set a default field label
  380. $this->label($field);
  381. if ($field === TRUE)
  382. {
  383. // Use wildcard
  384. $field = '*';
  385. }
  386. foreach ($callbacks as $callback)
  387. {
  388. // Convert to a proper callback
  389. $callback = $this->callback($callback);
  390. // Add the callback to specified field
  391. $this->callbacks[$field][] = $callback;
  392. }
  393. return $this;
  394. }
  395. /**
  396. * Validate by processing pre-filters, rules, callbacks, and post-filters.
  397. * All fields that have filters, rules, or callbacks will be initialized if
  398. * they are undefined. Validation will only be run if there is data already
  399. * in the array.
  400. *
  401. * @param object Validation object, used only for recursion
  402. * @param object name of field for errors
  403. * @return bool
  404. */
  405. public function validate($object = NULL, $field_name = NULL)
  406. {
  407. if ($object === NULL)
  408. {
  409. // Use the current object
  410. $object = $this;
  411. }
  412. $array = $this->safe_array();
  413. // Get all defined field names
  414. $fields = array_keys($array);
  415. foreach ($this->pre_filters as $field => $callbacks)
  416. {
  417. foreach ($callbacks as $callback)
  418. {
  419. if ($field === '*')
  420. {
  421. foreach ($fields as $f)
  422. {
  423. $array[$f] = is_array($array[$f]) ? array_map($callback, $array[$f]) : call_user_func($callback, $array[$f]);
  424. }
  425. }
  426. else
  427. {
  428. $array[$field] = is_array($array[$field]) ? array_map($callback, $array[$field]) : call_user_func($callback, $array[$field]);
  429. }
  430. }
  431. }
  432. foreach ($this->rules as $field => $callbacks)
  433. {
  434. foreach ($callbacks as $callback)
  435. {
  436. // Separate the callback, arguments and is false bool
  437. list ($callback, $args, $is_false) = $callback;
  438. // Function or method name of the rule
  439. $rule = is_array($callback) ? $callback[1] : $callback;
  440. if ($field === '*')
  441. {
  442. foreach ($fields as $f)
  443. {
  444. // Note that continue, instead of break, is used when
  445. // applying rules using a wildcard, so that all fields
  446. // will be validated.
  447. if (isset($this->errors[$f]))
  448. {
  449. // Prevent other rules from being evaluated if an error has occurred
  450. continue;
  451. }
  452. if (empty($array[$f]) AND ! in_array($rule, $this->empty_rules))
  453. {
  454. // This rule does not need to be processed on empty fields
  455. continue;
  456. }
  457. $result = ($args === NULL) ? call_user_func($callback, $array[$f]) : call_user_func($callback, $array[$f], $args);
  458. if (($result == $is_false))
  459. {
  460. $this->add_error($f, $rule, $args);
  461. // Stop validating this field when an error is found
  462. continue;
  463. }
  464. }
  465. }
  466. else
  467. {
  468. if (isset($this->errors[$field]))
  469. {
  470. // Prevent other rules from being evaluated if an error has occurred
  471. break;
  472. }
  473. if ( ! in_array($rule, $this->empty_rules) AND ! $this->required($array[$field]))
  474. {
  475. // This rule does not need to be processed on empty fields
  476. continue;
  477. }
  478. // Results of our test
  479. $result = ($args === NULL) ? call_user_func($callback, $array[$field]) : call_user_func($callback, $array[$field], $args);
  480. if (($result == $is_false))
  481. {
  482. $rule = $is_false ? '!'.$rule : $rule;
  483. $this->add_error($field, $rule, $args);
  484. // Stop validating this field when an error is found
  485. break;
  486. }
  487. }
  488. }
  489. }
  490. foreach ($this->callbacks as $field => $callbacks)
  491. {
  492. foreach ($callbacks as $callback)
  493. {
  494. if ($field === '*')
  495. {
  496. foreach ($fields as $f)
  497. {
  498. // Note that continue, instead of break, is used when
  499. // applying rules using a wildcard, so that all fields
  500. // will be validated.
  501. if (isset($this->errors[$f]))
  502. {
  503. // Stop validating this field when an error is found
  504. continue;
  505. }
  506. call_user_func($callback, $this, $f);
  507. }
  508. }
  509. else
  510. {
  511. if (isset($this->errors[$field]))
  512. {
  513. // Stop validating this field when an error is found
  514. break;
  515. }
  516. call_user_func($callback, $this, $field);
  517. }
  518. }
  519. }
  520. foreach ($this->post_filters as $field => $callbacks)
  521. {
  522. foreach ($callbacks as $callback)
  523. {
  524. if ($field === '*')
  525. {
  526. foreach ($fields as $f)
  527. {
  528. $array[$f] = is_array($array[$f]) ? array_map($callback, $array[$f]) : call_user_func($callback, $array[$f]);
  529. }
  530. }
  531. else
  532. {
  533. $array[$field] = is_array($array[$field]) ? array_map($callback, $array[$field]) : call_user_func($callback, $array[$field]);
  534. }
  535. }
  536. }
  537. // Swap the array back into the object
  538. $this->exchangeArray($array);
  539. // Return TRUE if there are no errors
  540. return $this->errors === array();
  541. }
  542. /**
  543. * Add an error to an input.
  544. *
  545. * @chainable
  546. * @param string input name
  547. * @param string unique error name
  548. * @param string arguments to pass to lang file
  549. * @return object
  550. */
  551. public function add_error($field, $name, $args = NULL)
  552. {
  553. $this->errors[$field] = array($name, $args);
  554. return $this;
  555. }
  556. /**
  557. * Return the errors array.
  558. *
  559. * @param boolean load errors from a message file
  560. * @return array
  561. */
  562. public function errors($file = NULL)
  563. {
  564. if ($file === NULL)
  565. {
  566. $errors = array();
  567. foreach($this->errors as $field => $error)
  568. {
  569. $errors[$field] = $error[0];
  570. }
  571. return $errors;
  572. }
  573. else
  574. {
  575. $errors = array();
  576. foreach ($this->errors as $input => $error)
  577. {
  578. // Locations to check for error messages
  579. $error_locations = array
  580. (
  581. "validation/{$file}.{$input}.{$error[0]}",
  582. "validation/{$file}.{$input}.default",
  583. "validation/default.{$error[0]}"
  584. );
  585. if (($message = Kohana::message($error_locations[0])) !== $error_locations[0])
  586. {
  587. // Found a message for this field and error
  588. }
  589. elseif (($message = Kohana::message($error_locations[1])) !== $error_locations[1])
  590. {
  591. // Found a default message for this field
  592. }
  593. elseif (($message = Kohana::message($error_locations[2])) !== $error_locations[2])
  594. {
  595. // Found a default message for this error
  596. }
  597. else
  598. {
  599. // No message exists, display the path expected
  600. $message = "validation/{$file}.{$input}.{$error[0]}";
  601. }
  602. // Start the translation values list
  603. $values = array(':field' => __($this->labels[$input]));
  604. if ( ! empty($error[1]))
  605. {
  606. foreach ($error[1] as $key => $value)
  607. {
  608. // Add each parameter as a numbered value, starting from 1
  609. $values[':param'.($key + 1)] = __($value);
  610. }
  611. }
  612. // Translate the message using the default language
  613. $errors[$input] = __($message, $values);
  614. }
  615. return $errors;
  616. }
  617. }
  618. /**
  619. * Rule: required. Generates an error if the field has an empty value.
  620. *
  621. * @param mixed input value
  622. * @return bool
  623. */
  624. public function required($str)
  625. {
  626. if (is_object($str) AND $str instanceof ArrayObject)
  627. {
  628. // Get the array from the ArrayObject
  629. $str = $str->getArrayCopy();
  630. }
  631. if (is_array($str))
  632. {
  633. return ! empty($str);
  634. }
  635. else
  636. {
  637. return ! ($str === '' OR $str === NULL OR $str === FALSE);
  638. }
  639. }
  640. /**
  641. * Rule: matches. Generates an error if the field does not match one or more
  642. * other fields.
  643. *
  644. * @param mixed input value
  645. * @param array input names to match against
  646. * @return bool
  647. */
  648. public function matches($str, array $inputs)
  649. {
  650. foreach ($inputs as $key)
  651. {
  652. if ($str !== (isset($this[$key]) ? $this[$key] : NULL))
  653. return FALSE;
  654. }
  655. return TRUE;
  656. }
  657. /**
  658. * Rule: length. Generates an error if the field is too long or too short.
  659. *
  660. * @param mixed input value
  661. * @param array minimum, maximum, or exact length to match
  662. * @return bool
  663. */
  664. public function length($str, array $length)
  665. {
  666. if ( ! is_string($str))
  667. return FALSE;
  668. $size = mb_strlen($str);
  669. $status = FALSE;
  670. if (count($length) > 1)
  671. {
  672. list ($min, $max) = $length;
  673. if ($size >= $min AND $size <= $max)
  674. {
  675. $status = TRUE;
  676. }
  677. }
  678. else
  679. {
  680. $status = ($size === (int) $length[0]);
  681. }
  682. return $status;
  683. }
  684. /**
  685. * Rule: depends_on. Generates an error if the field does not depend on one
  686. * or more other fields.
  687. *
  688. * @param mixed field name
  689. * @param array field names to check dependency
  690. * @return bool
  691. */
  692. public function depends_on($field, array $fields)
  693. {
  694. foreach ($fields as $depends_on)
  695. {
  696. if ( ! isset($this[$depends_on]) OR $this[$depends_on] == NULL)
  697. return FALSE;
  698. }
  699. return TRUE;
  700. }
  701. /**
  702. * Rule: chars. Generates an error if the field contains characters outside of the list.
  703. *
  704. * @param string field value
  705. * @param array allowed characters
  706. * @return bool
  707. */
  708. public function chars($value, array $chars)
  709. {
  710. return ! preg_match('![^'.implode('', $chars).']!u', $value);
  711. }
  712. } // End Validation