PluginProbe
WPIDE – File Manager & Code Editor / 3.1
WPIDE – File Manager & Code Editor v3.1
3.5.8 3.5.7 2.0.14 2.0.15 2.0.16 2.0.2 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1 2.2 2.3 2.3.1 2.3.2 2.4.0 2.5 2.6 3.0 3.1 3.2 3.3 3.4 All 54 releases
wpide / vendor / rakit / validation / src / Validation.php

Validation.php in WPIDE – File Manager & Code Editor 3.1, at vendor/rakit/validation/src/Validation.php

717 lines 19.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Rakit\Validation;
4
5 use Closure;
6 use Rakit\Validation\Rules\Interfaces\BeforeValidate;
7 use Rakit\Validation\Rules\Interfaces\ModifyValue;
8 use Rakit\Validation\Rules\Required;
9
10 class Validation
11 {
12 use Traits\TranslationsTrait, Traits\MessagesTrait;
13
14 /** @var mixed */
15 protected $validator;
16
17 /** @var array */
18 protected $inputs = [];
19
20 /** @var array */
21 protected $attributes = [];
22
23 /** @var array */
24 protected $aliases = [];
25
26 /** @var string */
27 protected $messageSeparator = ':';
28
29 /** @var array */
30 protected $validData = [];
31
32 /** @var array */
33 protected $invalidData = [];
34
35 /** @var ErrorBag */
36 public $errors;
37
38 /**
39 * Constructor
40 *
41 * @param \Rakit\Validation\Validator $validator
42 * @param array $inputs
43 * @param array $rules
44 * @param array $messages
45 * @return void
46 */
47 public function __construct(
48 Validator $validator,
49 array $inputs,
50 array $rules,
51 array $messages = []
52 ) {
53 $this->validator = $validator;
54 $this->inputs = $this->resolveInputAttributes($inputs);
55 $this->messages = $messages;
56 $this->errors = new ErrorBag;
57 foreach ($rules as $attributeKey => $rules) {
58 $this->addAttribute($attributeKey, $rules);
59 }
60 }
61
62 /**
63 * Add attribute rules
64 *
65 * @param string $attributeKey
66 * @param string|array $rules
67 * @return void
68 */
69 public function addAttribute(string $attributeKey, $rules)
70 {
71 $resolvedRules = $this->resolveRules($rules);
72 $attribute = new Attribute($this, $attributeKey, $this->getAlias($attributeKey), $resolvedRules);
73 $this->attributes[$attributeKey] = $attribute;
74 }
75
76 /**
77 * Get attribute by key
78 *
79 * @param string $attributeKey
80 * @return null|\Rakit\Validation\Attribute
81 */
82 public function getAttribute(string $attributeKey)
83 {
84 return isset($this->attributes[$attributeKey])? $this->attributes[$attributeKey] : null;
85 }
86
87 /**
88 * Run validation
89 *
90 * @param array $inputs
91 * @return void
92 */
93 public function validate(array $inputs = [])
94 {
95 $this->errors = new ErrorBag; // reset error bag
96 $this->inputs = array_merge($this->inputs, $this->resolveInputAttributes($inputs));
97
98 // Before validation hooks
99 foreach ($this->attributes as $attributeKey => $attribute) {
100 foreach ($attribute->getRules() as $rule) {
101 if ($rule instanceof BeforeValidate) {
102 $rule->beforeValidate();
103 }
104 }
105 }
106
107 foreach ($this->attributes as $attributeKey => $attribute) {
108 $this->validateAttribute($attribute);
109 }
110 }
111
112 /**
113 * Get ErrorBag instance
114 *
115 * @return \Rakit\Validation\ErrorBag
116 */
117 public function errors(): ErrorBag
118 {
119 return $this->errors;
120 }
121
122 /**
123 * Validate attribute
124 *
125 * @param \Rakit\Validation\Attribute $attribute
126 * @return void
127 */
128 protected function validateAttribute(Attribute $attribute)
129 {
130 if ($this->isArrayAttribute($attribute)) {
131 $attributes = $this->parseArrayAttribute($attribute);
132 foreach ($attributes as $i => $attr) {
133 $this->validateAttribute($attr);
134 }
135 return;
136 }
137
138 $attributeKey = $attribute->getKey();
139 $rules = $attribute->getRules();
140
141 $value = $this->getValue($attributeKey);
142 $isEmptyValue = $this->isEmptyValue($value);
143
144 if ($attribute->hasRule('nullable') && $isEmptyValue) {
145 $rules = [];
146 }
147
148 $isValid = true;
149 foreach ($rules as $ruleValidator) {
150 $ruleValidator->setAttribute($attribute);
151
152 if ($ruleValidator instanceof ModifyValue) {
153 $value = $ruleValidator->modifyValue($value);
154 $isEmptyValue = $this->isEmptyValue($value);
155 }
156
157 $valid = $ruleValidator->check($value);
158
159 if ($isEmptyValue and $this->ruleIsOptional($attribute, $ruleValidator)) {
160 continue;
161 }
162
163 if (!$valid) {
164 $isValid = false;
165 $this->addError($attribute, $value, $ruleValidator);
166 if ($ruleValidator->isImplicit()) {
167 break;
168 }
169 }
170 }
171
172 if ($isValid) {
173 $this->setValidData($attribute, $value);
174 } else {
175 $this->setInvalidData($attribute, $value);
176 }
177 }
178
179 /**
180 * Check whether given $attribute is array attribute
181 *
182 * @param \Rakit\Validation\Attribute $attribute
183 * @return bool
184 */
185 protected function isArrayAttribute(Attribute $attribute): bool
186 {
187 $key = $attribute->getKey();
188 return strpos($key, '*') !== false;
189 }
190
191 /**
192 * Parse array attribute into it's child attributes
193 *
194 * @param \Rakit\Validation\Attribute $attribute
195 * @return array
196 */
197 protected function parseArrayAttribute(Attribute $attribute): array
198 {
199 $attributeKey = $attribute->getKey();
200 $data = Helper::arrayDot($this->initializeAttributeOnData($attributeKey));
201
202 $pattern = str_replace('\*', '([^\.]+)', preg_quote($attributeKey));
203
204 $data = array_merge($data, $this->extractValuesForWildcards(
205 $data,
206 $attributeKey
207 ));
208
209 $attributes = [];
210
211 foreach ($data as $key => $value) {
212 if ((bool) preg_match('/^'.$pattern.'\z/', $key, $match)) {
213 $attr = new Attribute($this, $key, null, $attribute->getRules());
214 $attr->setPrimaryAttribute($attribute);
215 $attr->setKeyIndexes(array_slice($match, 1));
216 $attributes[] = $attr;
217 }
218 }
219
220 // set other attributes to each attributes
221 foreach ($attributes as $i => $attr) {
222 $otherAttributes = $attributes;
223 unset($otherAttributes[$i]);
224 $attr->setOtherAttributes($otherAttributes);
225 }
226
227 return $attributes;
228 }
229
230 /**
231 * Gather a copy of the attribute data filled with any missing attributes.
232 * Adapted from: https://github.com/illuminate/validation/blob/v5.3.23/Validator.php#L334
233 *
234 * @param string $attribute
235 * @return array
236 */
237 protected function initializeAttributeOnData(string $attributeKey): array
238 {
239 $explicitPath = $this->getLeadingExplicitAttributePath($attributeKey);
240
241 $data = $this->extractDataFromPath($explicitPath);
242
243 $asteriskPos = strpos($attributeKey, '*');
244
245 if (false === $asteriskPos || $asteriskPos === (mb_strlen($attributeKey, 'UTF-8') - 1)) {
246 return $data;
247 }
248
249 return Helper::arraySet($data, $attributeKey, null, true);
250 }
251
252 /**
253 * Get all of the exact attribute values for a given wildcard attribute.
254 * Adapted from: https://github.com/illuminate/validation/blob/v5.3.23/Validator.php#L354
255 *
256 * @param array $data
257 * @param string $attributeKey
258 * @return array
259 */
260 public function extractValuesForWildcards(array $data, string $attributeKey): array
261 {
262 $keys = [];
263
264 $pattern = str_replace('\*', '[^\.]+', preg_quote($attributeKey));
265
266 foreach ($data as $key => $value) {
267 if ((bool) preg_match('/^'.$pattern.'/', $key, $matches)) {
268 $keys[] = $matches[0];
269 }
270 }
271
272 $keys = array_unique($keys);
273
274 $data = [];
275
276 foreach ($keys as $key) {
277 $data[$key] = Helper::arrayGet($this->inputs, $key);
278 }
279
280 return $data;
281 }
282
283 /**
284 * Get the explicit part of the attribute name.
285 * Adapted from: https://github.com/illuminate/validation/blob/v5.3.23/Validator.php#L2817
286 *
287 * E.g. 'foo.bar.*.baz' -> 'foo.bar'
288 *
289 * Allows us to not spin through all of the flattened data for some operations.
290 *
291 * @param string $attributeKey
292 * @return string|null null when root wildcard
293 */
294 protected function getLeadingExplicitAttributePath(string $attributeKey)
295 {
296 return rtrim(explode('*', $attributeKey)[0], '.') ?: null;
297 }
298
299 /**
300 * Extract data based on the given dot-notated path.
301 * Adapted from: https://github.com/illuminate/validation/blob/v5.3.23/Validator.php#L2830
302 *
303 * Used to extract a sub-section of the data for faster iteration.
304 *
305 * @param string|null $attributeKey
306 * @return array
307 */
308 protected function extractDataFromPath($attributeKey): array
309 {
310 $results = [];
311
312 $value = Helper::arrayGet($this->inputs, $attributeKey, '__missing__');
313
314 if ($value != '__missing__') {
315 Helper::arraySet($results, $attributeKey, $value);
316 }
317
318 return $results;
319 }
320
321 /**
322 * Add error to the $this->errors
323 *
324 * @param \Rakit\Validation\Attribute $attribute
325 * @param mixed $value
326 * @param \Rakit\Validation\Rule $ruleValidator
327 * @return void
328 */
329 protected function addError(Attribute $attribute, $value, Rule $ruleValidator)
330 {
331 $ruleName = $ruleValidator->getKey();
332 $message = $this->resolveMessage($attribute, $value, $ruleValidator);
333
334 $this->errors->add($attribute->getKey(), $ruleName, $message);
335 }
336
337 /**
338 * Check $value is empty value
339 *
340 * @param mixed $value
341 * @return boolean
342 */
343 protected function isEmptyValue($value): bool
344 {
345 $requiredValidator = new Required;
346 return false === $requiredValidator->check($value, []);
347 }
348
349 /**
350 * Check the rule is optional
351 *
352 * @param \Rakit\Validation\Attribute $attribute
353 * @param \Rakit\Validation\Rule $rule
354 * @return bool
355 */
356 protected function ruleIsOptional(Attribute $attribute, Rule $rule): bool
357 {
358 return false === $attribute->isRequired() and
359 false === $rule->isImplicit() and
360 false === $rule instanceof Required;
361 }
362
363 /**
364 * Resolve attribute name
365 *
366 * @param \Rakit\Validation\Attribute $attribute
367 * @return string
368 */
369 protected function resolveAttributeName(Attribute $attribute): string
370 {
371 $primaryAttribute = $attribute->getPrimaryAttribute();
372 if (isset($this->aliases[$attribute->getKey()])) {
373 return $this->aliases[$attribute->getKey()];
374 } elseif ($primaryAttribute and isset($this->aliases[$primaryAttribute->getKey()])) {
375 return $this->aliases[$primaryAttribute->getKey()];
376 } elseif ($this->validator->isUsingHumanizedKey()) {
377 return $attribute->getHumanizedKey();
378 } else {
379 return $attribute->getKey();
380 }
381 }
382
383 /**
384 * Resolve message
385 *
386 * @param \Rakit\Validation\Attribute $attribute
387 * @param mixed $value
388 * @param \Rakit\Validation\Rule $validator
389 * @return mixed
390 */
391 protected function resolveMessage(Attribute $attribute, $value, Rule $validator): string
392 {
393 $primaryAttribute = $attribute->getPrimaryAttribute();
394 $params = array_merge($validator->getParameters(), $validator->getParametersTexts());
395 $attributeKey = $attribute->getKey();
396 $ruleKey = $validator->getKey();
397 $alias = $attribute->getAlias() ?: $this->resolveAttributeName($attribute);
398 $message = $validator->getMessage(); // default rule message
399 $messageKeys = [
400 $attributeKey.$this->messageSeparator.$ruleKey,
401 $attributeKey,
402 $ruleKey
403 ];
404
405 if ($primaryAttribute) {
406 // insert primaryAttribute keys
407 // $messageKeys = [
408 // $attributeKey.$this->messageSeparator.$ruleKey,
409 // >> here [1] <<
410 // $attributeKey,
411 // >> and here [3] <<
412 // $ruleKey
413 // ];
414 $primaryAttributeKey = $primaryAttribute->getKey();
415 array_splice($messageKeys, 1, 0, $primaryAttributeKey.$this->messageSeparator.$ruleKey);
416 array_splice($messageKeys, 3, 0, $primaryAttributeKey);
417 }
418
419 foreach ($messageKeys as $key) {
420 if (isset($this->messages[$key])) {
421 $message = $this->messages[$key];
422 break;
423 }
424 }
425
426 // Replace message params
427 $vars = array_merge($params, [
428 'attribute' => $alias,
429 'value' => $value,
430 ]);
431
432 foreach ($vars as $key => $value) {
433 $value = $this->stringify($value);
434 $message = str_replace(':'.$key, $value, $message);
435 }
436
437 // Replace key indexes
438 $keyIndexes = $attribute->getKeyIndexes();
439 foreach ($keyIndexes as $pathIndex => $index) {
440 $replacers = [
441 "[{$pathIndex}]" => $index,
442 ];
443
444 if (is_numeric($index)) {
445 $replacers["{{$pathIndex}}"] = $index + 1;
446 }
447
448 $message = str_replace(array_keys($replacers), array_values($replacers), $message);
449 }
450
451 return $message;
452 }
453
454 /**
455 * Stringify $value
456 *
457 * @param mixed $value
458 * @return string
459 */
460 protected function stringify($value): string
461 {
462 if (is_string($value) || is_numeric($value)) {
463 return $value;
464 } elseif (is_array($value) || is_object($value)) {
465 return json_encode($value);
466 } else {
467 return '';
468 }
469 }
470
471 /**
472 * Resolve $rules
473 *
474 * @param mixed $rules
475 * @return array
476 */
477 protected function resolveRules($rules): array
478 {
479 if (is_string($rules)) {
480 $rules = explode('|', $rules);
481 }
482
483 $resolvedRules = [];
484 $validatorFactory = $this->getValidator();
485
486 foreach ($rules as $i => $rule) {
487 if (empty($rule)) {
488 continue;
489 }
490 $params = [];
491
492 if (is_string($rule)) {
493 list($rulename, $params) = $this->parseRule($rule);
494 $validator = call_user_func_array($validatorFactory, array_merge([$rulename], $params));
495 } elseif ($rule instanceof Rule) {
496 $validator = $rule;
497 } elseif ($rule instanceof Closure) {
498 $validator = call_user_func_array($validatorFactory, ['callback', $rule]);
499 } else {
500 $ruleName = is_object($rule) ? get_class($rule) : gettype($rule);
501 $message = "Rule must be a string, Closure or '".Rule::class."' instance. ".$ruleName." given";
502 throw new \Exception();
503 }
504
505 $resolvedRules[] = $validator;
506 }
507
508 return $resolvedRules;
509 }
510
511 /**
512 * Parse $rule
513 *
514 * @param string $rule
515 * @return array
516 */
517 protected function parseRule(string $rule): array
518 {
519 $exp = explode(':', $rule, 2);
520 $rulename = $exp[0];
521 if ($rulename !== 'regex') {
522 $params = isset($exp[1])? explode(',', $exp[1]) : [];
523 } else {
524 $params = [$exp[1]];
525 }
526
527 return [$rulename, $params];
528 }
529
530 /**
531 * Given $attributeKey and $alias then assign alias
532 *
533 * @param mixed $attributeKey
534 * @param mixed $alias
535 * @return void
536 */
537 public function setAlias(string $attributeKey, string $alias)
538 {
539 $this->aliases[$attributeKey] = $alias;
540 }
541
542 /**
543 * Get attribute alias from given key
544 *
545 * @param mixed $attributeKey
546 * @return string|null
547 */
548 public function getAlias(string $attributeKey)
549 {
550 return isset($this->aliases[$attributeKey])? $this->aliases[$attributeKey] : null;
551 }
552
553 /**
554 * Set attributes aliases
555 *
556 * @param array $aliases
557 * @return void
558 */
559 public function setAliases(array $aliases)
560 {
561 $this->aliases = array_merge($this->aliases, $aliases);
562 }
563
564 /**
565 * Check validations are passed
566 *
567 * @return bool
568 */
569 public function passes(): bool
570 {
571 return $this->errors->count() == 0;
572 }
573
574 /**
575 * Check validations are failed
576 *
577 * @return bool
578 */
579 public function fails(): bool
580 {
581 return !$this->passes();
582 }
583
584 /**
585 * Given $key and get value
586 *
587 * @param string $key
588 * @return mixed
589 */
590 public function getValue(string $key)
591 {
592 return Helper::arrayGet($this->inputs, $key);
593 }
594
595 /**
596 * Set input value
597 *
598 * @param string $key
599 * @param mixed $value
600 * @return void
601 */
602 public function setValue(string $key, $value)
603 {
604 Helper::arraySet($this->inputs, $key, $value);
605 }
606
607 /**
608 * Given $key and check value is exsited
609 *
610 * @param string $key
611 * @return boolean
612 */
613 public function hasValue(string $key): bool
614 {
615 return Helper::arrayHas($this->inputs, $key);
616 }
617
618 /**
619 * Get Validator class instance
620 *
621 * @return \Rakit\Validation\Validator
622 */
623 public function getValidator(): Validator
624 {
625 return $this->validator;
626 }
627
628 /**
629 * Given $inputs and resolve input attributes
630 *
631 * @param array $inputs
632 * @return array
633 */
634 protected function resolveInputAttributes(array $inputs): array
635 {
636 $resolvedInputs = [];
637 foreach ($inputs as $key => $rules) {
638 $exp = explode(':', $key);
639
640 if (count($exp) > 1) {
641 // set attribute alias
642 $this->aliases[$exp[0]] = $exp[1];
643 }
644
645 $resolvedInputs[$exp[0]] = $rules;
646 }
647
648 return $resolvedInputs;
649 }
650
651 /**
652 * Get validated data
653 *
654 * @return array
655 */
656 public function getValidatedData(): array
657 {
658 return array_merge($this->validData, $this->invalidData);
659 }
660
661 /**
662 * Set valid data
663 *
664 * @param \Rakit\Validation\Attribute $attribute
665 * @param mixed $value
666 * @return void
667 */
668 protected function setValidData(Attribute $attribute, $value)
669 {
670 $key = $attribute->getKey();
671 if ($attribute->isArrayAttribute() || $attribute->isUsingDotNotation()) {
672 Helper::arraySet($this->validData, $key, $value);
673 Helper::arrayUnset($this->invalidData, $key);
674 } else {
675 $this->validData[$key] = $value;
676 }
677 }
678
679 /**
680 * Get valid data
681 *
682 * @return array
683 */
684 public function getValidData(): array
685 {
686 return $this->validData;
687 }
688
689 /**
690 * Set invalid data
691 *
692 * @param \Rakit\Validation\Attribute $attribute
693 * @param mixed $value
694 * @return void
695 */
696 protected function setInvalidData(Attribute $attribute, $value)
697 {
698 $key = $attribute->getKey();
699 if ($attribute->isArrayAttribute() || $attribute->isUsingDotNotation()) {
700 Helper::arraySet($this->invalidData, $key, $value);
701 Helper::arrayUnset($this->validData, $key);
702 } else {
703 $this->invalidData[$key] = $value;
704 }
705 }
706
707 /**
708 * Get invalid data
709 *
710 * @return void
711 */
712 public function getInvalidData(): array
713 {
714 return $this->invalidData;
715 }
716 }
717