Failed Conditions
Pull Request — master (#214)
by Marco
19:05
created

Assert::isIterable()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 9

Duplication

Lines 9
Ratio 100 %

Code Coverage

Tests 1
CRAP Score 4

Importance

Changes 0
Metric Value
dl 9
loc 9
ccs 1
cts 1
cp 1
rs 9.9666
c 0
b 0
f 0
cc 4
nc 2
nop 2
crap 4
1
<?php
2
3
/*
4
 * This file is part of the webmozart/assert package.
5
 *
6
 * (c) Bernhard Schussek <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Webmozart\Assert;
13
14
use ArrayAccess;
15
use BadMethodCallException;
16
use Closure;
17
use Countable;
18
use DateTime;
19
use DateTimeImmutable;
20
use Exception;
21
use InvalidArgumentException;
22
use ResourceBundle;
23
use SimpleXMLElement;
24
use Throwable;
25
use Traversable;
26
27
/**
28
 * Efficient assertions to validate the input/output of your methods.
29
 *
30
 * @mixin Mixin
31
 *
32
 * @since  1.0
33
 *
34
 * @author Bernhard Schussek <[email protected]>
35
 */
36
class Assert
0 ignored issues
show
Coding Style introduced by
Since you have declared the constructor as private, maybe you should also declare the class as final.
Loading history...
37
{
38
    /**
39
     * @psalm-pure
40
     * @psalm-assert string $value
41
     *
42
     * @param mixed  $value
43
     * @param string $message
44
     *
45
     * @throws InvalidArgumentException
46
     */
47 241
    public static function string($value, $message = '')
48
    {
49 241
        if (!\is_string($value)) {
50 45
            static::reportInvalidArgument(\sprintf(
51 45
                $message ?: 'Expected a string. Got: %s',
52 45
                static::typeToString($value)
53
            ));
54
        }
55 196
    }
56
57
    /**
58
     * @psalm-pure
59
     * @psalm-assert non-empty-string $value
60
     *
61
     * @param mixed  $value
62
     * @param string $message
63
     *
64
     * @throws InvalidArgumentException
65
     */
66 16
    public static function stringNotEmpty($value, $message = '')
67
    {
68 16
        static::string($value, $message);
69 12
        static::notEq($value, '', $message);
70 8
    }
71
72
    /**
73
     * @psalm-pure
74
     * @psalm-assert int $value
75
     *
76
     * @param mixed  $value
77
     * @param string $message
78
     *
79
     * @throws InvalidArgumentException
80
     */
81 17 View Code Duplication
    public static function integer($value, $message = '')
82
    {
83 17
        if (!\is_int($value)) {
84 13
            static::reportInvalidArgument(\sprintf(
85 13
                $message ?: 'Expected an integer. Got: %s',
86 13
                static::typeToString($value)
87
            ));
88
        }
89 4
    }
90
91
    /**
92
     * @psalm-pure
93
     * @psalm-assert numeric $value
94
     *
95
     * @param mixed  $value
96
     * @param string $message
97
     *
98
     * @throws InvalidArgumentException
99
     */
100 16
    public static function integerish($value, $message = '')
101
    {
102 16
        if (!\is_numeric($value) || $value != (int) $value) {
103 4
            static::reportInvalidArgument(\sprintf(
104 4
                $message ?: 'Expected an integerish value. Got: %s',
105 4
                static::typeToString($value)
106
            ));
107
        }
108 12
    }
109
110
    /**
111
     * @psalm-pure
112
     * @psalm-assert positive-int $value
113
     *
114
     * @param mixed  $value
115
     * @param string $message
116
     *
117
     * @throws InvalidArgumentException
118
     */
119 16 View Code Duplication
    public static function positiveInteger($value, $message = '')
120
    {
121 16
        if (! (\is_int($value) && $value > 0)) {
122 8
            static::reportInvalidArgument(\sprintf(
123 8
                $message ?: 'Expected a positive integer. Got: %s',
124 8
                static::valueToString($value)
125
            ));
126
        }
127 8
    }
128
129
    /**
130
     * @psalm-pure
131
     * @psalm-assert positive-int|0 $value
132
     *
133
     * @param mixed  $value
134
     * @param string $message
135
     *
136
     * @throws InvalidArgumentException
137
     */
138 20 View Code Duplication
    public static function naturalNumber($value, $message = '')
139
    {
140 20
        if (! (\is_int($value) && $value > 0)) {
141 4
            static::reportInvalidArgument(\sprintf(
142 4
                $message ?: 'Expected a positive integer. Got: %s',
143 4
                static::valueToString($value)
144
            ));
145
        }
146 16
    }
147
148
    /**
149
     * @psalm-pure
150
     * @psalm-assert float $value
151
     *
152
     * @param mixed  $value
153
     * @param string $message
154
     *
155
     * @throws InvalidArgumentException
156
     */
157 24
    public static function float($value, $message = '')
158
    {
159 24
        if (!\is_float($value)) {
160 16
            static::reportInvalidArgument(\sprintf(
161 16
                $message ?: 'Expected a float. Got: %s',
162 16
                static::typeToString($value)
163
            ));
164
        }
165 8
    }
166
167
    /**
168
     * @psalm-pure
169
     * @psalm-assert numeric $value
170
     *
171
     * @param mixed  $value
172
     * @param string $message
173
     *
174
     * @throws InvalidArgumentException
175
     */
176 16
    public static function numeric($value, $message = '')
177
    {
178 16
        if (!\is_numeric($value)) {
179 8
            static::reportInvalidArgument(\sprintf(
180 8
                $message ?: 'Expected a numeric. Got: %s',
181 8
                static::typeToString($value)
182
            ));
183
        }
184 8
    }
185
186
    /**
187
     * @psalm-pure
188
     * @psalm-assert int $value
189
     *
190
     * @param mixed  $value
191
     * @param string $message
192
     *
193
     * @throws InvalidArgumentException
194
     */
195 23 View Code Duplication
    public static function natural($value, $message = '')
196
    {
197 23
        if (!\is_int($value) || $value < 0) {
198 11
            static::reportInvalidArgument(\sprintf(
199 11
                $message ?: 'Expected a non-negative integer. Got: %s',
200 11
                static::valueToString($value)
201
            ));
202
        }
203 12
    }
204
205
    /**
206
     * @psalm-pure
207
     * @psalm-assert bool $value
208
     *
209
     * @param mixed  $value
210
     * @param string $message
211
     *
212
     * @throws InvalidArgumentException
213
     */
214 23
    public static function boolean($value, $message = '')
215
    {
216 23
        if (!\is_bool($value)) {
217 15
            static::reportInvalidArgument(\sprintf(
218 15
                $message ?: 'Expected a boolean. Got: %s',
219 15
                static::typeToString($value)
220
            ));
221
        }
222 8
    }
223
224
    /**
225
     * @psalm-pure
226
     * @psalm-assert scalar $value
227
     *
228
     * @param mixed  $value
229
     * @param string $message
230
     *
231
     * @throws InvalidArgumentException
232
     */
233
    public static function scalar($value, $message = '')
234 16
    {
235
        if (!\is_scalar($value)) {
236 16
            static::reportInvalidArgument(\sprintf(
237 4
                $message ?: 'Expected a scalar. Got: %s',
238 4
                static::typeToString($value)
239 4
            ));
240
        }
241
    }
242
243 12
    /**
244 4
     * @psalm-pure
245 4
     * @psalm-assert object $value
246 4
     *
247 4
     * @param mixed  $value
248
     * @param string $message
249
     *
250 8
     * @throws InvalidArgumentException
251
     */
252
    public static function object($value, $message = '')
253
    {
254
        if (!\is_object($value)) {
255
            static::reportInvalidArgument(\sprintf(
256
                $message ?: 'Expected an object. Got: %s',
257
                static::typeToString($value)
258
            ));
259
        }
260
    }
261 20
262
    /**
263 20
     * @psalm-pure
264 8
     * @psalm-assert resource $value
265 8
     *
266 8
     * @param mixed       $value
267
     * @param string|null $type    type of resource this should be. @see https://www.php.net/manual/en/function.get-resource-type.php
268
     * @param string      $message
269 12
     *
270
     * @throws InvalidArgumentException
271
     */
272
    public static function resource($value, $type = null, $message = '')
273
    {
274
        if (!\is_resource($value)) {
275
            static::reportInvalidArgument(\sprintf(
276
                $message ?: 'Expected a resource. Got: %s',
277
                static::typeToString($value)
278
            ));
279
        }
280 20
281
        if ($type && $type !== \get_resource_type($value)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $type of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
282 20
            static::reportInvalidArgument(\sprintf(
283 12
                $message ?: 'Expected a resource of type %2$s. Got: %s',
284 12
                static::typeToString($value),
285 12
                $type
286
            ));
287
        }
288 8
    }
289
290
    /**
291
     * @psalm-pure
292
     * @psalm-assert callable $value
293
     *
294
     * @param mixed  $value
295
     * @param string $message
296
     *
297
     * @throws InvalidArgumentException
298
     */
299
    public static function isCallable($value, $message = '')
300
    {
301 20
        if (!\is_callable($value)) {
302
            static::reportInvalidArgument(\sprintf(
303 20
                $message ?: 'Expected a callable. Got: %s',
304 20
                static::typeToString($value)
305
            ));
306 20
        }
307
    }
308 20
309
    /**
310
     * @psalm-pure
311 20
     * @psalm-assert array $value
312 8
     *
313 8
     * @param mixed  $value
314 8
     * @param string $message
315
     *
316
     * @throws InvalidArgumentException
317 12
     */
318
    public static function isArray($value, $message = '')
319
    {
320
        if (!\is_array($value)) {
321
            static::reportInvalidArgument(\sprintf(
322
                $message ?: 'Expected an array. Got: %s',
323
                static::typeToString($value)
324
            ));
325
        }
326
    }
327
328 20
    /**
329
     * @psalm-pure
330 20
     * @psalm-assert iterable $value
331 8
     *
332 8
     * @deprecated use "isIterable" or "isInstanceOf" instead
333 8
     *
334
     * @param mixed  $value
335
     * @param string $message
336 12
     *
337
     * @throws InvalidArgumentException
338
     */
339
    public static function isTraversable($value, $message = '')
340
    {
341
        @\trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
342
            \sprintf(
343
                'The "%s" assertion is deprecated. You should stop using it, as it will soon be removed in 2.0 version. Use "isIterable" or "isInstanceOf" instead.',
344
                __METHOD__
345
            ),
346
            \E_USER_DEPRECATED
347 28
        );
348
349
        if (!\is_array($value) && !($value instanceof Traversable)) {
350 28
            static::reportInvalidArgument(\sprintf(
351 28
                $message ?: 'Expected a traversable. Got: %s',
352 28
                static::typeToString($value)
353 28
            ));
354
        }
355 12
    }
356 12
357 12
    /**
358
     * @psalm-pure
359
     * @psalm-assert array|ArrayAccess $value
360 16
     *
361
     * @param mixed  $value
362
     * @param string $message
363
     *
364
     * @throws InvalidArgumentException
365
     */
366 View Code Duplication
    public static function isArrayAccessible($value, $message = '')
367
    {
368
        if (!\is_array($value) && !($value instanceof ArrayAccess)) {
369
            static::reportInvalidArgument(\sprintf(
370
                $message ?: 'Expected an array accessible. Got: %s',
371 1044
                static::typeToString($value)
372
            ));
373 1044
        }
374 8
    }
375 8
376 8
    /**
377
     * @psalm-pure
378
     * @psalm-assert countable $value
379 1040
     *
380
     * @param mixed  $value
381
     * @param string $message
382
     *
383
     * @throws InvalidArgumentException
384
     */
385
    public static function isCountable($value, $message = '')
386
    {
387
        if (
388
            !\is_array($value)
389
            && !($value instanceof Countable)
390
            && !($value instanceof ResourceBundle)
0 ignored issues
show
Bug introduced by
The class ResourceBundle does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
391
            && !($value instanceof SimpleXMLElement)
392
        ) {
393 19
            static::reportInvalidArgument(\sprintf(
394
                $message ?: 'Expected a countable. Got: %s',
395 19
                static::typeToString($value)
396 15
            ));
397 15
        }
398 15
    }
399 15
400
    /**
401
     * @psalm-pure
402 4
     * @psalm-assert iterable $value
403
     *
404
     * @param mixed  $value
405
     * @param string $message
406
     *
407
     * @throws InvalidArgumentException
408
     */
409 View Code Duplication
    public static function isIterable($value, $message = '')
410
    {
411
        if (!\is_array($value) && !($value instanceof Traversable)) {
412
            static::reportInvalidArgument(\sprintf(
413
                $message ?: 'Expected an iterable. Got: %s',
414
                static::typeToString($value)
415
            ));
416 16
        }
417
    }
418 16
419 4
    /**
420 4
     * @psalm-pure
421 4
     * @psalm-template ExpectedType of object
422 4
     * @psalm-param class-string<ExpectedType> $class
423
     * @psalm-assert ExpectedType $value
424
     *
425 12
     * @param mixed         $value
426
     * @param string|object $class
427
     * @param string        $message
428
     *
429
     * @throws InvalidArgumentException
430
     */
431
    public static function isInstanceOf($value, $class, $message = '')
432
    {
433
        if (!($value instanceof $class)) {
434
            static::reportInvalidArgument(\sprintf(
435
                $message ?: 'Expected an instance of %2$s. Got: %s',
436
                static::typeToString($value),
437 20
                $class
438
            ));
439 20
        }
440 20
    }
441 8
442
    /**
443
     * @psalm-pure
444
     * @psalm-template ExpectedType of object
445 12
     * @psalm-param class-string<ExpectedType> $class
446 12
     * @psalm-assert !ExpectedType $value
447 12
     *
448 12
     * @param mixed         $value
449
     * @param string|object $class
450
     * @param string        $message
451
     *
452
     * @throws InvalidArgumentException
453
     */
454
    public static function notInstanceOf($value, $class, $message = '')
455
    {
456
        if ($value instanceof $class) {
457
            static::reportInvalidArgument(\sprintf(
458
                $message ?: 'Expected an instance other than %2$s. Got: %s',
459
                static::typeToString($value),
460
                $class
461
            ));
462
        }
463
    }
464 20
465
    /**
466 20
     * @psalm-pure
467
     * @psalm-param array<class-string> $classes
468 16
     *
469 12
     * @param mixed                $value
470 12
     * @param array<object|string> $classes
471 12
     * @param string               $message
472
     *
473
     * @throws InvalidArgumentException
474
     */
475 4
    public static function isInstanceOfAny($value, array $classes, $message = '')
476
    {
477
        foreach ($classes as $class) {
478
            if ($value instanceof $class) {
479
                return;
480
            }
481
        }
482
483
        static::reportInvalidArgument(\sprintf(
484
            $message ?: 'Expected an instance of any of %2$s. Got: %s',
485
            static::typeToString($value),
486
            \implode(', ', \array_map(array('static', 'valueToString'), $classes))
487
        ));
488
    }
489
490 20
    /**
491
     * @psalm-pure
492 20
     * @psalm-template ExpectedType of object
493
     * @psalm-param class-string<ExpectedType> $class
494 16
     * @psalm-assert ExpectedType|class-string<ExpectedType> $value
495 4
     *
496 4
     * @param object|string $value
497 4
     * @param string        $class
498
     * @param string        $message
499
     *
500
     * @throws InvalidArgumentException
501 12
     */
502 View Code Duplication
    public static function isAOf($value, $class, $message = '')
503
    {
504
        static::string($class, 'Expected class as a string. Got: %s');
505
506
        if (!\is_a($value, $class, \is_string($value))) {
507
            static::reportInvalidArgument(sprintf(
508
                $message ?: 'Expected an instance of this class or to this class among his parents %2$s. Got: %s',
509
                static::typeToString($value),
510
                $class
511
            ));
512
        }
513 24
    }
514
515 24
    /**
516 24
     * @psalm-pure
517
     * @psalm-template UnexpectedType of object
518 20
     * @psalm-param class-string<UnexpectedType> $class
519 8
     * @psalm-assert !UnexpectedType $value
520
     * @psalm-assert !class-string<UnexpectedType> $value
521
     *
522
     * @param object|string $value
523 12
     * @param string        $class
524 12
     * @param string        $message
525 12
     *
526 12
     * @throws InvalidArgumentException
527
     */
528 View Code Duplication
    public static function isNotA($value, $class, $message = '')
529
    {
530
        static::string($class, 'Expected class as a string. Got: %s');
531
532
        if (\is_a($value, $class, \is_string($value))) {
533
            static::reportInvalidArgument(sprintf(
534
                $message ?: 'Expected an instance of this class or to this class among his parents other than %2$s. Got: %s',
535
                static::typeToString($value),
536
                $class
537
            ));
538
        }
539 23
    }
540
541 23
    /**
542 8
     * @psalm-pure
543 8
     * @psalm-param array<class-string> $classes
544 8
     *
545
     * @param object|string $value
546
     * @param string[]      $classes
547 15
     * @param string        $message
548
     *
549
     * @throws InvalidArgumentException
550
     */
551
    public static function isAnyOf($value, array $classes, $message = '')
552
    {
553
        foreach ($classes as $class) {
554
            static::string($class, 'Expected class as a string. Got: %s');
555
556
            if (\is_a($value, $class, \is_string($value))) {
557
                return;
558 55
            }
559
        }
560 55
561 23
        static::reportInvalidArgument(sprintf(
562 23
            $message ?: 'Expected an any of instance of this class or to this class among his parents other than %2$s. Got: %s',
563 23
            static::typeToString($value),
564
            \implode(', ', \array_map(array('static', 'valueToString'), $classes))
565
        ));
566 32
    }
567
568
    /**
569
     * @psalm-pure
570
     * @psalm-assert empty $value
571
     *
572
     * @param mixed  $value
573
     * @param string $message
574
     *
575
     * @throws InvalidArgumentException
576
     */
577 11
    public static function isEmpty($value, $message = '')
578
    {
579 11
        if (!empty($value)) {
580 8
            static::reportInvalidArgument(\sprintf(
581 8
                $message ?: 'Expected an empty value. Got: %s',
582 8
                static::valueToString($value)
583
            ));
584
        }
585 3
    }
586
587
    /**
588
     * @psalm-pure
589
     * @psalm-assert !empty $value
590
     *
591
     * @param mixed  $value
592
     * @param string $message
593
     *
594
     * @throws InvalidArgumentException
595
     */
596 11
    public static function notEmpty($value, $message = '')
597
    {
598 11
        if (empty($value)) {
599 3
            static::reportInvalidArgument(\sprintf(
600 3
                $message ?: 'Expected a non-empty value. Got: %s',
601
                static::valueToString($value)
602
            ));
603 8
        }
604
    }
605
606
    /**
607
     * @psalm-pure
608
     * @psalm-assert null $value
609
     *
610
     * @param mixed  $value
611
     * @param string $message
612
     *
613
     * @throws InvalidArgumentException
614 15
     */
615 View Code Duplication
    public static function null($value, $message = '')
616 15
    {
617 11
        if (null !== $value) {
618 11
            static::reportInvalidArgument(\sprintf(
619 11
                $message ?: 'Expected null. Got: %s',
620
                static::valueToString($value)
621
            ));
622 4
        }
623
    }
624
625
    /**
626
     * @psalm-pure
627
     * @psalm-assert !null $value
628
     *
629
     * @param mixed  $value
630
     * @param string $message
631
     *
632
     * @throws InvalidArgumentException
633 19
     */
634
    public static function notNull($value, $message = '')
635 19
    {
636 15
        if (null === $value) {
637 15
            static::reportInvalidArgument(
638 15
                $message ?: 'Expected a value other than null.'
639
            );
640
        }
641 4
    }
642
643
    /**
644
     * @psalm-pure
645
     * @psalm-assert true $value
646
     *
647
     * @param mixed  $value
648
     * @param string $message
649
     *
650
     * @throws InvalidArgumentException
651
     */
652 19 View Code Duplication
    public static function true($value, $message = '')
653
    {
654 19
        if (true !== $value) {
655 4
            static::reportInvalidArgument(\sprintf(
656 4
                $message ?: 'Expected a value to be true. Got: %s',
657
                static::valueToString($value)
658
            ));
659 15
        }
660
    }
661
662
    /**
663
     * @psalm-pure
664
     * @psalm-assert false $value
665
     *
666
     * @param mixed  $value
667 51
     * @param string $message
668
     *
669 51
     * @throws InvalidArgumentException
670 19
     */
671 19 View Code Duplication
    public static function false($value, $message = '')
672 19
    {
673
        if (false !== $value) {
674
            static::reportInvalidArgument(\sprintf(
675 32
                $message ?: 'Expected a value to be false. Got: %s',
676
                static::valueToString($value)
677
            ));
678
        }
679
    }
680
681
    /**
682
     * @psalm-pure
683 51
     * @psalm-assert !false $value
684
     *
685 51
     * @param mixed  $value
686 35
     * @param string $message
687 35
     *
688 35
     * @throws InvalidArgumentException
689
     */
690
    public static function notFalse($value, $message = '')
691 16
    {
692
        if (false === $value) {
693
            static::reportInvalidArgument(
694
                $message ?: 'Expected a value other than false.'
695
            );
696
        }
697
    }
698
699 51
    /**
700
     * @param mixed  $value
701 51
     * @param string $message
702 31
     *
703 31
     * @throws InvalidArgumentException
704 31
     */
705 View Code Duplication
    public static function ip($value, $message = '')
706
    {
707 20
        if (false === \filter_var($value, \FILTER_VALIDATE_IP)) {
708
            static::reportInvalidArgument(\sprintf(
709
                $message ?: 'Expected a value to be an IP. Got: %s',
710
                static::valueToString($value)
711
            ));
712
        }
713
    }
714
715 20
    /**
716
     * @param mixed  $value
717 20
     * @param string $message
718 12
     *
719 12
     * @throws InvalidArgumentException
720 12
     */
721 View Code Duplication
    public static function ipv4($value, $message = '')
722
    {
723 8
        if (false === \filter_var($value, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4)) {
724
            static::reportInvalidArgument(\sprintf(
725
                $message ?: 'Expected a value to be an IPv4. Got: %s',
726
                static::valueToString($value)
727
            ));
728
        }
729
    }
730
731
    /**
732
     * @param mixed  $value
733 12
     * @param string $message
734
     *
735 12
     * @throws InvalidArgumentException
736 12
     */
737 View Code Duplication
    public static function ipv6($value, $message = '')
738 12
    {
739 8
        if (false === \filter_var($value, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) {
740
            static::reportInvalidArgument(\sprintf(
741 8
                $message ?: 'Expected a value to be an IPv6. Got: %s',
742 8
                static::valueToString($value)
743 8
            ));
744 8
        }
745
    }
746
747 4
    /**
748
     * @param mixed  $value
749
     * @param string $message
750
     *
751
     * @throws InvalidArgumentException
752
     */
753 View Code Duplication
    public static function email($value, $message = '')
754
    {
755
        if (false === \filter_var($value, FILTER_VALIDATE_EMAIL)) {
756 33
            static::reportInvalidArgument(\sprintf(
757
                $message ?: 'Expected a value to be a valid e-mail address. Got: %s',
758 33
                static::valueToString($value)
759 17
            ));
760 17
        }
761 17
    }
762 17
763
    /**
764
     * Does non strict comparisons on the items, so ['3', 3] will not pass the assertion.
765 16
     *
766
     * @param array  $values
767
     * @param string $message
768
     *
769
     * @throws InvalidArgumentException
770
     */
771
    public static function uniqueValues(array $values, $message = '')
772
    {
773
        $allValues = \count($values);
774 28
        $uniqueValues = \count(\array_unique($values));
775
776 28
        if ($allValues !== $uniqueValues) {
777 16
            $difference = $allValues - $uniqueValues;
778 16
779 16
            static::reportInvalidArgument(\sprintf(
780
                $message ?: 'Expected an array of unique values, but %s of them %s duplicated',
781
                $difference,
782 12
                (1 === $difference ? 'is' : 'are')
783
            ));
784
        }
785
    }
786
787
    /**
788
     * @param mixed  $value
789
     * @param mixed  $expect
790
     * @param string $message
791
     *
792
     * @throws InvalidArgumentException
793 16
     */
794 View Code Duplication
    public static function eq($value, $expect, $message = '')
795 16
    {
796 12
        if ($expect != $value) {
797 12
            static::reportInvalidArgument(\sprintf(
798 12
                $message ?: 'Expected a value equal to %2$s. Got: %s',
799 12
                static::valueToString($value),
800
                static::valueToString($expect)
801
            ));
802 4
        }
803
    }
804
805
    /**
806
     * @param mixed  $value
807
     * @param mixed  $expect
808
     * @param string $message
809
     *
810
     * @throws InvalidArgumentException
811
     */
812
    public static function notEq($value, $expect, $message = '')
813 16
    {
814
        if ($expect == $value) {
815 16
            static::reportInvalidArgument(\sprintf(
816 4
                $message ?: 'Expected a different value than %s.',
817 4
                static::valueToString($expect)
818 4
            ));
819
        }
820
    }
821 12
822
    /**
823
     * @psalm-pure
824
     *
825
     * @param mixed  $value
826
     * @param mixed  $expect
827
     * @param string $message
828
     *
829
     * @throws InvalidArgumentException
830
     */
831 View Code Duplication
    public static function same($value, $expect, $message = '')
832 8
    {
833
        if ($expect !== $value) {
834 8
            static::reportInvalidArgument(\sprintf(
835 4
                $message ?: 'Expected a value identical to %2$s. Got: %s',
836 4
                static::valueToString($value),
837 4
                static::valueToString($expect)
838 4
            ));
839
        }
840
    }
841 4
842
    /**
843
     * @psalm-pure
844
     *
845
     * @param mixed  $value
846
     * @param mixed  $expect
847
     * @param string $message
848
     *
849
     * @throws InvalidArgumentException
850
     */
851
    public static function notSame($value, $expect, $message = '')
852 12
    {
853
        if ($expect === $value) {
854 12
            static::reportInvalidArgument(\sprintf(
855 4
                $message ?: 'Expected a value not identical to %s.',
856 4
                static::valueToString($expect)
857 4
            ));
858 4
        }
859
    }
860
861 8
    /**
862
     * @psalm-pure
863
     *
864
     * @param mixed  $value
865
     * @param mixed  $limit
866
     * @param string $message
867
     *
868
     * @throws InvalidArgumentException
869
     */
870 View Code Duplication
    public static function greaterThan($value, $limit, $message = '')
871
    {
872 9
        if ($value <= $limit) {
873
            static::reportInvalidArgument(\sprintf(
874 9
                $message ?: 'Expected a value greater than %2$s. Got: %s',
875 5
                static::valueToString($value),
876 5
                static::valueToString($limit)
877 5
            ));
878 5
        }
879
    }
880
881 4
    /**
882
     * @psalm-pure
883
     *
884
     * @param mixed  $value
885
     * @param mixed  $limit
886
     * @param string $message
887
     *
888
     * @throws InvalidArgumentException
889
     */
890 View Code Duplication
    public static function greaterThanEq($value, $limit, $message = '')
891
    {
892 12
        if ($value < $limit) {
893
            static::reportInvalidArgument(\sprintf(
894 12
                $message ?: 'Expected a value greater than or equal to %2$s. Got: %s',
895 4
                static::valueToString($value),
896 4
                static::valueToString($limit)
897 4
            ));
898 4
        }
899
    }
900
901 8
    /**
902
     * @psalm-pure
903
     *
904
     * @param mixed  $value
905
     * @param mixed  $limit
906
     * @param string $message
907
     *
908
     * @throws InvalidArgumentException
909
     */
910 View Code Duplication
    public static function lessThan($value, $limit, $message = '')
911
    {
912
        if ($value >= $limit) {
913
            static::reportInvalidArgument(\sprintf(
914
                $message ?: 'Expected a value less than %2$s. Got: %s',
915 16
                static::valueToString($value),
916
                static::valueToString($limit)
917 16
            ));
918 8
        }
919 8
    }
920 8
921 8
    /**
922 8
     * @psalm-pure
923
     *
924
     * @param mixed  $value
925 8
     * @param mixed  $limit
926
     * @param string $message
927
     *
928
     * @throws InvalidArgumentException
929
     */
930 View Code Duplication
    public static function lessThanEq($value, $limit, $message = '')
931
    {
932
        if ($value > $limit) {
933
            static::reportInvalidArgument(\sprintf(
934
                $message ?: 'Expected a value less than or equal to %2$s. Got: %s',
935
                static::valueToString($value),
936
                static::valueToString($limit)
937
            ));
938 8
        }
939
    }
940 8
941 4
    /**
942
     * Inclusive range, so Assert::(3, 3, 5) passes.
943
     *
944
     * @psalm-pure
945
     *
946
     * @param mixed  $value
947
     * @param mixed  $min
948
     * @param mixed  $max
949
     * @param string $message
950
     *
951
     * @throws InvalidArgumentException
952
     */
953 View Code Duplication
    public static function range($value, $min, $max, $message = '')
954 16
    {
955
        if ($value < $min || $value > $max) {
956 16
            static::reportInvalidArgument(\sprintf(
957 8
                $message ?: 'Expected a value between %2$s and %3$s. Got: %s',
958 8
                static::valueToString($value),
959 8
                static::valueToString($min),
960 8
                static::valueToString($max)
961
            ));
962
        }
963 8
    }
964
965
    /**
966
     * A more human-readable alias of Assert::inArray().
967
     *
968
     * @psalm-pure
969
     *
970
     * @param mixed  $value
971
     * @param array  $values
972
     * @param string $message
973
     *
974 80
     * @throws InvalidArgumentException
975
     */
976 80
    public static function oneOf($value, array $values, $message = '')
977 32
    {
978 32
        static::inArray($value, $values, $message);
979 32
    }
980 32
981
    /**
982
     * Does strict comparison, so Assert::inArray(3, ['3']) does not pass the assertion.
983 48
     *
984
     * @psalm-pure
985
     *
986
     * @param mixed  $value
987
     * @param array  $values
988
     * @param string $message
989
     *
990
     * @throws InvalidArgumentException
991
     */
992
    public static function inArray($value, array $values, $message = '')
993
    {
994 80
        if (!\in_array($value, $values, true)) {
995
            static::reportInvalidArgument(\sprintf(
996 80
                $message ?: 'Expected one of: %2$s. Got: %s',
997 48
                static::valueToString($value),
998 48
                \implode(', ', \array_map(array('static', 'valueToString'), $values))
999 48
            ));
1000 48
        }
1001
    }
1002
1003 32
    /**
1004
     * @psalm-pure
1005
     *
1006
     * @param string $value
1007
     * @param string $subString
1008
     * @param string $message
1009
     *
1010
     * @throws InvalidArgumentException
1011
     */
1012 View Code Duplication
    public static function contains($value, $subString, $message = '')
1013 40
    {
1014
        if (false === \strpos($value, $subString)) {
1015 40
            static::reportInvalidArgument(\sprintf(
1016 24
                $message ?: 'Expected a value to contain %2$s. Got: %s',
1017 24
                static::valueToString($value),
1018 24
                static::valueToString($subString)
1019
            ));
1020
        }
1021 16
    }
1022
1023
    /**
1024
     * @psalm-pure
1025
     *
1026
     * @param string $value
1027
     * @param string $subString
1028
     * @param string $message
1029
     *
1030
     * @throws InvalidArgumentException
1031
     */
1032 48 View Code Duplication
    public static function notContains($value, $subString, $message = '')
1033
    {
1034 48
        if (false !== \strpos($value, $subString)) {
1035 32
            static::reportInvalidArgument(\sprintf(
1036 32
                $message ?: '%2$s was not expected to be contained in a value. Got: %s',
1037 32
                static::valueToString($value),
1038 32
                static::valueToString($subString)
1039
            ));
1040
        }
1041 16
    }
1042
1043
    /**
1044
     * @psalm-pure
1045
     *
1046
     * @param string $value
1047
     * @param string $message
1048
     *
1049
     * @throws InvalidArgumentException
1050
     */
1051 View Code Duplication
    public static function notWhitespaceOnly($value, $message = '')
1052 48
    {
1053
        if (\preg_match('/^\s*$/', $value)) {
1054 48
            static::reportInvalidArgument(\sprintf(
1055 16
                $message ?: 'Expected a non-whitespace string. Got: %s',
1056 16
                static::valueToString($value)
1057 16
            ));
1058 16
        }
1059
    }
1060
1061 32
    /**
1062
     * @psalm-pure
1063
     *
1064
     * @param string $value
1065
     * @param string $prefix
1066
     * @param string $message
1067
     *
1068
     * @throws InvalidArgumentException
1069
     */
1070 View Code Duplication
    public static function startsWith($value, $prefix, $message = '')
1071 35
    {
1072
        if (0 !== \strpos($value, $prefix)) {
1073 35
            static::reportInvalidArgument(\sprintf(
1074
                $message ?: 'Expected a value to start with %2$s. Got: %s',
1075 24
                static::valueToString($value),
1076
                static::valueToString($prefix)
1077 24
            ));
1078 20
        }
1079 20
    }
1080 20
1081 20
    /**
1082
     * @psalm-pure
1083
     *
1084 24
     * @param string $value
1085 12
     * @param string $prefix
1086 12
     * @param string $message
1087 12
     *
1088
     * @throws InvalidArgumentException
1089
     */
1090 12 View Code Duplication
    public static function notStartsWith($value, $prefix, $message = '')
1091
    {
1092
        if (0 === \strpos($value, $prefix)) {
1093
            static::reportInvalidArgument(\sprintf(
1094
                $message ?: 'Expected a value not to start with %2$s. Got: %s',
1095
                static::valueToString($value),
1096
                static::valueToString($prefix)
1097
            ));
1098
        }
1099
    }
1100
1101 48
    /**
1102
     * @psalm-pure
1103 48
     *
1104 32
     * @param mixed  $value
1105 32
     * @param string $message
1106 32
     *
1107 32
     * @throws InvalidArgumentException
1108
     */
1109
    public static function startsWithLetter($value, $message = '')
1110 16
    {
1111
        static::string($value);
1112
1113
        $valid = isset($value[0]);
1114
1115
        if ($valid) {
1116
            $locale = \setlocale(LC_CTYPE, 0);
1117
            \setlocale(LC_CTYPE, 'C');
1118
            $valid = \ctype_alpha($value[0]);
1119
            \setlocale(LC_CTYPE, $locale);
1120
        }
1121 48
1122
        if (!$valid) {
1123 48
            static::reportInvalidArgument(\sprintf(
1124 16
                $message ?: 'Expected a value to start with a letter. Got: %s',
1125 16
                static::valueToString($value)
1126 16
            ));
1127 16
        }
1128
    }
1129
1130 32
    /**
1131
     * @psalm-pure
1132
     *
1133
     * @param string $value
1134
     * @param string $suffix
1135
     * @param string $message
1136
     *
1137
     * @throws InvalidArgumentException
1138
     */
1139 View Code Duplication
    public static function endsWith($value, $suffix, $message = '')
1140
    {
1141 12
        if ($suffix !== \substr($value, -\strlen($suffix))) {
1142
            static::reportInvalidArgument(\sprintf(
1143 12
                $message ?: 'Expected a value to end with %2$s. Got: %s',
1144 8
                static::valueToString($value),
1145 8
                static::valueToString($suffix)
1146 8
            ));
1147
        }
1148
    }
1149 4
1150
    /**
1151
     * @psalm-pure
1152
     *
1153
     * @param string $value
1154
     * @param string $suffix
1155
     * @param string $message
1156
     *
1157
     * @throws InvalidArgumentException
1158
     */
1159 View Code Duplication
    public static function notEndsWith($value, $suffix, $message = '')
1160 12
    {
1161
        if ($suffix === \substr($value, -\strlen($suffix))) {
1162 12
            static::reportInvalidArgument(\sprintf(
1163 4
                $message ?: 'Expected a value not to end with %2$s. Got: %s',
1164 4
                static::valueToString($value),
1165 4
                static::valueToString($suffix)
1166 4
            ));
1167 4
        }
1168
    }
1169
1170 8
    /**
1171
     * @psalm-pure
1172
     *
1173
     * @param string $value
1174
     * @param string $pattern
1175
     * @param string $message
1176
     *
1177
     * @throws InvalidArgumentException
1178
     */
1179
    public static function regex($value, $pattern, $message = '')
1180 28
    {
1181
        if (!\preg_match($pattern, $value)) {
1182 28
            static::reportInvalidArgument(\sprintf(
1183
                $message ?: 'The value %s does not match the expected pattern.',
1184 28
                static::valueToString($value)
1185 16
            ));
1186 16
        }
1187 16
    }
1188
1189
    /**
1190 12
     * @psalm-pure
1191
     *
1192
     * @param string $value
1193
     * @param string $pattern
1194
     * @param string $message
1195
     *
1196
     * @throws InvalidArgumentException
1197
     */
1198
    public static function notRegex($value, $pattern, $message = '')
1199
    {
1200 20
        if (\preg_match($pattern, $value, $matches, PREG_OFFSET_CAPTURE)) {
1201
            static::reportInvalidArgument(\sprintf(
1202 20
                $message ?: 'The value %s matches the pattern %s (at offset %d).',
1203
                static::valueToString($value),
1204 12
                static::valueToString($pattern),
1205 12
                $matches[0][1]
1206 12
            ));
1207 12
        }
1208
    }
1209 12
1210 8
    /**
1211 8
     * @psalm-pure
1212 8
     *
1213
     * @param mixed  $value
1214
     * @param string $message
1215 4
     *
1216
     * @throws InvalidArgumentException
1217
     */
1218 View Code Duplication
    public static function unicodeLetters($value, $message = '')
1219
    {
1220
        static::string($value);
1221
1222
        if (!\preg_match('/^\p{L}+$/u', $value)) {
1223
            static::reportInvalidArgument(\sprintf(
1224
                $message ?: 'Expected a value to contain only Unicode letters. Got: %s',
1225 12
                static::valueToString($value)
1226
            ));
1227 12
        }
1228 12
    }
1229 12
1230 12
    /**
1231
     * @psalm-pure
1232 12
     *
1233 8
     * @param mixed  $value
1234 8
     * @param string $message
1235 8
     *
1236
     * @throws InvalidArgumentException
1237
     */
1238 4 View Code Duplication
    public static function alpha($value, $message = '')
1239
    {
1240
        static::string($value);
1241
1242
        $locale = \setlocale(LC_CTYPE, 0);
1243
        \setlocale(LC_CTYPE, 'C');
1244
        $valid = !\ctype_alpha($value);
1245
        \setlocale(LC_CTYPE, $locale);
1246
1247
        if ($valid) {
1248 12
            static::reportInvalidArgument(\sprintf(
1249
                $message ?: 'Expected a value to contain only letters. Got: %s',
1250 12
                static::valueToString($value)
1251 12
            ));
1252 12
        }
1253 12
    }
1254
1255 12
    /**
1256 8
     * @psalm-pure
1257 8
     *
1258 8
     * @param string $value
1259
     * @param string $message
1260
     *
1261 4
     * @throws InvalidArgumentException
1262
     */
1263 View Code Duplication
    public static function digits($value, $message = '')
1264
    {
1265
        $locale = \setlocale(LC_CTYPE, 0);
1266
        \setlocale(LC_CTYPE, 'C');
1267
        $valid = !\ctype_digit($value);
1268
        \setlocale(LC_CTYPE, $locale);
1269
1270
        if ($valid) {
1271
            static::reportInvalidArgument(\sprintf(
1272 16
                $message ?: 'Expected a value to contain digits only. Got: %s',
1273
                static::valueToString($value)
1274 16
            ));
1275 16
        }
1276 16
    }
1277 16
1278
    /**
1279 16
     * @psalm-pure
1280 12
     *
1281 12
     * @param string $value
1282 12
     * @param string $message
1283
     *
1284
     * @throws InvalidArgumentException
1285 4
     */
1286 View Code Duplication
    public static function alnum($value, $message = '')
1287
    {
1288
        $locale = \setlocale(LC_CTYPE, 0);
1289
        \setlocale(LC_CTYPE, 'C');
1290
        $valid = !\ctype_alnum($value);
1291
        \setlocale(LC_CTYPE, $locale);
1292
1293
        if ($valid) {
1294
            static::reportInvalidArgument(\sprintf(
1295
                $message ?: 'Expected a value to contain letters and digits only. Got: %s',
1296 16
                static::valueToString($value)
1297
            ));
1298 16
        }
1299 16
    }
1300 16
1301 16
    /**
1302
     * @psalm-pure
1303 16
     * @psalm-assert lowercase-string $value
1304 12
     *
1305 12
     * @param string $value
1306 12
     * @param string $message
1307
     *
1308
     * @throws InvalidArgumentException
1309 4
     */
1310 View Code Duplication
    public static function lower($value, $message = '')
1311
    {
1312
        $locale = \setlocale(LC_CTYPE, 0);
1313
        \setlocale(LC_CTYPE, 'C');
1314
        $valid = !\ctype_lower($value);
1315
        \setlocale(LC_CTYPE, $locale);
1316
1317
        if ($valid) {
1318
            static::reportInvalidArgument(\sprintf(
1319
                $message ?: 'Expected a value to contain lowercase characters only. Got: %s',
1320 36
                static::valueToString($value)
1321
            ));
1322 36
        }
1323 24
    }
1324 24
1325 24
    /**
1326 24
     * @psalm-pure
1327
     * @psalm-assert !lowercase-string $value
1328
     *
1329 12
     * @param string $value
1330
     * @param string $message
1331
     *
1332
     * @throws InvalidArgumentException
1333
     */
1334 View Code Duplication
    public static function upper($value, $message = '')
1335
    {
1336
        $locale = \setlocale(LC_CTYPE, 0);
1337
        \setlocale(LC_CTYPE, 'C');
1338
        $valid = !\ctype_upper($value);
1339
        \setlocale(LC_CTYPE, $locale);
1340
1341
        if ($valid) {
1342 36
            static::reportInvalidArgument(\sprintf(
1343
                $message ?: 'Expected a value to contain uppercase characters only. Got: %s',
1344 36
                static::valueToString($value)
1345 12
            ));
1346 12
        }
1347 12
    }
1348 12
1349
    /**
1350
     * @psalm-pure
1351 24
     *
1352
     * @param string $value
1353
     * @param int    $length
1354
     * @param string $message
1355
     *
1356
     * @throws InvalidArgumentException
1357
     */
1358
    public static function length($value, $length, $message = '')
1359
    {
1360
        if ($length !== static::strlen($value)) {
1361
            static::reportInvalidArgument(\sprintf(
1362
                $message ?: 'Expected a value to contain %2$s characters. Got: %s',
1363
                static::valueToString($value),
1364 36
                $length
1365
            ));
1366 36
        }
1367 12
    }
1368 12
1369 12
    /**
1370 12
     * Inclusive min.
1371
     *
1372
     * @psalm-pure
1373 24
     *
1374
     * @param string    $value
1375
     * @param int|float $min
1376
     * @param string    $message
1377
     *
1378
     * @throws InvalidArgumentException
1379
     */
1380 View Code Duplication
    public static function minLength($value, $min, $message = '')
1381
    {
1382
        if (static::strlen($value) < $min) {
1383
            static::reportInvalidArgument(\sprintf(
1384
                $message ?: 'Expected a value to contain at least %2$s characters. Got: %s',
1385
                static::valueToString($value),
1386
                $min
1387 60
            ));
1388
        }
1389 60
    }
1390
1391 60
    /**
1392 24
     * Inclusive max.
1393 24
     *
1394 24
     * @psalm-pure
1395 24
     *
1396 24
     * @param string    $value
1397
     * @param int|float $max
1398
     * @param string    $message
1399 36
     *
1400
     * @throws InvalidArgumentException
1401
     */
1402 View Code Duplication
    public static function maxLength($value, $max, $message = '')
1403
    {
1404
        if (static::strlen($value) > $max) {
1405
            static::reportInvalidArgument(\sprintf(
1406
                $message ?: 'Expected a value to contain at most %2$s characters. Got: %s',
1407
                static::valueToString($value),
1408
                $max
1409 36
            ));
1410
        }
1411 36
    }
1412
1413 36
    /**
1414 12
     * Inclusive , so Assert::lengthBetween('asd', 3, 5); passes the assertion.
1415 12
     *
1416 12
     * @psalm-pure
1417
     *
1418
     * @param string    $value
1419 24
     * @param int|float $min
1420
     * @param int|float $max
1421
     * @param string    $message
1422
     *
1423
     * @throws InvalidArgumentException
1424
     */
1425 View Code Duplication
    public static function lengthBetween($value, $min, $max, $message = '')
1426
    {
1427 12
        $length = static::strlen($value);
1428
1429 12
        if ($length < $min || $length > $max) {
1430
            static::reportInvalidArgument(\sprintf(
1431 8
                $message ?: 'Expected a value to contain between %2$s and %3$s characters. Got: %s',
1432 4
                static::valueToString($value),
1433 4
                $min,
1434 4
                $max
1435
            ));
1436
        }
1437 4
    }
1438
1439
    /**
1440
     * Will also pass if $value is a directory, use Assert::file() instead if you need to be sure it is a file.
1441
     *
1442
     * @param mixed  $value
1443
     * @param string $message
1444
     *
1445 12
     * @throws InvalidArgumentException
1446
     */
1447 12 View Code Duplication
    public static function fileExists($value, $message = '')
1448
    {
1449 8
        static::string($value);
1450 4
1451 4
        if (!\file_exists($value)) {
1452 4
            static::reportInvalidArgument(\sprintf(
1453
                $message ?: 'The file %s does not exist.',
1454
                static::valueToString($value)
1455 4
            ));
1456
        }
1457
    }
1458
1459
    /**
1460
     * @param mixed  $value
1461
     * @param string $message
1462
     *
1463
     * @throws InvalidArgumentException
1464
     */
1465 View Code Duplication
    public static function file($value, $message = '')
1466
    {
1467
        static::fileExists($value, $message);
1468
1469
        if (!\is_file($value)) {
1470
            static::reportInvalidArgument(\sprintf(
1471
                $message ?: 'The path %s is not a file.',
1472
                static::valueToString($value)
1473
            ));
1474
        }
1475
    }
1476
1477
    /**
1478
     * @param mixed  $value
1479
     * @param string $message
1480
     *
1481
     * @throws InvalidArgumentException
1482
     */
1483 View Code Duplication
    public static function directory($value, $message = '')
1484
    {
1485
        static::fileExists($value, $message);
1486
1487
        if (!\is_dir($value)) {
1488
            static::reportInvalidArgument(\sprintf(
1489
                $message ?: 'The path %s is no directory.',
1490
                static::valueToString($value)
1491
            ));
1492
        }
1493
    }
1494
1495
    /**
1496
     * @param string $value
1497 8
     * @param string $message
1498
     *
1499 8
     * @throws InvalidArgumentException
1500 4
     */
1501 4 View Code Duplication
    public static function readable($value, $message = '')
1502 4
    {
1503
        if (!\is_readable($value)) {
1504
            static::reportInvalidArgument(\sprintf(
1505 4
                $message ?: 'The path %s is not readable.',
1506
                static::valueToString($value)
1507
            ));
1508
        }
1509
    }
1510
1511
    /**
1512
     * @param string $value
1513
     * @param string $message
1514
     *
1515
     * @throws InvalidArgumentException
1516
     */
1517 View Code Duplication
    public static function writable($value, $message = '')
1518
    {
1519 8
        if (!\is_writable($value)) {
1520
            static::reportInvalidArgument(\sprintf(
1521 8
                $message ?: 'The path %s is not writable.',
1522 4
                static::valueToString($value)
1523 4
            ));
1524 4
        }
1525 4
    }
1526
1527
    /**
1528 4
     * @psalm-assert class-string $value
1529
     *
1530
     * @param mixed  $value
1531
     * @param string $message
1532
     *
1533
     * @throws InvalidArgumentException
1534
     */
1535 View Code Duplication
    public static function classExists($value, $message = '')
1536
    {
1537
        if (!\class_exists($value)) {
1538 8
            static::reportInvalidArgument(\sprintf(
1539
                $message ?: 'Expected an existing class name. Got: %s',
1540 8
                static::valueToString($value)
1541 4
            ));
1542 4
        }
1543 4
    }
1544
1545
    /**
1546 4
     * @psalm-pure
1547
     * @psalm-template ExpectedType of object
1548
     * @psalm-param class-string<ExpectedType> $class
1549
     * @psalm-assert class-string<ExpectedType>|ExpectedType $value
1550
     *
1551
     * @param mixed         $value
1552
     * @param string|object $class
1553
     * @param string        $message
1554
     *
1555
     * @throws InvalidArgumentException
1556
     */
1557
    public static function subclassOf($value, $class, $message = '')
1558
    {
1559
        if (!\is_subclass_of($value, $class)) {
1560 8
            static::reportInvalidArgument(\sprintf(
1561
                $message ?: 'Expected a sub-class of %2$s. Got: %s',
1562 8
                static::valueToString($value),
1563 4
                static::valueToString($class)
1564 4
            ));
1565 4
        }
1566 4
    }
1567
1568
    /**
1569 4
     * @psalm-assert class-string $value
1570
     *
1571
     * @param mixed  $value
1572
     * @param string $message
1573
     *
1574
     * @throws InvalidArgumentException
1575
     */
1576 View Code Duplication
    public static function interfaceExists($value, $message = '')
1577
    {
1578
        if (!\interface_exists($value)) {
1579
            static::reportInvalidArgument(\sprintf(
1580
                $message ?: 'Expected an existing interface name. got %s',
1581 12
                static::valueToString($value)
1582
            ));
1583 12
        }
1584 4
    }
1585 4
1586 4
    /**
1587
     * @psalm-pure
1588
     * @psalm-template ExpectedType of object
1589 8
     * @psalm-param class-string<ExpectedType> $interface
1590
     * @psalm-assert class-string<ExpectedType> $value
1591
     *
1592
     * @param mixed  $value
1593
     * @param mixed  $interface
1594
     * @param string $message
1595
     *
1596
     * @throws InvalidArgumentException
1597
     */
1598 View Code Duplication
    public static function implementsInterface($value, $interface, $message = '')
1599
    {
1600
        if (!\in_array($interface, \class_implements($value))) {
1601 12
            static::reportInvalidArgument(\sprintf(
1602
                $message ?: 'Expected an implementation of %2$s. Got: %s',
1603 12
                static::valueToString($value),
1604 8
                static::valueToString($interface)
1605 8
            ));
1606 8
        }
1607
    }
1608
1609 4
    /**
1610
     * @psalm-pure
1611
     * @psalm-param class-string|object $classOrObject
1612
     *
1613
     * @param string|object $classOrObject
1614
     * @param mixed         $property
1615
     * @param string        $message
1616
     *
1617
     * @throws InvalidArgumentException
1618
     */
1619 View Code Duplication
    public static function propertyExists($classOrObject, $property, $message = '')
1620
    {
1621 27
        if (!\property_exists($classOrObject, $property)) {
1622
            static::reportInvalidArgument(\sprintf(
1623 27
                $message ?: 'Expected the property %s to exist.',
1624 19
                static::valueToString($property)
1625 19
            ));
1626 19
        }
1627
    }
1628
1629 8
    /**
1630
     * @psalm-pure
1631
     * @psalm-param class-string|object $classOrObject
1632
     *
1633
     * @param string|object $classOrObject
1634
     * @param mixed         $property
1635
     * @param string        $message
1636
     *
1637
     * @throws InvalidArgumentException
1638
     */
1639 View Code Duplication
    public static function propertyNotExists($classOrObject, $property, $message = '')
1640
    {
1641 27
        if (\property_exists($classOrObject, $property)) {
1642
            static::reportInvalidArgument(\sprintf(
1643 27
                $message ?: 'Expected the property %s to not exist.',
1644 8
                static::valueToString($property)
1645 8
            ));
1646 8
        }
1647
    }
1648
1649 19
    /**
1650
     * @psalm-pure
1651
     * @psalm-param class-string|object $classOrObject
1652
     *
1653
     * @param string|object $classOrObject
1654
     * @param mixed         $method
1655
     * @param string        $message
1656
     *
1657
     * @throws InvalidArgumentException
1658
     */
1659 View Code Duplication
    public static function methodExists($classOrObject, $method, $message = '')
1660 12
    {
1661
        if (!(\is_string($classOrObject) || \is_object($classOrObject)) || !\method_exists($classOrObject, $method)) {
1662 12
            static::reportInvalidArgument(\sprintf(
1663 4
                $message ?: 'Expected the method %s to exist.',
1664 4
                static::valueToString($method)
1665 4
            ));
1666
        }
1667
    }
1668 8
1669
    /**
1670
     * @psalm-pure
1671
     * @psalm-param class-string|object $classOrObject
1672
     *
1673
     * @param string|object $classOrObject
1674
     * @param mixed         $method
1675
     * @param string        $message
1676
     *
1677
     * @throws InvalidArgumentException
1678
     */
1679 12 View Code Duplication
    public static function methodNotExists($classOrObject, $method, $message = '')
1680
    {
1681 12
        if ((\is_string($classOrObject) || \is_object($classOrObject)) && \method_exists($classOrObject, $method)) {
1682 8
            static::reportInvalidArgument(\sprintf(
1683 8
                $message ?: 'Expected the method %s to not exist.',
1684 8
                static::valueToString($method)
1685
            ));
1686
        }
1687 4
    }
1688
1689
    /**
1690
     * @psalm-pure
1691
     *
1692
     * @param array      $array
1693
     * @param string|int $key
1694
     * @param string     $message
1695
     *
1696
     * @throws InvalidArgumentException
1697
     */
1698 View Code Duplication
    public static function keyExists($array, $key, $message = '')
1699
    {
1700 28
        if (!(isset($array[$key]) || \array_key_exists($key, $array))) {
1701
            static::reportInvalidArgument(\sprintf(
1702 28
                $message ?: 'Expected the key %s to exist.',
1703 20
                static::valueToString($key)
1704 20
            ));
1705 20
        }
1706
    }
1707
1708 8
    /**
1709
     * @psalm-pure
1710
     *
1711
     * @param array      $array
1712
     * @param string|int $key
1713
     * @param string     $message
1714
     *
1715
     * @throws InvalidArgumentException
1716
     */
1717 View Code Duplication
    public static function keyNotExists($array, $key, $message = '')
1718
    {
1719 8
        if (isset($array[$key]) || \array_key_exists($key, $array)) {
1720
            static::reportInvalidArgument(\sprintf(
1721 8
                $message ?: 'Expected the key %s to not exist.',
1722 8
                static::valueToString($key)
1723
            ));
1724 8
        }
1725 8
    }
1726 8
1727 8
    /**
1728
     * Checks if a value is a valid array key (int or string).
1729
     *
1730 4
     * @psalm-pure
1731
     * @psalm-assert array-key $value
1732
     *
1733
     * @param mixed  $value
1734
     * @param string $message
1735
     *
1736
     * @throws InvalidArgumentException
1737
     */
1738
    public static function validArrayKey($value, $message = '')
1739
    {
1740
        if (!(\is_int($value) || \is_string($value))) {
1741 12
            static::reportInvalidArgument(\sprintf(
1742
                $message ?: 'Expected string or integer. Got: %s',
1743 12
                static::typeToString($value)
1744 4
            ));
1745 4
        }
1746 4
    }
1747 4
1748
    /**
1749
     * Does not check if $array is countable, this can generate a warning on php versions after 7.2.
1750 8
     *
1751
     * @param Countable|array $array
1752
     * @param int             $number
1753
     * @param string          $message
1754
     *
1755
     * @throws InvalidArgumentException
1756
     */
1757
    public static function count($array, $number, $message = '')
1758
    {
1759
        static::eq(
1760
            \count($array),
1761 12
            $number,
1762
            \sprintf(
1763 12
                $message ?: 'Expected an array to contain %d elements. Got: %d.',
1764 4
                $number,
1765 4
                \count($array)
1766 4
            )
1767 4
        );
1768
    }
1769
1770 8
    /**
1771
     * Does not check if $array is countable, this can generate a warning on php versions after 7.2.
1772
     *
1773
     * @param Countable|array $array
1774
     * @param int|float       $min
1775
     * @param string          $message
1776
     *
1777
     * @throws InvalidArgumentException
1778
     */
1779 View Code Duplication
    public static function minCount($array, $min, $message = '')
1780
    {
1781
        if (\count($array) < $min) {
1782 20
            static::reportInvalidArgument(\sprintf(
1783
                $message ?: 'Expected an array to contain at least %2$d elements. Got: %d',
1784 20
                \count($array),
1785
                $min
1786 20
            ));
1787 8
        }
1788 8
    }
1789 8
1790 8
    /**
1791 8
     * Does not check if $array is countable, this can generate a warning on php versions after 7.2.
1792
     *
1793
     * @param Countable|array $array
1794 12
     * @param int|float       $max
1795
     * @param string          $message
1796
     *
1797
     * @throws InvalidArgumentException
1798
     */
1799 View Code Duplication
    public static function maxCount($array, $max, $message = '')
1800
    {
1801
        if (\count($array) > $max) {
1802
            static::reportInvalidArgument(\sprintf(
1803
                $message ?: 'Expected an array to contain at most %2$d elements. Got: %d',
1804
                \count($array),
1805 80
                $max
1806
            ));
1807 80
        }
1808 32
    }
1809 32
1810
    /**
1811
     * Does not check if $array is countable, this can generate a warning on php versions after 7.2.
1812 48
     *
1813
     * @param Countable|array $array
1814
     * @param int|float       $min
1815
     * @param int|float       $max
1816
     * @param string          $message
1817
     *
1818
     * @throws InvalidArgumentException
1819
     */
1820
    public static function countBetween($array, $min, $max, $message = '')
1821
    {
1822
        $count = \count($array);
1823 40
1824
        if ($count < $min || $count > $max) {
1825 40
            static::reportInvalidArgument(\sprintf(
1826 24
                $message ?: 'Expected an array to contain between %2$d and %3$d elements. Got: %d',
1827 20
                $count,
1828
                $min,
1829
                $max
1830
            ));
1831
        }
1832
    }
1833
1834
    /**
1835
     * @psalm-pure
1836
     * @psalm-assert list $array
1837
     *
1838
     * @param mixed  $array
1839
     * @param string $message
1840 32
     *
1841
     * @throws InvalidArgumentException
1842
     */
1843 32 View Code Duplication
    public static function isList($array, $message = '')
1844 32
    {
1845
        if (!\is_array($array) || $array !== \array_values($array)) {
1846 16
            static::reportInvalidArgument(
1847 16
                $message ?: 'Expected list - non-associative array.'
1848
            );
1849
        }
1850 16
    }
1851
1852
    /**
1853
     * @psalm-pure
1854
     * @psalm-assert non-empty-list $array
1855
     *
1856
     * @param mixed  $array
1857
     * @param string $message
1858
     *
1859
     * @throws InvalidArgumentException
1860
     */
1861
    public static function isNonEmptyList($array, $message = '')
1862
    {
1863
        static::isList($array, $message);
1864 16
        static::notEmpty($array, $message);
1865
    }
1866 16
1867 8
    /**
1868 4
     * @psalm-pure
1869
     * @psalm-template T
1870
     * @psalm-param mixed|array<T> $array
1871
     * @psalm-assert array<string, T> $array
1872
     *
1873
     * @param mixed  $array
1874
     * @param string $message
1875
     *
1876
     * @throws InvalidArgumentException
1877
     */
1878 56
    public static function isMap($array, $message = '')
1879
    {
1880 56
        if (
1881
            !\is_array($array) ||
1882
            \array_keys($array) !== \array_filter(\array_keys($array), '\is_string')
1883
        ) {
1884 56
            static::reportInvalidArgument(
1885 4
                $message ?: 'Expected map - associative array with string keys.'
1886
            );
1887
        }
1888 52
    }
1889 20
1890 20
    /**
1891 20
     * @psalm-pure
1892
     * @psalm-template T
1893
     * @psalm-param mixed|array<T> $array
1894 32
     * @psalm-assert array<string, T> $array
1895
     * @psalm-assert !empty $array
1896
     *
1897
     * @param mixed  $array
1898
     * @param string $message
1899
     *
1900
     * @throws InvalidArgumentException
1901
     */
1902
    public static function isNonEmptyMap($array, $message = '')
1903
    {
1904
        static::isMap($array, $message);
1905 24
        static::notEmpty($array, $message);
1906
    }
1907 24
1908
    /**
1909 24
     * @psalm-pure
1910
     *
1911
     * @param string $value
1912 24
     * @param string $message
1913 24
     *
1914 20
     * @throws InvalidArgumentException
1915 20
     */
1916 20
    public static function uuid($value, $message = '')
1917
    {
1918 4
        $value = \str_replace(array('urn:', 'uuid:', '{', '}'), '', $value);
1919 4
1920 4
        // The nil UUID is special form of UUID that is specified to have all
1921 4
        // 128 bits set to zero.
1922
        if ('00000000-0000-0000-0000-000000000000' === $value) {
1923
            return;
1924
        }
1925 8
1926
        if (!\preg_match('/^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$/', $value)) {
1927 8
            static::reportInvalidArgument(\sprintf(
1928 8
                $message ?: 'Value %s is not a valid UUID.',
1929
                static::valueToString($value)
1930
            ));
1931
        }
1932
    }
1933
1934
    /**
1935 1642
     * @psalm-param class-string<Throwable> $class
1936
     *
1937 1642
     * @param Closure $expression
1938 607
     * @param string  $class
1939 501
     * @param string  $message
1940 501
     *
1941
     * @throws InvalidArgumentException
1942
     */
1943 354
    public static function throws(Closure $expression, $class = 'Exception', $message = '')
1944
    {
1945
        static::string($class);
1946 1035
1947 1034
        $actual = 'none';
1948
1949 1034
        try {
1950 1034
            $expression();
1951
        } catch (Exception $e) {
1952 1034
            $actual = \get_class($e);
1953 1034
            if ($e instanceof $class) {
1954
                return;
1955 1034
            }
1956
        } catch (Throwable $e) {
1957
            $actual = \get_class($e);
1958 504
            if ($e instanceof $class) {
1959
                return;
1960
            }
1961 1
        }
1962
1963
        static::reportInvalidArgument($message ?: \sprintf(
1964
            'Expected to throw "%s", got "%s"',
1965
            $class,
1966
            $actual
1967
        ));
1968
    }
1969 751
1970
    /**
1971 751
     * @throws BadMethodCallException
1972 20
     */
1973
    public static function __callStatic($name, $arguments)
1974
    {
1975 733
        if ('nullOr' === \substr($name, 0, 6)) {
1976 15
            if (null !== $arguments[0]) {
1977
                $method = \lcfirst(\substr($name, 6));
1978
                \call_user_func_array(array('static', $method), $arguments);
1979 723
            }
1980 25
1981
            return;
1982
        }
1983 698
1984 21
        if ('all' === \substr($name, 0, 3)) {
1985
            static::isIterable($arguments[0]);
1986
1987 677
            $method = \lcfirst(\substr($name, 3));
1988 3
            $args = $arguments;
1989 1
1990
            foreach ($arguments[0] as $entry) {
1991
                $args[0] = $entry;
1992 2
1993 1
                \call_user_func_array(array('static', $method), $args);
1994
            }
1995
1996 1
            return;
1997
        }
1998
1999 676
        throw new BadMethodCallException('No such method: '.$name);
2000 1
    }
2001
2002
    /**
2003 676
     * @param mixed $value
2004 574
     *
2005
     * @return string
2006
     */
2007 114
    protected static function valueToString($value)
2008
    {
2009
        if (null === $value) {
2010
            return 'null';
2011
        }
2012
2013
        if (true === $value) {
2014
            return 'true';
2015 251
        }
2016
2017 251
        if (false === $value) {
2018
            return 'false';
2019
        }
2020 168
2021
        if (\is_array($value)) {
2022 168
            return 'array';
2023
        }
2024
2025
        if (\is_object($value)) {
2026 168
            if (\method_exists($value, '__toString')) {
2027
                return \get_class($value).': '.self::valueToString($value->__toString());
2028
            }
2029
2030 168
            if ($value instanceof DateTime || $value instanceof DateTimeImmutable) {
2031
                return \get_class($value).': '.self::valueToString($value->format('c'));
2032
            }
2033
2034
            return \get_class($value);
2035
        }
2036
2037
        if (\is_resource($value)) {
2038
            return 'resource';
2039
        }
2040 1065
2041
        if (\is_string($value)) {
2042 1065
            return '"'.$value.'"';
2043
        }
2044
2045
        return (string) $value;
2046
    }
2047
2048
    /**
2049
     * @param mixed $value
2050
     *
2051
     * @return string
2052
     */
2053
    protected static function typeToString($value)
2054
    {
2055
        return \is_object($value) ? \get_class($value) : \gettype($value);
2056
    }
2057
2058
    protected static function strlen($value)
2059
    {
2060
        if (!\function_exists('mb_detect_encoding')) {
2061
            return \strlen($value);
2062
        }
2063
2064
        if (false === $encoding = \mb_detect_encoding($value)) {
2065
            return \strlen($value);
2066
        }
2067
2068
        return \mb_strlen($value, $encoding);
2069
    }
2070
2071
    /**
2072
     * @param string $message
2073
     *
2074
     * @throws InvalidArgumentException
2075
     *
2076
     * @psalm-pure this method is not supposed to perform side-effects
2077
     */
2078
    protected static function reportInvalidArgument($message)
2079
    {
2080
        throw new InvalidArgumentException($message);
2081
    }
2082
2083
    private function __construct()
2084
    {
2085
    }
2086
}
2087