Completed
Branch development (b1b115)
by Johannes
10:28
created

ValidatesAttributes   F

Complexity

Total Complexity 205

Size/Duplication

Total Lines 1456
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 205
c 0
b 0
f 0
dl 0
loc 1456
rs 0.8

How to fix   Complexity   

Complex Class

Complex classes like ValidatesAttributes often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ValidatesAttributes, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Illuminate\Validation\Concerns;
4
5
use DateTime;
6
use Countable;
7
use Exception;
8
use Throwable;
9
use DateTimeZone;
10
use DateTimeInterface;
11
use Illuminate\Support\Arr;
12
use Illuminate\Support\Str;
13
use InvalidArgumentException;
14
use Illuminate\Validation\Rules\Exists;
15
use Illuminate\Validation\Rules\Unique;
16
use Illuminate\Validation\ValidationData;
17
use Symfony\Component\HttpFoundation\File\File;
18
use Symfony\Component\HttpFoundation\File\UploadedFile;
19
20
trait ValidatesAttributes
21
{
22
    /**
23
     * Validate that an attribute was "accepted".
24
     *
25
     * This validation rule implies the attribute is "required".
26
     *
27
     * @param  string  $attribute
28
     * @param  mixed   $value
29
     * @return bool
30
     */
31
    public function validateAccepted($attribute, $value)
32
    {
33
        $acceptable = ['yes', 'on', '1', 1, true, 'true'];
34
35
        return $this->validateRequired($attribute, $value) && in_array($value, $acceptable, true);
36
    }
37
38
    /**
39
     * Validate that an attribute is an active URL.
40
     *
41
     * @param  string  $attribute
42
     * @param  mixed   $value
43
     * @return bool
44
     */
45
    public function validateActiveUrl($attribute, $value)
46
    {
47
        if (! is_string($value)) {
48
            return false;
49
        }
50
51
        if ($url = parse_url($value, PHP_URL_HOST)) {
52
            try {
53
                return count(dns_get_record($url, DNS_A | DNS_AAAA)) > 0;
54
            } catch (Exception $e) {
55
                return false;
56
            }
57
        }
58
59
        return false;
60
    }
61
62
    /**
63
     * "Break" on first validation fail.
64
     *
65
     * Always returns true, just lets us put "bail" in rules.
66
     *
67
     * @return bool
68
     */
69
    public function validateBail()
70
    {
71
        return true;
72
    }
73
74
    /**
75
     * Validate the date is before a given date.
76
     *
77
     * @param  string  $attribute
78
     * @param  mixed   $value
79
     * @param  array   $parameters
80
     * @return bool
81
     */
82
    public function validateBefore($attribute, $value, $parameters)
83
    {
84
        $this->requireParameterCount(1, $parameters, 'before');
85
86
        return $this->compareDates($attribute, $value, $parameters, '<');
87
    }
88
89
    /**
90
     * Validate the date is before or equal a given date.
91
     *
92
     * @param  string  $attribute
93
     * @param  mixed   $value
94
     * @param  array   $parameters
95
     * @return bool
96
     */
97
    public function validateBeforeOrEqual($attribute, $value, $parameters)
98
    {
99
        $this->requireParameterCount(1, $parameters, 'before_or_equal');
100
101
        return $this->compareDates($attribute, $value, $parameters, '<=');
102
    }
103
104
    /**
105
     * Validate the date is after a given date.
106
     *
107
     * @param  string  $attribute
108
     * @param  mixed   $value
109
     * @param  array   $parameters
110
     * @return bool
111
     */
112
    public function validateAfter($attribute, $value, $parameters)
113
    {
114
        $this->requireParameterCount(1, $parameters, 'after');
115
116
        return $this->compareDates($attribute, $value, $parameters, '>');
117
    }
118
119
    /**
120
     * Validate the date is equal or after a given date.
121
     *
122
     * @param  string  $attribute
123
     * @param  mixed   $value
124
     * @param  array   $parameters
125
     * @return bool
126
     */
127
    public function validateAfterOrEqual($attribute, $value, $parameters)
128
    {
129
        $this->requireParameterCount(1, $parameters, 'after_or_equal');
130
131
        return $this->compareDates($attribute, $value, $parameters, '>=');
132
    }
133
134
    /**
135
     * Compare a given date against another using an operator.
136
     *
137
     * @param  string  $attribute
138
     * @param  mixed  $value
139
     * @param  array  $parameters
140
     * @param  string  $operator
141
     * @return bool
142
     */
143
    protected function compareDates($attribute, $value, $parameters, $operator)
144
    {
145
        if (! is_string($value) && ! is_numeric($value) && ! $value instanceof DateTimeInterface) {
146
            return false;
147
        }
148
149
        if ($format = $this->getDateFormat($attribute)) {
150
            return $this->checkDateTimeOrder(
151
                $format, $value, $this->getValue($parameters[0]) ?: $parameters[0], $operator
152
            );
153
        }
154
155
        if (! $date = $this->getDateTimestamp($parameters[0])) {
156
            $date = $this->getDateTimestamp($this->getValue($parameters[0]));
157
        }
158
159
        return $this->compare($this->getDateTimestamp($value), $date, $operator);
160
    }
161
162
    /**
163
     * Get the date format for an attribute if it has one.
164
     *
165
     * @param  string  $attribute
166
     * @return string|null
167
     */
168
    protected function getDateFormat($attribute)
169
    {
170
        if ($result = $this->getRule($attribute, 'DateFormat')) {
171
            return $result[1][0];
172
        }
173
    }
174
175
    /**
176
     * Get the date timestamp.
177
     *
178
     * @param  mixed  $value
179
     * @return int
180
     */
181
    protected function getDateTimestamp($value)
182
    {
183
        return $value instanceof DateTimeInterface ? $value->getTimestamp() : strtotime($value);
184
    }
185
186
    /**
187
     * Given two date/time strings, check that one is after the other.
188
     *
189
     * @param  string  $format
190
     * @param  string  $first
191
     * @param  string  $second
192
     * @param  string  $operator
193
     * @return bool
194
     */
195
    protected function checkDateTimeOrder($format, $first, $second, $operator)
196
    {
197
        $first = $this->getDateTimeWithOptionalFormat($format, $first);
198
199
        $second = $this->getDateTimeWithOptionalFormat($format, $second);
200
201
        return ($first && $second) && ($this->compare($first, $second, $operator));
202
    }
203
204
    /**
205
     * Get a DateTime instance from a string.
206
     *
207
     * @param  string  $format
208
     * @param  string  $value
209
     * @return \DateTime|null
210
     */
211
    protected function getDateTimeWithOptionalFormat($format, $value)
212
    {
213
        if ($date = DateTime::createFromFormat('!'.$format, $value)) {
214
            return $date;
215
        }
216
217
        try {
218
            return new DateTime($value);
219
        } catch (Exception $e) {
220
            //
221
        }
222
    }
223
224
    /**
225
     * Validate that an attribute contains only alphabetic characters.
226
     *
227
     * @param  string  $attribute
228
     * @param  mixed   $value
229
     * @return bool
230
     */
231
    public function validateAlpha($attribute, $value)
232
    {
233
        return is_string($value) && preg_match('/^[\pL\pM]+$/u', $value);
234
    }
235
236
    /**
237
     * Validate that an attribute contains only alpha-numeric characters, dashes, and underscores.
238
     *
239
     * @param  string  $attribute
240
     * @param  mixed   $value
241
     * @return bool
242
     */
243
    public function validateAlphaDash($attribute, $value)
244
    {
245
        if (! is_string($value) && ! is_numeric($value)) {
246
            return false;
247
        }
248
249
        return preg_match('/^[\pL\pM\pN_-]+$/u', $value) > 0;
250
    }
251
252
    /**
253
     * Validate that an attribute contains only alpha-numeric characters.
254
     *
255
     * @param  string  $attribute
256
     * @param  mixed   $value
257
     * @return bool
258
     */
259
    public function validateAlphaNum($attribute, $value)
260
    {
261
        if (! is_string($value) && ! is_numeric($value)) {
262
            return false;
263
        }
264
265
        return preg_match('/^[\pL\pM\pN]+$/u', $value) > 0;
266
    }
267
268
    /**
269
     * Validate that an attribute is an array.
270
     *
271
     * @param  string  $attribute
272
     * @param  mixed   $value
273
     * @return bool
274
     */
275
    public function validateArray($attribute, $value)
276
    {
277
        return is_array($value);
278
    }
279
280
    /**
281
     * Validate the size of an attribute is between a set of values.
282
     *
283
     * @param  string  $attribute
284
     * @param  mixed   $value
285
     * @param  array   $parameters
286
     * @return bool
287
     */
288
    public function validateBetween($attribute, $value, $parameters)
289
    {
290
        $this->requireParameterCount(2, $parameters, 'between');
291
292
        $size = $this->getSize($attribute, $value);
293
294
        return $size >= $parameters[0] && $size <= $parameters[1];
295
    }
296
297
    /**
298
     * Validate that an attribute is a boolean.
299
     *
300
     * @param  string  $attribute
301
     * @param  mixed   $value
302
     * @return bool
303
     */
304
    public function validateBoolean($attribute, $value)
305
    {
306
        $acceptable = [true, false, 0, 1, '0', '1'];
307
308
        return in_array($value, $acceptable, true);
309
    }
310
311
    /**
312
     * Validate that an attribute has a matching confirmation.
313
     *
314
     * @param  string  $attribute
315
     * @param  mixed   $value
316
     * @return bool
317
     */
318
    public function validateConfirmed($attribute, $value)
319
    {
320
        return $this->validateSame($attribute, $value, [$attribute.'_confirmation']);
321
    }
322
323
    /**
324
     * Validate that an attribute is a valid date.
325
     *
326
     * @param  string  $attribute
327
     * @param  mixed   $value
328
     * @return bool
329
     */
330
    public function validateDate($attribute, $value)
331
    {
332
        if ($value instanceof DateTimeInterface) {
333
            return true;
334
        }
335
336
        if ((! is_string($value) && ! is_numeric($value)) || strtotime($value) === false) {
337
            return false;
338
        }
339
340
        $date = date_parse($value);
341
342
        return checkdate($date['month'], $date['day'], $date['year']);
343
    }
344
345
    /**
346
     * Validate that an attribute matches a date format.
347
     *
348
     * @param  string  $attribute
349
     * @param  mixed   $value
350
     * @param  array   $parameters
351
     * @return bool
352
     */
353
    public function validateDateFormat($attribute, $value, $parameters)
354
    {
355
        $this->requireParameterCount(1, $parameters, 'date_format');
356
357
        if (! is_string($value) && ! is_numeric($value)) {
358
            return false;
359
        }
360
361
        $format = $parameters[0];
362
363
        $date = DateTime::createFromFormat('!'.$format, $value);
364
365
        return $date && $date->format($format) == $value;
366
    }
367
368
    /**
369
     * Validate that an attribute is equal to another date.
370
     *
371
     * @param  string  $attribute
372
     * @param  mixed   $value
373
     * @param  array   $parameters
374
     * @return bool
375
     */
376
    public function validateDateEquals($attribute, $value, $parameters)
377
    {
378
        $this->requireParameterCount(1, $parameters, 'date_equals');
379
380
        return $this->compareDates($attribute, $value, $parameters, '=');
381
    }
382
383
    /**
384
     * Validate that an attribute is different from another attribute.
385
     *
386
     * @param  string  $attribute
387
     * @param  mixed   $value
388
     * @param  array   $parameters
389
     * @return bool
390
     */
391
    public function validateDifferent($attribute, $value, $parameters)
392
    {
393
        $this->requireParameterCount(1, $parameters, 'different');
394
395
        foreach ($parameters as $parameter) {
396
            $other = Arr::get($this->data, $parameter);
397
398
            if (is_null($other) || $value === $other) {
399
                return false;
400
            }
401
        }
402
403
        return true;
404
    }
405
406
    /**
407
     * Validate that an attribute has a given number of digits.
408
     *
409
     * @param  string  $attribute
410
     * @param  mixed   $value
411
     * @param  array   $parameters
412
     * @return bool
413
     */
414
    public function validateDigits($attribute, $value, $parameters)
415
    {
416
        $this->requireParameterCount(1, $parameters, 'digits');
417
418
        return ! preg_match('/[^0-9]/', $value)
419
                    && strlen((string) $value) == $parameters[0];
420
    }
421
422
    /**
423
     * Validate that an attribute is between a given number of digits.
424
     *
425
     * @param  string  $attribute
426
     * @param  mixed   $value
427
     * @param  array   $parameters
428
     * @return bool
429
     */
430
    public function validateDigitsBetween($attribute, $value, $parameters)
431
    {
432
        $this->requireParameterCount(2, $parameters, 'digits_between');
433
434
        $length = strlen((string) $value);
435
436
        return ! preg_match('/[^0-9]/', $value)
437
                    && $length >= $parameters[0] && $length <= $parameters[1];
438
    }
439
440
    /**
441
     * Validate the dimensions of an image matches the given values.
442
     *
443
     * @param  string $attribute
444
     * @param  mixed $value
445
     * @param  array $parameters
446
     * @return bool
447
     */
448
    public function validateDimensions($attribute, $value, $parameters)
449
    {
450
        if (! $this->isValidFileInstance($value) || ! $sizeDetails = @getimagesize($value->getRealPath())) {
451
            return false;
452
        }
453
454
        $this->requireParameterCount(1, $parameters, 'dimensions');
455
456
        list($width, $height) = $sizeDetails;
457
458
        $parameters = $this->parseNamedParameters($parameters);
459
460
        if ($this->failsBasicDimensionChecks($parameters, $width, $height) ||
461
            $this->failsRatioCheck($parameters, $width, $height)) {
462
            return false;
463
        }
464
465
        return true;
466
    }
467
468
    /**
469
     * Test if the given width and height fail any conditions.
470
     *
471
     * @param  array  $parameters
472
     * @param  int  $width
473
     * @param  int  $height
474
     * @return bool
475
     */
476
    protected function failsBasicDimensionChecks($parameters, $width, $height)
477
    {
478
        return (isset($parameters['width']) && $parameters['width'] != $width) ||
479
               (isset($parameters['min_width']) && $parameters['min_width'] > $width) ||
480
               (isset($parameters['max_width']) && $parameters['max_width'] < $width) ||
481
               (isset($parameters['height']) && $parameters['height'] != $height) ||
482
               (isset($parameters['min_height']) && $parameters['min_height'] > $height) ||
483
               (isset($parameters['max_height']) && $parameters['max_height'] < $height);
484
    }
485
486
    /**
487
     * Determine if the given parameters fail a dimension ratio check.
488
     *
489
     * @param  array  $parameters
490
     * @param  int  $width
491
     * @param  int  $height
492
     * @return bool
493
     */
494
    protected function failsRatioCheck($parameters, $width, $height)
495
    {
496
        if (! isset($parameters['ratio'])) {
497
            return false;
498
        }
499
500
        list($numerator, $denominator) = array_replace(
501
            [1, 1], array_filter(sscanf($parameters['ratio'], '%f/%d'))
502
        );
503
504
        $precision = 1 / max($width, $height);
505
506
        return abs($numerator / $denominator - $width / $height) > $precision;
507
    }
508
509
    /**
510
     * Validate an attribute is unique among other values.
511
     *
512
     * @param  string  $attribute
513
     * @param  mixed   $value
514
     * @param  array   $parameters
515
     * @return bool
516
     */
517
    public function validateDistinct($attribute, $value, $parameters)
518
    {
519
        $attributeName = $this->getPrimaryAttribute($attribute);
520
521
        $attributeData = ValidationData::extractDataFromPath(
522
            ValidationData::getLeadingExplicitAttributePath($attributeName), $this->data
523
        );
524
525
        $pattern = str_replace('\*', '[^.]+', preg_quote($attributeName, '#'));
526
527
        $data = Arr::where(Arr::dot($attributeData), function ($value, $key) use ($attribute, $pattern) {
528
            return $key != $attribute && (bool) preg_match('#^'.$pattern.'\z#u', $key);
529
        });
530
531
        if (in_array('ignore_case', $parameters)) {
532
            return empty(preg_grep('/^'.preg_quote($value, '/').'$/iu', $data));
533
        }
534
535
        return ! in_array($value, array_values($data));
536
    }
537
538
    /**
539
     * Validate that an attribute is a valid e-mail address.
540
     *
541
     * @param  string  $attribute
542
     * @param  mixed   $value
543
     * @return bool
544
     */
545
    public function validateEmail($attribute, $value)
546
    {
547
        return filter_var($value, FILTER_VALIDATE_EMAIL) !== false;
548
    }
549
550
    /**
551
     * Validate the existence of an attribute value in a database table.
552
     *
553
     * @param  string  $attribute
554
     * @param  mixed   $value
555
     * @param  array   $parameters
556
     * @return bool
557
     */
558
    public function validateExists($attribute, $value, $parameters)
559
    {
560
        $this->requireParameterCount(1, $parameters, 'exists');
561
562
        list($connection, $table) = $this->parseTable($parameters[0]);
563
564
        // The second parameter position holds the name of the column that should be
565
        // verified as existing. If this parameter is not specified we will guess
566
        // that the columns being "verified" shares the given attribute's name.
567
        $column = $this->getQueryColumn($parameters, $attribute);
568
569
        $expected = (is_array($value)) ? count($value) : 1;
570
571
        return $this->getExistCount(
572
            $connection, $table, $column, $value, $parameters
573
        ) >= $expected;
574
    }
575
576
    /**
577
     * Get the number of records that exist in storage.
578
     *
579
     * @param  mixed   $connection
580
     * @param  string  $table
581
     * @param  string  $column
582
     * @param  mixed   $value
583
     * @param  array   $parameters
584
     * @return int
585
     */
586
    protected function getExistCount($connection, $table, $column, $value, $parameters)
587
    {
588
        $verifier = $this->getPresenceVerifierFor($connection);
589
590
        $extra = $this->getExtraConditions(
591
            array_values(array_slice($parameters, 2))
592
        );
593
594
        if ($this->currentRule instanceof Exists) {
595
            $extra = array_merge($extra, $this->currentRule->queryCallbacks());
596
        }
597
598
        return is_array($value)
599
                ? $verifier->getMultiCount($table, $column, $value, $extra)
600
                : $verifier->getCount($table, $column, $value, null, null, $extra);
601
    }
602
603
    /**
604
     * Validate the uniqueness of an attribute value on a given database table.
605
     *
606
     * If a database column is not specified, the attribute will be used.
607
     *
608
     * @param  string  $attribute
609
     * @param  mixed   $value
610
     * @param  array   $parameters
611
     * @return bool
612
     */
613
    public function validateUnique($attribute, $value, $parameters)
614
    {
615
        $this->requireParameterCount(1, $parameters, 'unique');
616
617
        list($connection, $table) = $this->parseTable($parameters[0]);
618
619
        // The second parameter position holds the name of the column that needs to
620
        // be verified as unique. If this parameter isn't specified we will just
621
        // assume that this column to be verified shares the attribute's name.
622
        $column = $this->getQueryColumn($parameters, $attribute);
623
624
        list($idColumn, $id) = [null, null];
625
626
        if (isset($parameters[2])) {
627
            list($idColumn, $id) = $this->getUniqueIds($parameters);
628
        }
629
630
        // The presence verifier is responsible for counting rows within this store
631
        // mechanism which might be a relational database or any other permanent
632
        // data store like Redis, etc. We will use it to determine uniqueness.
633
        $verifier = $this->getPresenceVerifierFor($connection);
634
635
        $extra = $this->getUniqueExtra($parameters);
636
637
        if ($this->currentRule instanceof Unique) {
638
            $extra = array_merge($extra, $this->currentRule->queryCallbacks());
639
        }
640
641
        return $verifier->getCount(
642
            $table, $column, $value, $id, $idColumn, $extra
643
        ) == 0;
644
    }
645
646
    /**
647
     * Get the excluded ID column and value for the unique rule.
648
     *
649
     * @param  array  $parameters
650
     * @return array
651
     */
652
    protected function getUniqueIds($parameters)
653
    {
654
        $idColumn = $parameters[3] ?? 'id';
655
656
        return [$idColumn, $this->prepareUniqueId($parameters[2])];
657
    }
658
659
    /**
660
     * Prepare the given ID for querying.
661
     *
662
     * @param  mixed  $id
663
     * @return int
664
     */
665
    protected function prepareUniqueId($id)
666
    {
667
        if (preg_match('/\[(.*)\]/', $id, $matches)) {
668
            $id = $this->getValue($matches[1]);
669
        }
670
671
        if (strtolower($id) == 'null') {
672
            $id = null;
673
        }
674
675
        if (filter_var($id, FILTER_VALIDATE_INT) !== false) {
676
            $id = (int) $id;
677
        }
678
679
        return $id;
680
    }
681
682
    /**
683
     * Get the extra conditions for a unique rule.
684
     *
685
     * @param  array  $parameters
686
     * @return array
687
     */
688
    protected function getUniqueExtra($parameters)
689
    {
690
        if (isset($parameters[4])) {
691
            return $this->getExtraConditions(array_slice($parameters, 4));
692
        }
693
694
        return [];
695
    }
696
697
    /**
698
     * Parse the connection / table for the unique / exists rules.
699
     *
700
     * @param  string  $table
701
     * @return array
702
     */
703
    protected function parseTable($table)
704
    {
705
        return Str::contains($table, '.') ? explode('.', $table, 2) : [null, $table];
706
    }
707
708
    /**
709
     * Get the column name for an exists / unique query.
710
     *
711
     * @param  array  $parameters
712
     * @param  string  $attribute
713
     * @return bool
714
     */
715
    protected function getQueryColumn($parameters, $attribute)
716
    {
717
        return isset($parameters[1]) && $parameters[1] !== 'NULL'
718
                    ? $parameters[1] : $this->guessColumnForQuery($attribute);
719
    }
720
721
    /**
722
     * Guess the database column from the given attribute name.
723
     *
724
     * @param  string  $attribute
725
     * @return string
726
     */
727
    public function guessColumnForQuery($attribute)
728
    {
729
        if (in_array($attribute, Arr::collapse($this->implicitAttributes))
730
                && ! is_numeric($last = last(explode('.', $attribute)))) {
731
            return $last;
732
        }
733
734
        return $attribute;
735
    }
736
737
    /**
738
     * Get the extra conditions for a unique / exists rule.
739
     *
740
     * @param  array  $segments
741
     * @return array
742
     */
743
    protected function getExtraConditions(array $segments)
744
    {
745
        $extra = [];
746
747
        $count = count($segments);
748
749
        for ($i = 0; $i < $count; $i += 2) {
750
            $extra[$segments[$i]] = $segments[$i + 1];
751
        }
752
753
        return $extra;
754
    }
755
756
    /**
757
     * Validate the given value is a valid file.
758
     *
759
     * @param  string  $attribute
760
     * @param  mixed   $value
761
     * @return bool
762
     */
763
    public function validateFile($attribute, $value)
764
    {
765
        return $this->isValidFileInstance($value);
766
    }
767
768
    /**
769
     * Validate the given attribute is filled if it is present.
770
     *
771
     * @param  string  $attribute
772
     * @param  mixed   $value
773
     * @return bool
774
     */
775
    public function validateFilled($attribute, $value)
776
    {
777
        if (Arr::has($this->data, $attribute)) {
778
            return $this->validateRequired($attribute, $value);
779
        }
780
781
        return true;
782
    }
783
784
    /**
785
     * Validate the MIME type of a file is an image MIME type.
786
     *
787
     * @param  string  $attribute
788
     * @param  mixed   $value
789
     * @return bool
790
     */
791
    public function validateImage($attribute, $value)
792
    {
793
        return $this->validateMimes($attribute, $value, ['jpeg', 'png', 'gif', 'bmp', 'svg']);
794
    }
795
796
    /**
797
     * Validate an attribute is contained within a list of values.
798
     *
799
     * @param  string  $attribute
800
     * @param  mixed   $value
801
     * @param  array   $parameters
802
     * @return bool
803
     */
804
    public function validateIn($attribute, $value, $parameters)
805
    {
806
        if (is_array($value) && $this->hasRule($attribute, 'Array')) {
807
            foreach ($value as $element) {
808
                if (is_array($element)) {
809
                    return false;
810
                }
811
            }
812
813
            return count(array_diff($value, $parameters)) == 0;
814
        }
815
816
        return ! is_array($value) && in_array((string) $value, $parameters);
817
    }
818
819
    /**
820
     * Validate that the values of an attribute is in another attribute.
821
     *
822
     * @param  string  $attribute
823
     * @param  mixed   $value
824
     * @param  array   $parameters
825
     * @return bool
826
     */
827
    public function validateInArray($attribute, $value, $parameters)
828
    {
829
        $this->requireParameterCount(1, $parameters, 'in_array');
830
831
        $explicitPath = ValidationData::getLeadingExplicitAttributePath($parameters[0]);
832
833
        $attributeData = ValidationData::extractDataFromPath($explicitPath, $this->data);
834
835
        $otherValues = Arr::where(Arr::dot($attributeData), function ($value, $key) use ($parameters) {
836
            return Str::is($parameters[0], $key);
837
        });
838
839
        return in_array($value, $otherValues);
840
    }
841
842
    /**
843
     * Validate that an attribute is an integer.
844
     *
845
     * @param  string  $attribute
846
     * @param  mixed   $value
847
     * @return bool
848
     */
849
    public function validateInteger($attribute, $value)
850
    {
851
        return filter_var($value, FILTER_VALIDATE_INT) !== false;
852
    }
853
854
    /**
855
     * Validate that an attribute is a valid IP.
856
     *
857
     * @param  string  $attribute
858
     * @param  mixed   $value
859
     * @return bool
860
     */
861
    public function validateIp($attribute, $value)
862
    {
863
        return filter_var($value, FILTER_VALIDATE_IP) !== false;
864
    }
865
866
    /**
867
     * Validate that an attribute is a valid IPv4.
868
     *
869
     * @param  string  $attribute
870
     * @param  mixed   $value
871
     * @return bool
872
     */
873
    public function validateIpv4($attribute, $value)
874
    {
875
        return filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false;
876
    }
877
878
    /**
879
     * Validate that an attribute is a valid IPv6.
880
     *
881
     * @param  string  $attribute
882
     * @param  mixed   $value
883
     * @return bool
884
     */
885
    public function validateIpv6($attribute, $value)
886
    {
887
        return filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false;
888
    }
889
890
    /**
891
     * Validate the attribute is a valid JSON string.
892
     *
893
     * @param  string  $attribute
894
     * @param  mixed   $value
895
     * @return bool
896
     */
897
    public function validateJson($attribute, $value)
898
    {
899
        if (! is_scalar($value) && ! method_exists($value, '__toString')) {
900
            return false;
901
        }
902
903
        json_decode($value);
904
905
        return json_last_error() === JSON_ERROR_NONE;
906
    }
907
908
    /**
909
     * Validate the size of an attribute is less than a maximum value.
910
     *
911
     * @param  string  $attribute
912
     * @param  mixed   $value
913
     * @param  array   $parameters
914
     * @return bool
915
     */
916
    public function validateMax($attribute, $value, $parameters)
917
    {
918
        $this->requireParameterCount(1, $parameters, 'max');
919
920
        if ($value instanceof UploadedFile && ! $value->isValid()) {
921
            return false;
922
        }
923
924
        return $this->getSize($attribute, $value) <= $parameters[0];
925
    }
926
927
    /**
928
     * Validate the guessed extension of a file upload is in a set of file extensions.
929
     *
930
     * @param  string  $attribute
931
     * @param  mixed  $value
932
     * @param  array   $parameters
933
     * @return bool
934
     */
935
    public function validateMimes($attribute, $value, $parameters)
936
    {
937
        if (! $this->isValidFileInstance($value)) {
938
            return false;
939
        }
940
941
        if ($this->shouldBlockPhpUpload($value, $parameters)) {
942
            return false;
943
        }
944
945
        return $value->getPath() !== '' && in_array($value->guessExtension(), $parameters);
946
    }
947
948
    /**
949
     * Validate the MIME type of a file upload attribute is in a set of MIME types.
950
     *
951
     * @param  string  $attribute
952
     * @param  mixed  $value
953
     * @param  array  $parameters
954
     * @return bool
955
     */
956
    public function validateMimetypes($attribute, $value, $parameters)
957
    {
958
        if (! $this->isValidFileInstance($value)) {
959
            return false;
960
        }
961
962
        if ($this->shouldBlockPhpUpload($value, $parameters)) {
963
            return false;
964
        }
965
966
        return $value->getPath() !== '' &&
967
                (in_array($value->getMimeType(), $parameters) ||
968
                 in_array(explode('/', $value->getMimeType())[0].'/*', $parameters));
969
    }
970
971
    /**
972
     * Check if PHP uploads are explicitly allowed.
973
     *
974
     * @param  mixed  $value
975
     * @param  array  $parameters
976
     * @return bool
977
     */
978
    protected function shouldBlockPhpUpload($value, $parameters)
979
    {
980
        if (in_array('php', $parameters)) {
981
            return false;
982
        }
983
984
        return ($value instanceof UploadedFile)
985
           ? trim(strtolower($value->getClientOriginalExtension())) === 'php'
986
           : trim(strtolower($value->getExtension())) === 'php';
987
    }
988
989
    /**
990
     * Validate the size of an attribute is greater than a minimum value.
991
     *
992
     * @param  string  $attribute
993
     * @param  mixed   $value
994
     * @param  array   $parameters
995
     * @return bool
996
     */
997
    public function validateMin($attribute, $value, $parameters)
998
    {
999
        $this->requireParameterCount(1, $parameters, 'min');
1000
1001
        return $this->getSize($attribute, $value) >= $parameters[0];
1002
    }
1003
1004
    /**
1005
     * "Indicate" validation should pass if value is null.
1006
     *
1007
     * Always returns true, just lets us put "nullable" in rules.
1008
     *
1009
     * @return bool
1010
     */
1011
    public function validateNullable()
1012
    {
1013
        return true;
1014
    }
1015
1016
    /**
1017
     * Validate an attribute is not contained within a list of values.
1018
     *
1019
     * @param  string  $attribute
1020
     * @param  mixed   $value
1021
     * @param  array   $parameters
1022
     * @return bool
1023
     */
1024
    public function validateNotIn($attribute, $value, $parameters)
1025
    {
1026
        return ! $this->validateIn($attribute, $value, $parameters);
1027
    }
1028
1029
    /**
1030
     * Validate that an attribute is numeric.
1031
     *
1032
     * @param  string  $attribute
1033
     * @param  mixed   $value
1034
     * @return bool
1035
     */
1036
    public function validateNumeric($attribute, $value)
1037
    {
1038
        return is_numeric($value);
1039
    }
1040
1041
    /**
1042
     * Validate that an attribute exists even if not filled.
1043
     *
1044
     * @param  string  $attribute
1045
     * @param  mixed   $value
1046
     * @return bool
1047
     */
1048
    public function validatePresent($attribute, $value)
1049
    {
1050
        return Arr::has($this->data, $attribute);
1051
    }
1052
1053
    /**
1054
     * Validate that an attribute passes a regular expression check.
1055
     *
1056
     * @param  string  $attribute
1057
     * @param  mixed   $value
1058
     * @param  array   $parameters
1059
     * @return bool
1060
     */
1061
    public function validateRegex($attribute, $value, $parameters)
1062
    {
1063
        if (! is_string($value) && ! is_numeric($value)) {
1064
            return false;
1065
        }
1066
1067
        $this->requireParameterCount(1, $parameters, 'regex');
1068
1069
        return preg_match($parameters[0], $value) > 0;
1070
    }
1071
1072
    /**
1073
     * Validate that a required attribute exists.
1074
     *
1075
     * @param  string  $attribute
1076
     * @param  mixed   $value
1077
     * @return bool
1078
     */
1079
    public function validateRequired($attribute, $value)
1080
    {
1081
        if (is_null($value)) {
1082
            return false;
1083
        } elseif (is_string($value) && trim($value) === '') {
1084
            return false;
1085
        } elseif ((is_array($value) || $value instanceof Countable) && count($value) < 1) {
1086
            return false;
1087
        } elseif ($value instanceof File) {
1088
            return (string) $value->getPath() !== '';
1089
        }
1090
1091
        return true;
1092
    }
1093
1094
    /**
1095
     * Validate that an attribute exists when another attribute has a given value.
1096
     *
1097
     * @param  string  $attribute
1098
     * @param  mixed   $value
1099
     * @param  mixed   $parameters
1100
     * @return bool
1101
     */
1102
    public function validateRequiredIf($attribute, $value, $parameters)
1103
    {
1104
        $this->requireParameterCount(2, $parameters, 'required_if');
1105
1106
        $other = Arr::get($this->data, $parameters[0]);
1107
1108
        $values = array_slice($parameters, 1);
1109
1110
        if (is_bool($other)) {
1111
            $values = $this->convertValuesToBoolean($values);
1112
        }
1113
1114
        if (in_array($other, $values)) {
1115
            return $this->validateRequired($attribute, $value);
1116
        }
1117
1118
        return true;
1119
    }
1120
1121
    /**
1122
     * Convert the given values to boolean if they are string "true" / "false".
1123
     *
1124
     * @param  array  $values
1125
     * @return array
1126
     */
1127
    protected function convertValuesToBoolean($values)
1128
    {
1129
        return array_map(function ($value) {
1130
            if ($value === 'true') {
1131
                return true;
1132
            } elseif ($value === 'false') {
1133
                return false;
1134
            }
1135
1136
            return $value;
1137
        }, $values);
1138
    }
1139
1140
    /**
1141
     * Validate that an attribute exists when another attribute does not have a given value.
1142
     *
1143
     * @param  string  $attribute
1144
     * @param  mixed  $value
1145
     * @param  mixed  $parameters
1146
     * @return bool
1147
     */
1148
    public function validateRequiredUnless($attribute, $value, $parameters)
1149
    {
1150
        $this->requireParameterCount(2, $parameters, 'required_unless');
1151
1152
        $data = Arr::get($this->data, $parameters[0]);
1153
1154
        $values = array_slice($parameters, 1);
1155
1156
        if (! in_array($data, $values)) {
1157
            return $this->validateRequired($attribute, $value);
1158
        }
1159
1160
        return true;
1161
    }
1162
1163
    /**
1164
     * Validate that an attribute exists when any other attribute exists.
1165
     *
1166
     * @param  string  $attribute
1167
     * @param  mixed   $value
1168
     * @param  mixed   $parameters
1169
     * @return bool
1170
     */
1171
    public function validateRequiredWith($attribute, $value, $parameters)
1172
    {
1173
        if (! $this->allFailingRequired($parameters)) {
1174
            return $this->validateRequired($attribute, $value);
1175
        }
1176
1177
        return true;
1178
    }
1179
1180
    /**
1181
     * Validate that an attribute exists when all other attributes exists.
1182
     *
1183
     * @param  string  $attribute
1184
     * @param  mixed   $value
1185
     * @param  mixed   $parameters
1186
     * @return bool
1187
     */
1188
    public function validateRequiredWithAll($attribute, $value, $parameters)
1189
    {
1190
        if (! $this->anyFailingRequired($parameters)) {
1191
            return $this->validateRequired($attribute, $value);
1192
        }
1193
1194
        return true;
1195
    }
1196
1197
    /**
1198
     * Validate that an attribute exists when another attribute does not.
1199
     *
1200
     * @param  string  $attribute
1201
     * @param  mixed   $value
1202
     * @param  mixed   $parameters
1203
     * @return bool
1204
     */
1205
    public function validateRequiredWithout($attribute, $value, $parameters)
1206
    {
1207
        if ($this->anyFailingRequired($parameters)) {
1208
            return $this->validateRequired($attribute, $value);
1209
        }
1210
1211
        return true;
1212
    }
1213
1214
    /**
1215
     * Validate that an attribute exists when all other attributes do not.
1216
     *
1217
     * @param  string  $attribute
1218
     * @param  mixed   $value
1219
     * @param  mixed   $parameters
1220
     * @return bool
1221
     */
1222
    public function validateRequiredWithoutAll($attribute, $value, $parameters)
1223
    {
1224
        if ($this->allFailingRequired($parameters)) {
1225
            return $this->validateRequired($attribute, $value);
1226
        }
1227
1228
        return true;
1229
    }
1230
1231
    /**
1232
     * Determine if any of the given attributes fail the required test.
1233
     *
1234
     * @param  array  $attributes
1235
     * @return bool
1236
     */
1237
    protected function anyFailingRequired(array $attributes)
1238
    {
1239
        foreach ($attributes as $key) {
1240
            if (! $this->validateRequired($key, $this->getValue($key))) {
1241
                return true;
1242
            }
1243
        }
1244
1245
        return false;
1246
    }
1247
1248
    /**
1249
     * Determine if all of the given attributes fail the required test.
1250
     *
1251
     * @param  array  $attributes
1252
     * @return bool
1253
     */
1254
    protected function allFailingRequired(array $attributes)
1255
    {
1256
        foreach ($attributes as $key) {
1257
            if ($this->validateRequired($key, $this->getValue($key))) {
1258
                return false;
1259
            }
1260
        }
1261
1262
        return true;
1263
    }
1264
1265
    /**
1266
     * Validate that two attributes match.
1267
     *
1268
     * @param  string  $attribute
1269
     * @param  mixed   $value
1270
     * @param  array   $parameters
1271
     * @return bool
1272
     */
1273
    public function validateSame($attribute, $value, $parameters)
1274
    {
1275
        $this->requireParameterCount(1, $parameters, 'same');
1276
1277
        $other = Arr::get($this->data, $parameters[0]);
1278
1279
        return $value === $other;
1280
    }
1281
1282
    /**
1283
     * Validate the size of an attribute.
1284
     *
1285
     * @param  string  $attribute
1286
     * @param  mixed   $value
1287
     * @param  array   $parameters
1288
     * @return bool
1289
     */
1290
    public function validateSize($attribute, $value, $parameters)
1291
    {
1292
        $this->requireParameterCount(1, $parameters, 'size');
1293
1294
        return $this->getSize($attribute, $value) == $parameters[0];
1295
    }
1296
1297
    /**
1298
     * "Validate" optional attributes.
1299
     *
1300
     * Always returns true, just lets us put sometimes in rules.
1301
     *
1302
     * @return bool
1303
     */
1304
    public function validateSometimes()
1305
    {
1306
        return true;
1307
    }
1308
1309
    /**
1310
     * Validate that an attribute is a string.
1311
     *
1312
     * @param  string  $attribute
1313
     * @param  mixed   $value
1314
     * @return bool
1315
     */
1316
    public function validateString($attribute, $value)
1317
    {
1318
        return is_string($value);
1319
    }
1320
1321
    /**
1322
     * Validate that an attribute is a valid timezone.
1323
     *
1324
     * @param  string  $attribute
1325
     * @param  mixed   $value
1326
     * @return bool
1327
     */
1328
    public function validateTimezone($attribute, $value)
1329
    {
1330
        try {
1331
            new DateTimeZone($value);
1332
        } catch (Exception $e) {
1333
            return false;
1334
        } catch (Throwable $e) {
1335
            return false;
1336
        }
1337
1338
        return true;
1339
    }
1340
1341
    /**
1342
     * Validate that an attribute is a valid URL.
1343
     *
1344
     * @param  string  $attribute
1345
     * @param  mixed   $value
1346
     * @return bool
1347
     */
1348
    public function validateUrl($attribute, $value)
1349
    {
1350
        if (! is_string($value)) {
1351
            return false;
1352
        }
1353
1354
        /*
1355
         * This pattern is derived from Symfony\Component\Validator\Constraints\UrlValidator (2.7.4).
1356
         *
1357
         * (c) Fabien Potencier <[email protected]> http://symfony.com
1358
         */
1359
        $pattern = '~^
1360
            ((aaa|aaas|about|acap|acct|acr|adiumxtra|afp|afs|aim|apt|attachment|aw|barion|beshare|bitcoin|blob|bolo|callto|cap|chrome|chrome-extension|cid|coap|coaps|com-eventbrite-attendee|content|crid|cvs|data|dav|dict|dlna-playcontainer|dlna-playsingle|dns|dntp|dtn|dvb|ed2k|example|facetime|fax|feed|feedready|file|filesystem|finger|fish|ftp|geo|gg|git|gizmoproject|go|gopher|gtalk|h323|ham|hcp|http|https|iax|icap|icon|im|imap|info|iotdisco|ipn|ipp|ipps|irc|irc6|ircs|iris|iris.beep|iris.lwz|iris.xpc|iris.xpcs|itms|jabber|jar|jms|keyparc|lastfm|ldap|ldaps|magnet|mailserver|mailto|maps|market|message|mid|mms|modem|ms-help|ms-settings|ms-settings-airplanemode|ms-settings-bluetooth|ms-settings-camera|ms-settings-cellular|ms-settings-cloudstorage|ms-settings-emailandaccounts|ms-settings-language|ms-settings-location|ms-settings-lock|ms-settings-nfctransactions|ms-settings-notifications|ms-settings-power|ms-settings-privacy|ms-settings-proximity|ms-settings-screenrotation|ms-settings-wifi|ms-settings-workplace|msnim|msrp|msrps|mtqp|mumble|mupdate|mvn|news|nfs|ni|nih|nntp|notes|oid|opaquelocktoken|pack|palm|paparazzi|pkcs11|platform|pop|pres|prospero|proxy|psyc|query|redis|rediss|reload|res|resource|rmi|rsync|rtmfp|rtmp|rtsp|rtsps|rtspu|secondlife|s3|service|session|sftp|sgn|shttp|sieve|sip|sips|skype|smb|sms|smtp|snews|snmp|soap.beep|soap.beeps|soldat|spotify|ssh|steam|stun|stuns|submit|svn|tag|teamspeak|tel|teliaeid|telnet|tftp|things|thismessage|tip|tn3270|turn|turns|tv|udp|unreal|urn|ut2004|vemmi|ventrilo|videotex|view-source|wais|webcal|ws|wss|wtai|wyciwyg|xcon|xcon-userid|xfire|xmlrpc\.beep|xmlrpc.beeps|xmpp|xri|ymsgr|z39\.50|z39\.50r|z39\.50s))://                                 # protocol
1361
            (([\pL\pN-]+:)?([\pL\pN-]+)@)?          # basic auth
1362
            (
1363
                ([\pL\pN\pS-\.])+(\.?([\pL]|xn\-\-[\pL\pN-]+)+\.?) # a domain name
1364
                    |                                              # or
1365
                \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}                 # an IP address
1366
                    |                                              # or
1367
                \[
1368
                    (?:(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){6})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:::(?:(?:(?:[0-9a-f]{1,4})):){5})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){4})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,1}(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){3})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,2}(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){2})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,3}(?:(?:[0-9a-f]{1,4})))?::(?:(?:[0-9a-f]{1,4})):)(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,4}(?:(?:[0-9a-f]{1,4})))?::)(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,5}(?:(?:[0-9a-f]{1,4})))?::)(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,6}(?:(?:[0-9a-f]{1,4})))?::))))
1369
                \]  # an IPv6 address
1370
            )
1371
            (:[0-9]+)?                              # a port (optional)
1372
            (/?|/\S+|\?\S*|\#\S*)                   # a /, nothing, a / with something, a query or a fragment
1373
        $~ixu';
1374
1375
        return preg_match($pattern, $value) > 0;
1376
    }
1377
1378
    /**
1379
     * Get the size of an attribute.
1380
     *
1381
     * @param  string  $attribute
1382
     * @param  mixed   $value
1383
     * @return mixed
1384
     */
1385
    protected function getSize($attribute, $value)
1386
    {
1387
        $hasNumeric = $this->hasRule($attribute, $this->numericRules);
1388
1389
        // This method will determine if the attribute is a number, string, or file and
1390
        // return the proper size accordingly. If it is a number, then number itself
1391
        // is the size. If it is a file, we take kilobytes, and for a string the
1392
        // entire length of the string will be considered the attribute size.
1393
        if (is_numeric($value) && $hasNumeric) {
1394
            return $value;
1395
        } elseif (is_array($value)) {
1396
            return count($value);
1397
        } elseif ($value instanceof File) {
1398
            return $value->getSize() / 1024;
1399
        }
1400
1401
        return mb_strlen($value);
1402
    }
1403
1404
    /**
1405
     * Check that the given value is a valid file instance.
1406
     *
1407
     * @param  mixed  $value
1408
     * @return bool
1409
     */
1410
    public function isValidFileInstance($value)
1411
    {
1412
        if ($value instanceof UploadedFile && ! $value->isValid()) {
1413
            return false;
1414
        }
1415
1416
        return $value instanceof File;
1417
    }
1418
1419
    /**
1420
     * Determine if a comparison passes between the given values.
1421
     *
1422
     * @param  mixed  $first
1423
     * @param  mixed  $second
1424
     * @param  string  $operator
1425
     * @return bool
1426
     */
1427
    protected function compare($first, $second, $operator)
1428
    {
1429
        switch ($operator) {
1430
            case '<':
1431
                return $first < $second;
1432
            case '>':
1433
                return $first > $second;
1434
            case '<=':
1435
                return $first <= $second;
1436
            case '>=':
1437
                return $first >= $second;
1438
            case '=':
1439
                return $first == $second;
1440
            default:
1441
                throw new InvalidArgumentException;
1442
        }
1443
    }
1444
1445
    /**
1446
     * Parse named parameters to $key => $value items.
1447
     *
1448
     * @param  array  $parameters
1449
     * @return array
1450
     */
1451
    protected function parseNamedParameters($parameters)
1452
    {
1453
        return array_reduce($parameters, function ($result, $item) {
1454
            list($key, $value) = array_pad(explode('=', $item, 2), 2, null);
1455
1456
            $result[$key] = $value;
1457
1458
            return $result;
1459
        });
1460
    }
1461
1462
    /**
1463
     * Require a certain number of parameters to be present.
1464
     *
1465
     * @param  int    $count
1466
     * @param  array  $parameters
1467
     * @param  string  $rule
1468
     * @return void
1469
     *
1470
     * @throws \InvalidArgumentException
1471
     */
1472
    protected function requireParameterCount($count, $parameters, $rule)
1473
    {
1474
        if (count($parameters) < $count) {
1475
            throw new InvalidArgumentException("Validation rule $rule requires at least $count parameters.");
1476
        }
1477
    }
1478
}
1479