Completed
Push — master ( 5a3cd6...c0879b )
by Terry
02:54
created

Assert::int()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Terah\Assert;
4
5
    /**
6
     * Assert
7
     *
8
     * LICENSE
9
     *
10
     * This source file is subject to the new BSD license that is bundled
11
     * with this package in the file LICENSE.txt.
12
     * If you did not receive a copy of the license and are unable to
13
     * obtain it through the world-wide-web, please send an email
14
     * to [email protected] so I can send you a copy immediately.
15
     */
16
17
/**
18
 * Assert library
19
 *
20
 * @author Benjamin Eberlei <[email protected]>
21
 * @author Terry Cullen <[email protected]>
22
 *
23
 */
24
class Assert
25
{
26
    const INVALID_FLOAT = 9;
27
    const INVALID_INTEGER = 10;
28
    const INVALID_DIGIT = 11;
29
    const INVALID_INTEGERISH = 12;
30
    const INVALID_BOOLEAN = 13;
31
    const VALUE_EMPTY = 14;
32
    const VALUE_NULL = 15;
33
    const INVALID_STRING = 16;
34
    const INVALID_REGEX = 17;
35
    const INVALID_MIN_LENGTH = 18;
36
    const INVALID_MAX_LENGTH = 19;
37
    const INVALID_STRING_START = 20;
38
    const INVALID_STRING_CONTAINS = 21;
39
    const INVALID_CHOICE = 22;
40
    const INVALID_NUMERIC = 23;
41
    const INVALID_ARRAY = 24;
42
    const INVALID_KEY_EXISTS = 26;
43
    const INVALID_NOT_BLANK = 27;
44
    const INVALID_INSTANCE_OF = 28;
45
    const INVALID_SUBCLASS_OF = 29;
46
    const INVALID_RANGE = 30;
47
    const INVALID_ALNUM = 31;
48
    const INVALID_TRUE = 32;
49
    const INVALID_EQ = 33;
50
    const INVALID_SAME = 34;
51
    const INVALID_MIN = 35;
52
    const INVALID_MAX = 36;
53
    const INVALID_LENGTH = 37;
54
    const INVALID_FALSE = 38;
55
    const INVALID_STRING_END = 39;
56
    const INVALID_UUID = 40;
57
    const INVALID_COUNT = 41;
58
    const INVALID_NOT_EQ = 42;
59
    const INVALID_NOT_SAME = 43;
60
    const INVALID_TRAVERSABLE = 44;
61
    const INVALID_ARRAY_ACCESSIBLE = 45;
62
    const INVALID_KEY_ISSET = 46;
63
    const INVALID_DIRECTORY = 101;
64
    const INVALID_FILE = 102;
65
    const INVALID_READABLE = 103;
66
    const INVALID_WRITEABLE = 104;
67
    const INVALID_CLASS = 105;
68
    const INVALID_EMAIL = 201;
69
    const INTERFACE_NOT_IMPLEMENTED = 202;
70
    const INVALID_URL = 203;
71
    const INVALID_NOT_INSTANCE_OF = 204;
72
    const VALUE_NOT_EMPTY = 205;
73
    const INVALID_JSON_STRING = 206;
74
    const INVALID_OBJECT = 207;
75
    const INVALID_METHOD = 208;
76
    const INVALID_SCALAR = 209;
77
    const INVALID_DATE = 210;
78
    const INVALID_CALLABLE = 211;
79
    const INVALID_KEYS_EXIST = 300;
80
    const INVALID_PROPERTY_EXISTS = 301;
81
    const INVALID_PROPERTIES_EXIST = 302;
82
    const INVALID_UTF8 = 303;
83
    const INVALID_DOMAIN_NAME = 304;
84
    const INVALID_NOT_FALSE = 305;
85
    const INVALID_FILE_OR_DIR = 306;
86
    const INVALID_ASCII = 307;
87
    const INVALID_NOT_REGEX = 308;
88
    const INVALID_GREATER_THAN = 309;
89
    const INVALID_LESS_THAN = 310;
90
    const INVALID_GREATER_THAN_OR_EQ = 311;
91
    const INVALID_LESS_THAN_OR_EQ = 312;
92
93
    /** @var bool */
94
    protected $nullOr       = false;
95
96
    /** @var bool */
97
    protected $emptyOr     = false;
98
99
    /** @var mixed */
100
    protected $value        = null;
101
102
    /** @var bool */
103
    protected $all          = false;
104
105
    /** @var null|string */
106
    protected $propertyPath = null;
107
    /**
108
     * Exception to throw when an assertion failed.
109
     *
110
     * @var string
111
     */
112
    protected $exceptionClass = 'Terah\Assert\AssertionFailedException';
113
114
    /**
115
     * @param mixed $value
116
     */
117
    public function __construct($value)
118
    {
119
        $this->value($value);
120
    }
121
122
    /**
123
     * @param $value
124
     * @return Assert
125
     */
126
    public static function that($value)
127
    {
128
        return new Assert($value);
129
    }
130
131
    /**
132
     * @param mixed $value
133
     * @return Assert
134
     */
135
    public function reset($value)
136
    {
137
        return $this->all(false)->nullOr(false)->value($value);
138
    }
139
140
    /**
141
     * @param mixed $value
142
     * @return Assert
143
     */
144
    public function value($value)
145
    {
146
        $this->value = $value;
147
        return $this;
148
    }
149
150
    /**
151
     * @param bool $nullOr
152
     * @return Assert
153
     */
154
    public function nullOr($nullOr = true)
155
    {
156
        $this->nullOr = $nullOr;
157
        return $this;
158
    }
159
160
    /**
161
     * @param bool $emptyOr
162
     * @return Assert
163
     */
164
    public function emptyOr($emptyOr = true)
165
    {
166
        $this->emptyOr = $emptyOr;
167
        return $this;
168
    }
169
170
    /**
171
     * @param bool $all
172
     * @return Assert
173
     */
174
    public function all($all = true)
175
    {
176
        $this->all = $all;
177
        return $this;
178
    }
179
180
    /**
181
     * Helper method that handles building the assertion failure exceptions.
182
     * They are returned from this method so that the stack trace still shows
183
     * the assertions method.
184
     *
185
     * @param string $message
186
     * @param int    $code
187
     * @param string $propertyPath
188
     * @param array  $constraints
189
     * @return AssertionFailedException
190
     */
191
    protected function createException($message, $code, $propertyPath, array $constraints = [])
192
    {
193
        $exceptionClass = $this->exceptionClass;
194
        $propertyPath = is_null($propertyPath) ? $this->propertyPath : $propertyPath;
195
        return new $exceptionClass($message, $code, $propertyPath, $this->value, $constraints);
196
    }
197
198
    /**
199
     * @param $exceptionClass
200
     * @return Assert
201
     */
202
    public function setExceptionClass($exceptionClass)
203
    {
204
        $this->exceptionClass = $exceptionClass;
205
        return $this;
206
    }
207
208
    /**
209
     * @param string $name
210
     * @return Assert
211
     */
212
    public function name($name)
213
    {
214
        $this->propertyPath = $name;
215
        return $this;
216
    }
217
218
    /**
219
     * Assert that two values are equal (using == ).
220
     *
221
     * @param mixed       $value2
222
     * @param string|null $message
223
     * @param string|null $propertyPath
224
     * @return Assert
225
     * @throws AssertionFailedException
226
     */
227
    public function eq($value2, $message = null, $propertyPath = null)
228
    {
229
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
230
        {
231
            return $this;
232
        }
233
        if ( $this->value != $value2 )
234
        {
235
            $message = sprintf(
236
                $message ?: 'Value "%s" does not equal expected value "%s".',
237
                $this->stringify($this->value),
238
                $this->stringify($value2)
239
            );
240
            throw $this->createException($message, self::INVALID_EQ, $propertyPath, ['expected' => $value2]);
241
        }
242
        return $this;
243
    }
244
245
    /**
246
     *
247
     * @param mixed       $value2
248
     * @param string|null $message
249
     * @param string|null $propertyPath
250
     * @return Assert
251
     * @throws AssertionFailedException
252
     */
253 View Code Duplication
    public function greaterThan($value2, $message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
254
    {
255
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
256
        {
257
            return $this;
258
        }
259
        if ( ! ( $this->value > $value2 ) )
260
        {
261
            $message = sprintf(
262
                $message ?: 'Value "%s" does not greater then expected value "%s".',
263
                $this->stringify($this->value),
264
                $this->stringify($value2)
265
            );
266
            throw $this->createException($message, self::INVALID_EQ, $propertyPath, ['expected' => $value2]);
267
        }
268
        return $this;
269
    }
270
271
    /**
272
     *
273
     * @param mixed       $value2
274
     * @param string|null $message
275
     * @param string|null $propertyPath
276
     * @return Assert
277
     * @throws AssertionFailedException
278
     */
279 View Code Duplication
    public function greaterThanOrEq($value2, $message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
280
    {
281
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
282
        {
283
            return $this;
284
        }
285
        if ( ! ( $this->value >= $value2 ) )
286
        {
287
            $message = sprintf(
288
                $message ?: 'Value "%s" does not greater than or equal to expected value "%s".',
289
                $this->stringify($this->value),
290
                $this->stringify($value2)
291
            );
292
            throw $this->createException($message, self::INVALID_EQ, $propertyPath, ['expected' => $value2]);
293
        }
294
        return $this;
295
    }
296
297
    /**
298
     *
299
     * @param mixed       $value2
300
     * @param string|null $message
301
     * @param string|null $propertyPath
302
     * @return Assert
303
     * @throws AssertionFailedException
304
     */
305 View Code Duplication
    public function lessThan($value2, $message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
306
    {
307
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
308
        {
309
            return $this;
310
        }
311
        if ( ! ( $this->value < $value2 ) )
312
        {
313
            $message = sprintf(
314
                $message ?: 'Value "%s" does not less then expected value "%s".',
315
                $this->stringify($this->value),
316
                $this->stringify($value2)
317
            );
318
            throw $this->createException($message, self::INVALID_LESS_THAN, $propertyPath, ['expected' => $value2]);
319
        }
320
        return $this;
321
    }
322
323
    /**
324
     *
325
     * @param mixed       $value2
326
     * @param string|null $message
327
     * @param string|null $propertyPath
328
     * @return Assert
329
     * @throws AssertionFailedException
330
     */
331 View Code Duplication
    public function lessThanOrEq($value2, $message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
332
    {
333
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
334
        {
335
            return $this;
336
        }
337
        if ( ! ( $this->value <= $value2 ) )
338
        {
339
            $message = sprintf(
340
                $message ?: 'Value "%s" does not less than or equal to expected value "%s".',
341
                $this->stringify($this->value),
342
                $this->stringify($value2)
343
            );
344
            throw $this->createException($message, self::INVALID_LESS_THAN_OR_EQ, $propertyPath, ['expected' => $value2]);
345
        }
346
        return $this;
347
    }
348
349
    /**
350
     * Assert that two values are the same (using ===).
351
     *
352
     * @param mixed       $value2
353
     * @param string|null $message
354
     * @param string|null $propertyPath
355
     * @return Assert
356
     * @throws AssertionFailedException
357
     */
358
    public function same($value2, $message = null, $propertyPath = null)
359
    {
360
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
361
        {
362
            return $this;
363
        }
364
        if ( $this->value !== $value2 )
365
        {
366
            $message = sprintf(
367
                $message ?: 'Value "%s" is not the same as expected value "%s".',
368
                $this->stringify($this->value),
369
                $this->stringify($value2)
370
            );
371
            throw $this->createException($message, self::INVALID_SAME, $propertyPath, ['expected' => $value2]);
372
        }
373
        return $this;
374
    }
375
376
    /**
377
     * Assert that two values are not equal (using == ).
378
     *
379
     * @param mixed       $value2
380
     * @param string|null $message
381
     * @param string|null $propertyPath
382
     * @return Assert
383
     * @throws AssertionFailedException
384
     */
385
    public function notEq($value2, $message = null, $propertyPath = null)
386
    {
387
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
388
        {
389
            return $this;
390
        }
391
        if ( $this->value == $value2 )
392
        {
393
            $message = sprintf(
394
                $message ?: 'Value "%s" is equal to expected value "%s".',
395
                $this->stringify($this->value),
396
                $this->stringify($value2)
397
            );
398
            throw $this->createException($message, self::INVALID_NOT_EQ, $propertyPath, ['expected' => $value2]);
399
        }
400
        return $this;
401
    }
402
403
    /**
404
     * @param string|null $message
405
     * @param string|null        $propertyPath
406
     *
407
     * @return $this
408
     * @throws AssertionFailedException
409
     */
410
    public function isCallable($message = null, $propertyPath = null)
411
    {
412
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
413
        {
414
            return $this;
415
        }
416
        if ( !is_callable($this->value) )
417
        {
418
            $message = sprintf(
419
                $message ?: 'Value "%s" is not callable.',
420
                $this->stringify($this->value)
421
            );
422
            throw $this->createException($message, self::INVALID_NOT_EQ, $propertyPath);
423
        }
424
        return $this;
425
    }
426
427
    /**
428
     * Assert that two values are not the same (using === ).
429
     *
430
     * @param mixed       $value2
431
     * @param string|null $message
432
     * @param string|null $propertyPath
433
     * @return Assert
434
     * @throws AssertionFailedException
435
     */
436
    public function notSame($value2, $message = null, $propertyPath = null)
437
    {
438
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
439
        {
440
            return $this;
441
        }
442
        if ( $this->value === $value2 )
443
        {
444
            $message = sprintf(
445
                $message ?: 'Value "%s" is the same as expected value "%s".',
446
                $this->stringify($this->value),
447
                $this->stringify($value2)
448
            );
449
            throw $this->createException($message, self::INVALID_NOT_SAME, $propertyPath, ['expected' => $value2]);
450
        }
451
        return $this;
452
    }
453
454
    /**
455
     * @param string|null $message
456
     * @param string|null $propertyPath
457
     * @return Assert
458
     * @throws AssertionFailedException
459
     */
460
    public function id($message = null, $propertyPath = null)
461
    {
462
        $message = $message ?: 'Value "%s" is not an integer id.';
463
        return $this->nonEmptyInt($message, $propertyPath)->range(1, PHP_INT_MAX);
464
    }
465
466
    /**
467
     * @param string|null $message
468
     * @param string|null $propertyPath
469
     * @return Assert
470
     * @throws AssertionFailedException
471
     */
472
    public function flag($message = null, $propertyPath = null)
473
    {
474
        $message = $message ?: 'Value "%s" is not a 0 or 1.';
475
        return $this->range(0, 1, $message, $propertyPath);
476
    }
477
478
    /**
479
     * @param string|null $message
480
     * @param string|null $propertyPath
481
     * @return Assert
482
     * @throws AssertionFailedException
483
     */
484
    public function status($message = null, $propertyPath = null)
485
    {
486
        $message = $message ?: 'Value "%s" is not a valid status.';
487
        return $this->integer($message, $propertyPath)->inArray([-1, 0, 1]);
488
    }
489
490
    /**
491
     * @param string|null $message
492
     * @param string|null $propertyPath
493
     * @return Assert
494
     */
495
    public function nullOrId($message = null, $propertyPath = null)
496
    {
497
        return $this->nullOr()->id($message, $propertyPath);
498
    }
499
500
    /**
501
     * @param string|null $message
502
     * @param string|null $propertyPath
503
     * @return Assert
504
     */
505
    public function allIds($message = null, $propertyPath = null)
506
    {
507
        return $this->all()->id($message, $propertyPath);
508
    }
509
510
    /**
511
     * @param string|null $message
512
     * @param string|null $propertyPath
513
     * @return Assert
514
     * @throws AssertionFailedException
515
     */
516
    public function int($message = null, $propertyPath = null)
517
    {
518
        return $this->integer($message, $propertyPath);
519
    }
520
521
    /**
522
     * Assert that value is a php integer.
523
     *
524
     * @param string|null $message
525
     * @param string|null $propertyPath
526
     * @return Assert
527
     * @throws AssertionFailedException
528
     */
529
    public function integer($message = null, $propertyPath = null)
530
    {
531
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
532
        {
533
            return $this;
534
        }
535
        if ( !is_int($this->value) )
536
        {
537
            $message = sprintf(
538
                $message ?: 'Value "%s" is not an integer.',
539
                $this->stringify($this->value)
540
            );
541
            throw $this->createException($message, self::INVALID_INTEGER, $propertyPath);
542
        }
543
        return $this;
544
    }
545
546
    /**
547
     * Assert that value is a php float.
548
     *
549
     * @param string|null $message
550
     * @param string|null $propertyPath
551
     * @return Assert
552
     * @throws AssertionFailedException
553
     */
554
    public function float($message = null, $propertyPath = null)
555
    {
556
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
557
        {
558
            return $this;
559
        }
560
        if ( !is_float($this->value) )
561
        {
562
            $message = sprintf(
563
                $message ?: 'Value "%s" is not a float.',
564
                $this->stringify($this->value)
565
            );
566
            throw $this->createException($message, self::INVALID_FLOAT, $propertyPath);
567
        }
568
        return $this;
569
    }
570
571
    /**
572
     * Validates if an integer or integerish is a digit.
573
     *
574
     * @param string|null $message
575
     * @param string|null $propertyPath
576
     * @return Assert
577
     * @throws AssertionFailedException
578
     */
579
    public function digit($message = null, $propertyPath = null)
580
    {
581
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
582
        {
583
            return $this;
584
        }
585
        if ( !ctype_digit((string)$this->value) )
586
        {
587
            $message = sprintf(
588
                $message ?: 'Value "%s" is not a digit.',
589
                $this->stringify($this->value)
590
            );
591
            throw $this->createException($message, self::INVALID_DIGIT, $propertyPath);
592
        }
593
        return $this;
594
    }
595
596
    /**
597
     * Validates if an string is a date .
598
     *
599
     * @param string|null $message
600
     * @param string|null $propertyPath
601
     * @return Assert
602
     * @throws AssertionFailedException
603
     */
604
    public function date($message = null, $propertyPath = null)
605
    {
606
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
607
        {
608
            return $this;
609
        }
610
        $this->notEmpty($message, $propertyPath);
611
        if ( strtotime($this->value) === false )
612
        {
613
            $message = sprintf(
614
                $message ?: 'Value "%s" is not a date.',
615
                $this->stringify($this->value)
616
            );
617
            throw $this->createException($message, self::INVALID_DATE, $propertyPath);
618
        }
619
        return $this;
620
    }
621
622
    /**
623
     * Assert that value is a php integer'ish.
624
     *
625
     * @param string|null $message
626
     * @param string|null $propertyPath
627
     * @return Assert
628
     * @throws AssertionFailedException
629
     */
630
    public function integerish($message = null, $propertyPath = null)
631
    {
632
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
633
        {
634
            return $this;
635
        }
636
        if ( is_object($this->value) || strval(intval($this->value)) != $this->value || is_bool($this->value) || is_null($this->value) )
637
        {
638
            $message = sprintf(
639
                $message ?: 'Value "%s" is not an integer or a number castable to integer.',
640
                $this->stringify($this->value)
641
            );
642
            throw $this->createException($message, self::INVALID_INTEGERISH, $propertyPath);
643
        }
644
        return $this;
645
    }
646
647
    /**
648
     * Assert that value is php boolean
649
     *
650
     * @param string|null $message
651
     * @param string|null $propertyPath
652
     * @return Assert
653
     * @throws AssertionFailedException
654
     */
655
    public function boolean($message = null, $propertyPath = null)
656
    {
657
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
658
        {
659
            return $this;
660
        }
661
        if ( !is_bool($this->value) )
662
        {
663
            $message = sprintf(
664
                $message ?: 'Value "%s" is not a boolean.',
665
                $this->stringify($this->value)
666
            );
667
            throw $this->createException($message, self::INVALID_BOOLEAN, $propertyPath);
668
        }
669
        return $this;
670
    }
671
672
    /**
673
     * Assert that value is a PHP scalar
674
     *
675
     * @param string|null $message
676
     * @param string|null $propertyPath
677
     * @return Assert
678
     * @throws AssertionFailedException
679
     */
680
    public function scalar($message = null, $propertyPath = null)
681
    {
682
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
683
        {
684
            return $this;
685
        }
686
        if ( !is_scalar($this->value) )
687
        {
688
            $message = sprintf(
689
                $message ?: 'Value "%s" is not a scalar.',
690
                $this->stringify($this->value)
691
            );
692
            throw $this->createException($message, self::INVALID_SCALAR, $propertyPath);
693
        }
694
        return $this;
695
    }
696
697
    /**
698
     * Assert that value is not empty
699
     *
700
     * @param string|null $message
701
     * @param string|null $propertyPath
702
     * @return Assert
703
     * @throws AssertionFailedException
704
     */
705 View Code Duplication
    public function notEmpty($message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
706
    {
707
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
708
        {
709
            return $this;
710
        }
711
        if ( ( is_object($this->value) && empty((array)$this->value) ) || empty($this->value) )
712
        {
713
            $message = sprintf(
714
                $message ?: 'Value "%s" is empty, but non empty value was expected.',
715
                $this->stringify($this->value)
716
            );
717
            throw $this->createException($message, self::VALUE_EMPTY, $propertyPath);
718
        }
719
        return $this;
720
    }
721
722
    /**
723
     * Assert that value is empty
724
     *
725
     * @param string|null $message
726
     * @param string|null $propertyPath
727
     * @return Assert
728
     * @throws AssertionFailedException
729
     */
730
    public function noContent($message = null, $propertyPath = null)
731
    {
732
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
733
        {
734
            return $this;
735
        }
736
        if ( !empty( $this->value ) )
737
        {
738
            $message = sprintf(
739
                $message ?: 'Value "%s" is not empty, but empty value was expected.',
740
                $this->stringify($this->value)
741
            );
742
            throw $this->createException($message, self::VALUE_NOT_EMPTY, $propertyPath);
743
        }
744
        return $this;
745
    }
746
747
    /**
748
     * Assert that value is not null
749
     *
750
     * @param string|null $message
751
     * @param string|null $propertyPath
752
     * @return Assert
753
     * @throws AssertionFailedException
754
     */
755
    public function notNull($message = null, $propertyPath = null)
756
    {
757
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
758
        {
759
            return $this;
760
        }
761
        if ( $this->value === null )
762
        {
763
            $message = sprintf(
764
                $message ?: 'Value "%s" is null, but non null value was expected.',
765
                $this->stringify($this->value)
766
            );
767
            throw $this->createException($message, self::VALUE_NULL, $propertyPath);
768
        }
769
        return $this;
770
    }
771
772
    /**
773
     * Assert that value is a string
774
     *
775
     * @param string|null $message
776
     * @param string|null $propertyPath
777
     * @return Assert
778
     * @throws AssertionFailedException
779
     */
780
    public function string($message = null, $propertyPath = null)
781
    {
782
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
783
        {
784
            return $this;
785
        }
786
        if ( !is_string($this->value) )
787
        {
788
            $message = sprintf(
789
                $message ?: 'Value "%s" expected to be string, type %s given.',
790
                $this->stringify($this->value),
791
                gettype($this->value)
792
            );
793
            throw $this->createException($message, self::INVALID_STRING, $propertyPath);
794
        }
795
        return $this;
796
    }
797
798
    /**
799
     * Assert that value matches a regex
800
     *
801
     * @param string      $pattern
802
     * @param string|null $message
803
     * @param string|null $propertyPath
804
     * @return Assert
805
     * @throws AssertionFailedException
806
     */
807
    public function regex($pattern, $message = null, $propertyPath = null)
808
    {
809
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
810
        {
811
            return $this;
812
        }
813
        $this->string($message, $propertyPath);
814
        if ( !preg_match($pattern, $this->value) )
815
        {
816
            $message = sprintf(
817
                $message ?: 'Value "%s" does not match expression.',
818
                $this->stringify($this->value)
819
            );
820
            throw $this->createException($message, self::INVALID_REGEX, $propertyPath, ['pattern' => $pattern]);
821
        }
822
        return $this;
823
    }
824
825
    /**
826
     * @param string $pattern
827
     * @param string|null $message
828
     * @param string|null $propertyPath
829
     * @return $this
830
     * @throws AssertionFailedException
831
     */
832
    public function notRegex($pattern, $message = null, $propertyPath = null)
833
    {
834
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
835
        {
836
            return $this;
837
        }
838
        $this->string($message, $propertyPath);
839
        if ( preg_match($pattern, $this->value) )
840
        {
841
            $message = sprintf(
842
                $message ?: 'Value "%s" does not match expression.',
843
                $this->stringify($this->value)
844
            );
845
            throw $this->createException($message, self::INVALID_REGEX, $propertyPath, ['pattern' => $pattern]);
846
        }
847
        return $this;
848
    }
849
850
    /**
851
     * Assert that string has a given length.
852
     *
853
     * @param int         $length
854
     * @param string|null $message
855
     * @param string|null $propertyPath
856
     * @param string      $encoding
857
     * @return Assert
858
     * @throws AssertionFailedException
859
     */
860
    public function length($length, $message = null, $propertyPath = null, $encoding = 'utf8')
861
    {
862
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
863
        {
864
            return $this;
865
        }
866
        $this->string($message, $propertyPath);
867
        if ( mb_strlen($this->value, $encoding) !== $length )
868
        {
869
            $message     = sprintf(
870
                $message ?: 'Value "%s" has to be %d exactly characters long, but length is %d.',
871
                $this->stringify($this->value),
872
                $length,
873
                mb_strlen($this->value, $encoding)
874
            );
875
            $constraints = ['length' => $length, 'encoding' => $encoding];
876
            throw $this->createException($message, self::INVALID_LENGTH, $propertyPath, $constraints);
877
        }
878
        return $this;
879
    }
880
881
    /**
882
     * Assert that a string is at least $minLength chars long.
883
     *
884
     * @param int         $minLength
885
     * @param string|null $message
886
     * @param string|null $propertyPath
887
     * @param string      $encoding
888
     * @return Assert
889
     * @throws AssertionFailedException
890
     */
891
    public function minLength($minLength, $message = null, $propertyPath = null, $encoding = 'utf8')
892
    {
893
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
894
        {
895
            return $this;
896
        }
897
        $this->string($message, $propertyPath);
898
        if ( mb_strlen($this->value, $encoding) < $minLength )
899
        {
900
            $message     = sprintf(
901
                $message
902
                    ?: 'Value "%s" is too short, it should have more than %d characters, but only has %d characters.',
903
                $this->stringify($this->value),
904
                $minLength,
905
                mb_strlen($this->value, $encoding)
906
            );
907
            $constraints = ['min_length' => $minLength, 'encoding' => $encoding];
908
            throw $this->createException($message, self::INVALID_MIN_LENGTH, $propertyPath, $constraints);
909
        }
910
        return $this;
911
    }
912
913
    /**
914
     * Assert that string value is not longer than $maxLength chars.
915
     *
916
     * @param integer     $maxLength
917
     * @param string|null $message
918
     * @param string|null $propertyPath
919
     * @param string      $encoding
920
     * @return Assert
921
     * @throws AssertionFailedException
922
     */
923
    public function maxLength($maxLength, $message = null, $propertyPath = null, $encoding = 'utf8')
924
    {
925
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
926
        {
927
            return $this;
928
        }
929
        $this->string($message, $propertyPath);
930
        if ( mb_strlen($this->value, $encoding) > $maxLength )
931
        {
932
            $message     = sprintf(
933
                $message ?: 'Value "%s" is too long, it should have no more than %d characters, but has %d characters.',
934
                $this->stringify($this->value),
935
                $maxLength,
936
                mb_strlen($this->value, $encoding)
937
            );
938
            $constraints = ['max_length' => $maxLength, 'encoding' => $encoding];
939
            throw $this->createException($message, self::INVALID_MAX_LENGTH, $propertyPath, $constraints);
940
        }
941
        return $this;
942
    }
943
944
    /**
945
     * Assert that string length is between min,max lengths.
946
     *
947
     * @param integer     $minLength
948
     * @param integer     $maxLength
949
     * @param string|null $message
950
     * @param string|null $propertyPath
951
     * @param string      $encoding
952
     * @return Assert
953
     * @throws AssertionFailedException
954
     */
955
    public function betweenLength($minLength, $maxLength, $message = null, $propertyPath = null, $encoding = 'utf8')
956
    {
957
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
958
        {
959
            return $this;
960
        }
961
        $this->string($message, $propertyPath);
962
        if ( mb_strlen($this->value, $encoding) < $minLength )
963
        {
964
            $message     = sprintf(
965
                $message
966
                    ?: 'Value "%s" is too short, it should have more than %d characters, but only has %d characters.',
967
                $this->stringify($this->value),
968
                $minLength,
969
                mb_strlen($this->value, $encoding)
970
            );
971
            $constraints = ['min_length' => $minLength, 'encoding' => $encoding];
972
            throw $this->createException($message, self::INVALID_MIN_LENGTH, $propertyPath, $constraints);
973
        }
974
        if ( mb_strlen($this->value, $encoding) > $maxLength )
975
        {
976
            $message     = sprintf(
977
                $message ?: 'Value "%s" is too long, it should have no more than %d characters, but has %d characters.',
978
                $this->stringify($this->value),
979
                $maxLength,
980
                mb_strlen($this->value, $encoding)
981
            );
982
            $constraints = ['max_length' => $maxLength, 'encoding' => $encoding];
983
            throw $this->createException($message, self::INVALID_MAX_LENGTH, $propertyPath, $constraints);
984
        }
985
        return $this;
986
    }
987
988
    /**
989
     * Assert that string starts with a sequence of chars.
990
     *
991
     * @param string      $needle
992
     * @param string|null $message
993
     * @param string|null $propertyPath
994
     * @param string      $encoding
995
     * @return Assert
996
     * @throws AssertionFailedException
997
     */
998 View Code Duplication
    public function startsWith($needle, $message = null, $propertyPath = null, $encoding = 'utf8')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
999
    {
1000
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1001
        {
1002
            return $this;
1003
        }
1004
        $this->string($message, $propertyPath);
1005
        if ( mb_strpos($this->value, $needle, null, $encoding) !== 0 )
1006
        {
1007
            $message     = sprintf(
1008
                $message ?: 'Value "%s" does not start with "%s".',
1009
                $this->stringify($this->value),
1010
                $this->stringify($needle)
1011
            );
1012
            $constraints = ['needle' => $needle, 'encoding' => $encoding];
1013
            throw $this->createException($message, self::INVALID_STRING_START, $propertyPath, $constraints);
1014
        }
1015
        return $this;
1016
    }
1017
1018
    /**
1019
     * Assert that string ends with a sequence of chars.
1020
     *
1021
     * @param string      $needle
1022
     * @param string|null $message
1023
     * @param string|null $propertyPath
1024
     * @param string      $encoding
1025
     * @return Assert
1026
     * @throws AssertionFailedException
1027
     */
1028
    public function endsWith($needle, $message = null, $propertyPath = null, $encoding = 'utf8')
1029
    {
1030
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1031
        {
1032
            return $this;
1033
        }
1034
        $this->string($message, $propertyPath);
1035
        $stringPosition = mb_strlen($this->value, $encoding) - mb_strlen($needle, $encoding);
1036
        if ( mb_strripos($this->value, $needle, null, $encoding) !== $stringPosition )
1037
        {
1038
            $message     = sprintf(
1039
                $message ?: 'Value "%s" does not end with "%s".',
1040
                $this->stringify($this->value),
1041
                $this->stringify($needle)
1042
            );
1043
            $constraints = ['needle' => $needle, 'encoding' => $encoding];
1044
            throw $this->createException($message, self::INVALID_STRING_END, $propertyPath, $constraints);
1045
        }
1046
        return $this;
1047
    }
1048
1049
    /**
1050
     * Assert that string contains a sequence of chars.
1051
     *
1052
     * @param string      $needle
1053
     * @param string|null $message
1054
     * @param string|null $propertyPath
1055
     * @param string      $encoding
1056
     * @return Assert
1057
     * @throws AssertionFailedException
1058
     */
1059 View Code Duplication
    public function contains($needle, $message = null, $propertyPath = null, $encoding = 'utf8')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1060
    {
1061
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1062
        {
1063
            return $this;
1064
        }
1065
        $this->string($message, $propertyPath);
1066
        if ( mb_strpos($this->value, $needle, null, $encoding) === false )
1067
        {
1068
            $message     = sprintf(
1069
                $message ?: 'Value "%s" does not contain "%s".',
1070
                $this->stringify($this->value),
1071
                $this->stringify($needle)
1072
            );
1073
            $constraints = ['needle' => $needle, 'encoding' => $encoding];
1074
            throw $this->createException($message, self::INVALID_STRING_CONTAINS, $propertyPath, $constraints);
1075
        }
1076
        return $this;
1077
    }
1078
1079
    /**
1080
     * Assert that value is in array of choices.
1081
     *
1082
     * @param array       $choices
1083
     * @param string|null $message
1084
     * @param string|null $propertyPath
1085
     * @return Assert
1086
     * @throws AssertionFailedException
1087
     */
1088
    public function choice(array $choices, $message = null, $propertyPath = null)
1089
    {
1090
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1091
        {
1092
            return $this;
1093
        }
1094
        if ( !in_array($this->value, $choices, true) )
1095
        {
1096
            $message = sprintf(
1097
                $message ?: 'Value "%s" is not an element of the valid values: %s',
1098
                $this->stringify($this->value),
1099
                implode(", ", array_map('Terah\Assert\Assert::stringify', $choices))
1100
            );
1101
            throw $this->createException($message, self::INVALID_CHOICE, $propertyPath, ['choices' => $choices]);
1102
        }
1103
        return $this;
1104
    }
1105
1106
    /**
1107
     * Alias of {@see choice()}
1108
     *
1109
     * @throws AssertionFailedException
1110
     *
1111
     * @param array $choices
1112
     * @param string|null $message
1113
     * @param string|null $propertyPath
1114
     * @return $this
1115
     */
1116
    public function inArray(array $choices, $message = null, $propertyPath = null)
1117
    {
1118
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1119
        {
1120
            return $this;
1121
        }
1122
        $this->choice($choices, $message, $propertyPath);
1123
        return $this;
1124
    }
1125
1126
    /**
1127
     * Assert that value is numeric.
1128
     *
1129
     * @param string|null $message
1130
     * @param string|null $propertyPath
1131
     * @return Assert
1132
     * @throws AssertionFailedException
1133
     */
1134
    public function numeric($message = null, $propertyPath = null)
1135
    {
1136
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1137
        {
1138
            return $this;
1139
        }
1140
        if ( ! is_numeric($this->value) )
1141
        {
1142
            $message = sprintf(
1143
                $message ?: 'Value "%s" is not numeric.',
1144
                $this->stringify($this->value)
1145
            );
1146
            throw $this->createException($message, self::INVALID_NUMERIC, $propertyPath);
1147
        }
1148
        return $this;
1149
    }
1150
1151
    /**
1152
     * @param string|null $message
1153
     * @param string|null $propertyPath
1154
     * @return Assert
1155
     * @throws AssertionFailedException
1156
     */
1157
    public function nonEmptyArray($message = null, $propertyPath = null)
1158
    {
1159
        $message = $message ?: 'Value "%s" is not a non-empty array.';
1160
        return $this->isArray($message, $propertyPath)->notEmpty($message, $propertyPath);
1161
    }
1162
1163
    /**
1164
     * @param string|null $message
1165
     * @param string|null $propertyPath
1166
     * @return Assert
1167
     * @throws AssertionFailedException
1168
     */
1169
    public function nonEmptyInt($message = null, $propertyPath = null)
1170
    {
1171
        $message = $message ?: 'Value "%s" is not a non-empty integer.';
1172
        return $this->integer($message, $propertyPath)->notEmpty($message, $propertyPath);
1173
    }
1174
1175
    /**
1176
     * @param string|null $message
1177
     * @param string|null $propertyPath
1178
     * @return Assert
1179
     * @throws AssertionFailedException
1180
     */
1181
    public function nonEmptyString($message = null, $propertyPath = null)
1182
    {
1183
        $message = $message ?: 'Value "%s" is not a non-empty string.';
1184
        return $this->integer($message, $propertyPath)->notEmpty($message, $propertyPath);
1185
    }
1186
1187
    /**
1188
     * Assert that value is an array.
1189
     *
1190
     * @param string|null $message
1191
     * @param string|null $propertyPath
1192
     * @return Assert
1193
     * @throws AssertionFailedException
1194
     */
1195
    public function isArray($message = null, $propertyPath = null)
1196
    {
1197
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1198
        {
1199
            return $this;
1200
        }
1201
        if ( !is_array($this->value) )
1202
        {
1203
            $message = sprintf(
1204
                $message ?: 'Value "%s" is not an array.',
1205
                $this->stringify($this->value)
1206
            );
1207
            throw $this->createException($message, self::INVALID_ARRAY, $propertyPath);
1208
        }
1209
        return $this;
1210
    }
1211
1212
    /**
1213
     * Assert that value is an array or a traversable object.
1214
     *
1215
     * @param string|null $message
1216
     * @param string|null $propertyPath
1217
     * @return Assert
1218
     * @throws AssertionFailedException
1219
     */
1220
    public function isTraversable($message = null, $propertyPath = null)
1221
    {
1222
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1223
        {
1224
            return $this;
1225
        }
1226
        if ( !is_array($this->value) && !$this->value instanceof \Traversable )
1227
        {
1228
            $message = sprintf(
1229
                $message ?: 'Value "%s" is not an array and does not implement Traversable.',
1230
                $this->stringify($this->value)
1231
            );
1232
            throw $this->createException($message, self::INVALID_TRAVERSABLE, $propertyPath);
1233
        }
1234
        return $this;
1235
    }
1236
1237
    /**
1238
     * Assert that value is an array or an array-accessible object.
1239
     *
1240
     * @param string|null $message
1241
     * @param string|null $propertyPath
1242
     * @return Assert
1243
     * @throws AssertionFailedException
1244
     */
1245
    public function isArrayAccessible($message = null, $propertyPath = null)
1246
    {
1247
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1248
        {
1249
            return $this;
1250
        }
1251
        if ( !is_array($this->value) && !$this->value instanceof \ArrayAccess )
1252
        {
1253
            $message = sprintf(
1254
                $message ?: 'Value "%s" is not an array and does not implement ArrayAccess.',
1255
                $this->stringify($this->value)
1256
            );
1257
            throw $this->createException($message, self::INVALID_ARRAY_ACCESSIBLE, $propertyPath);
1258
        }
1259
        return $this;
1260
    }
1261
1262
    /**
1263
     * Assert that key exists in an array
1264
     *
1265
     * @param string|integer $key
1266
     * @param string|null    $message
1267
     * @param string|null    $propertyPath
1268
     * @return Assert
1269
     * @throws AssertionFailedException
1270
     */
1271
    public function keyExists($key, $message = null, $propertyPath = null)
1272
    {
1273
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1274
        {
1275
            return $this;
1276
        }
1277
        $this->isArray($message, $propertyPath);
1278
        if ( !array_key_exists($key, $this->value) )
1279
        {
1280
            $message = sprintf(
1281
                $message ?: 'Array does not contain an element with key "%s"',
1282
                $this->stringify($key)
1283
            );
1284
            throw $this->createException($message, self::INVALID_KEY_EXISTS, $propertyPath, ['key' => $key]);
1285
        }
1286
        return $this;
1287
    }
1288
1289
    /**
1290
     * Assert that keys exist in array
1291
     *
1292
     * @param array       $keys
1293
     * @param string|null $message
1294
     * @param string|null $propertyPath
1295
     * @return Assert
1296
     * @throws AssertionFailedException
1297
     */
1298 View Code Duplication
    public function keysExist($keys, $message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1299
    {
1300
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1301
        {
1302
            return $this;
1303
        }
1304
        $this->isArray($message, $propertyPath);
1305
        foreach ( $keys as $key )
1306
        {
1307
            if ( !array_key_exists($key, $this->value) )
1308
            {
1309
                $message = $message
1310
                    ?: sprintf(
1311
                        'Array does not contain an element with key "%s"',
1312
                        $this->stringify($key)
1313
                    );
1314
                throw $this->createException($message, self::INVALID_KEYS_EXIST, $propertyPath, ['key' => $key]);
1315
            }
1316
        }
1317
        return $this;
1318
    }
1319
1320
    /**
1321
     * Assert that property exists in array
1322
     *
1323
     * @param string|integer $key
1324
     * @param string|null    $message
1325
     * @param string|null    $propertyPath
1326
     * @return Assert
1327
     * @throws AssertionFailedException
1328
     */
1329 View Code Duplication
    public function propertyExists($key, $message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1330
    {
1331
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1332
        {
1333
            return $this;
1334
        }
1335
        $this->isObject($message, $propertyPath);
1336
        if ( !property_exists($this->value, $key) && !isset( $this->value->{$key} ) )
1337
        {
1338
            $message = $message
1339
                ?: sprintf(
1340
                    'Object does not contain a property with key "%s"',
1341
                    $this->stringify($key)
1342
                );
1343
            throw $this->createException($message, self::INVALID_PROPERTY_EXISTS, $propertyPath, ['key' => $key]);
1344
        }
1345
        return $this;
1346
    }
1347
1348
    /**
1349
     * Assert that properties exists in array
1350
     *
1351
     * @param array       $keys
1352
     * @param string|null $message
1353
     * @param string|null $propertyPath
1354
     * @return Assert
1355
     * @throws AssertionFailedException
1356
     */
1357 View Code Duplication
    public function propertiesExist(array $keys, $message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1358
    {
1359
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1360
        {
1361
            return $this;
1362
        }
1363
        $this->isObject($message, $propertyPath);
1364
        foreach ( $keys as $key )
1365
        {
1366
            // Using isset to allow resolution of magically defined properties
1367
            if ( !property_exists($this->value, $key) && !isset( $this->value->{$key} ) )
1368
            {
1369
                $message = $message
1370
                    ?: sprintf(
1371
                        'Object does not contain a property with key "%s"',
1372
                        $this->stringify($key)
1373
                    );
1374
                throw $this->createException($message, self::INVALID_PROPERTIES_EXIST, $propertyPath, ['key' => $key]);
1375
            }
1376
        }
1377
        return $this;
1378
    }
1379
1380
    /**
1381
     * Assert that string is valid utf8
1382
     *
1383
     * @param string|null $message
1384
     * @param string|null $propertyPath
1385
     * @return Assert
1386
     * @throws AssertionFailedException
1387
     */
1388
    public function utf8($message = null, $propertyPath = null)
1389
    {
1390
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1391
        {
1392
            return $this;
1393
        }
1394
        $this->string($message, $propertyPath);
1395
        if ( mb_detect_encoding($this->value, 'UTF-8', true) !== 'UTF-8' )
1396
        {
1397
            $message = $message
1398
                ?: sprintf(
1399
                    'Value "%s" was expected to be a valid UTF8 string',
1400
                    $this->stringify($this->value)
1401
                );
1402
            throw $this->createException($message, self::INVALID_UTF8, $propertyPath);
1403
        }
1404
        return $this;
1405
    }
1406
1407
1408
    /**
1409
     * Assert that string is valid utf8
1410
     *
1411
     * @param string|null $message
1412
     * @param string|null $propertyPath
1413
     * @return Assert
1414
     * @throws AssertionFailedException
1415
     */
1416 View Code Duplication
    public function ascii($message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1417
    {
1418
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1419
        {
1420
            return $this;
1421
        }
1422
        $this->string($message, $propertyPath);
1423
        if ( ! preg_match('/^[ -~]+$/', $this->value) )
1424
        {
1425
            $message = $message
1426
                ?: sprintf(
1427
                    'Value "%s" was expected to be a valid ASCII string',
1428
                    $this->stringify($this->value)
1429
                );
1430
            throw $this->createException($message, self::INVALID_ASCII, $propertyPath);
1431
        }
1432
        return $this;
1433
    }
1434
1435
    /**
1436
     * Assert that key exists in an array/array-accessible object using isset()
1437
     *
1438
     * @param string|integer $key
1439
     * @param string|null    $message
1440
     * @param string|null    $propertyPath
1441
     * @return Assert
1442
     * @throws AssertionFailedException
1443
     */
1444
    public function keyIsset($key, $message = null, $propertyPath = null)
1445
    {
1446
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1447
        {
1448
            return $this;
1449
        }
1450
        $this->isArrayAccessible($message, $propertyPath);
1451
        if ( !isset( $this->value[$key] ) )
1452
        {
1453
            $message = sprintf(
1454
                $message ?: 'The element with key "%s" was not found',
1455
                $this->stringify($key)
1456
            );
1457
            throw $this->createException($message, self::INVALID_KEY_ISSET, $propertyPath, ['key' => $key]);
1458
        }
1459
        return $this;
1460
    }
1461
1462
    /**
1463
     * Assert that key exists in an array/array-accessible object and it's value is not empty.
1464
     *
1465
     * @param string|integer $key
1466
     * @param string|null    $message
1467
     * @param string|null    $propertyPath
1468
     * @return Assert
1469
     * @throws AssertionFailedException
1470
     */
1471
    public function notEmptyKey($key, $message = null, $propertyPath = null)
1472
    {
1473
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1474
        {
1475
            return $this;
1476
        }
1477
        $this->keyIsset($key, $message, $propertyPath);
1478
        (new Assert($this->value[$key]))->setExceptionClass($this->exceptionClass)->notEmpty($message, $propertyPath);
1479
        return $this;
1480
    }
1481
1482
    /**
1483
     * Assert that value is not blank
1484
     *
1485
     * @param string|null $message
1486
     * @param string|null $propertyPath
1487
     * @return Assert
1488
     * @throws AssertionFailedException
1489
     */
1490 View Code Duplication
    public function notBlank($message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1491
    {
1492
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1493
        {
1494
            return $this;
1495
        }
1496
        if ( false === $this->value || ( empty( $this->value ) && '0' != $this->value ) )
1497
        {
1498
            $message = sprintf(
1499
                $message ?: 'Value "%s" is blank, but was expected to contain a value.',
1500
                $this->stringify($this->value)
1501
            );
1502
            throw $this->createException($message, self::INVALID_NOT_BLANK, $propertyPath);
1503
        }
1504
        return $this;
1505
    }
1506
1507
    /**
1508
     * Assert that value is instance of given class-name.
1509
     *
1510
     * @param string      $className
1511
     * @param string|null $message
1512
     * @param string|null $propertyPath
1513
     * @return Assert
1514
     * @throws AssertionFailedException
1515
     */
1516
    public function isInstanceOf($className, $message = null, $propertyPath = null)
1517
    {
1518
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1519
        {
1520
            return $this;
1521
        }
1522
        if ( !( $this->value instanceof $className ) )
1523
        {
1524
            $message = sprintf(
1525
                $message ?: 'Class "%s" was expected to be instanceof of "%s" but is not.',
1526
                $this->stringify($this->value),
1527
                $className
1528
            );
1529
            throw $this->createException($message, self::INVALID_INSTANCE_OF, $propertyPath, ['class' => $className]);
1530
        }
1531
        return $this;
1532
    }
1533
1534
    /**
1535
     * Assert that value is not instance of given class-name.
1536
     *
1537
     * @param string      $className
1538
     * @param string|null $message
1539
     * @param string|null $propertyPath
1540
     * @return Assert
1541
     * @throws AssertionFailedException
1542
     */
1543
    public function notIsInstanceOf($className, $message = null, $propertyPath = null)
1544
    {
1545
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1546
        {
1547
            return $this;
1548
        }
1549
        if ( $this->value instanceof $className )
1550
        {
1551
            $message = sprintf(
1552
                $message ?: 'Class "%s" was not expected to be instanceof of "%s".',
1553
                $this->stringify($this->value),
1554
                $className
1555
            );
1556
            throw $this->createException($message, self::INVALID_NOT_INSTANCE_OF, $propertyPath, ['class' => $className]);
1557
        }
1558
        return $this;
1559
    }
1560
1561
    /**
1562
     * Assert that value is subclass of given class-name.
1563
     *
1564
     * @param string      $className
1565
     * @param string|null $message
1566
     * @param string|null $propertyPath
1567
     * @return Assert
1568
     * @throws AssertionFailedException
1569
     */
1570
    public function subclassOf($className, $message = null, $propertyPath = null)
1571
    {
1572
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1573
        {
1574
            return $this;
1575
        }
1576
        if ( !is_subclass_of($this->value, $className) )
1577
        {
1578
            $message = sprintf(
1579
                $message ?: 'Class "%s" was expected to be subclass of "%s".',
1580
                $this->stringify($this->value),
1581
                $className
1582
            );
1583
            throw $this->createException($message, self::INVALID_SUBCLASS_OF, $propertyPath, ['class' => $className]);
1584
        }
1585
        return $this;
1586
    }
1587
1588
    /**
1589
     * Assert that value is in range of numbers.
1590
     *
1591
     * @param integer     $minValue
1592
     * @param integer     $maxValue
1593
     * @param string|null $message
1594
     * @param string|null $propertyPath
1595
     * @return Assert
1596
     * @throws AssertionFailedException
1597
     */
1598
    public function range($minValue, $maxValue, $message = null, $propertyPath = null)
1599
    {
1600
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1601
        {
1602
            return $this;
1603
        }
1604
        $this->numeric($message, $propertyPath);
1605
        if ( $this->value < $minValue || $this->value > $maxValue )
1606
        {
1607
            $message = sprintf(
1608
                $message ?: 'Number "%s" was expected to be at least "%d" and at most "%d".',
1609
                $this->stringify($this->value),
1610
                $this->stringify($minValue),
1611
                $this->stringify($maxValue)
1612
            );
1613
            throw $this->createException($message, self::INVALID_RANGE, $propertyPath, [
1614
                'min' => $minValue,
1615
                'max' => $maxValue
1616
            ]);
1617
        }
1618
        return $this;
1619
    }
1620
1621
    /**
1622
     * Assert that a value is at least as big as a given limit
1623
     *
1624
     * @param mixed       $minValue
1625
     * @param string|null $message
1626
     * @param string|null $propertyPath
1627
     * @return Assert
1628
     * @throws AssertionFailedException
1629
     */
1630
    public function min($minValue, $message = null, $propertyPath = null)
1631
    {
1632
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1633
        {
1634
            return $this;
1635
        }
1636
        $this->numeric($message, $propertyPath);
1637
        if ( $this->value < $minValue )
1638
        {
1639
            $message = sprintf(
1640
                $message ?: 'Number "%s" was expected to be at least "%d".',
1641
                $this->stringify($this->value),
1642
                $this->stringify($minValue)
1643
            );
1644
            throw $this->createException($message, self::INVALID_MIN, $propertyPath, ['min' => $minValue]);
1645
        }
1646
        return $this;
1647
    }
1648
1649
    /**
1650
     * Assert that a number is smaller as a given limit
1651
     *
1652
     * @param mixed       $maxValue
1653
     * @param string|null $message
1654
     * @param string|null $propertyPath
1655
     * @return Assert
1656
     * @throws AssertionFailedException
1657
     */
1658
    public function max($maxValue, $message = null, $propertyPath = null)
1659
    {
1660
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1661
        {
1662
            return $this;
1663
        }
1664
        $this->numeric($message, $propertyPath);
1665
        if ( $this->value > $maxValue )
1666
        {
1667
            $message = sprintf(
1668
                $message ?: 'Number "%s" was expected to be at most "%d".',
1669
                $this->stringify($this->value),
1670
                $this->stringify($maxValue)
1671
            );
1672
            throw $this->createException($message, self::INVALID_MAX, $propertyPath, ['max' => $maxValue]);
1673
        }
1674
        return $this;
1675
    }
1676
1677
    /**
1678
     * Assert that a file exists
1679
     *
1680
     * @param string|null $message
1681
     * @param string|null $propertyPath
1682
     * @return Assert
1683
     * @throws AssertionFailedException
1684
     */
1685
    public function file($message = null, $propertyPath = null)
1686
    {
1687
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1688
        {
1689
            return $this;
1690
        }
1691
        $this->string($message, $propertyPath);
1692
        $this->notEmpty($message, $propertyPath);
1693
        if ( !is_file($this->value) )
1694
        {
1695
            $message = sprintf(
1696
                $message ?: 'File "%s" was expected to exist.',
1697
                $this->stringify($this->value)
1698
            );
1699
            throw $this->createException($message, self::INVALID_FILE, $propertyPath);
1700
        }
1701
        return $this;
1702
    }
1703
1704
    /**
1705
     * @param string|null $message
1706
     * @param string|null $propertyPath
1707
     * @return $this
1708
     * @throws AssertionFailedException
1709
     */
1710
    public function fileExists($message = null, $propertyPath = null)
1711
    {
1712
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1713
        {
1714
            return $this;
1715
        }
1716
        $this->string($message, $propertyPath);
1717
        $this->notEmpty($message, $propertyPath);
1718
        if ( ! file_exists($this->value) )
1719
        {
1720
            $message = sprintf(
1721
                $message ?: 'File or directory "%s" was expected to exist.',
1722
                $this->stringify($this->value)
1723
            );
1724
            throw $this->createException($message, self::INVALID_FILE_OR_DIR, $propertyPath);
1725
        }
1726
        return $this;
1727
    }
1728
1729
    /**
1730
     * Assert that a directory exists
1731
     *
1732
     * @param string|null $message
1733
     * @param string|null $propertyPath
1734
     * @return Assert
1735
     * @throws AssertionFailedException
1736
     */
1737 View Code Duplication
    public function directory($message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1738
    {
1739
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1740
        {
1741
            return $this;
1742
        }
1743
        $this->string($message, $propertyPath);
1744
        if ( !is_dir($this->value) )
1745
        {
1746
            $message = sprintf(
1747
                $message ?: 'Path "%s" was expected to be a directory.',
1748
                $this->stringify($this->value)
1749
            );
1750
            throw $this->createException($message, self::INVALID_DIRECTORY, $propertyPath);
1751
        }
1752
        return $this;
1753
    }
1754
1755
    /**
1756
     * Assert that the value is something readable
1757
     *
1758
     * @param string|null $message
1759
     * @param string|null $propertyPath
1760
     * @return Assert
1761
     * @throws AssertionFailedException
1762
     */
1763 View Code Duplication
    public function readable($message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1764
    {
1765
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1766
        {
1767
            return $this;
1768
        }
1769
        $this->string($message, $propertyPath);
1770
        if ( !is_readable($this->value) )
1771
        {
1772
            $message = sprintf(
1773
                $message ?: 'Path "%s" was expected to be readable.',
1774
                $this->stringify($this->value)
1775
            );
1776
            throw $this->createException($message, self::INVALID_READABLE, $propertyPath);
1777
        }
1778
        return $this;
1779
    }
1780
1781
    /**
1782
     * Assert that the value is something writeable
1783
     *
1784
     * @param string|null $message
1785
     * @param string|null $propertyPath
1786
     * @return Assert
1787
     * @throws AssertionFailedException
1788
     */
1789 View Code Duplication
    public function writeable($message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1790
    {
1791
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1792
        {
1793
            return $this;
1794
        }
1795
        $this->string($message, $propertyPath);
1796
        if ( !is_writeable($this->value) )
1797
        {
1798
            $message = sprintf(
1799
                $message ?: 'Path "%s" was expected to be writeable.',
1800
                $this->stringify($this->value)
1801
            );
1802
            throw $this->createException($message, self::INVALID_WRITEABLE, $propertyPath);
1803
        }
1804
        return $this;
1805
    }
1806
1807
    /**
1808
     * Assert that value is an email adress (using
1809
     * input_filter/FILTER_VALIDATE_EMAIL).
1810
     *
1811
     * @param string|null $message
1812
     * @param string|null $propertyPath
1813
     * @return Assert
1814
     * @throws AssertionFailedException
1815
     */
1816
    public function email($message = null, $propertyPath = null)
1817
    {
1818
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1819
        {
1820
            return $this;
1821
        }
1822
        $this->string($message, $propertyPath);
1823
        if ( ! filter_var($this->value, FILTER_VALIDATE_EMAIL) )
1824
        {
1825
            $message = sprintf(
1826
                $message ?: 'Value "%s" was expected to be a valid e-mail address.',
1827
                $this->stringify($this->value)
1828
            );
1829
            throw $this->createException($message, self::INVALID_EMAIL, $propertyPath);
1830
        }
1831
        else
1832
        {
1833
            $host = substr($this->value, strpos($this->value, '@') + 1);
1834
            // Likely not a FQDN, bug in PHP FILTER_VALIDATE_EMAIL prior to PHP 5.3.3
1835
            if ( version_compare(PHP_VERSION, '5.3.3', '<') && strpos($host, '.') === false )
1836
            {
1837
                $message = sprintf(
1838
                    $message ?: 'Value "%s" was expected to be a valid e-mail address.',
1839
                    $this->stringify($this->value)
1840
                );
1841
                throw $this->createException($message, self::INVALID_EMAIL, $propertyPath);
1842
            }
1843
        }
1844
        return $this;
1845
    }
1846
1847
    /**
1848
     * @param null $message
1849
     * @param null $propertyPath
1850
     * @return Assert
1851
     * @throws AssertionFailedException
1852
     */
1853
    public function emailPrefix($message = null, $propertyPath = null)
1854
    {
1855
        $this->value($this->value . '@example.com');
1856
        return $this->email($message, $propertyPath);
1857
    }
1858
1859
    /**
1860
     * Assert that value is an URL.
1861
     *
1862
     * This code snipped was taken from the Symfony project and modified to the special demands of this method.
1863
     *
1864
     * @param string|null $message
1865
     * @param string|null $propertyPath
1866
     * @return Assert
1867
     * @throws AssertionFailedException
1868
     *
1869
     *
1870
     * @link https://github.com/symfony/Validator/blob/master/Constraints/UrlValidator.php
1871
     * @link https://github.com/symfony/Validator/blob/master/Constraints/Url.php
1872
     */
1873
    public function url($message = null, $propertyPath = null)
1874
    {
1875
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1876
        {
1877
            return $this;
1878
        }
1879
        $this->string($message, $propertyPath);
1880
        $protocols = ['http', 'https'];
1881
        $pattern   = '~^
1882
            (%s)://                                 # protocol
1883
            (
1884
                ([\pL\pN\pS-]+\.)+[\pL]+                   # a domain name
1885
                    |                                     #  or
1886
                \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}      # a IP address
1887
                    |                                     #  or
1888
                \[
1889
                    (?:(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){6})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:::(?:(?:(?:[0-9a-f]{1,4})):){5})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){4})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,1}(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){3})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,2}(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){2})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,3}(?:(?:[0-9a-f]{1,4})))?::(?:(?:[0-9a-f]{1,4})):)(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,4}(?:(?:[0-9a-f]{1,4})))?::)(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,5}(?:(?:[0-9a-f]{1,4})))?::)(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,6}(?:(?:[0-9a-f]{1,4})))?::))))
1890
                \]  # a IPv6 address
1891
            )
1892
            (:[0-9]+)?                              # a port (optional)
1893
            (/?|/\S+)                               # a /, nothing or a / with something
1894
        $~ixu';
1895
        $pattern   = sprintf($pattern, implode('|', $protocols));
1896
        if ( !preg_match($pattern, $this->value) )
1897
        {
1898
            $message = sprintf(
1899
                $message ?: 'Value "%s" was expected to be a valid URL starting with http or https',
1900
                $this->stringify($this->value)
1901
            );
1902
            throw $this->createException($message, self::INVALID_URL, $propertyPath);
1903
        }
1904
        return $this;
1905
    }
1906
1907
    /**
1908
     * Assert that value is domain name.
1909
     *
1910
     * This code snipped was taken from the Symfony project and modified to the special demands of this method.
1911
     *
1912
     * @param string|null $message
1913
     * @param string|null $propertyPath
1914
     * @return Assert
1915
     * @throws AssertionFailedException
1916
     *
1917
     */
1918
    public function domainName($message = null, $propertyPath = null)
1919
    {
1920
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1921
        {
1922
            return $this;
1923
        }
1924
        $this->string($message, $propertyPath);
1925
        $pattern   = '/^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}$/';
1926
        if ( ! preg_match($pattern, $this->value) )
1927
        {
1928
            $message = sprintf(
1929
                $message ?: 'Value "%s" was expected to be a valid domain name',
1930
                $this->stringify($this->value)
1931
            );
1932
            throw $this->createException($message, self::INVALID_DOMAIN_NAME, $propertyPath);
1933
        }
1934
        return $this;
1935
    }
1936
1937
    /**
1938
     * Assert that value is alphanumeric.
1939
     *
1940
     * @param string|null $message
1941
     * @param string|null $propertyPath
1942
     * @return Assert
1943
     * @throws AssertionFailedException
1944
     */
1945 View Code Duplication
    public function alnum($message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1946
    {
1947
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1948
        {
1949
            return $this;
1950
        }
1951
        try
1952
        {
1953
            $this->regex('(^([a-zA-Z]{1}[a-zA-Z0-9]*)$)', $message, $propertyPath);
1954
        }
1955
        catch (AssertionFailedException $e)
1956
        {
1957
            $message = sprintf(
1958
                $message
1959
                    ?: 'Value "%s" is not alphanumeric, starting with letters and containing only letters and numbers.',
1960
                $this->stringify($this->value)
1961
            );
1962
            throw $this->createException($message, self::INVALID_ALNUM, $propertyPath);
1963
        }
1964
        return $this;
1965
    }
1966
1967
    /**
1968
     * Assert that the value is boolean True.
1969
     *
1970
     * @param string|null $message
1971
     * @param string|null $propertyPath
1972
     * @return Assert
1973
     * @throws AssertionFailedException
1974
     */
1975
    public function true($message = null, $propertyPath = null)
1976
    {
1977
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1978
        {
1979
            return $this;
1980
        }
1981
        if ( $this->value !== true )
1982
        {
1983
            $message = sprintf(
1984
                $message ?: 'Value "%s" is not TRUE.',
1985
                $this->stringify($this->value)
1986
            );
1987
            throw $this->createException($message, self::INVALID_TRUE, $propertyPath);
1988
        }
1989
        return $this;
1990
    }
1991
1992
    /**
1993
     * Assert that the value is boolean True.
1994
     *
1995
     * @param string|null $message
1996
     * @param string|null $propertyPath
1997
     * @return Assert
1998
     * @throws AssertionFailedException
1999
     */
2000
    public function truthy($message = null, $propertyPath = null)
2001
    {
2002
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2003
        {
2004
            return $this;
2005
        }
2006
        if ( ! $this->value )
2007
        {
2008
            $message = sprintf(
2009
                $message ?: 'Value "%s" is not truthy.',
2010
                $this->stringify($this->value)
2011
            );
2012
            throw $this->createException($message, self::INVALID_TRUE, $propertyPath);
2013
        }
2014
        return $this;
2015
    }
2016
2017
    /**
2018
     * Assert that the value is boolean False.
2019
     *
2020
     * @param string|null $message
2021
     * @param string|null $propertyPath
2022
     * @return Assert
2023
     * @throws AssertionFailedException
2024
     */
2025
    public function false($message = null, $propertyPath = null)
2026
    {
2027
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2028
        {
2029
            return $this;
2030
        }
2031
        if ( $this->value !== false )
2032
        {
2033
            $message = sprintf(
2034
                $message ?: 'Value "%s" is not FALSE.',
2035
                $this->stringify($this->value)
2036
            );
2037
            throw $this->createException($message, self::INVALID_FALSE, $propertyPath);
2038
        }
2039
        return $this;
2040
    }
2041
2042
    /**
2043
     * Assert that the value is not boolean False.
2044
     *
2045
     * @param string|null $message
2046
     * @param string|null $propertyPath
2047
     * @return Assert
2048
     * @throws AssertionFailedException
2049
     */
2050
    public function notFalse($message = null, $propertyPath = null)
2051
    {
2052
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2053
        {
2054
            return $this;
2055
        }
2056
        if ( $this->value === false )
2057
        {
2058
            $message = sprintf(
2059
                $message ?: 'Value "%s" is not FALSE.',
2060
                $this->stringify($this->value)
2061
            );
2062
            throw $this->createException($message, self::INVALID_NOT_FALSE, $propertyPath);
2063
        }
2064
        return $this;
2065
    }
2066
2067
    /**
2068
     * Assert that the class exists.
2069
     *
2070
     * @param string|null $message
2071
     * @param string|null $propertyPath
2072
     * @return Assert
2073
     * @throws AssertionFailedException
2074
     */
2075
    public function classExists($message = null, $propertyPath = null)
2076
    {
2077
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2078
        {
2079
            return $this;
2080
        }
2081
        if ( !class_exists($this->value) )
2082
        {
2083
            $message = sprintf(
2084
                $message ?: 'Class "%s" does not exist.',
2085
                $this->stringify($this->value)
2086
            );
2087
            throw $this->createException($message, self::INVALID_CLASS, $propertyPath);
2088
        }
2089
        return $this;
2090
    }
2091
2092
    /**
2093
     * Assert that the class implements the interface
2094
     *
2095
     * @param string      $interfaceName
2096
     * @param string|null $message
2097
     * @param string|null $propertyPath
2098
     * @return Assert
2099
     * @throws AssertionFailedException
2100
     */
2101
    public function implementsInterface($interfaceName, $message = null, $propertyPath = null)
2102
    {
2103
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2104
        {
2105
            return $this;
2106
        }
2107
        $reflection = new \ReflectionClass($this->value);
2108
        if ( !$reflection->implementsInterface($interfaceName) )
2109
        {
2110
            $message = sprintf(
2111
                $message ?: 'Class "%s" does not implement interface "%s".',
2112
                $this->stringify($this->value),
2113
                $this->stringify($interfaceName)
2114
            );
2115
            throw $this->createException($message, self::INTERFACE_NOT_IMPLEMENTED, $propertyPath, ['interface' => $interfaceName]);
2116
        }
2117
        return $this;
2118
    }
2119
2120
    /**
2121
     * Assert that the given string is a valid json string.
2122
     *
2123
     * NOTICE:
2124
     * Since this does a json_decode to determine its validity
2125
     * you probably should consider, when using the variable
2126
     * content afterwards, just to decode and check for yourself instead
2127
     * of using this assertion.
2128
     *
2129
     * @param string|null $message
2130
     * @param string|null $propertyPath
2131
     * @return Assert
2132
     * @throws AssertionFailedException
2133
     */
2134
    public function isJsonString($message = null, $propertyPath = null)
2135
    {
2136
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2137
        {
2138
            return $this;
2139
        }
2140
        if ( null === json_decode($this->value) && JSON_ERROR_NONE !== json_last_error() )
2141
        {
2142
            $message = sprintf(
2143
                $message ?: 'Value "%s" is not a valid JSON string.',
2144
                $this->stringify($this->value)
2145
            );
2146
            throw $this->createException($message, self::INVALID_JSON_STRING, $propertyPath);
2147
        }
2148
        return $this;
2149
    }
2150
2151
    /**
2152
     * Assert that the given string is a valid UUID
2153
     *
2154
     * Uses code from {@link https://github.com/ramsey/uuid} that is MIT licensed.
2155
     *
2156
     * @param string|null $message
2157
     * @param string|null $propertyPath
2158
     * @return Assert
2159
     * @throws AssertionFailedException
2160
     */
2161
    public function uuid($message = null, $propertyPath = null)
2162
    {
2163
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2164
        {
2165
            return $this;
2166
        }
2167
        $this->value = str_replace(['urn:', 'uuid:', '{', '}'], '', $this->value);
2168
        if ( $this->value === '00000000-0000-0000-0000-000000000000' )
2169
        {
2170
            return $this;
2171
        }
2172
        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}$/', $this->value) )
2173
        {
2174
            $message = sprintf(
2175
                $message ?: 'Value "%s" is not a valid UUID.',
2176
                $this->stringify($this->value)
2177
            );
2178
            throw $this->createException($message, self::INVALID_UUID, $propertyPath);
2179
        }
2180
        return $this;
2181
    }
2182
2183
    /**
2184
     * Assert that the count of countable is equal to count.
2185
     *
2186
     * @param int    $count
2187
     * @param string $message
2188
     * @param string $propertyPath
2189
     * @return Assert
2190
     * @throws AssertionFailedException
2191
     */
2192
    public function count($count, $message = null, $propertyPath = null)
2193
    {
2194
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2195
        {
2196
            return $this;
2197
        }
2198
        if ( $count !== count($this->value) )
2199
        {
2200
            $message = sprintf(
2201
                $message ?: 'List does not contain exactly "%d" elements.',
2202
                $this->stringify($this->value),
2203
                $this->stringify($count)
2204
            );
2205
            throw $this->createException($message, self::INVALID_COUNT, $propertyPath, ['count' => $count]);
2206
        }
2207
        return $this;
2208
    }
2209
2210
    /**
2211
     * @param $func
2212
     * @param $args
2213
     * @return bool
2214
     * @throws AssertionFailedException
2215
     */
2216
    protected function doAllOrNullOr($func, $args)
2217
    {
2218
        if ( $this->nullOr && is_null($this->value) )
2219
        {
2220
            return true;
2221
        }
2222
        if ( $this->emptyOr && empty($this->value) )
2223
        {
2224
            return true;
2225
        }
2226
        if ( $this->all && (new Assert($this->value))->setExceptionClass($this->exceptionClass)->isTraversable() )
2227
        {
2228
            foreach ( $this->value as $idx => $value )
2229
            {
2230
                $object = (new Assert($value))->setExceptionClass($this->exceptionClass);
2231
                call_user_func_array([$object, $func], $args);
2232
            }
2233
            return true;
2234
        }
2235
        return ( $this->nullOr && is_null($this->value) ) || ( $this->emptyOr && empty($this->value) ) ? true : false;
2236
    }
2237
2238
    /**
2239
     * Determines if the values array has every choice as key and that this choice has content.
2240
     *
2241
     * @param array $choices
2242
     * @param string|null $message
2243
     * @param string|null $propertyPath
2244
     * @return $this
2245
     */
2246
    public function choicesNotEmpty(array $choices, $message = null, $propertyPath = null)
2247
    {
2248
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2249
        {
2250
            return $this;
2251
        }
2252
        $this->notEmpty($message, $propertyPath);
2253
        foreach ( $choices as $choice )
2254
        {
2255
            $this->notEmptyKey($choice, $message, $propertyPath);
2256
        }
2257
        return $this;
2258
    }
2259
2260
    /**
2261
     * Determines that the named method is defined in the provided object.
2262
     *
2263
     * @param mixed $object
2264
     * @param string|null $message
2265
     * @param string|null $propertyPath
2266
     * @returns Assert
2267
     * @throws
2268
     */
2269 View Code Duplication
    public function methodExists($object, $message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2270
    {
2271
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2272
        {
2273
            return $this;
2274
        }
2275
        (new Assert($object))->setExceptionClass($this->exceptionClass)->isObject($message, $propertyPath);
2276
        if ( !method_exists($object, $this->value) )
2277
        {
2278
            $message = sprintf(
2279
                $message ?: 'Expected "%s" does not a exist in provided object.',
2280
                $this->stringify($this->value)
2281
            );
2282
            throw $this->createException($message, self::INVALID_METHOD, $propertyPath);
2283
        }
2284
        return $this;
2285
    }
2286
2287
    /**
2288
     * Determines that the provided value is an object.
2289
     *
2290
     * @param string|null $message
2291
     * @param string|null $propertyPath
2292
     * @return $this
2293
     * @throws AssertionFailedException
2294
     */
2295
    public function isObject($message = null, $propertyPath = null)
2296
    {
2297
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2298
        {
2299
            return $this;
2300
        }
2301
        if ( !is_object($this->value) )
2302
        {
2303
            $message = sprintf(
2304
                $message ?: 'Provided "%s" is not a valid object.',
2305
                $this->stringify($this->value)
2306
            );
2307
            throw $this->createException($message, self::INVALID_OBJECT, $propertyPath);
2308
        }
2309
        return $this;
2310
    }
2311
2312
    /**
2313
     * Make a string version of a value.
2314
     *
2315
     * @param $value
2316
     * @return string
2317
     */
2318
    private function stringify($value)
2319
    {
2320
        if ( is_bool($value) )
2321
        {
2322
            return $value ? '<TRUE>' : '<FALSE>';
2323
        }
2324
        if ( is_scalar($value) )
2325
        {
2326
            $val = (string)$value;
2327
            if ( strlen($val) > 100 )
2328
            {
2329
                $val = substr($val, 0, 97) . '...';
2330
            }
2331
            return $val;
2332
        }
2333
        if ( is_array($value) )
2334
        {
2335
            return '<ARRAY>';
2336
        }
2337
        if ( is_object($value) )
2338
        {
2339
            return get_class($value);
2340
        }
2341
        if ( is_resource($value) )
2342
        {
2343
            return '<RESOURCE>';
2344
        }
2345
        if ( $value === null )
2346
        {
2347
            return '<NULL>';
2348
        }
2349
        return 'unknown';
2350
    }
2351
}
2352
2353