Passed
Push — master ( c69a4e...3c6756 )
by Michael
21:44 queued 13:20
created

Assert::isInstanceOf()   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
dl 0
loc 7
rs 10
c 0
b 0
f 0
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 ResourceBundle;
22
use SimpleXMLElement;
23
use Throwable;
24
use Traversable;
25
26
/**
27
 * Efficient assertions to validate the input/output of your methods.
28
 *
29
 * @since  1.0
30
 *
31
 * @author Bernhard Schussek <[email protected]>
32
 */
33
class Assert
34
{
35
    use Mixin;
36
37
    /**
38
     * @psalm-pure
39
     * @psalm-assert string $value
40
     *
41
     * @param mixed  $value
42
     * @param string $message
43
     *
44
     * @throws InvalidArgumentException
45
     */
46
    public static function string($value, $message = '')
47
    {
48
        if (!\is_string($value)) {
49
            static::reportInvalidArgument(\sprintf(
50
                $message ?: 'Expected a string. Got: %s',
51
                static::typeToString($value)
52
            ));
53
        }
54
    }
55
56
    /**
57
     * @psalm-pure
58
     * @psalm-assert non-empty-string $value
59
     *
60
     * @param mixed  $value
61
     * @param string $message
62
     *
63
     * @throws InvalidArgumentException
64
     */
65
    public static function stringNotEmpty($value, $message = '')
66
    {
67
        static::string($value, $message);
68
        static::notEq($value, '', $message);
69
    }
70
71
    /**
72
     * @psalm-pure
73
     * @psalm-assert int $value
74
     *
75
     * @param mixed  $value
76
     * @param string $message
77
     *
78
     * @throws InvalidArgumentException
79
     */
80
    public static function integer($value, $message = '')
81
    {
82
        if (!\is_int($value)) {
83
            static::reportInvalidArgument(\sprintf(
84
                $message ?: 'Expected an integer. Got: %s',
85
                static::typeToString($value)
86
            ));
87
        }
88
    }
89
90
    /**
91
     * @psalm-pure
92
     * @psalm-assert numeric $value
93
     *
94
     * @param mixed  $value
95
     * @param string $message
96
     *
97
     * @throws InvalidArgumentException
98
     */
99
    public static function integerish($value, $message = '')
100
    {
101
        if (!\is_numeric($value) || $value != (int) $value) {
102
            static::reportInvalidArgument(\sprintf(
103
                $message ?: 'Expected an integerish value. Got: %s',
104
                static::typeToString($value)
105
            ));
106
        }
107
    }
108
109
    /**
110
     * @psalm-pure
111
     * @psalm-assert positive-int $value
112
     *
113
     * @param mixed  $value
114
     * @param string $message
115
     *
116
     * @throws InvalidArgumentException
117
     */
118
    public static function positiveInteger($value, $message = '')
119
    {
120
        if (!(\is_int($value) && $value > 0)) {
121
            static::reportInvalidArgument(\sprintf(
122
                $message ?: 'Expected a positive integer. Got: %s',
123
                static::valueToString($value)
124
            ));
125
        }
126
    }
127
128
    /**
129
     * @psalm-pure
130
     * @psalm-assert float $value
131
     *
132
     * @param mixed  $value
133
     * @param string $message
134
     *
135
     * @throws InvalidArgumentException
136
     */
137
    public static function float($value, $message = '')
138
    {
139
        if (!\is_float($value)) {
140
            static::reportInvalidArgument(\sprintf(
141
                $message ?: 'Expected a float. Got: %s',
142
                static::typeToString($value)
143
            ));
144
        }
145
    }
146
147
    /**
148
     * @psalm-pure
149
     * @psalm-assert numeric $value
150
     *
151
     * @param mixed  $value
152
     * @param string $message
153
     *
154
     * @throws InvalidArgumentException
155
     */
156
    public static function numeric($value, $message = '')
157
    {
158
        if (!\is_numeric($value)) {
159
            static::reportInvalidArgument(\sprintf(
160
                $message ?: 'Expected a numeric. Got: %s',
161
                static::typeToString($value)
162
            ));
163
        }
164
    }
165
166
    /**
167
     * @psalm-pure
168
     * @psalm-assert positive-int|0 $value
169
     *
170
     * @param mixed  $value
171
     * @param string $message
172
     *
173
     * @throws InvalidArgumentException
174
     */
175
    public static function natural($value, $message = '')
176
    {
177
        if (!\is_int($value) || $value < 0) {
178
            static::reportInvalidArgument(\sprintf(
179
                $message ?: 'Expected a non-negative integer. Got: %s',
180
                static::valueToString($value)
181
            ));
182
        }
183
    }
184
185
    /**
186
     * @psalm-pure
187
     * @psalm-assert bool $value
188
     *
189
     * @param mixed  $value
190
     * @param string $message
191
     *
192
     * @throws InvalidArgumentException
193
     */
194
    public static function boolean($value, $message = '')
195
    {
196
        if (!\is_bool($value)) {
197
            static::reportInvalidArgument(\sprintf(
198
                $message ?: 'Expected a boolean. Got: %s',
199
                static::typeToString($value)
200
            ));
201
        }
202
    }
203
204
    /**
205
     * @psalm-pure
206
     * @psalm-assert scalar $value
207
     *
208
     * @param mixed  $value
209
     * @param string $message
210
     *
211
     * @throws InvalidArgumentException
212
     */
213
    public static function scalar($value, $message = '')
214
    {
215
        if (!\is_scalar($value)) {
216
            static::reportInvalidArgument(\sprintf(
217
                $message ?: 'Expected a scalar. Got: %s',
218
                static::typeToString($value)
219
            ));
220
        }
221
    }
222
223
    /**
224
     * @psalm-pure
225
     * @psalm-assert object $value
226
     *
227
     * @param mixed  $value
228
     * @param string $message
229
     *
230
     * @throws InvalidArgumentException
231
     */
232
    public static function object($value, $message = '')
233
    {
234
        if (!\is_object($value)) {
235
            static::reportInvalidArgument(\sprintf(
236
                $message ?: 'Expected an object. Got: %s',
237
                static::typeToString($value)
238
            ));
239
        }
240
    }
241
242
    /**
243
     * @psalm-pure
244
     * @psalm-assert resource $value
245
     *
246
     * @param mixed       $value
247
     * @param string|null $type    type of resource this should be. @see https://www.php.net/manual/en/function.get-resource-type.php
248
     * @param string      $message
249
     *
250
     * @throws InvalidArgumentException
251
     */
252
    public static function resource($value, $type = null, $message = '')
253
    {
254
        if (!\is_resource($value)) {
255
            static::reportInvalidArgument(\sprintf(
256
                $message ?: 'Expected a resource. Got: %s',
257
                static::typeToString($value)
258
            ));
259
        }
260
261
        if ($type && $type !== \get_resource_type($value)) {
262
            static::reportInvalidArgument(\sprintf(
263
                $message ?: 'Expected a resource of type %2$s. Got: %s',
264
                static::typeToString($value),
265
                $type
266
            ));
267
        }
268
    }
269
270
    /**
271
     * @psalm-pure
272
     * @psalm-assert callable $value
273
     *
274
     * @param mixed  $value
275
     * @param string $message
276
     *
277
     * @throws InvalidArgumentException
278
     */
279
    public static function isCallable($value, $message = '')
280
    {
281
        if (!\is_callable($value)) {
282
            static::reportInvalidArgument(\sprintf(
283
                $message ?: 'Expected a callable. Got: %s',
284
                static::typeToString($value)
285
            ));
286
        }
287
    }
288
289
    /**
290
     * @psalm-pure
291
     * @psalm-assert array $value
292
     *
293
     * @param mixed  $value
294
     * @param string $message
295
     *
296
     * @throws InvalidArgumentException
297
     */
298
    public static function isArray($value, $message = '')
299
    {
300
        if (!\is_array($value)) {
301
            static::reportInvalidArgument(\sprintf(
302
                $message ?: 'Expected an array. Got: %s',
303
                static::typeToString($value)
304
            ));
305
        }
306
    }
307
308
    /**
309
     * @psalm-pure
310
     * @psalm-assert iterable $value
311
     *
312
     * @deprecated use "isIterable" or "isInstanceOf" instead
313
     *
314
     * @param mixed  $value
315
     * @param string $message
316
     *
317
     * @throws InvalidArgumentException
318
     */
319
    public static function isTraversable($value, $message = '')
320
    {
321
        @\trigger_error(
322
            \sprintf(
323
                '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.',
324
                __METHOD__
325
            ),
326
            \E_USER_DEPRECATED
327
        );
328
329
        if (!\is_array($value) && !($value instanceof Traversable)) {
330
            static::reportInvalidArgument(\sprintf(
331
                $message ?: 'Expected a traversable. Got: %s',
332
                static::typeToString($value)
333
            ));
334
        }
335
    }
336
337
    /**
338
     * @psalm-pure
339
     * @psalm-assert array|ArrayAccess $value
340
     *
341
     * @param mixed  $value
342
     * @param string $message
343
     *
344
     * @throws InvalidArgumentException
345
     */
346
    public static function isArrayAccessible($value, $message = '')
347
    {
348
        if (!\is_array($value) && !($value instanceof ArrayAccess)) {
349
            static::reportInvalidArgument(\sprintf(
350
                $message ?: 'Expected an array accessible. Got: %s',
351
                static::typeToString($value)
352
            ));
353
        }
354
    }
355
356
    /**
357
     * @psalm-pure
358
     * @psalm-assert countable $value
359
     *
360
     * @param mixed  $value
361
     * @param string $message
362
     *
363
     * @throws InvalidArgumentException
364
     */
365
    public static function isCountable($value, $message = '')
366
    {
367
        if (
368
            !\is_array($value)
369
            && !($value instanceof Countable)
370
            && !($value instanceof ResourceBundle)
371
            && !($value instanceof SimpleXMLElement)
372
        ) {
373
            static::reportInvalidArgument(\sprintf(
374
                $message ?: 'Expected a countable. Got: %s',
375
                static::typeToString($value)
376
            ));
377
        }
378
    }
379
380
    /**
381
     * @psalm-pure
382
     * @psalm-assert iterable $value
383
     *
384
     * @param mixed  $value
385
     * @param string $message
386
     *
387
     * @throws InvalidArgumentException
388
     */
389
    public static function isIterable($value, $message = '')
390
    {
391
        if (!\is_array($value) && !($value instanceof Traversable)) {
392
            static::reportInvalidArgument(\sprintf(
393
                $message ?: 'Expected an iterable. Got: %s',
394
                static::typeToString($value)
395
            ));
396
        }
397
    }
398
399
    /**
400
     * @psalm-pure
401
     * @psalm-template ExpectedType of object
402
     * @psalm-param class-string<ExpectedType> $class
403
     * @psalm-assert ExpectedType $value
404
     *
405
     * @param mixed         $value
406
     * @param string|object $class
407
     * @param string        $message
408
     *
409
     * @throws InvalidArgumentException
410
     */
411
    public static function isInstanceOf($value, $class, $message = '')
412
    {
413
        if (!($value instanceof $class)) {
414
            static::reportInvalidArgument(\sprintf(
415
                $message ?: 'Expected an instance of %2$s. Got: %s',
416
                static::typeToString($value),
417
                $class
0 ignored issues
show
Bug introduced by
It seems like $class can also be of type object; however, parameter $values of sprintf() does only seem to accept double|integer|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

417
                /** @scrutinizer ignore-type */ $class
Loading history...
418
            ));
419
        }
420
    }
421
422
    /**
423
     * @psalm-pure
424
     * @psalm-template ExpectedType of object
425
     * @psalm-param class-string<ExpectedType> $class
426
     * @psalm-assert !ExpectedType $value
427
     *
428
     * @param mixed         $value
429
     * @param string|object $class
430
     * @param string        $message
431
     *
432
     * @throws InvalidArgumentException
433
     */
434
    public static function notInstanceOf($value, $class, $message = '')
435
    {
436
        if ($value instanceof $class) {
437
            static::reportInvalidArgument(\sprintf(
438
                $message ?: 'Expected an instance other than %2$s. Got: %s',
439
                static::typeToString($value),
440
                $class
0 ignored issues
show
Bug introduced by
It seems like $class can also be of type object; however, parameter $values of sprintf() does only seem to accept double|integer|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

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

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