PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.6.0
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.6.0
2.11.0 2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 All 78 releases
fluent-community / vendor / wpfluent / framework / src / WPFluent / Validator / ValidatesAttributes.php

ValidatesAttributes.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 2.6.0, at vendor/wpfluent/framework/src/WPFluent/Validator/ValidatesAttributes.php

892 lines 22.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCommunity\Framework\Validator;
4
5 use Countable;
6 use Exception;
7 use ValueError;
8 use DateTimeInterface;
9 use InvalidArgumentException;
10 use FluentCommunity\Framework\Support\Str;
11 use FluentCommunity\Framework\Support\Arr;
12 use FluentCommunity\Framework\Support\Helper;
13 use FluentCommunity\Framework\Support\DateTime;
14 use FluentCommunity\Framework\Validator\Contracts\File;
15
16 trait ValidatesAttributes
17 {
18 use ValidateDatabaseRulesTrait;
19
20 /**
21 * Require a certain number of parameters to be present.
22 *
23 * @param int $count
24 * @param array $parameters
25 * @param string $rule
26 *
27 * @return void
28 *
29 * @throws \InvalidArgumentException
30 */
31 protected function requireParameterCount($count, $parameters, $rule)
32 {
33 if (count($parameters) < $count) {
34 throw new InvalidArgumentException(
35 "Validation rule $rule requires at least $count parameters."
36 );
37 }
38 }
39
40 /**
41 * Get the size of an attribute.
42 *
43 * @param string $attribute
44 * @param mixed $value
45 *
46 * @return mixed
47 */
48 protected function getSize($attribute, $value)
49 {
50 // This method will determine if the attribute is a number, string, or file and
51 // return the proper size accordingly. If it is a number, then number itself
52 // is the size. If it is a file, we take kilobytes, and for a string the
53 // entire length of the string will be considered the attribute size.
54 $type = $this->deduceType($value, $attribute);
55
56 switch ($type) {
57 case 'numeric':
58 return $value;
59 case 'array':
60 return count($value);
61 case 'file':
62 return $value->getSize() / 1024;
63 default:
64 return mb_strlen($value);
65 }
66 }
67
68 /**
69 * Deduce the value type of an attribute.
70 *
71 * @param $value
72 *
73 * @return string
74 */
75 protected function deduceType($value, $attribute = null)
76 {
77 if (is_numeric($value)) {
78 return $this->guessType($value, $attribute);
79 } elseif (is_array($value)) {
80 return 'array';
81 } elseif ($value instanceof File) {
82 return 'file';
83 }
84
85 return 'string';
86 }
87
88 /**
89 * Guess the real type by examining rules.
90 *
91 * @param mixed $value
92 * @param string|null $attribute
93 * @return string
94 */
95 protected function guessType($value, $attribute)
96 {
97 if ($attribute && in_array('string', $this->rules[$attribute])) {
98 return 'string';
99 }
100
101 return 'numeric';
102 }
103
104 /**
105 * Check if parameter should be converted to boolean.
106 *
107 * @param string $parameter
108 * @return bool
109 */
110 protected function shouldConvertToBoolean($parameter)
111 {
112 return in_array('boolean', Arr::get($this->rules, $parameter, []));
113 }
114
115 /**
116 * Convert the given values to boolean if they are string "true" / "false".
117 *
118 * @param array $values
119 *
120 * @return array
121 */
122 protected function convertValuesToBoolean($values)
123 {
124 return array_map(function ($value) {
125 if ($value === 'true') {
126 return true;
127 } elseif ($value === 'false') {
128 return false;
129 }
130
131 return $value;
132 }, $values);
133 }
134
135 /**
136 * Convert the given values to null if they are string "null".
137 *
138 * @param array $values
139 * @return array
140 */
141 protected function convertValuesToNull($values)
142 {
143 return array_map(function ($value) {
144 return Str::lower($value) === 'null' ? null : $value;
145 }, $values);
146 }
147
148 /**
149 * Validate that a required attribute exists.
150 *
151 * @param string $attribute
152 * @param mixed $value
153 *
154 * @return bool
155 */
156 protected function validateRequired($attribute, $value)
157 {
158 if (is_null($value)) {
159 return false;
160 } elseif (is_string($value) && trim($value) === '') {
161 return false;
162 } elseif ((is_array($value) || $value instanceof Countable) && count($value) < 1) {
163 return false;
164 } elseif ($value instanceof File) {
165 return (string) $value->getPath() != '';
166 }
167
168 return true;
169 }
170
171 /**
172 * Validate that an attribute exists when another attribute has a given value.
173 *
174 * @param string $attribute
175 * @param mixed $value
176 * @param mixed $parameters
177 *
178 * @return bool
179 */
180 protected function validateRequiredIf($attribute, $value, $parameters)
181 {
182 $this->requireParameterCount(2, $parameters, 'required_if');
183
184 if (preg_match('/\.\d\./', $attribute, $matches)) {
185 $parameters[0] = str_replace(['.*.'], $matches, $parameters[0]);
186 }
187
188 $other = Arr::get($this->data, $parameters[0]);
189
190 $values = array_slice($parameters, 1);
191
192 if (is_bool($other)) {
193 $values = $this->convertValuesToBoolean($values);
194 }
195
196 if (in_array($other, $values)) {
197 return $this->validateRequired($attribute, $value);
198 }
199
200 return true;
201 }
202
203 /**
204 * Validate that an attribute is required only if **any** of the
205 * other specified fields are present.
206 *
207 * @param string $attribute
208 * @param mixed $value
209 * @param array $parameters
210 * @return bool
211 */
212 protected function validateRequiredWith($attribute, $value, $parameters)
213 {
214 $this->requireParameterCount(1, $parameters, 'required_with');
215
216 foreach ($parameters as $field) {
217 if ($this->isPresent($field) && !$this->isPresent($attribute)) {
218 return $this->validateRequired($attribute, $value);
219 }
220 }
221
222 return true;
223 }
224
225 /**
226 * Validate that an attribute is required only if **all** of the
227 * other specified fields are present.
228 *
229 * @param string $attribute
230 * @param mixed $value
231 * @param array $parameters
232 * @return bool
233 */
234 protected function validateRequiredWithAll($attribute, $value, $parameters)
235 {
236 $this->requireParameterCount(1, $parameters, 'required_with_all');
237
238 $allPresent = true;
239
240 foreach ($parameters as $field) {
241 if (!$this->isPresent($field)) {
242 $allPresent = false;
243 break;
244 }
245 }
246
247 if ($allPresent && !$this->isPresent($attribute)) {
248 return $this->validateRequired($attribute, $value);
249 }
250
251 return true;
252 }
253
254 /**
255 * Validate that an attribute is required only if **any** of the
256 * other specified fields are **not present**.
257 *
258 * @param string $attribute
259 * @param mixed $value
260 * @param array $parameters
261 * @return bool
262 */
263 protected function validateRequiredWithout($attribute, $value, $parameters)
264 {
265 $this->requireParameterCount(1, $parameters, 'required_without');
266
267 foreach ($parameters as $field) {
268 if (!$this->isPresent($field) && !$this->isPresent($attribute)) {
269 return $this->validateRequired($attribute, $value);
270 }
271 }
272
273 return true;
274 }
275
276 /**
277 * Validate that an attribute is required only if **all** of the
278 * other specified fields are **not present**.
279 *
280 * @param string $attribute
281 * @param mixed $value
282 * @param array $parameters
283 * @return bool
284 */
285 protected function validateRequiredWithoutAll($attribute, $value, $parameters)
286 {
287 $this->requireParameterCount(1, $parameters, 'required_without_all');
288
289 $allMissing = true;
290
291 foreach ($parameters as $field) {
292 if ($this->isPresent($field)) {
293 $allMissing = false;
294 break;
295 }
296 }
297
298 if ($allMissing && !$this->isPresent($attribute)) {
299 return $this->validateRequired($attribute, $value);
300 }
301
302 return true;
303 }
304
305 /**
306 * Determine if the given attribute is present.
307 *
308 * @param string $attribute
309 * @return boolean
310 */
311 protected function isPresent($attribute)
312 {
313 $attribute = str_replace(['[', ']'], ['.', ''], $attribute);
314
315 if (!Arr::has($this->data, $attribute)) {
316 return false;
317 }
318
319 $value = Arr::get($this->data, $attribute);
320
321 return !is_null($value) && $value !== '';
322 }
323
324 /**
325 * Validate that an attribute is greater than value.
326 *
327 * @param string $attribute
328 * @param mixed $value
329 * @param array $parameters
330 * @return bool
331 */
332 protected function validateGt($attribute, $value, $parameters)
333 {
334 $this->requireParameterCount(1, $parameters, 'gt');
335
336 if (!is_numeric($value) || !is_numeric($parameters[0])) {
337 return false;
338 }
339
340 return $value > $parameters[0];
341 }
342
343 /**
344 * Validate that an attribute is greater than or equal to value.
345 *
346 * @param string $attribute
347 * @param mixed $value
348 * @param array $parameters
349 * @return bool
350 */
351 protected function validateGte($attribute, $value, $parameters)
352 {
353 $this->requireParameterCount(1, $parameters, 'gte');
354
355 if (!is_numeric($value) || !is_numeric($parameters[0])) {
356 return false;
357 }
358
359 return $value >= $parameters[0];
360 }
361
362 /**
363 * Validate that an attribute is less than value.
364 *
365 * @param string $attribute
366 * @param mixed $value
367 * @param array $parameters
368 * @return bool
369 */
370 protected function validateLt($attribute, $value, $parameters)
371 {
372 $this->requireParameterCount(1, $parameters, 'lt');
373
374 if (!is_numeric($value) || !is_numeric($parameters[0])) {
375 return false;
376 }
377
378 return $value < $parameters[0];
379 }
380
381 /**
382 * Validate that an attribute is less than or equal to value.
383 *
384 * @param string $attribute
385 * @param mixed $value
386 * @param array $parameters
387 * @return bool
388 */
389 protected function validateLte($attribute, $value, $parameters)
390 {
391 $this->requireParameterCount(1, $parameters, 'lte');
392
393 if (!is_numeric($value) || !is_numeric($parameters[0])) {
394 return false;
395 }
396
397 return $value <= $parameters[0];
398 }
399
400 /**
401 * Validate that an attribute is a valid e-mail address.
402 *
403 * @param string $attribute
404 * @param mixed $value
405 *
406 * @return bool
407 */
408 protected function validateEmail($attribute, $value)
409 {
410 return ! ! is_email($value);
411 }
412
413 /**
414 * Validate the size of an attribute.
415 *
416 * @param string $attribute
417 * @param mixed $value
418 * @param array $parameters
419 *
420 * @return bool
421 */
422 protected function validateSize($attribute, $value, $parameters)
423 {
424 $this->requireParameterCount(1, $parameters, 'size');
425
426 return $this->getSize($attribute, $value) == $parameters[0];
427 }
428
429 /**
430 * Validate the size of an attribute is greater than a minimum value.
431 *
432 * @param string $attribute
433 * @param mixed $value
434 * @param array $parameters
435 *
436 * @return bool
437 */
438 protected function validateMin($attribute, $value, $parameters)
439 {
440 $this->requireParameterCount(1, $parameters, 'min');
441
442 return $this->getSize($attribute, $value) >= $parameters[0];
443 }
444
445 /**
446 * Validate the size of an attribute is less than a maximum value.
447 *
448 * @param string $attribute
449 * @param mixed $value
450 * @param array $parameters
451 *
452 * @return bool
453 */
454 protected function validateMax($attribute, $value, $parameters)
455 {
456 $this->requireParameterCount(1, $parameters, 'max');
457
458 return $this->getSize($attribute, $value) <= $parameters[0];
459 }
460
461 /**
462 * Validate that two attributes match.
463 *
464 * @param string $attribute
465 * @param mixed $value
466 * @param array $parameters
467 *
468 * @return bool
469 */
470 protected function validateSame($attribute, $value, $parameters)
471 {
472 $this->requireParameterCount(1, $parameters, 'same');
473
474 $other = @$this->data[$parameters[0]];
475
476 return $value === $other;
477 }
478
479 /**
480 * Validate that an attribute is a valid URL.
481 *
482 * @param string $attribute
483 * @param mixed $value
484 *
485 * @return bool
486 */
487 protected function validateUrl($attribute, $value)
488 {
489 return (bool) Str::isUrl($value);
490 }
491
492 /**
493 * Validate that an attribute is a string.
494 *
495 * @param string $attribute
496 * @param mixed $value
497 * @return bool
498 */
499 protected function validateString($attribute, $value)
500 {
501 return is_string($value);
502 }
503
504 /**
505 * Validate that an attribute is a integer.
506 *
507 * @param string $attribute
508 * @param mixed $value
509 * @return bool
510 */
511 protected function validateInt($attribute, $value)
512 {
513 return $this->validateInteger($attribute, $value);
514 }
515
516 /**
517 * Validate that an attribute is a integer.
518 *
519 * @param string $attribute
520 * @param mixed $value
521 * @return bool
522 */
523 protected function validateInteger($attribute, $value)
524 {
525 return is_int($value);
526 }
527
528 /**
529 * Validate that an attribute has a given number of decimal places.
530 *
531 * @param string $attribute
532 * @param mixed $value
533 * @param array<int, int|string> $parameters
534 * @return bool
535 */
536 protected function validateDecimal($attribute, $value, $parameters)
537 {
538 $this->requireParameterCount(1, $parameters, 'decimal');
539
540 if (!$this->validateNumeric($attribute, $value)) {
541 return false;
542 }
543
544 $matches = [];
545
546 if (preg_match('/^[+-]?\d*\.?(\d*)$/', $value, $matches) !== 1) {
547 return false;
548 }
549
550 $decimals = strlen(end($matches));
551
552 if (!isset($parameters[1])) {
553 return $decimals == $parameters[0];
554 }
555
556 return $decimals >= $parameters[0] && $decimals <= $parameters[1];
557 }
558
559 /**
560 * Validate that an attribute is numeric.
561 *
562 * @param string $attribute
563 * @param mixed $value
564 *
565 * @return bool
566 */
567 protected function validateNumeric($attribute, $value)
568 {
569 return is_numeric($value);
570 }
571
572 /**
573 * Validate that an attribute is a valid date.
574 *
575 * @param string $attribute
576 * @param mixed $value
577 * @return bool
578 */
579 protected function validateDate($attribute, $value)
580 {
581 if ($value instanceof DateTimeInterface) {
582 return true;
583 }
584
585 try {
586 if ((!is_string($value) && !is_numeric($value)) || strtotime($value) === false) {
587 return false;
588 }
589 } catch (Exception $e) {
590 return false;
591 }
592
593 $date = date_parse($value);
594
595 return checkdate($date['month'], $date['day'], $date['year']);
596 }
597
598 /**
599 * Validate that an attribute matches a date format.
600 *
601 * @param string $attribute
602 * @param mixed $value
603 * @param array<int, int|string> $parameters
604 * @return bool
605 */
606 protected function validateDateFormat($attribute, $value, $parameters)
607 {
608 $this->requireParameterCount(1, $parameters, 'date_format');
609
610 if (!is_string($value) && !is_numeric($value)) {
611 return false;
612 }
613
614 foreach ($parameters as $format) {
615 try {
616 $date = DateTime::createFromFormat('!'.$format, $value);
617
618 if ($date && $date->format($format) == $value) {
619 return true;
620 }
621 } catch (ValueError $e) {
622 return false;
623 }
624 }
625
626 return false;
627 }
628
629 /**
630 * Validate that an attribute is alphabetic.
631 *
632 * @param string $attribute
633 * @param mixed $value
634 *
635 * @return bool
636 */
637 protected function validateAlpha($attribute, $value)
638 {
639 return is_string($value) && preg_match('/^[\pL\pM]+$/u', $value);
640 }
641
642 /**
643 * Validate that an attribute is alphanum.
644 *
645 * @param string $attribute
646 * @param mixed $value
647 *
648 * @return bool
649 */
650 protected function validateAlphanum($attribute, $value)
651 {
652 if (! is_string($value) && ! is_numeric($value)) {
653 return false;
654 }
655
656 return preg_match('/^[\pL\pM\pN]+$/u', $value) > 0;
657 }
658
659 /**
660 * Validate that an attribute is alphanum and -_.
661 *
662 * @param string $attribute
663 * @param mixed $value
664 *
665 * @return bool
666 */
667 protected function validateAlphadash($attribute, $value)
668 {
669 if (! is_string($value) && ! is_numeric($value)) {
670 return false;
671 }
672
673 return preg_match('/^[\pL\pM\pN_-]+$/u', $value) > 0;
674 }
675
676 /**
677 * Validate the guessed extension of a file upload is in a set of file extensions.
678 *
679 * @param string $attribute
680 * @param mixed $value
681 * @param array $parameters
682 *
683 * @return bool
684 */
685 protected function validateMimes($attribute, $value, $parameters)
686 {
687 if (! $this->isValidFileInstance($value)) {
688 return false;
689 }
690
691 if ($this->shouldBlockPhpUpload($value, $parameters)) {
692 return false;
693 }
694
695 /**
696 * @var $value \FluentCommunity\Framework\Validator\Contracts\File
697 */
698 return $value->getPath() != '' && in_array(
699 $value->guessExtension(), $parameters
700 );
701 }
702
703 /**
704 * Validate the MIME type of a file upload attribute is in a set of MIME types.
705 *
706 * @param string $attribute
707 * @param mixed $value
708 * @param array $parameters
709 * @return bool
710 */
711 protected function validateMimetypes($attribute, $value, $parameters)
712 {
713 if (! $this->isValidFileInstance($value)) {
714 return false;
715 }
716
717 if ($this->shouldBlockPhpUpload($value, $parameters)) {
718 return false;
719 }
720
721 return $value->getPath() != '' && (
722 in_array($value->getMimeType(), $parameters) ||
723 in_array(explode('/', $value->getMimeType())[0].'/*', $parameters)
724 );
725 }
726
727 /**
728 * Check that the given value is a valid file instance.
729 *
730 * @param mixed $value
731 *
732 * @return bool
733 */
734 protected function isValidFileInstance($value)
735 {
736 return $value instanceof File && $value->isValid();
737 }
738
739 /**
740 * Check if PHP uploads are explicitly allowed.
741 *
742 * @param mixed $value
743 * @param array $parameters
744 *
745 * @return bool
746 */
747 protected function shouldBlockPhpUpload($value, $parameters)
748 {
749 if (in_array('php', $parameters)) {
750 return false;
751 }
752
753 /**
754 * @var $value \FluentCommunity\Framework\Validator\Contracts\File
755 */
756 return strtolower($value->getClientOriginalExtension()) === 'php';
757 }
758
759 /**
760 * Validate that an attribute exists even if not filled.
761 *
762 * @param string $attribute
763 * @param mixed $value
764 *
765 * @return bool
766 */
767 protected function validatePresent($attribute, $value)
768 {
769 $attribute = str_replace(['[', ']'], ['.', ''], $attribute);
770
771 return Arr::has($this->data, $attribute);
772 }
773
774 /**
775 * Validate that an attribute has a given number of digits.
776 *
777 * @param string $attribute
778 * @param mixed $value
779 * @param array $parameters
780 *
781 * @return bool
782 */
783 protected function validateDigits($attribute, $value, $parameters)
784 {
785 $this->requireParameterCount(1, $parameters, 'digits');
786
787 return $this->validateNumeric($attribute, $value)
788 && strlen((string) $value) == $parameters[0];
789 }
790
791 /**
792 * Validate that an attribute is an array.
793 *
794 * @param string $attribute
795 * @param mixed $value
796 * @param array $parameters
797 * @return bool
798 */
799 protected function validateArray($attribute, $value, $parameters = [])
800 {
801 if (! is_array($value)) {
802 return false;
803 }
804
805 if (empty($parameters)) {
806 return true;
807 }
808
809 return empty(array_diff_key($value, array_fill_keys($parameters, '')));
810 }
811
812 /**
813 * Validate that an array has all of the given keys.
814 *
815 * @param string $attribute
816 * @param mixed $value
817 * @param array<int, int|string> $parameters
818 * @return bool
819 */
820 protected function validateRequiredArrayKeys($attribute, $value, $parameters)
821 {
822 $this->requireParameterCount(1, $parameters, 'required_array_keys');
823
824 if (!is_array($value)) {
825 return false;
826 }
827
828 foreach ($parameters as $key) {
829 if (!Arr::exists($value, $key)) {
830 return false;
831 }
832 }
833
834 return true;
835 }
836
837 /**
838 * Validate that an attribute passes a regular expression check.
839 *
840 * @param string $attribute
841 * @param mixed $value
842 * @param array<int, int|string> $parameters
843 * @return bool
844 */
845 protected function validateRegex($attribute, $value, $parameters)
846 {
847 if (! is_string($value) && ! is_numeric($value)) {
848 return false;
849 }
850
851 $this->requireParameterCount(1, $parameters, 'regex');
852
853 return preg_match($parameters[0], $value) > 0;
854 }
855
856 /**
857 * Validate an attribute is contained within a list of values.
858 *
859 * @param string $attribute
860 * @param mixed $value
861 * @param array $parameters
862 * @return bool
863 */
864 protected function validateIn($attribute, $value, $parameters)
865 {
866 if (is_array($value) && $this->hasRule($attribute, 'Array')) {
867 foreach ($value as $element) {
868 if (is_array($element)) {
869 return false;
870 }
871 }
872
873 return count(array_diff($value, $parameters)) === 0;
874 }
875
876 return !is_array($value) && in_array((string) $value, $parameters);
877 }
878
879 /**
880 * Validate an attribute is not contained within a list of values.
881 *
882 * @param string $attribute
883 * @param mixed $value
884 * @param array $parameters
885 * @return bool
886 */
887 protected function validateNotIn($attribute, $value, $parameters)
888 {
889 return !$this->validateIn($attribute, $value, $parameters);
890 }
891 }
892