ValidationRule.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. <?php
  2. /**
  3. * ValidationRule.
  4. *
  5. * Provides the Model validation logic.
  6. *
  7. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  8. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  9. *
  10. * Licensed under The MIT License
  11. * For full copyright and license information, please see the LICENSE.txt
  12. * Redistributions of files must retain the above copyright notice.
  13. *
  14. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  15. * @link http://cakephp.org CakePHP(tm) Project
  16. * @since 2.2.0
  17. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  18. */
  19. namespace Cake\Validation;
  20. /**
  21. * ValidationRule object. Represents a validation method, error message and
  22. * rules for applying such method to a field.
  23. *
  24. * @link http://book.cakephp.org/2.0/en/data-validation.html
  25. */
  26. class ValidationRule {
  27. /**
  28. * The method to be called for a given scope
  29. *
  30. * @var string|callable
  31. */
  32. protected $_rule;
  33. /**
  34. * The 'on' key
  35. *
  36. * @var string
  37. */
  38. protected $_on = null;
  39. /**
  40. * The 'last' key
  41. *
  42. * @var bool
  43. */
  44. protected $_last = false;
  45. /**
  46. * The 'message' key
  47. *
  48. * @var string
  49. */
  50. protected $_message = null;
  51. /**
  52. * Key under which the object or class where the method to be used for
  53. * validation will be found
  54. *
  55. * @var string
  56. */
  57. protected $_provider = 'default';
  58. /**
  59. * Extra arguments to be passed to the validation method
  60. *
  61. * @var array
  62. */
  63. protected $_pass = [];
  64. /**
  65. * Constructor
  66. *
  67. * @param array $validator [optional] The validator properties
  68. */
  69. public function __construct(array $validator = array()) {
  70. $this->_addValidatorProps($validator);
  71. }
  72. /**
  73. * Returns whether this rule should break validation process for associated field
  74. * after it fails
  75. *
  76. * @return bool
  77. */
  78. public function isLast() {
  79. return (bool)$this->_last;
  80. }
  81. /**
  82. * Dispatches the validation rule to the given validator method and returns
  83. * a boolean indicating whether the rule passed or not. If a string is returned
  84. * it is assumed that the rule failed and the error message was given as a result.
  85. *
  86. * @param mixed $value The data to validate
  87. * @param array $providers associative array with objects or class names that will
  88. * be passed as the last argument for the validation method
  89. * @param array $context A key value list of data that could be used as context
  90. * during validation. Recognized keys are:
  91. * - newRecord: (boolean) whether or not the data to be validated belongs to a
  92. * new record
  93. * - data: The full data that was passed to the validation process
  94. * - field: The name of the field that is being processed
  95. * @return bool|string
  96. * @throws \InvalidArgumentException when the supplied rule is not a valid
  97. * callable for the configured scope
  98. */
  99. public function process($value, array $providers, array $context = []) {
  100. $context += ['data' => [], 'newRecord' => true, 'providers' => $providers];
  101. if ($this->_skip($context)) {
  102. return true;
  103. }
  104. if (!is_string($this->_rule) && is_callable($this->_rule)) {
  105. $callable = $this->_rule;
  106. $isCallable = true;
  107. } else {
  108. $provider = $providers[$this->_provider];
  109. $callable = [$provider, $this->_rule];
  110. $isCallable = is_callable($callable);
  111. }
  112. if (!$isCallable) {
  113. $message = 'Unable to call method "%s" in "%s" provider for field "%s"';
  114. throw new \InvalidArgumentException(
  115. sprintf($message, $this->_rule, $this->_provider, $context['field'])
  116. );
  117. }
  118. if ($this->_pass) {
  119. $args = array_merge([$value], $this->_pass, [$context]);
  120. $result = call_user_func_array($callable, $args);
  121. } else {
  122. $result = $callable($value, $context);
  123. }
  124. if ($result === false) {
  125. return $this->_message ?: false;
  126. }
  127. return $result;
  128. }
  129. /**
  130. * Checks if the validation rule should be skipped
  131. *
  132. * @param array $context A key value list of data that could be used as context
  133. * during validation. Recognized keys are:
  134. * - newRecord: (boolean) whether or not the data to be validated belongs to a
  135. * new record
  136. * - data: The full data that was passed to the validation process
  137. * - providers associative array with objects or class names that will
  138. * be passed as the last argument for the validation method
  139. * @return bool True if the ValidationRule should be skipped
  140. */
  141. protected function _skip($context) {
  142. if (!is_string($this->_on) && is_callable($this->_on)) {
  143. $function = $this->_on;
  144. return !$function($context);
  145. }
  146. $newRecord = $context['newRecord'];
  147. if (!empty($this->_on)) {
  148. if ($this->_on === 'create' && !$newRecord || $this->_on === 'update' && $newRecord) {
  149. return true;
  150. }
  151. }
  152. return false;
  153. }
  154. /**
  155. * Sets the rule properties from the rule entry in validate
  156. *
  157. * @param array $validator [optional]
  158. * @return void
  159. */
  160. protected function _addValidatorProps($validator = array()) {
  161. foreach ($validator as $key => $value) {
  162. if (!isset($value) || empty($value)) {
  163. continue;
  164. }
  165. if ($key === 'rule' && is_array($value) && !is_callable($value)) {
  166. $this->_pass = array_slice($value, 1);
  167. $value = array_shift($value);
  168. }
  169. if (in_array($key, ['rule', 'on', 'message', 'last', 'provider', 'pass'])) {
  170. $this->{"_$key"} = $value;
  171. }
  172. }
  173. }
  174. }