ValidationRule.php 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. <?php
  2. /**
  3. * ValidationRule.
  4. *
  5. * Provides the Model validation logic.
  6. *
  7. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  8. * Copyright (c) Cake Software Foundation, Inc. (https://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. (https://cakefoundation.org)
  15. * @link https://cakephp.org CakePHP(tm) Project
  16. * @since 2.2.0
  17. * @license https://opensource.org/licenses/mit-license.php MIT License
  18. */
  19. namespace Cake\Validation;
  20. use InvalidArgumentException;
  21. /**
  22. * ValidationRule object. Represents a validation method, error message and
  23. * rules for applying such method to a field.
  24. */
  25. class ValidationRule
  26. {
  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;
  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;
  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 = [])
  70. {
  71. $this->_addValidatorProps($validator);
  72. }
  73. /**
  74. * Returns whether this rule should break validation process for associated field
  75. * after it fails
  76. *
  77. * @return bool
  78. */
  79. public function isLast()
  80. {
  81. return (bool)$this->_last;
  82. }
  83. /**
  84. * Dispatches the validation rule to the given validator method and returns
  85. * a boolean indicating whether the rule passed or not. If a string is returned
  86. * it is assumed that the rule failed and the error message was given as a result.
  87. *
  88. * @param mixed $value The data to validate
  89. * @param array $providers associative array with objects or class names that will
  90. * be passed as the last argument for the validation method
  91. * @param array $context A key value list of data that could be used as context
  92. * during validation. Recognized keys are:
  93. * - newRecord: (boolean) whether or not the data to be validated belongs to a
  94. * new record
  95. * - data: The full data that was passed to the validation process
  96. * - field: The name of the field that is being processed
  97. * @return bool|string|array
  98. * @throws \InvalidArgumentException when the supplied rule is not a valid
  99. * callable for the configured scope
  100. */
  101. public function process($value, array $providers, array $context = [])
  102. {
  103. $context += ['data' => [], 'newRecord' => true, 'providers' => $providers];
  104. if ($this->_skip($context)) {
  105. return true;
  106. }
  107. if (!is_string($this->_rule) && is_callable($this->_rule)) {
  108. $callable = $this->_rule;
  109. $isCallable = true;
  110. } else {
  111. $provider = $providers[$this->_provider];
  112. $callable = [$provider, $this->_rule];
  113. $isCallable = is_callable($callable);
  114. }
  115. if (!$isCallable) {
  116. $message = 'Unable to call method "%s" in "%s" provider for field "%s"';
  117. throw new InvalidArgumentException(
  118. sprintf($message, $this->_rule, $this->_provider, $context['field'])
  119. );
  120. }
  121. if ($this->_pass) {
  122. $args = array_values(array_merge([$value], $this->_pass, [$context]));
  123. $result = $callable(...$args);
  124. } else {
  125. $result = $callable($value, $context);
  126. }
  127. if ($result === false) {
  128. return $this->_message ?: false;
  129. }
  130. return $result;
  131. }
  132. /**
  133. * Checks if the validation rule should be skipped
  134. *
  135. * @param array $context A key value list of data that could be used as context
  136. * during validation. Recognized keys are:
  137. * - newRecord: (boolean) whether or not the data to be validated belongs to a
  138. * new record
  139. * - data: The full data that was passed to the validation process
  140. * - providers associative array with objects or class names that will
  141. * be passed as the last argument for the validation method
  142. * @return bool True if the ValidationRule should be skipped
  143. */
  144. protected function _skip($context)
  145. {
  146. if (!is_string($this->_on) && is_callable($this->_on)) {
  147. $function = $this->_on;
  148. return !$function($context);
  149. }
  150. $newRecord = $context['newRecord'];
  151. if (!empty($this->_on)) {
  152. if (($this->_on === 'create' && !$newRecord) || ($this->_on === 'update' && $newRecord)) {
  153. return true;
  154. }
  155. }
  156. return false;
  157. }
  158. /**
  159. * Sets the rule properties from the rule entry in validate
  160. *
  161. * @param array $validator [optional]
  162. * @return void
  163. */
  164. protected function _addValidatorProps($validator = [])
  165. {
  166. foreach ($validator as $key => $value) {
  167. if (!isset($value) || empty($value)) {
  168. continue;
  169. }
  170. if ($key === 'rule' && is_array($value) && !is_callable($value)) {
  171. $this->_pass = array_slice($value, 1);
  172. $value = array_shift($value);
  173. }
  174. if (in_array($key, ['rule', 'on', 'message', 'last', 'provider', 'pass'], true)) {
  175. $this->{"_$key"} = $value;
  176. }
  177. }
  178. }
  179. /**
  180. * Returns the value of a property by name
  181. *
  182. * @param string $property The name of the property to retrieve.
  183. * @return mixed
  184. */
  185. public function get($property)
  186. {
  187. $property = '_' . $property;
  188. if (isset($this->{$property})) {
  189. return $this->{$property};
  190. }
  191. }
  192. }