Completed
Push — master ( 8edf14...9f0de3 )
by Michael
36s queued 16s
created

Assert::inArray()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 7
rs 10
cc 3
nc 2
nop 3
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
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
    public static function string($value, $message = '')
48
    {
49
        if (!\is_string($value)) {
50
            static::reportInvalidArgument(\sprintf(
51
                $message ?: 'Expected a string. Got: %s',
52
                static::typeToString($value)
53
            ));
54
        }
55
    }
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
    public static function stringNotEmpty($value, $message = '')
67
    {
68
        static::string($value, $message);
69
        static::notEq($value, '', $message);
70
    }
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
    public static function integer($value, $message = '')
82
    {
83
        if (!\is_int($value)) {
84
            static::reportInvalidArgument(\sprintf(
85
                $message ?: 'Expected an integer. Got: %s',
86
                static::typeToString($value)
87
            ));
88
        }
89
    }
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
    public static function integerish($value, $message = '')
101
    {
102
        if (!\is_numeric($value) || $value != (int) $value) {
103
            static::reportInvalidArgument(\sprintf(
104
                $message ?: 'Expected an integerish value. Got: %s',
105
                static::typeToString($value)
106
            ));
107
        }
108
    }
109
110
    /**
111
     * @psalm-pure
112
     * @psalm-assert float $value
113
     *
114
     * @param mixed  $value
115
     * @param string $message
116
     *
117
     * @throws InvalidArgumentException
118
     */
119
    public static function float($value, $message = '')
120
    {
121
        if (!\is_float($value)) {
122
            static::reportInvalidArgument(\sprintf(
123
                $message ?: 'Expected a float. Got: %s',
124
                static::typeToString($value)
125
            ));
126
        }
127
    }
128
129
    /**
130
     * @psalm-pure
131
     * @psalm-assert numeric $value
132
     *
133
     * @param mixed  $value
134
     * @param string $message
135
     *
136
     * @throws InvalidArgumentException
137
     */
138
    public static function numeric($value, $message = '')
139
    {
140
        if (!\is_numeric($value)) {
141
            static::reportInvalidArgument(\sprintf(
142
                $message ?: 'Expected a numeric. Got: %s',
143
                static::typeToString($value)
144
            ));
145
        }
146
    }
147
148
    /**
149
     * @psalm-pure
150
     * @psalm-assert int $value
151
     *
152
     * @param mixed  $value
153
     * @param string $message
154
     *
155
     * @throws InvalidArgumentException
156
     */
157
    public static function natural($value, $message = '')
158
    {
159
        if (!\is_int($value) || $value < 0) {
160
            static::reportInvalidArgument(\sprintf(
161
                $message ?: 'Expected a non-negative integer. Got: %s',
162
                static::valueToString($value)
163
            ));
164
        }
165
    }
166
167
    /**
168
     * @psalm-pure
169
     * @psalm-assert bool $value
170
     *
171
     * @param mixed  $value
172
     * @param string $message
173
     *
174
     * @throws InvalidArgumentException
175
     */
176
    public static function boolean($value, $message = '')
177
    {
178
        if (!\is_bool($value)) {
179
            static::reportInvalidArgument(\sprintf(
180
                $message ?: 'Expected a boolean. Got: %s',
181
                static::typeToString($value)
182
            ));
183
        }
184
    }
185
186
    /**
187
     * @psalm-pure
188
     * @psalm-assert scalar $value
189
     *
190
     * @param mixed  $value
191
     * @param string $message
192
     *
193
     * @throws InvalidArgumentException
194
     */
195
    public static function scalar($value, $message = '')
196
    {
197
        if (!\is_scalar($value)) {
198
            static::reportInvalidArgument(\sprintf(
199
                $message ?: 'Expected a scalar. Got: %s',
200
                static::typeToString($value)
201
            ));
202
        }
203
    }
204
205
    /**
206
     * @psalm-pure
207
     * @psalm-assert object $value
208
     *
209
     * @param mixed  $value
210
     * @param string $message
211
     *
212
     * @throws InvalidArgumentException
213
     */
214
    public static function object($value, $message = '')
215
    {
216
        if (!\is_object($value)) {
217
            static::reportInvalidArgument(\sprintf(
218
                $message ?: 'Expected an object. Got: %s',
219
                static::typeToString($value)
220
            ));
221
        }
222
    }
223
224
    /**
225
     * @psalm-pure
226
     * @psalm-assert resource $value
227
     *
228
     * @param mixed       $value
229
     * @param string|null $type    type of resource this should be. @see https://www.php.net/manual/en/function.get-resource-type.php
230
     * @param string      $message
231
     *
232
     * @throws InvalidArgumentException
233
     */
234
    public static function resource($value, $type = null, $message = '')
235
    {
236
        if (!\is_resource($value)) {
237
            static::reportInvalidArgument(\sprintf(
238
                $message ?: 'Expected a resource. Got: %s',
239
                static::typeToString($value)
240
            ));
241
        }
242
243
        if ($type && $type !== \get_resource_type($value)) {
244
            static::reportInvalidArgument(\sprintf(
245
                $message ?: 'Expected a resource of type %2$s. Got: %s',
246
                static::typeToString($value),
247
                $type
248
            ));
249
        }
250
    }
251
252
    /**
253
     * @psalm-pure
254
     * @psalm-assert callable $value
255
     *
256
     * @param mixed  $value
257
     * @param string $message
258
     *
259
     * @throws InvalidArgumentException
260
     */
261
    public static function isCallable($value, $message = '')
262
    {
263
        if (!\is_callable($value)) {
264
            static::reportInvalidArgument(\sprintf(
265
                $message ?: 'Expected a callable. Got: %s',
266
                static::typeToString($value)
267
            ));
268
        }
269
    }
270
271
    /**
272
     * @psalm-pure
273
     * @psalm-assert array $value
274
     *
275
     * @param mixed  $value
276
     * @param string $message
277
     *
278
     * @throws InvalidArgumentException
279
     */
280
    public static function isArray($value, $message = '')
281
    {
282
        if (!\is_array($value)) {
283
            static::reportInvalidArgument(\sprintf(
284
                $message ?: 'Expected an array. Got: %s',
285
                static::typeToString($value)
286
            ));
287
        }
288
    }
289
290
    /**
291
     * @psalm-pure
292
     * @psalm-assert iterable $value
293
     *
294
     * @deprecated use "isIterable" or "isInstanceOf" instead
295
     *
296
     * @param mixed  $value
297
     * @param string $message
298
     *
299
     * @throws InvalidArgumentException
300
     */
301
    public static function isTraversable($value, $message = '')
302
    {
303
        @\trigger_error(
304
            \sprintf(
305
                '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.',
306
                __METHOD__
307
            ),
308
            \E_USER_DEPRECATED
309
        );
310
311
        if (!\is_array($value) && !($value instanceof Traversable)) {
312
            static::reportInvalidArgument(\sprintf(
313
                $message ?: 'Expected a traversable. Got: %s',
314
                static::typeToString($value)
315
            ));
316
        }
317
    }
318
319
    /**
320
     * @psalm-pure
321
     * @psalm-assert array|ArrayAccess $value
322
     *
323
     * @param mixed  $value
324
     * @param string $message
325
     *
326
     * @throws InvalidArgumentException
327
     */
328
    public static function isArrayAccessible($value, $message = '')
329
    {
330
        if (!\is_array($value) && !($value instanceof ArrayAccess)) {
331
            static::reportInvalidArgument(\sprintf(
332
                $message ?: 'Expected an array accessible. Got: %s',
333
                static::typeToString($value)
334
            ));
335
        }
336
    }
337
338
    /**
339
     * @psalm-pure
340
     * @psalm-assert countable $value
341
     *
342
     * @param mixed  $value
343
     * @param string $message
344
     *
345
     * @throws InvalidArgumentException
346
     */
347
    public static function isCountable($value, $message = '')
348
    {
349
        if (
350
            !\is_array($value)
351
            && !($value instanceof Countable)
352
            && !($value instanceof ResourceBundle)
353
            && !($value instanceof SimpleXMLElement)
354
        ) {
355
            static::reportInvalidArgument(\sprintf(
356
                $message ?: 'Expected a countable. Got: %s',
357
                static::typeToString($value)
358
            ));
359
        }
360
    }
361
362
    /**
363
     * @psalm-pure
364
     * @psalm-assert iterable $value
365
     *
366
     * @param mixed  $value
367
     * @param string $message
368
     *
369
     * @throws InvalidArgumentException
370
     */
371
    public static function isIterable($value, $message = '')
372
    {
373
        if (!\is_array($value) && !($value instanceof Traversable)) {
374
            static::reportInvalidArgument(\sprintf(
375
                $message ?: 'Expected an iterable. Got: %s',
376
                static::typeToString($value)
377
            ));
378
        }
379
    }
380
381
    /**
382
     * @psalm-pure
383
     * @psalm-template ExpectedType of object
384
     * @psalm-param class-string<ExpectedType> $class
385
     * @psalm-assert ExpectedType $value
386
     *
387
     * @param mixed         $value
388
     * @param string|object $class
389
     * @param string        $message
390
     *
391
     * @throws InvalidArgumentException
392
     */
393
    public static function isInstanceOf($value, $class, $message = '')
394
    {
395
        if (!($value instanceof $class)) {
396
            static::reportInvalidArgument(\sprintf(
397
                $message ?: 'Expected an instance of %2$s. Got: %s',
398
                static::typeToString($value),
399
                $class
0 ignored issues
show
Bug introduced by
It seems like $class can also be of type object; however, parameter $args of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

399
                /** @scrutinizer ignore-type */ $class
Loading history...
400
            ));
401
        }
402
    }
403
404
    /**
405
     * @psalm-pure
406
     * @psalm-template ExpectedType of object
407
     * @psalm-param class-string<ExpectedType> $class
408
     * @psalm-assert !ExpectedType $value
409
     *
410
     * @param mixed         $value
411
     * @param string|object $class
412
     * @param string        $message
413
     *
414
     * @throws InvalidArgumentException
415
     */
416
    public static function notInstanceOf($value, $class, $message = '')
417
    {
418
        if ($value instanceof $class) {
419
            static::reportInvalidArgument(\sprintf(
420
                $message ?: 'Expected an instance other than %2$s. Got: %s',
421
                static::typeToString($value),
422
                $class
0 ignored issues
show
Bug introduced by
It seems like $class can also be of type object; however, parameter $args of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

422
                /** @scrutinizer ignore-type */ $class
Loading history...
423
            ));
424
        }
425
    }
426
427
    /**
428
     * @psalm-pure
429
     * @psalm-param array<class-string> $classes
430
     *
431
     * @param mixed                $value
432
     * @param array<object|string> $classes
433
     * @param string               $message
434
     *
435
     * @throws InvalidArgumentException
436
     */
437
    public static function isInstanceOfAny($value, array $classes, $message = '')
438
    {
439
        foreach ($classes as $class) {
440
            if ($value instanceof $class) {
441
                return;
442
            }
443
        }
444
445
        static::reportInvalidArgument(\sprintf(
446
            $message ?: 'Expected an instance of any of %2$s. Got: %s',
447
            static::typeToString($value),
448
            \implode(', ', \array_map(array('static', 'valueToString'), $classes))
449
        ));
450
    }
451
452
    /**
453
     * @psalm-pure
454
     * @psalm-template ExpectedType of object
455
     * @psalm-param class-string<ExpectedType> $class
456
     * @psalm-assert ExpectedType|class-string<ExpectedType> $value
457
     *
458
     * @param object|string $value
459
     * @param string        $class
460
     * @param string        $message
461
     *
462
     * @throws InvalidArgumentException
463
     */
464
    public static function isAOf($value, $class, $message = '')
465
    {
466
        static::string($class, 'Expected class as a string. Got: %s');
467
468
        if (!\is_a($value, $class, \is_string($value))) {
469
            static::reportInvalidArgument(sprintf(
470
                $message ?: 'Expected an instance of this class or to this class among his parents %2$s. Got: %s',
471
                static::typeToString($value),
472
                $class
473
            ));
474
        }
475
    }
476
477
    /**
478
     * @psalm-pure
479
     * @psalm-template UnexpectedType of object
480
     * @psalm-param class-string<UnexpectedType> $class
481
     * @psalm-assert !UnexpectedType $value
482
     * @psalm-assert !class-string<UnexpectedType> $value
483
     *
484
     * @param object|string $value
485
     * @param string        $class
486
     * @param string        $message
487
     *
488
     * @throws InvalidArgumentException
489
     */
490
    public static function isNotA($value, $class, $message = '')
491
    {
492
        static::string($class, 'Expected class as a string. Got: %s');
493
494
        if (\is_a($value, $class, \is_string($value))) {
495
            static::reportInvalidArgument(sprintf(
496
                $message ?: 'Expected an instance of this class or to this class among his parents other than %2$s. Got: %s',
497
                static::typeToString($value),
498
                $class
499
            ));
500
        }
501
    }
502
503
    /**
504
     * @psalm-pure
505
     * @psalm-param array<class-string> $classes
506
     *
507
     * @param object|string $value
508
     * @param string[]      $classes
509
     * @param string        $message
510
     *
511
     * @throws InvalidArgumentException
512
     */
513
    public static function isAnyOf($value, array $classes, $message = '')
514
    {
515
        foreach ($classes as $class) {
516
            static::string($class, 'Expected class as a string. Got: %s');
517
518
            if (\is_a($value, $class, \is_string($value))) {
519
                return;
520
            }
521
        }
522
523
        static::reportInvalidArgument(sprintf(
524
            $message ?: 'Expected an any of instance of this class or to this class among his parents other than %2$s. Got: %s',
525
            static::typeToString($value),
526
            \implode(', ', \array_map(array('static', 'valueToString'), $classes))
527
        ));
528
    }
529
530
    /**
531
     * @psalm-pure
532
     * @psalm-assert empty $value
533
     *
534
     * @param mixed  $value
535
     * @param string $message
536
     *
537
     * @throws InvalidArgumentException
538
     */
539
    public static function isEmpty($value, $message = '')
540
    {
541
        if (!empty($value)) {
542
            static::reportInvalidArgument(\sprintf(
543
                $message ?: 'Expected an empty value. Got: %s',
544
                static::valueToString($value)
545
            ));
546
        }
547
    }
548
549
    /**
550
     * @psalm-pure
551
     * @psalm-assert !empty $value
552
     *
553
     * @param mixed  $value
554
     * @param string $message
555
     *
556
     * @throws InvalidArgumentException
557
     */
558
    public static function notEmpty($value, $message = '')
559
    {
560
        if (empty($value)) {
561
            static::reportInvalidArgument(\sprintf(
562
                $message ?: 'Expected a non-empty value. Got: %s',
563
                static::valueToString($value)
564
            ));
565
        }
566
    }
567
568
    /**
569
     * @psalm-pure
570
     * @psalm-assert null $value
571
     *
572
     * @param mixed  $value
573
     * @param string $message
574
     *
575
     * @throws InvalidArgumentException
576
     */
577
    public static function null($value, $message = '')
578
    {
579
        if (null !== $value) {
580
            static::reportInvalidArgument(\sprintf(
581
                $message ?: 'Expected null. Got: %s',
582
                static::valueToString($value)
583
            ));
584
        }
585
    }
586
587
    /**
588
     * @psalm-pure
589
     * @psalm-assert !null $value
590
     *
591
     * @param mixed  $value
592
     * @param string $message
593
     *
594
     * @throws InvalidArgumentException
595
     */
596
    public static function notNull($value, $message = '')
597
    {
598
        if (null === $value) {
599
            static::reportInvalidArgument(
600
                $message ?: 'Expected a value other than null.'
601
            );
602
        }
603
    }
604
605
    /**
606
     * @psalm-pure
607
     * @psalm-assert true $value
608
     *
609
     * @param mixed  $value
610
     * @param string $message
611
     *
612
     * @throws InvalidArgumentException
613
     */
614
    public static function true($value, $message = '')
615
    {
616
        if (true !== $value) {
617
            static::reportInvalidArgument(\sprintf(
618
                $message ?: 'Expected a value to be true. Got: %s',
619
                static::valueToString($value)
620
            ));
621
        }
622
    }
623
624
    /**
625
     * @psalm-pure
626
     * @psalm-assert false $value
627
     *
628
     * @param mixed  $value
629
     * @param string $message
630
     *
631
     * @throws InvalidArgumentException
632
     */
633
    public static function false($value, $message = '')
634
    {
635
        if (false !== $value) {
636
            static::reportInvalidArgument(\sprintf(
637
                $message ?: 'Expected a value to be false. Got: %s',
638
                static::valueToString($value)
639
            ));
640
        }
641
    }
642
643
    /**
644
     * @psalm-pure
645
     * @psalm-assert !false $value
646
     *
647
     * @param mixed  $value
648
     * @param string $message
649
     *
650
     * @throws InvalidArgumentException
651
     */
652
    public static function notFalse($value, $message = '')
653
    {
654
        if (false === $value) {
655
            static::reportInvalidArgument(
656
                $message ?: 'Expected a value other than false.'
657
            );
658
        }
659
    }
660
661
    /**
662
     * @param mixed  $value
663
     * @param string $message
664
     *
665
     * @throws InvalidArgumentException
666
     */
667
    public static function ip($value, $message = '')
668
    {
669
        if (false === \filter_var($value, \FILTER_VALIDATE_IP)) {
670
            static::reportInvalidArgument(\sprintf(
671
                $message ?: 'Expected a value to be an IP. Got: %s',
672
                static::valueToString($value)
673
            ));
674
        }
675
    }
676
677
    /**
678
     * @param mixed  $value
679
     * @param string $message
680
     *
681
     * @throws InvalidArgumentException
682
     */
683
    public static function ipv4($value, $message = '')
684
    {
685
        if (false === \filter_var($value, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4)) {
686
            static::reportInvalidArgument(\sprintf(
687
                $message ?: 'Expected a value to be an IPv4. Got: %s',
688
                static::valueToString($value)
689
            ));
690
        }
691
    }
692
693
    /**
694
     * @param mixed  $value
695
     * @param string $message
696
     *
697
     * @throws InvalidArgumentException
698
     */
699
    public static function ipv6($value, $message = '')
700
    {
701
        if (false === \filter_var($value, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) {
702
            static::reportInvalidArgument(\sprintf(
703
                $message ?: 'Expected a value to be an IPv6. Got: %s',
704
                static::valueToString($value)
705
            ));
706
        }
707
    }
708
709
    /**
710
     * @param mixed  $value
711
     * @param string $message
712
     *
713
     * @throws InvalidArgumentException
714
     */
715
    public static function email($value, $message = '')
716
    {
717
        if (false === \filter_var($value, FILTER_VALIDATE_EMAIL)) {
718
            static::reportInvalidArgument(\sprintf(
719
                $message ?: 'Expected a value to be a valid e-mail address. Got: %s',
720
                static::valueToString($value)
721
            ));
722
        }
723
    }
724
725
    /**
726
     * Does non strict comparisons on the items, so ['3', 3] will not pass the assertion.
727
     *
728
     * @param array  $values
729
     * @param string $message
730
     *
731
     * @throws InvalidArgumentException
732
     */
733
    public static function uniqueValues(array $values, $message = '')
734
    {
735
        $allValues = \count($values);
736
        $uniqueValues = \count(\array_unique($values));
737
738
        if ($allValues !== $uniqueValues) {
739
            $difference = $allValues - $uniqueValues;
740
741
            static::reportInvalidArgument(\sprintf(
742
                $message ?: 'Expected an array of unique values, but %s of them %s duplicated',
743
                $difference,
744
                (1 === $difference ? 'is' : 'are')
745
            ));
746
        }
747
    }
748
749
    /**
750
     * @param mixed  $value
751
     * @param mixed  $expect
752
     * @param string $message
753
     *
754
     * @throws InvalidArgumentException
755
     */
756
    public static function eq($value, $expect, $message = '')
757
    {
758
        if ($expect != $value) {
759
            static::reportInvalidArgument(\sprintf(
760
                $message ?: 'Expected a value equal to %2$s. Got: %s',
761
                static::valueToString($value),
762
                static::valueToString($expect)
763
            ));
764
        }
765
    }
766
767
    /**
768
     * @param mixed  $value
769
     * @param mixed  $expect
770
     * @param string $message
771
     *
772
     * @throws InvalidArgumentException
773
     */
774
    public static function notEq($value, $expect, $message = '')
775
    {
776
        if ($expect == $value) {
777
            static::reportInvalidArgument(\sprintf(
778
                $message ?: 'Expected a different value than %s.',
779
                static::valueToString($expect)
780
            ));
781
        }
782
    }
783
784
    /**
785
     * @psalm-pure
786
     *
787
     * @param mixed  $value
788
     * @param mixed  $expect
789
     * @param string $message
790
     *
791
     * @throws InvalidArgumentException
792
     */
793
    public static function same($value, $expect, $message = '')
794
    {
795
        if ($expect !== $value) {
796
            static::reportInvalidArgument(\sprintf(
797
                $message ?: 'Expected a value identical to %2$s. Got: %s',
798
                static::valueToString($value),
799
                static::valueToString($expect)
800
            ));
801
        }
802
    }
803
804
    /**
805
     * @psalm-pure
806
     *
807
     * @param mixed  $value
808
     * @param mixed  $expect
809
     * @param string $message
810
     *
811
     * @throws InvalidArgumentException
812
     */
813
    public static function notSame($value, $expect, $message = '')
814
    {
815
        if ($expect === $value) {
816
            static::reportInvalidArgument(\sprintf(
817
                $message ?: 'Expected a value not identical to %s.',
818
                static::valueToString($expect)
819
            ));
820
        }
821
    }
822
823
    /**
824
     * @psalm-pure
825
     *
826
     * @param mixed  $value
827
     * @param mixed  $limit
828
     * @param string $message
829
     *
830
     * @throws InvalidArgumentException
831
     */
832
    public static function greaterThan($value, $limit, $message = '')
833
    {
834
        if ($value <= $limit) {
835
            static::reportInvalidArgument(\sprintf(
836
                $message ?: 'Expected a value greater than %2$s. Got: %s',
837
                static::valueToString($value),
838
                static::valueToString($limit)
839
            ));
840
        }
841
    }
842
843
    /**
844
     * @psalm-pure
845
     *
846
     * @param mixed  $value
847
     * @param mixed  $limit
848
     * @param string $message
849
     *
850
     * @throws InvalidArgumentException
851
     */
852
    public static function greaterThanEq($value, $limit, $message = '')
853
    {
854
        if ($value < $limit) {
855
            static::reportInvalidArgument(\sprintf(
856
                $message ?: 'Expected a value greater than or equal to %2$s. Got: %s',
857
                static::valueToString($value),
858
                static::valueToString($limit)
859
            ));
860
        }
861
    }
862
863
    /**
864
     * @psalm-pure
865
     *
866
     * @param mixed  $value
867
     * @param mixed  $limit
868
     * @param string $message
869
     *
870
     * @throws InvalidArgumentException
871
     */
872
    public static function lessThan($value, $limit, $message = '')
873
    {
874
        if ($value >= $limit) {
875
            static::reportInvalidArgument(\sprintf(
876
                $message ?: 'Expected a value less than %2$s. Got: %s',
877
                static::valueToString($value),
878
                static::valueToString($limit)
879
            ));
880
        }
881
    }
882
883
    /**
884
     * @psalm-pure
885
     *
886
     * @param mixed  $value
887
     * @param mixed  $limit
888
     * @param string $message
889
     *
890
     * @throws InvalidArgumentException
891
     */
892
    public static function lessThanEq($value, $limit, $message = '')
893
    {
894
        if ($value > $limit) {
895
            static::reportInvalidArgument(\sprintf(
896
                $message ?: 'Expected a value less than or equal to %2$s. Got: %s',
897
                static::valueToString($value),
898
                static::valueToString($limit)
899
            ));
900
        }
901
    }
902
903
    /**
904
     * Inclusive range, so Assert::(3, 3, 5) passes.
905
     *
906
     * @psalm-pure
907
     *
908
     * @param mixed  $value
909
     * @param mixed  $min
910
     * @param mixed  $max
911
     * @param string $message
912
     *
913
     * @throws InvalidArgumentException
914
     */
915
    public static function range($value, $min, $max, $message = '')
916
    {
917
        if ($value < $min || $value > $max) {
918
            static::reportInvalidArgument(\sprintf(
919
                $message ?: 'Expected a value between %2$s and %3$s. Got: %s',
920
                static::valueToString($value),
921
                static::valueToString($min),
922
                static::valueToString($max)
923
            ));
924
        }
925
    }
926
927
    /**
928
     * A more human-readable alias of Assert::inArray().
929
     *
930
     * @psalm-pure
931
     *
932
     * @param mixed  $value
933
     * @param array  $values
934
     * @param string $message
935
     *
936
     * @throws InvalidArgumentException
937
     */
938
    public static function oneOf($value, array $values, $message = '')
939
    {
940
        static::inArray($value, $values, $message);
941
    }
942
943
    /**
944
     * Does strict comparison, so Assert::inArray(3, ['3']) does not pass the assertion.
945
     *
946
     * @psalm-pure
947
     *
948
     * @param mixed  $value
949
     * @param array  $values
950
     * @param string $message
951
     *
952
     * @throws InvalidArgumentException
953
     */
954
    public static function inArray($value, array $values, $message = '')
955
    {
956
        if (!\in_array($value, $values, true)) {
957
            static::reportInvalidArgument(\sprintf(
958
                $message ?: 'Expected one of: %2$s. Got: %s',
959
                static::valueToString($value),
960
                \implode(', ', \array_map(array('static', 'valueToString'), $values))
961
            ));
962
        }
963
    }
964
965
    /**
966
     * @psalm-pure
967
     *
968
     * @param string $value
969
     * @param string $subString
970
     * @param string $message
971
     *
972
     * @throws InvalidArgumentException
973
     */
974
    public static function contains($value, $subString, $message = '')
975
    {
976
        if (false === \strpos($value, $subString)) {
977
            static::reportInvalidArgument(\sprintf(
978
                $message ?: 'Expected a value to contain %2$s. Got: %s',
979
                static::valueToString($value),
980
                static::valueToString($subString)
981
            ));
982
        }
983
    }
984
985
    /**
986
     * @psalm-pure
987
     *
988
     * @param string $value
989
     * @param string $subString
990
     * @param string $message
991
     *
992
     * @throws InvalidArgumentException
993
     */
994
    public static function notContains($value, $subString, $message = '')
995
    {
996
        if (false !== \strpos($value, $subString)) {
997
            static::reportInvalidArgument(\sprintf(
998
                $message ?: '%2$s was not expected to be contained in a value. Got: %s',
999
                static::valueToString($value),
1000
                static::valueToString($subString)
1001
            ));
1002
        }
1003
    }
1004
1005
    /**
1006
     * @psalm-pure
1007
     *
1008
     * @param string $value
1009
     * @param string $message
1010
     *
1011
     * @throws InvalidArgumentException
1012
     */
1013
    public static function notWhitespaceOnly($value, $message = '')
1014
    {
1015
        if (\preg_match('/^\s*$/', $value)) {
1016
            static::reportInvalidArgument(\sprintf(
1017
                $message ?: 'Expected a non-whitespace string. Got: %s',
1018
                static::valueToString($value)
1019
            ));
1020
        }
1021
    }
1022
1023
    /**
1024
     * @psalm-pure
1025
     *
1026
     * @param string $value
1027
     * @param string $prefix
1028
     * @param string $message
1029
     *
1030
     * @throws InvalidArgumentException
1031
     */
1032
    public static function startsWith($value, $prefix, $message = '')
1033
    {
1034
        if (0 !== \strpos($value, $prefix)) {
1035
            static::reportInvalidArgument(\sprintf(
1036
                $message ?: 'Expected a value to start with %2$s. Got: %s',
1037
                static::valueToString($value),
1038
                static::valueToString($prefix)
1039
            ));
1040
        }
1041
    }
1042
1043
    /**
1044
     * @psalm-pure
1045
     *
1046
     * @param string $value
1047
     * @param string $prefix
1048
     * @param string $message
1049
     *
1050
     * @throws InvalidArgumentException
1051
     */
1052
    public static function notStartsWith($value, $prefix, $message = '')
1053
    {
1054
        if (0 === \strpos($value, $prefix)) {
1055
            static::reportInvalidArgument(\sprintf(
1056
                $message ?: 'Expected a value not to start with %2$s. Got: %s',
1057
                static::valueToString($value),
1058
                static::valueToString($prefix)
1059
            ));
1060
        }
1061
    }
1062
1063
    /**
1064
     * @psalm-pure
1065
     *
1066
     * @param mixed  $value
1067
     * @param string $message
1068
     *
1069
     * @throws InvalidArgumentException
1070
     */
1071
    public static function startsWithLetter($value, $message = '')
1072
    {
1073
        static::string($value);
1074
1075
        $valid = isset($value[0]);
1076
1077
        if ($valid) {
1078
            $locale = \setlocale(LC_CTYPE, 0);
1079
            \setlocale(LC_CTYPE, 'C');
1080
            $valid = \ctype_alpha($value[0]);
1081
            \setlocale(LC_CTYPE, $locale);
1082
        }
1083
1084
        if (!$valid) {
1085
            static::reportInvalidArgument(\sprintf(
1086
                $message ?: 'Expected a value to start with a letter. Got: %s',
1087
                static::valueToString($value)
1088
            ));
1089
        }
1090
    }
1091
1092
    /**
1093
     * @psalm-pure
1094
     *
1095
     * @param string $value
1096
     * @param string $suffix
1097
     * @param string $message
1098
     *
1099
     * @throws InvalidArgumentException
1100
     */
1101
    public static function endsWith($value, $suffix, $message = '')
1102
    {
1103
        if ($suffix !== \substr($value, -\strlen($suffix))) {
1104
            static::reportInvalidArgument(\sprintf(
1105
                $message ?: 'Expected a value to end with %2$s. Got: %s',
1106
                static::valueToString($value),
1107
                static::valueToString($suffix)
1108
            ));
1109
        }
1110
    }
1111
1112
    /**
1113
     * @psalm-pure
1114
     *
1115
     * @param string $value
1116
     * @param string $suffix
1117
     * @param string $message
1118
     *
1119
     * @throws InvalidArgumentException
1120
     */
1121
    public static function notEndsWith($value, $suffix, $message = '')
1122
    {
1123
        if ($suffix === \substr($value, -\strlen($suffix))) {
1124
            static::reportInvalidArgument(\sprintf(
1125
                $message ?: 'Expected a value not to end with %2$s. Got: %s',
1126
                static::valueToString($value),
1127
                static::valueToString($suffix)
1128
            ));
1129
        }
1130
    }
1131
1132
    /**
1133
     * @psalm-pure
1134
     *
1135
     * @param string $value
1136
     * @param string $pattern
1137
     * @param string $message
1138
     *
1139
     * @throws InvalidArgumentException
1140
     */
1141
    public static function regex($value, $pattern, $message = '')
1142
    {
1143
        if (!\preg_match($pattern, $value)) {
1144
            static::reportInvalidArgument(\sprintf(
1145
                $message ?: 'The value %s does not match the expected pattern.',
1146
                static::valueToString($value)
1147
            ));
1148
        }
1149
    }
1150
1151
    /**
1152
     * @psalm-pure
1153
     *
1154
     * @param string $value
1155
     * @param string $pattern
1156
     * @param string $message
1157
     *
1158
     * @throws InvalidArgumentException
1159
     */
1160
    public static function notRegex($value, $pattern, $message = '')
1161
    {
1162
        if (\preg_match($pattern, $value, $matches, PREG_OFFSET_CAPTURE)) {
1163
            static::reportInvalidArgument(\sprintf(
1164
                $message ?: 'The value %s matches the pattern %s (at offset %d).',
1165
                static::valueToString($value),
1166
                static::valueToString($pattern),
1167
                $matches[0][1]
1168
            ));
1169
        }
1170
    }
1171
1172
    /**
1173
     * @psalm-pure
1174
     *
1175
     * @param mixed  $value
1176
     * @param string $message
1177
     *
1178
     * @throws InvalidArgumentException
1179
     */
1180
    public static function unicodeLetters($value, $message = '')
1181
    {
1182
        static::string($value);
1183
1184
        if (!\preg_match('/^\p{L}+$/u', $value)) {
1185
            static::reportInvalidArgument(\sprintf(
1186
                $message ?: 'Expected a value to contain only Unicode letters. Got: %s',
1187
                static::valueToString($value)
1188
            ));
1189
        }
1190
    }
1191
1192
    /**
1193
     * @psalm-pure
1194
     *
1195
     * @param mixed  $value
1196
     * @param string $message
1197
     *
1198
     * @throws InvalidArgumentException
1199
     */
1200
    public static function alpha($value, $message = '')
1201
    {
1202
        static::string($value);
1203
1204
        $locale = \setlocale(LC_CTYPE, 0);
1205
        \setlocale(LC_CTYPE, 'C');
1206
        $valid = !\ctype_alpha($value);
1207
        \setlocale(LC_CTYPE, $locale);
1208
1209
        if ($valid) {
1210
            static::reportInvalidArgument(\sprintf(
1211
                $message ?: 'Expected a value to contain only letters. Got: %s',
1212
                static::valueToString($value)
1213
            ));
1214
        }
1215
    }
1216
1217
    /**
1218
     * @psalm-pure
1219
     *
1220
     * @param string $value
1221
     * @param string $message
1222
     *
1223
     * @throws InvalidArgumentException
1224
     */
1225
    public static function digits($value, $message = '')
1226
    {
1227
        $locale = \setlocale(LC_CTYPE, 0);
1228
        \setlocale(LC_CTYPE, 'C');
1229
        $valid = !\ctype_digit($value);
1230
        \setlocale(LC_CTYPE, $locale);
1231
1232
        if ($valid) {
1233
            static::reportInvalidArgument(\sprintf(
1234
                $message ?: 'Expected a value to contain digits only. Got: %s',
1235
                static::valueToString($value)
1236
            ));
1237
        }
1238
    }
1239
1240
    /**
1241
     * @psalm-pure
1242
     *
1243
     * @param string $value
1244
     * @param string $message
1245
     *
1246
     * @throws InvalidArgumentException
1247
     */
1248
    public static function alnum($value, $message = '')
1249
    {
1250
        $locale = \setlocale(LC_CTYPE, 0);
1251
        \setlocale(LC_CTYPE, 'C');
1252
        $valid = !\ctype_alnum($value);
1253
        \setlocale(LC_CTYPE, $locale);
1254
1255
        if ($valid) {
1256
            static::reportInvalidArgument(\sprintf(
1257
                $message ?: 'Expected a value to contain letters and digits only. Got: %s',
1258
                static::valueToString($value)
1259
            ));
1260
        }
1261
    }
1262
1263
    /**
1264
     * @psalm-pure
1265
     * @psalm-assert lowercase-string $value
1266
     *
1267
     * @param string $value
1268
     * @param string $message
1269
     *
1270
     * @throws InvalidArgumentException
1271
     */
1272
    public static function lower($value, $message = '')
1273
    {
1274
        $locale = \setlocale(LC_CTYPE, 0);
1275
        \setlocale(LC_CTYPE, 'C');
1276
        $valid = !\ctype_lower($value);
1277
        \setlocale(LC_CTYPE, $locale);
1278
1279
        if ($valid) {
1280
            static::reportInvalidArgument(\sprintf(
1281
                $message ?: 'Expected a value to contain lowercase characters only. Got: %s',
1282
                static::valueToString($value)
1283
            ));
1284
        }
1285
    }
1286
1287
    /**
1288
     * @psalm-pure
1289
     * @psalm-assert !lowercase-string $value
1290
     *
1291
     * @param string $value
1292
     * @param string $message
1293
     *
1294
     * @throws InvalidArgumentException
1295
     */
1296
    public static function upper($value, $message = '')
1297
    {
1298
        $locale = \setlocale(LC_CTYPE, 0);
1299
        \setlocale(LC_CTYPE, 'C');
1300
        $valid = !\ctype_upper($value);
1301
        \setlocale(LC_CTYPE, $locale);
1302
1303
        if ($valid) {
1304
            static::reportInvalidArgument(\sprintf(
1305
                $message ?: 'Expected a value to contain uppercase characters only. Got: %s',
1306
                static::valueToString($value)
1307
            ));
1308
        }
1309
    }
1310
1311
    /**
1312
     * @psalm-pure
1313
     *
1314
     * @param string $value
1315
     * @param int    $length
1316
     * @param string $message
1317
     *
1318
     * @throws InvalidArgumentException
1319
     */
1320
    public static function length($value, $length, $message = '')
1321
    {
1322
        if ($length !== static::strlen($value)) {
1323
            static::reportInvalidArgument(\sprintf(
1324
                $message ?: 'Expected a value to contain %2$s characters. Got: %s',
1325
                static::valueToString($value),
1326
                $length
1327
            ));
1328
        }
1329
    }
1330
1331
    /**
1332
     * Inclusive min.
1333
     *
1334
     * @psalm-pure
1335
     *
1336
     * @param string    $value
1337
     * @param int|float $min
1338
     * @param string    $message
1339
     *
1340
     * @throws InvalidArgumentException
1341
     */
1342
    public static function minLength($value, $min, $message = '')
1343
    {
1344
        if (static::strlen($value) < $min) {
1345
            static::reportInvalidArgument(\sprintf(
1346
                $message ?: 'Expected a value to contain at least %2$s characters. Got: %s',
1347
                static::valueToString($value),
1348
                $min
1349
            ));
1350
        }
1351
    }
1352
1353
    /**
1354
     * Inclusive max.
1355
     *
1356
     * @psalm-pure
1357
     *
1358
     * @param string    $value
1359
     * @param int|float $max
1360
     * @param string    $message
1361
     *
1362
     * @throws InvalidArgumentException
1363
     */
1364
    public static function maxLength($value, $max, $message = '')
1365
    {
1366
        if (static::strlen($value) > $max) {
1367
            static::reportInvalidArgument(\sprintf(
1368
                $message ?: 'Expected a value to contain at most %2$s characters. Got: %s',
1369
                static::valueToString($value),
1370
                $max
1371
            ));
1372
        }
1373
    }
1374
1375
    /**
1376
     * Inclusive , so Assert::lengthBetween('asd', 3, 5); passes the assertion.
1377
     *
1378
     * @psalm-pure
1379
     *
1380
     * @param string    $value
1381
     * @param int|float $min
1382
     * @param int|float $max
1383
     * @param string    $message
1384
     *
1385
     * @throws InvalidArgumentException
1386
     */
1387
    public static function lengthBetween($value, $min, $max, $message = '')
1388
    {
1389
        $length = static::strlen($value);
1390
1391
        if ($length < $min || $length > $max) {
1392
            static::reportInvalidArgument(\sprintf(
1393
                $message ?: 'Expected a value to contain between %2$s and %3$s characters. Got: %s',
1394
                static::valueToString($value),
1395
                $min,
1396
                $max
1397
            ));
1398
        }
1399
    }
1400
1401
    /**
1402
     * Will also pass if $value is a directory, use Assert::file() instead if you need to be sure it is a file.
1403
     *
1404
     * @param mixed  $value
1405
     * @param string $message
1406
     *
1407
     * @throws InvalidArgumentException
1408
     */
1409
    public static function fileExists($value, $message = '')
1410
    {
1411
        static::string($value);
1412
1413
        if (!\file_exists($value)) {
1414
            static::reportInvalidArgument(\sprintf(
1415
                $message ?: 'The file %s does not exist.',
1416
                static::valueToString($value)
1417
            ));
1418
        }
1419
    }
1420
1421
    /**
1422
     * @param mixed  $value
1423
     * @param string $message
1424
     *
1425
     * @throws InvalidArgumentException
1426
     */
1427
    public static function file($value, $message = '')
1428
    {
1429
        static::fileExists($value, $message);
1430
1431
        if (!\is_file($value)) {
1432
            static::reportInvalidArgument(\sprintf(
1433
                $message ?: 'The path %s is not a file.',
1434
                static::valueToString($value)
1435
            ));
1436
        }
1437
    }
1438
1439
    /**
1440
     * @param mixed  $value
1441
     * @param string $message
1442
     *
1443
     * @throws InvalidArgumentException
1444
     */
1445
    public static function directory($value, $message = '')
1446
    {
1447
        static::fileExists($value, $message);
1448
1449
        if (!\is_dir($value)) {
1450
            static::reportInvalidArgument(\sprintf(
1451
                $message ?: 'The path %s is no directory.',
1452
                static::valueToString($value)
1453
            ));
1454
        }
1455
    }
1456
1457
    /**
1458
     * @param string $value
1459
     * @param string $message
1460
     *
1461
     * @throws InvalidArgumentException
1462
     */
1463
    public static function readable($value, $message = '')
1464
    {
1465
        if (!\is_readable($value)) {
1466
            static::reportInvalidArgument(\sprintf(
1467
                $message ?: 'The path %s is not readable.',
1468
                static::valueToString($value)
1469
            ));
1470
        }
1471
    }
1472
1473
    /**
1474
     * @param string $value
1475
     * @param string $message
1476
     *
1477
     * @throws InvalidArgumentException
1478
     */
1479
    public static function writable($value, $message = '')
1480
    {
1481
        if (!\is_writable($value)) {
1482
            static::reportInvalidArgument(\sprintf(
1483
                $message ?: 'The path %s is not writable.',
1484
                static::valueToString($value)
1485
            ));
1486
        }
1487
    }
1488
1489
    /**
1490
     * @psalm-assert class-string $value
1491
     *
1492
     * @param mixed  $value
1493
     * @param string $message
1494
     *
1495
     * @throws InvalidArgumentException
1496
     */
1497
    public static function classExists($value, $message = '')
1498
    {
1499
        if (!\class_exists($value)) {
1500
            static::reportInvalidArgument(\sprintf(
1501
                $message ?: 'Expected an existing class name. Got: %s',
1502
                static::valueToString($value)
1503
            ));
1504
        }
1505
    }
1506
1507
    /**
1508
     * @psalm-pure
1509
     * @psalm-template ExpectedType of object
1510
     * @psalm-param class-string<ExpectedType> $class
1511
     * @psalm-assert class-string<ExpectedType>|ExpectedType $value
1512
     *
1513
     * @param mixed         $value
1514
     * @param string|object $class
1515
     * @param string        $message
1516
     *
1517
     * @throws InvalidArgumentException
1518
     */
1519
    public static function subclassOf($value, $class, $message = '')
1520
    {
1521
        if (!\is_subclass_of($value, $class)) {
0 ignored issues
show
Bug introduced by
It seems like $class can also be of type object; however, parameter $class_name of is_subclass_of() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1521
        if (!\is_subclass_of($value, /** @scrutinizer ignore-type */ $class)) {
Loading history...
1522
            static::reportInvalidArgument(\sprintf(
1523
                $message ?: 'Expected a sub-class of %2$s. Got: %s',
1524
                static::valueToString($value),
1525
                static::valueToString($class)
1526
            ));
1527
        }
1528
    }
1529
1530
    /**
1531
     * @psalm-assert class-string $value
1532
     *
1533
     * @param mixed  $value
1534
     * @param string $message
1535
     *
1536
     * @throws InvalidArgumentException
1537
     */
1538
    public static function interfaceExists($value, $message = '')
1539
    {
1540
        if (!\interface_exists($value)) {
1541
            static::reportInvalidArgument(\sprintf(
1542
                $message ?: 'Expected an existing interface name. got %s',
1543
                static::valueToString($value)
1544
            ));
1545
        }
1546
    }
1547
1548
    /**
1549
     * @psalm-pure
1550
     * @psalm-template ExpectedType of object
1551
     * @psalm-param class-string<ExpectedType> $interface
1552
     * @psalm-assert class-string<ExpectedType> $value
1553
     *
1554
     * @param mixed  $value
1555
     * @param mixed  $interface
1556
     * @param string $message
1557
     *
1558
     * @throws InvalidArgumentException
1559
     */
1560
    public static function implementsInterface($value, $interface, $message = '')
1561
    {
1562
        if (!\in_array($interface, \class_implements($value))) {
1563
            static::reportInvalidArgument(\sprintf(
1564
                $message ?: 'Expected an implementation of %2$s. Got: %s',
1565
                static::valueToString($value),
1566
                static::valueToString($interface)
1567
            ));
1568
        }
1569
    }
1570
1571
    /**
1572
     * @psalm-pure
1573
     * @psalm-param class-string|object $classOrObject
1574
     *
1575
     * @param string|object $classOrObject
1576
     * @param mixed         $property
1577
     * @param string        $message
1578
     *
1579
     * @throws InvalidArgumentException
1580
     */
1581
    public static function propertyExists($classOrObject, $property, $message = '')
1582
    {
1583
        if (!\property_exists($classOrObject, $property)) {
1584
            static::reportInvalidArgument(\sprintf(
1585
                $message ?: 'Expected the property %s to exist.',
1586
                static::valueToString($property)
1587
            ));
1588
        }
1589
    }
1590
1591
    /**
1592
     * @psalm-pure
1593
     * @psalm-param class-string|object $classOrObject
1594
     *
1595
     * @param string|object $classOrObject
1596
     * @param mixed         $property
1597
     * @param string        $message
1598
     *
1599
     * @throws InvalidArgumentException
1600
     */
1601
    public static function propertyNotExists($classOrObject, $property, $message = '')
1602
    {
1603
        if (\property_exists($classOrObject, $property)) {
1604
            static::reportInvalidArgument(\sprintf(
1605
                $message ?: 'Expected the property %s to not exist.',
1606
                static::valueToString($property)
1607
            ));
1608
        }
1609
    }
1610
1611
    /**
1612
     * @psalm-pure
1613
     * @psalm-param class-string|object $classOrObject
1614
     *
1615
     * @param string|object $classOrObject
1616
     * @param mixed         $method
1617
     * @param string        $message
1618
     *
1619
     * @throws InvalidArgumentException
1620
     */
1621
    public static function methodExists($classOrObject, $method, $message = '')
1622
    {
1623
        if (!(\is_string($classOrObject) || \is_object($classOrObject)) || !\method_exists($classOrObject, $method)) {
1624
            static::reportInvalidArgument(\sprintf(
1625
                $message ?: 'Expected the method %s to exist.',
1626
                static::valueToString($method)
1627
            ));
1628
        }
1629
    }
1630
1631
    /**
1632
     * @psalm-pure
1633
     * @psalm-param class-string|object $classOrObject
1634
     *
1635
     * @param string|object $classOrObject
1636
     * @param mixed         $method
1637
     * @param string        $message
1638
     *
1639
     * @throws InvalidArgumentException
1640
     */
1641
    public static function methodNotExists($classOrObject, $method, $message = '')
1642
    {
1643
        if ((\is_string($classOrObject) || \is_object($classOrObject)) && \method_exists($classOrObject, $method)) {
1644
            static::reportInvalidArgument(\sprintf(
1645
                $message ?: 'Expected the method %s to not exist.',
1646
                static::valueToString($method)
1647
            ));
1648
        }
1649
    }
1650
1651
    /**
1652
     * @psalm-pure
1653
     *
1654
     * @param array      $array
1655
     * @param string|int $key
1656
     * @param string     $message
1657
     *
1658
     * @throws InvalidArgumentException
1659
     */
1660
    public static function keyExists($array, $key, $message = '')
1661
    {
1662
        if (!(isset($array[$key]) || \array_key_exists($key, $array))) {
1663
            static::reportInvalidArgument(\sprintf(
1664
                $message ?: 'Expected the key %s to exist.',
1665
                static::valueToString($key)
1666
            ));
1667
        }
1668
    }
1669
1670
    /**
1671
     * @psalm-pure
1672
     *
1673
     * @param array      $array
1674
     * @param string|int $key
1675
     * @param string     $message
1676
     *
1677
     * @throws InvalidArgumentException
1678
     */
1679
    public static function keyNotExists($array, $key, $message = '')
1680
    {
1681
        if (isset($array[$key]) || \array_key_exists($key, $array)) {
1682
            static::reportInvalidArgument(\sprintf(
1683
                $message ?: 'Expected the key %s to not exist.',
1684
                static::valueToString($key)
1685
            ));
1686
        }
1687
    }
1688
1689
    /**
1690
     * Checks if a value is a valid array key (int or string).
1691
     *
1692
     * @psalm-pure
1693
     * @psalm-assert array-key $value
1694
     *
1695
     * @param mixed  $value
1696
     * @param string $message
1697
     *
1698
     * @throws InvalidArgumentException
1699
     */
1700
    public static function validArrayKey($value, $message = '')
1701
    {
1702
        if (!(\is_int($value) || \is_string($value))) {
1703
            static::reportInvalidArgument(\sprintf(
1704
                $message ?: 'Expected string or integer. Got: %s',
1705
                static::typeToString($value)
1706
            ));
1707
        }
1708
    }
1709
1710
    /**
1711
     * Does not check if $array is countable, this can generate a warning on php versions after 7.2.
1712
     *
1713
     * @param Countable|array $array
1714
     * @param int             $number
1715
     * @param string          $message
1716
     *
1717
     * @throws InvalidArgumentException
1718
     */
1719
    public static function count($array, $number, $message = '')
1720
    {
1721
        static::eq(
1722
            \count($array),
1723
            $number,
1724
            \sprintf(
1725
                $message ?: 'Expected an array to contain %d elements. Got: %d.',
1726
                $number,
1727
                \count($array)
1728
            )
1729
        );
1730
    }
1731
1732
    /**
1733
     * Does not check if $array is countable, this can generate a warning on php versions after 7.2.
1734
     *
1735
     * @param Countable|array $array
1736
     * @param int|float       $min
1737
     * @param string          $message
1738
     *
1739
     * @throws InvalidArgumentException
1740
     */
1741
    public static function minCount($array, $min, $message = '')
1742
    {
1743
        if (\count($array) < $min) {
1744
            static::reportInvalidArgument(\sprintf(
1745
                $message ?: 'Expected an array to contain at least %2$d elements. Got: %d',
1746
                \count($array),
1747
                $min
1748
            ));
1749
        }
1750
    }
1751
1752
    /**
1753
     * Does not check if $array is countable, this can generate a warning on php versions after 7.2.
1754
     *
1755
     * @param Countable|array $array
1756
     * @param int|float       $max
1757
     * @param string          $message
1758
     *
1759
     * @throws InvalidArgumentException
1760
     */
1761
    public static function maxCount($array, $max, $message = '')
1762
    {
1763
        if (\count($array) > $max) {
1764
            static::reportInvalidArgument(\sprintf(
1765
                $message ?: 'Expected an array to contain at most %2$d elements. Got: %d',
1766
                \count($array),
1767
                $max
1768
            ));
1769
        }
1770
    }
1771
1772
    /**
1773
     * Does not check if $array is countable, this can generate a warning on php versions after 7.2.
1774
     *
1775
     * @param Countable|array $array
1776
     * @param int|float       $min
1777
     * @param int|float       $max
1778
     * @param string          $message
1779
     *
1780
     * @throws InvalidArgumentException
1781
     */
1782
    public static function countBetween($array, $min, $max, $message = '')
1783
    {
1784
        $count = \count($array);
1785
1786
        if ($count < $min || $count > $max) {
1787
            static::reportInvalidArgument(\sprintf(
1788
                $message ?: 'Expected an array to contain between %2$d and %3$d elements. Got: %d',
1789
                $count,
1790
                $min,
1791
                $max
1792
            ));
1793
        }
1794
    }
1795
1796
    /**
1797
     * @psalm-pure
1798
     * @psalm-assert list $array
1799
     *
1800
     * @param mixed  $array
1801
     * @param string $message
1802
     *
1803
     * @throws InvalidArgumentException
1804
     */
1805
    public static function isList($array, $message = '')
1806
    {
1807
        if (!\is_array($array) || $array !== \array_values($array)) {
1808
            static::reportInvalidArgument(
1809
                $message ?: 'Expected list - non-associative array.'
1810
            );
1811
        }
1812
    }
1813
1814
    /**
1815
     * @psalm-pure
1816
     * @psalm-assert non-empty-list $array
1817
     *
1818
     * @param mixed  $array
1819
     * @param string $message
1820
     *
1821
     * @throws InvalidArgumentException
1822
     */
1823
    public static function isNonEmptyList($array, $message = '')
1824
    {
1825
        static::isList($array, $message);
1826
        static::notEmpty($array, $message);
1827
    }
1828
1829
    /**
1830
     * @psalm-pure
1831
     * @psalm-template T
1832
     * @psalm-param mixed|array<T> $array
1833
     * @psalm-assert array<string, T> $array
1834
     *
1835
     * @param mixed  $array
1836
     * @param string $message
1837
     *
1838
     * @throws InvalidArgumentException
1839
     */
1840
    public static function isMap($array, $message = '')
1841
    {
1842
        if (
1843
            !\is_array($array) ||
1844
            \array_keys($array) !== \array_filter(\array_keys($array), '\is_string')
1845
        ) {
1846
            static::reportInvalidArgument(
1847
                $message ?: 'Expected map - associative array with string keys.'
1848
            );
1849
        }
1850
    }
1851
1852
    /**
1853
     * @psalm-pure
1854
     * @psalm-template T
1855
     * @psalm-param mixed|array<T> $array
1856
     * @psalm-assert array<string, T> $array
1857
     * @psalm-assert !empty $array
1858
     *
1859
     * @param mixed  $array
1860
     * @param string $message
1861
     *
1862
     * @throws InvalidArgumentException
1863
     */
1864
    public static function isNonEmptyMap($array, $message = '')
1865
    {
1866
        static::isMap($array, $message);
1867
        static::notEmpty($array, $message);
1868
    }
1869
1870
    /**
1871
     * @psalm-pure
1872
     *
1873
     * @param string $value
1874
     * @param string $message
1875
     *
1876
     * @throws InvalidArgumentException
1877
     */
1878
    public static function uuid($value, $message = '')
1879
    {
1880
        $value = \str_replace(array('urn:', 'uuid:', '{', '}'), '', $value);
1881
1882
        // The nil UUID is special form of UUID that is specified to have all
1883
        // 128 bits set to zero.
1884
        if ('00000000-0000-0000-0000-000000000000' === $value) {
1885
            return;
1886
        }
1887
1888
        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)) {
1889
            static::reportInvalidArgument(\sprintf(
1890
                $message ?: 'Value %s is not a valid UUID.',
1891
                static::valueToString($value)
1892
            ));
1893
        }
1894
    }
1895
1896
    /**
1897
     * @psalm-param class-string<Throwable> $class
1898
     *
1899
     * @param Closure $expression
1900
     * @param string  $class
1901
     * @param string  $message
1902
     *
1903
     * @throws InvalidArgumentException
1904
     */
1905
    public static function throws(Closure $expression, $class = 'Exception', $message = '')
1906
    {
1907
        static::string($class);
1908
1909
        $actual = 'none';
1910
1911
        try {
1912
            $expression();
1913
        } catch (Exception $e) {
1914
            $actual = \get_class($e);
1915
            if ($e instanceof $class) {
1916
                return;
1917
            }
1918
        } catch (Throwable $e) {
1919
            $actual = \get_class($e);
1920
            if ($e instanceof $class) {
1921
                return;
1922
            }
1923
        }
1924
1925
        static::reportInvalidArgument($message ?: \sprintf(
1926
            'Expected to throw "%s", got "%s"',
1927
            $class,
1928
            $actual
1929
        ));
1930
    }
1931
1932
    /**
1933
     * @throws BadMethodCallException
1934
     */
1935
    public static function __callStatic($name, $arguments)
1936
    {
1937
        if ('nullOr' === \substr($name, 0, 6)) {
1938
            if (null !== $arguments[0]) {
1939
                $method = \lcfirst(\substr($name, 6));
1940
                \call_user_func_array(array('static', $method), $arguments);
1941
            }
1942
1943
            return;
1944
        }
1945
1946
        if ('all' === \substr($name, 0, 3)) {
1947
            static::isIterable($arguments[0]);
1948
1949
            $method = \lcfirst(\substr($name, 3));
1950
            $args = $arguments;
1951
1952
            foreach ($arguments[0] as $entry) {
1953
                $args[0] = $entry;
1954
1955
                \call_user_func_array(array('static', $method), $args);
1956
            }
1957
1958
            return;
1959
        }
1960
1961
        throw new BadMethodCallException('No such method: '.$name);
1962
    }
1963
1964
    /**
1965
     * @param mixed $value
1966
     *
1967
     * @return string
1968
     */
1969
    protected static function valueToString($value)
1970
    {
1971
        if (null === $value) {
1972
            return 'null';
1973
        }
1974
1975
        if (true === $value) {
1976
            return 'true';
1977
        }
1978
1979
        if (false === $value) {
1980
            return 'false';
1981
        }
1982
1983
        if (\is_array($value)) {
1984
            return 'array';
1985
        }
1986
1987
        if (\is_object($value)) {
1988
            if (\method_exists($value, '__toString')) {
1989
                return \get_class($value).': '.self::valueToString($value->__toString());
1990
            }
1991
1992
            if ($value instanceof DateTime || $value instanceof DateTimeImmutable) {
1993
                return \get_class($value).': '.self::valueToString($value->format('c'));
1994
            }
1995
1996
            return \get_class($value);
1997
        }
1998
1999
        if (\is_resource($value)) {
2000
            return 'resource';
2001
        }
2002
2003
        if (\is_string($value)) {
2004
            return '"'.$value.'"';
2005
        }
2006
2007
        return (string) $value;
2008
    }
2009
2010
    /**
2011
     * @param mixed $value
2012
     *
2013
     * @return string
2014
     */
2015
    protected static function typeToString($value)
2016
    {
2017
        return \is_object($value) ? \get_class($value) : \gettype($value);
2018
    }
2019
2020
    protected static function strlen($value)
2021
    {
2022
        if (!\function_exists('mb_detect_encoding')) {
2023
            return \strlen($value);
2024
        }
2025
2026
        if (false === $encoding = \mb_detect_encoding($value)) {
2027
            return \strlen($value);
2028
        }
2029
2030
        return \mb_strlen($value, $encoding);
2031
    }
2032
2033
    /**
2034
     * @param string $message
2035
     *
2036
     * @throws InvalidArgumentException
2037
     *
2038
     * @psalm-pure this method is not supposed to perform side-effects
2039
     */
2040
    protected static function reportInvalidArgument($message)
2041
    {
2042
        throw new InvalidArgumentException($message);
2043
    }
2044
2045
    private function __construct()
2046
    {
2047
    }
2048
}
2049