ValidationRuleParser.php 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. <?php
  2. namespace Illuminate\Validation;
  3. use Closure;
  4. use Illuminate\Support\Arr;
  5. use Illuminate\Support\Str;
  6. use Illuminate\Validation\Rules\Exists;
  7. use Illuminate\Validation\Rules\Unique;
  8. use Illuminate\Contracts\Validation\Rule as RuleContract;
  9. class ValidationRuleParser
  10. {
  11. /**
  12. * The data being validated.
  13. *
  14. * @var array
  15. */
  16. public $data;
  17. /**
  18. * The implicit attributes.
  19. *
  20. * @var array
  21. */
  22. public $implicitAttributes = [];
  23. /**
  24. * Create a new validation rule parser.
  25. *
  26. * @param array $data
  27. * @return void
  28. */
  29. public function __construct(array $data)
  30. {
  31. $this->data = $data;
  32. }
  33. /**
  34. * Parse the human-friendly rules into a full rules array for the validator.
  35. *
  36. * @param array $rules
  37. * @return \stdClass
  38. */
  39. public function explode($rules)
  40. {
  41. $this->implicitAttributes = [];
  42. $rules = $this->explodeRules($rules);
  43. return (object) [
  44. 'rules' => $rules,
  45. 'implicitAttributes' => $this->implicitAttributes,
  46. ];
  47. }
  48. /**
  49. * Explode the rules into an array of explicit rules.
  50. *
  51. * @param array $rules
  52. * @return array
  53. */
  54. protected function explodeRules($rules)
  55. {
  56. foreach ($rules as $key => $rule) {
  57. if (Str::contains($key, '*')) {
  58. $rules = $this->explodeWildcardRules($rules, $key, [$rule]);
  59. unset($rules[$key]);
  60. } else {
  61. $rules[$key] = $this->explodeExplicitRule($rule);
  62. }
  63. }
  64. return $rules;
  65. }
  66. /**
  67. * Explode the explicit rule into an array if necessary.
  68. *
  69. * @param mixed $rule
  70. * @return array
  71. */
  72. protected function explodeExplicitRule($rule)
  73. {
  74. if (is_string($rule)) {
  75. return explode('|', $rule);
  76. } elseif (is_object($rule)) {
  77. return [$this->prepareRule($rule)];
  78. }
  79. return array_map([$this, 'prepareRule'], $rule);
  80. }
  81. /**
  82. * Prepare the given rule for the Validator.
  83. *
  84. * @param mixed $rule
  85. * @return mixed
  86. */
  87. protected function prepareRule($rule)
  88. {
  89. if ($rule instanceof Closure) {
  90. $rule = new ClosureValidationRule($rule);
  91. }
  92. if (! is_object($rule) ||
  93. $rule instanceof RuleContract ||
  94. ($rule instanceof Exists && $rule->queryCallbacks()) ||
  95. ($rule instanceof Unique && $rule->queryCallbacks())) {
  96. return $rule;
  97. }
  98. return (string) $rule;
  99. }
  100. /**
  101. * Define a set of rules that apply to each element in an array attribute.
  102. *
  103. * @param array $results
  104. * @param string $attribute
  105. * @param string|array $rules
  106. * @return array
  107. */
  108. protected function explodeWildcardRules($results, $attribute, $rules)
  109. {
  110. $pattern = str_replace('\*', '[^\.]*', preg_quote($attribute));
  111. $data = ValidationData::initializeAndGatherData($attribute, $this->data);
  112. foreach ($data as $key => $value) {
  113. if (Str::startsWith($key, $attribute) || (bool) preg_match('/^'.$pattern.'\z/', $key)) {
  114. foreach ((array) $rules as $rule) {
  115. $this->implicitAttributes[$attribute][] = $key;
  116. $results = $this->mergeRules($results, $key, $rule);
  117. }
  118. }
  119. }
  120. return $results;
  121. }
  122. /**
  123. * Merge additional rules into a given attribute(s).
  124. *
  125. * @param array $results
  126. * @param string|array $attribute
  127. * @param string|array $rules
  128. * @return array
  129. */
  130. public function mergeRules($results, $attribute, $rules = [])
  131. {
  132. if (is_array($attribute)) {
  133. foreach ((array) $attribute as $innerAttribute => $innerRules) {
  134. $results = $this->mergeRulesForAttribute($results, $innerAttribute, $innerRules);
  135. }
  136. return $results;
  137. }
  138. return $this->mergeRulesForAttribute(
  139. $results, $attribute, $rules
  140. );
  141. }
  142. /**
  143. * Merge additional rules into a given attribute.
  144. *
  145. * @param array $results
  146. * @param string $attribute
  147. * @param string|array $rules
  148. * @return array
  149. */
  150. protected function mergeRulesForAttribute($results, $attribute, $rules)
  151. {
  152. $merge = head($this->explodeRules([$rules]));
  153. $results[$attribute] = array_merge(
  154. isset($results[$attribute]) ? $this->explodeExplicitRule($results[$attribute]) : [], $merge
  155. );
  156. return $results;
  157. }
  158. /**
  159. * Extract the rule name and parameters from a rule.
  160. *
  161. * @param array|string $rules
  162. * @return array
  163. */
  164. public static function parse($rules)
  165. {
  166. if ($rules instanceof RuleContract) {
  167. return [$rules, []];
  168. }
  169. if (is_array($rules)) {
  170. $rules = static::parseArrayRule($rules);
  171. } else {
  172. $rules = static::parseStringRule($rules);
  173. }
  174. $rules[0] = static::normalizeRule($rules[0]);
  175. return $rules;
  176. }
  177. /**
  178. * Parse an array based rule.
  179. *
  180. * @param array $rules
  181. * @return array
  182. */
  183. protected static function parseArrayRule(array $rules)
  184. {
  185. return [Str::studly(trim(Arr::get($rules, 0))), array_slice($rules, 1)];
  186. }
  187. /**
  188. * Parse a string based rule.
  189. *
  190. * @param string $rules
  191. * @return array
  192. */
  193. protected static function parseStringRule($rules)
  194. {
  195. $parameters = [];
  196. // The format for specifying validation rules and parameters follows an
  197. // easy {rule}:{parameters} formatting convention. For instance the
  198. // rule "Max:3" states that the value may only be three letters.
  199. if (strpos($rules, ':') !== false) {
  200. list($rules, $parameter) = explode(':', $rules, 2);
  201. $parameters = static::parseParameters($rules, $parameter);
  202. }
  203. return [Str::studly(trim($rules)), $parameters];
  204. }
  205. /**
  206. * Parse a parameter list.
  207. *
  208. * @param string $rule
  209. * @param string $parameter
  210. * @return array
  211. */
  212. protected static function parseParameters($rule, $parameter)
  213. {
  214. $rule = strtolower($rule);
  215. if ($rule === 'regex' || $rule === 'not_regex' || $rule === 'notregex') {
  216. return [$parameter];
  217. }
  218. return str_getcsv($parameter);
  219. }
  220. /**
  221. * Normalizes a rule so that we can accept short types.
  222. *
  223. * @param string $rule
  224. * @return string
  225. */
  226. protected static function normalizeRule($rule)
  227. {
  228. switch ($rule) {
  229. case 'Int':
  230. return 'Integer';
  231. case 'Bool':
  232. return 'Boolean';
  233. default:
  234. return $rule;
  235. }
  236. }
  237. }