Assert::range()   C
last analyzed

Complexity

Conditions 7
Paths 4

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 15
nc 4
nop 4
dl 0
loc 25
rs 6.7272
c 0
b 0
f 0
1
<?php declare(strict_types=1);
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                 = 1;
27
    const INVALID_INTEGER               = 2;
28
    const INVALID_DIGIT                 = 3;
29
    const INVALID_INTEGERISH            = 4;
30
    const INVALID_BOOLEAN               = 5;
31
    const VALUE_EMPTY                   = 6;
32
    const VALUE_NULL                    = 7;
33
    const INVALID_STRING                = 8;
34
    const INVALID_REGEX                 = 9;
35
    const INVALID_MIN_LENGTH            = 10;
36
    const INVALID_MAX_LENGTH            = 11;
37
    const INVALID_STRING_START          = 12;
38
    const INVALID_STRING_CONTAINS       = 13;
39
    const INVALID_CHOICE                = 14;
40
    const INVALID_NUMERIC               = 15;
41
    const INVALID_ARRAY                 = 16;
42
    const INVALID_KEY_EXISTS            = 17;
43
    const INVALID_NOT_BLANK             = 18;
44
    const INVALID_INSTANCE_OF           = 19;
45
    const INVALID_SUBCLASS_OF           = 20;
46
    const INVALID_RANGE                 = 21;
47
    const INVALID_ALNUM                 = 22;
48
    const INVALID_TRUE                  = 23;
49
    const INVALID_EQ                    = 24;
50
    const INVALID_SAME                  = 25;
51
    const INVALID_MIN                   = 26;
52
    const INVALID_MAX                   = 27;
53
    const INVALID_LENGTH                = 28;
54
    const INVALID_FALSE                 = 29;
55
    const INVALID_STRING_END            = 30;
56
    const INVALID_UUID                  = 31;
57
    const INVALID_COUNT                 = 32;
58
    const INVALID_NOT_EQ                = 33;
59
    const INVALID_NOT_SAME              = 34;
60
    const INVALID_TRAVERSABLE           = 35;
61
    const INVALID_ARRAY_ACCESSIBLE      = 36;
62
    const INVALID_KEY_ISSET             = 37;
63
    const INVALID_SAMACCOUNTNAME        = 38;
64
    const INVALID_USERPRINCIPALNAME     = 39;
65
    const INVALID_DIRECTORY             = 40;
66
    const INVALID_FILE                  = 41;
67
    const INVALID_READABLE              = 42;
68
    const INVALID_WRITEABLE             = 43;
69
    const INVALID_CLASS                 = 44;
70
    const INVALID_EMAIL                 = 45;
71
    const INTERFACE_NOT_IMPLEMENTED     = 46;
72
    const INVALID_URL                   = 47;
73
    const INVALID_NOT_INSTANCE_OF       = 48;
74
    const VALUE_NOT_EMPTY               = 49;
75
    const INVALID_JSON_STRING           = 50;
76
    const INVALID_OBJECT                = 51;
77
    const INVALID_METHOD                = 52;
78
    const INVALID_SCALAR                = 53;
79
    const INVALID_DATE                  = 54;
80
    const INVALID_CALLABLE              = 55;
81
    const INVALID_KEYS_EXIST            = 56;
82
    const INVALID_PROPERTY_EXISTS       = 57;
83
    const INVALID_PROPERTIES_EXIST      = 58;
84
    const INVALID_UTF8                  = 59;
85
    const INVALID_DOMAIN_NAME           = 60;
86
    const INVALID_NOT_FALSE             = 61;
87
    const INVALID_FILE_OR_DIR           = 62;
88
    const INVALID_ASCII                 = 63;
89
    const INVALID_NOT_REGEX             = 64;
90
    const INVALID_GREATER_THAN          = 65;
91
    const INVALID_LESS_THAN             = 66;
92
    const INVALID_GREATER_THAN_OR_EQ    = 67;
93
    const INVALID_LESS_THAN_OR_EQ       = 68;
94
    const INVALID_IP_ADDRESS            = 69;
95
    const INVALID_AUS_MOBILE            = 70;
96
    const INVALID_ISNI                  = 71;
97
98
    const EMERGENCY                     = 'emergency';
99
    const ALERT                         = 'alert';
100
    const CRITICAL                      = 'critical';
101
    const ERROR                         = 'error';
102
    const WARNING                       = 'warning';
103
    const NOTICE                        = 'notice';
104
    const INFO                          = 'info';
105
    const DEBUG                         = 'debug';
106
107
    /** @var bool */
108
    protected $nullOr                   = false;
109
110
    /** @var bool */
111
    protected $emptyOr                  = false;
112
113
    /** @var mixed */
114
    protected $value                    = null;
115
116
    /** @var bool */
117
    protected $all                      = false;
118
119
    /** @var string */
120
    protected $fieldName                = '';
121
122
    /** @var string */
123
    protected $propertyPath             = '';
124
125
    /** @var string */
126
    protected $level                    = 'critical';
127
128
    /** @var int */
129
    protected $overrideCode             = null;
130
131
    /** @var string */
132
    protected $overrideError            = '';
133
    
134
    /**
135
     * Exception to throw when an assertion failed.
136
     *
137
     * @var string
138
     */
139
    protected $exceptionClass           = AssertionFailedException::class;
140
141
    /**
142
     * @param mixed $value
143
     */
144
    public function __construct($value)
145
    {
146
        $this->value($value);
147
    }
148
149
    /**
150
     * @param \Closure[] $validators
151
     * @return array
152
     */
153
    public static function runValidators(array $validators) : array
154
    {
155
        $errors = [];
156
        foreach ( $validators as $fieldName => $validator )
157
        {
158
            try
159
            {
160
                $validator->__invoke();
161
            }
162
            catch ( AssertionFailedException $e )
163
            {
164
                $errors[$fieldName]     = $e->getMessage();
165
            }
166
        }
167
168
        return $errors;
169
    }
170
171
    /**
172
     * @param        $value
173
     * @param string $fieldName
174
     * @param int    $code
175
     * @param string $error
176
     * @param string $level
177
     * @return Assert
178
     */
179 View Code Duplication
    public static function that($value, string $fieldName='', int $code=0, string $error='', string $level=Assert::WARNING) : Assert
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...
180
    {
181
        $assert = new static($value);
182
        if ( $fieldName )
183
        {
184
            $assert->fieldName($fieldName);
185
        }
186
        if ( $code )
187
        {
188
            $assert->code($code);
189
        }
190
        if ( $error )
191
        {
192
            $assert->error($error);
193
        }
194
        if ( $level )
195
        {
196
            $assert->level($level);
197
        }
198
199
        return $assert;
200
    }
201
202
    /**
203
     * @param mixed $value
204
     * @return Assert
205
     */
206
    public function reset($value) : Assert
207
    {
208
        return $this->all(false)->nullOr(false)->value($value);
209
    }
210
211
    /**
212
     * @param mixed $value
213
     * @return Assert
214
     */
215
    public function value($value) : Assert
216
    {
217
        $this->value = $value;
218
219
        return $this;
220
    }
221
222
    /**
223
     * Allow value to pass assertion if it is null.
224
     *
225
     * @param bool $nullOr
226
     * @return Assert
227
     */
228
    public function nullOr(bool $nullOr = true) : Assert
229
    {
230
        $this->nullOr = $nullOr;
231
232
        return $this;
233
    }
234
235
    /**
236
     * Allow value to pass assertion if it is empty.
237
     *
238
     * @param bool $emptyOr
239
     * @return Assert
240
     */
241
    public function emptyOr(bool $emptyOr = true) : Assert
242
    {
243
        $this->emptyOr = $emptyOr;
244
245
        return $this;
246
    }
247
248
    /**
249
     * Assert all values in the value array.
250
     *
251
     * @param bool $all
252
     * @return Assert
253
     */
254
    public function all(bool $all = true) : Assert
255
    {
256
        $this->all = $all;
257
258
        return $this;
259
    }
260
261
    /**
262
     * Helper method that handles building the assertion failure exceptions.
263
     * They are returned from this method so that the stack trace still shows
264
     * the assertions method.
265
     *
266
     * @param string $message
267
     * @param int    $code
268
     * @param string $fieldName
269
     * @param array  $constraints
270
     * @param string $level
271
     * @return AssertionFailedException
272
     */
273
    protected function createException(string $message, int $code, string $fieldName, array $constraints=[], string $level='') : AssertionFailedException
274
    {
275
        $exceptionClass = $this->exceptionClass;
276
        $fieldName      = empty($fieldName) ? $this->fieldName : $fieldName;
277
        $level          = empty($level) ? $this->level : $level;
278
279
        return new $exceptionClass($message, $code, $fieldName, $this->value, $constraints, $level, $this->propertyPath);
280
    }
281
282
    /**
283
     * @param string $exceptionClass
284
     * @return Assert
285
     */
286
    public function setExceptionClass(string $exceptionClass) : Assert
287
    {
288
        $this->exceptionClass = $exceptionClass;
289
290
        return $this;
291
    }
292
293
    /**
294
     * @param int $code
295
     * @return Assert
296
     */
297
    public function code(int $code) : Assert
298
    {
299
        $this->overrideCode = $code;
300
301
        return $this;
302
    }
303
304
    /**
305
     * @param string $fieldName
306
     * @return Assert
307
     */
308
    public function fieldName(string $fieldName) : Assert
309
    {
310
        $this->fieldName = $fieldName;
311
312
        return $this;
313
    }
314
315
    /**
316
     * @deprecated
317
     * @param string $fieldName
318
     * @return Assert
319
     */
320
    public function name(string $fieldName) : Assert
321
    {
322
        $this->fieldName = $fieldName;
323
324
        return $this;
325
    }
326
327
    /**
328
     * @param string $level
329
     * @return Assert
330
     */
331
    public function level(string $level) : Assert
332
    {
333
        $this->level = $level;
334
335
        return $this;
336
    }
337
338
    /**
339
     * @param string $error
340
     * @return Assert
341
     */
342
    public function error(string $error) : Assert
343
    {
344
        $this->overrideError = $error;
345
346
        return $this;
347
    }
348
349
    /**
350
     * User controlled way to define a sub-property causing
351
     * the failure of a currently asserted objects.
352
     *
353
     * Useful to transport information about the nature of the error
354
     * back to higher layers.
355
     *
356
     * @param string $propertyPath
357
     * @return Assert
358
     */
359
    public function propertyPath(string $propertyPath) : Assert
360
    {
361
        $this->propertyPath = $propertyPath;
362
363
        return $this;
364
    }
365
366
    /**
367
     * Assert that value is equal to a provided value (using == ).
368
     *
369
     * @param mixed  $value2
370
     * @param string $message
371
     * @param string $fieldName
372
     * @return Assert
373
     * @throws AssertionFailedException
374
     */
375
    public function eq($value2, string $message='', string $fieldName='') : Assert
376
    {
377
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
378
        {
379
            return $this;
380
        }
381
        if ( $this->value != $value2 )
382
        {
383
            $message = $message ?: $this->overrideError;
384
            $message = sprintf(
385
                $message ?: 'Value "%s" does not equal expected value "%s".',
386
                $this->stringify($this->value),
387
                $this->stringify($value2)
388
            );
389
390
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_EQ, $fieldName, ['expected' => $value2]);
391
        }
392
393
        return $this;
394
    }
395
396
    /**
397
     * Assert that value is greater than a provided value (exclusive).
398
     *
399
     * @param mixed  $value2
400
     * @param string $message
401
     * @param string $fieldName
402
     * @return Assert
403
     * @throws AssertionFailedException
404
     */
405
    public function greaterThan($value2, string $message='', string $fieldName='') : Assert
406
    {
407
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
408
        {
409
            return $this;
410
        }
411
        if ( ! ( $this->value > $value2 ) )
412
        {
413
            $message = $message ?: $this->overrideError;
414
            $message = sprintf(
415
                $message ?: 'Value "%s" does not greater then expected value "%s".',
416
                $this->stringify($this->value),
417
                $this->stringify($value2)
418
            );
419
420
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_EQ, $fieldName, ['expected' => $value2]);
421
        }
422
423
        return $this;
424
    }
425
426
    /**
427
     * Assert that value is greater than or equal to a provided value (inclusive).
428
     *
429
     * @param mixed  $value2
430
     * @param string $message
431
     * @param string $fieldName
432
     * @return Assert
433
     * @throws AssertionFailedException
434
     */
435
    public function greaterThanOrEq($value2, string $message='', string $fieldName='') : Assert
436
    {
437
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
438
        {
439
            return $this;
440
        }
441
        if ( ! ( $this->value >= $value2 ) )
442
        {
443
            $message = $message ?: $this->overrideError;
444
            $message = sprintf(
445
                $message ?: 'Value "%s" does not greater than or equal to expected value "%s".',
446
                $this->stringify($this->value),
447
                $this->stringify($value2)
448
            );
449
450
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_EQ, $fieldName, ['expected' => $value2]);
451
        }
452
453
        return $this;
454
    }
455
456
    /**
457
     * Assert that value is less than a provided value (exclusive).
458
     *
459
     * @param mixed  $value2
460
     * @param string $message
461
     * @param string $fieldName
462
     * @return Assert
463
     * @throws AssertionFailedException
464
     */
465
    public function lessThan($value2, string $message='', string $fieldName='') : Assert
466
    {
467
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
468
        {
469
            return $this;
470
        }
471
        if ( ! ( $this->value < $value2 ) )
472
        {
473
            $message = $message ?: $this->overrideError;
474
            $message = sprintf(
475
                $message ?: 'Value "%s" does not less then expected value "%s".',
476
                $this->stringify($this->value),
477
                $this->stringify($value2)
478
            );
479
480
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_LESS_THAN, $fieldName, ['expected' => $value2]);
481
        }
482
483
        return $this;
484
    }
485
486
    /**
487
     * Assert that value is less than or equal to a provided value (inclusive).
488
     *
489
     * @param mixed  $value2
490
     * @param string $message
491
     * @param string $fieldName
492
     * @return Assert
493
     * @throws AssertionFailedException
494
     */
495
    public function lessThanOrEq($value2, string $message='', string $fieldName='') : Assert
496
    {
497
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
498
        {
499
            return $this;
500
        }
501
        if ( ! ( $this->value <= $value2 ) )
502
        {
503
            $message = $message ?: $this->overrideError;
504
            $message = sprintf(
505
                $message ?: 'Value "%s" does not less than or equal to expected value "%s".',
506
                $this->stringify($this->value),
507
                $this->stringify($value2)
508
            );
509
510
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_LESS_THAN_OR_EQ, $fieldName, ['expected' => $value2]);
511
        }
512
513
        return $this;
514
    }
515
516
    /**
517
     * Assert that value is the same as a provided value (using === ).
518
     *
519
     * @param mixed  $value2
520
     * @param string $message
521
     * @param string $fieldName
522
     * @return Assert
523
     * @throws AssertionFailedException
524
     */
525
    public function same($value2, string $message='', string $fieldName='') : Assert
526
    {
527
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
528
        {
529
            return $this;
530
        }
531
        if ( $this->value !== $value2 )
532
        {
533
            $message = $message ?: $this->overrideError;
534
            $message = sprintf(
535
                $message ?: 'Value "%s" is not the same as expected value "%s".',
536
                $this->stringify($this->value),
537
                $this->stringify($value2)
538
            );
539
540
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_SAME, $fieldName, ['expected' => $value2]);
541
        }
542
543
        return $this;
544
    }
545
546
    /**
547
     * Assert that value is not equal to a provided value (using == ).
548
     *
549
     * @param mixed  $value2
550
     * @param string $message
551
     * @param string $fieldName
552
     * @return Assert
553
     * @throws AssertionFailedException
554
     */
555
    public function notEq($value2, string $message='', string $fieldName='') : Assert
556
    {
557
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
558
        {
559
            return $this;
560
        }
561
        if ( $this->value == $value2 )
562
        {
563
            $message = $message ?: $this->overrideError;
564
            $message = sprintf(
565
                $message ?: 'Value "%s" is equal to expected value "%s".',
566
                $this->stringify($this->value),
567
                $this->stringify($value2)
568
            );
569
570
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_NOT_EQ, $fieldName, ['expected' => $value2]);
571
        }
572
573
        return $this;
574
    }
575
576
    /**
577
     * Assert that value can be called as a function (using is_callable()).
578
     *
579
     * @param string $message
580
     * @param string $fieldName
581
     * @return Assert
582
     * @throws AssertionFailedException
583
     */
584
    public function isCallable(string $message='', string $fieldName='') : Assert
585
    {
586
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
587
        {
588
            return $this;
589
        }
590
        if ( !is_callable($this->value) )
591
        {
592
            $message = $message ?: $this->overrideError;
593
            $message = sprintf(
594
                $message ?: 'Value "%s" is not callable.',
595
                $this->stringify($this->value)
596
            );
597
598
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_NOT_EQ, $fieldName);
599
        }
600
601
        return $this;
602
    }
603
604
    /**
605
     * Assert that value is not the same as a provided value (using === ).
606
     *
607
     * @param mixed  $value2
608
     * @param string $message
609
     * @param string $fieldName
610
     * @return Assert
611
     * @throws AssertionFailedException
612
     */
613
    public function notSame($value2, string $message='', string $fieldName='') : Assert
614
    {
615
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
616
        {
617
            return $this;
618
        }
619
        if ( $this->value === $value2 )
620
        {
621
            $message = $message ?: $this->overrideError;
622
            $message = sprintf(
623
                $message ?: 'Value "%s" is the same as expected value "%s".',
624
                $this->stringify($this->value),
625
                $this->stringify($value2)
626
            );
627
628
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_NOT_SAME, $fieldName, ['expected' => $value2]);
629
        }
630
631
        return $this;
632
    }
633
634
    /**
635
     * Assert that value is a valid ID (non-empty, non-zero, valid integer).
636
     *
637
     * @param string $message
638
     * @param string $fieldName
639
     * @return Assert
640
     * @throws AssertionFailedException
641
     */
642 View Code Duplication
    public function id(string $message='', string $fieldName='') : Assert
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...
643
    {
644
        $message = $message ?: $this->overrideError;
645
        $message = $message ?: 'Value "%s" is not an integer id.';
646
647
        return $this->nonEmptyInt($message, $fieldName)->range(1, PHP_INT_MAX, $message, $fieldName);
648
    }
649
650
    /**
651
     * Assert that value is a unsigned int (non-empty valid integer, can be zero).
652
     * @param string $message
653
     * @param string $fieldName
654
     * @return Assert
655
     * @throws AssertionFailedException
656
     */
657 View Code Duplication
    public function unsignedInt(string $message='', string $fieldName='') : Assert
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...
658
    {
659
        $message = $message ?: $this->overrideError;
660
        $message = $message ?: 'Value "%s" is not an integer id.';
661
662
        return $this->int($message, $fieldName)->range(0, PHP_INT_MAX, $message, $fieldName);
663
    }
664
665
    /**
666
     * Assert that value is a valid flag (0 or 1).
667
     *
668
     * @param string $message
669
     * @param string $fieldName
670
     * @return Assert
671
     * @throws AssertionFailedException
672
     */
673 View Code Duplication
    public function flag(string $message='', string $fieldName='') : Assert
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...
674
    {
675
        $message = $message ?: $this->overrideError;
676
        $message = $message ?: 'Value "%s" is not a 0 or 1.';
677
678
        return $this->range(0, 1, $message, $fieldName);
679
    }
680
681
    /**
682
     * Assert that value is a valid status (-1, 0, or 1).
683
     *
684
     * @param string $message
685
     * @param string $fieldName
686
     * @return Assert
687
     * @throws AssertionFailedException
688
     */
689
    public function status(string $message='', string $fieldName='') : Assert
690
    {
691
        $message = $message ?: $this->overrideError;
692
        $message = $message ?: 'Value "%s" is not a valid status.';
693
694
        return $this->integer($message, $fieldName)->inArray([-1, 0, 1]);
695
    }
696
697
    /**
698
     * Assert that value is null or a valid ID.
699
     *
700
     * @param string $message
701
     * @param string $fieldName
702
     * @return Assert
703
     * @throws AssertionFailedException
704
     */
705
    public function nullOrId(string $message='', string $fieldName='') : Assert
706
    {
707
        return $this->nullOr()->id($message, $fieldName);
708
    }
709
710
    /**
711
     * Assert that values are all valid IDs.
712
     *
713
     * @param string $message
714
     * @param string $fieldName
715
     * @return Assert
716
     * @throws AssertionFailedException
717
     */
718
    public function allIds(string $message='', string $fieldName='') : Assert
719
    {
720
        return $this->all()->id($message, $fieldName);
721
    }
722
723
    /**
724
     * Alias of {@see integer()}.
725
     *
726
     * @param string $message
727
     * @param string $fieldName
728
     * @return Assert
729
     * @throws AssertionFailedException
730
     */
731
    public function int(string $message='', string $fieldName='') : Assert
732
    {
733
        return $this->integer($message, $fieldName);
734
    }
735
736
    /**
737
     * Assert that value is a valid PHP integer.
738
     *
739
     * @param string $message
740
     * @param string $fieldName
741
     * @return Assert
742
     * @throws AssertionFailedException
743
     */
744
    public function integer(string $message='', string $fieldName='') : Assert
745
    {
746
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
747
        {
748
            return $this;
749
        }
750
        if ( !is_int($this->value) )
751
        {
752
            $message = $message ?: $this->overrideError;
753
            $message = sprintf(
754
                $message ?: 'Value "%s" is not an integer.',
755
                $this->stringify($this->value)
756
            );
757
758
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_INTEGER, $fieldName);
759
        }
760
761
        return $this;
762
    }
763
764
    /**
765
     * Assert that value is a valid PHP float.
766
     *
767
     * @param string $message
768
     * @param string $fieldName
769
     * @return Assert
770
     * @throws AssertionFailedException
771
     */
772
    public function float(string $message='', string $fieldName='') : Assert
773
    {
774
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
775
        {
776
            return $this;
777
        }
778
        if ( ! is_float($this->value) )
779
        {
780
            $message = $message ?: $this->overrideError;
781
            $message = sprintf(
782
                $message ?: 'Value "%s" is not a float.',
783
                $this->stringify($this->value)
784
            );
785
786
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_FLOAT, $fieldName);
787
        }
788
789
        return $this;
790
    }
791
792
    /**
793
     * Assert that value (integer or integer'ish) is a digit.
794
     *
795
     * @param string $message
796
     * @param string $fieldName
797
     * @return Assert
798
     * @throws AssertionFailedException
799
     */
800
    public function digit(string $message='', string $fieldName='') : Assert
801
    {
802
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
803
        {
804
            return $this;
805
        }
806
        if ( ! ctype_digit((string)$this->value) )
807
        {
808
            $message = $message ?: $this->overrideError;
809
            $message = sprintf(
810
                $message ?: 'Value "%s" is not a digit.',
811
                $this->stringify($this->value)
812
            );
813
814
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_DIGIT, $fieldName);
815
        }
816
817
        return $this;
818
    }
819
820
    /**
821
     * Assert that value is a valid date.
822
     *
823
     * @param string $message
824
     * @param string $fieldName
825
     * @return Assert
826
     * @throws AssertionFailedException
827
     */
828
    public function date(string $message='', string $fieldName='') : Assert
829
    {
830
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
831
        {
832
            return $this;
833
        }
834
        $this->notEmpty($message, $fieldName);
835
        if ( $this->value instanceof \DateTime )
836
        {
837
            return $this;
838
        }
839 View Code Duplication
        if ( strtotime($this->value) === false )
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
840
        {
841
            $message = $message ?: $this->overrideError;
842
            $message = sprintf(
843
                $message ?: 'Value "%s" is not a date.',
844
                $this->stringify($this->value)
845
            );
846
847
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_DATE, $fieldName);
848
        }
849
850
        return $this;
851
    }
852
853
    /**
854
     * @param        $afterDate
855
     * @param string $message
856
     * @param string $fieldName
857
     * @return $this
858
     * @throws AssertionFailedException
859
     */
860
    public function after($afterDate, string $message='', string $fieldName='')
861
    {
862
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
863
        {
864
            return $this;
865
        }
866
        $this->notEmpty($message, $fieldName);
867
        if ( strtotime($this->value) === false && strtotime($afterDate) === false && strtotime($this->value) < strtotime($afterDate) )
868
        {
869
            $message = $message ?: $this->overrideError;
870
            $message = sprintf(
871
                $message ?: 'Value "%s" is not a date, comparison date is not a date or value is not before comparison date.',
872
                $this->stringify($this->value)
873
            );
874
875
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_DATE, $fieldName);
876
        }
877
878
        return $this;
879
    }
880
881
    /**
882
     * Assert that value is a PHP integer'ish.
883
     *
884
     * @param string $message
885
     * @param string $fieldName
886
     * @return Assert
887
     * @throws AssertionFailedException
888
     */
889
    public function integerish(string $message='', string $fieldName='') : Assert
890
    {
891
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
892
        {
893
            return $this;
894
        }
895
        if ( is_object($this->value) || strval(intval($this->value)) != $this->value || is_bool($this->value) || is_null($this->value) )
896
        {
897
            $message = $message ?: $this->overrideError;
898
            $message = sprintf(
899
                $message ?: 'Value "%s" is not an integer or a number castable to integer.',
900
                $this->stringify($this->value)
901
            );
902
903
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_INTEGERISH, $fieldName);
904
        }
905
906
        return $this;
907
    }
908
909
    /**
910
     * Assert that value is a valid PHP boolean.
911
     *
912
     * @param string $message
913
     * @param string $fieldName
914
     * @return Assert
915
     * @throws AssertionFailedException
916
     */
917
    public function boolean(string $message='', string $fieldName='') : Assert
918
    {
919
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
920
        {
921
            return $this;
922
        }
923
        if ( ! is_bool($this->value) )
924
        {
925
            $message = $message ?: $this->overrideError;
926
            $message = sprintf(
927
                $message ?: 'Value "%s" is not a boolean.',
928
                $this->stringify($this->value)
929
            );
930
931
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_BOOLEAN, $fieldName);
932
        }
933
934
        return $this;
935
    }
936
937
    /**
938
     * Assert that value is a valid PHP scalar.
939
     *
940
     * @param string $message
941
     * @param string $fieldName
942
     * @return Assert
943
     * @throws AssertionFailedException
944
     */
945
    public function scalar(string $message='', string $fieldName='') : Assert
946
    {
947
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
948
        {
949
            return $this;
950
        }
951
        if ( ! is_scalar($this->value) )
952
        {
953
            $message = $message ?: $this->overrideError;
954
            $message = sprintf(
955
                $message ?: 'Value "%s" is not a scalar.',
956
                $this->stringify($this->value)
957
            );
958
959
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_SCALAR, $fieldName);
960
        }
961
962
        return $this;
963
    }
964
965
    /**
966
     * Assert that value is not empty.
967
     *
968
     * @param string $message
969
     * @param string $fieldName
970
     * @return Assert
971
     * @throws AssertionFailedException
972
     */
973 View Code Duplication
    public function notEmpty(string $message='', string $fieldName='') : Assert
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...
974
    {
975
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
976
        {
977
            return $this;
978
        }
979
        if ( ( is_object($this->value) && empty((array)$this->value) ) || empty($this->value) )
980
        {
981
            $message = $message ?: $this->overrideError;
982
            $message = sprintf(
983
                $message ?: 'Value "%s" is empty, but non empty value was expected.',
984
                $this->stringify($this->value)
985
            );
986
987
            throw $this->createException($message, $this->overrideCode ?: self::VALUE_EMPTY, $fieldName);
988
        }
989
990
        return $this;
991
    }
992
993
    /**
994
     * Assert that value is empty.
995
     *
996
     * @param string $message
997
     * @param string $fieldName
998
     * @return Assert
999
     * @throws AssertionFailedException
1000
     */
1001
    public function noContent(string $message='', string $fieldName='') : Assert
1002
    {
1003
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1004
        {
1005
            return $this;
1006
        }
1007
        if ( !empty( $this->value ) )
1008
        {
1009
            $message = $message ?: $this->overrideError;
1010
            $message = sprintf(
1011
                $message ?: 'Value "%s" is not empty, but empty value was expected.',
1012
                $this->stringify($this->value)
1013
            );
1014
1015
            throw $this->createException($message, $this->overrideCode ?: self::VALUE_NOT_EMPTY, $fieldName);
1016
        }
1017
1018
        return $this;
1019
    }
1020
1021
    /**
1022
     * Assert that value is not null.
1023
     *
1024
     * @param string $message
1025
     * @param string $fieldName
1026
     * @return Assert
1027
     * @throws AssertionFailedException
1028
     */
1029
    public function notNull(string $message='', string $fieldName='') : Assert
1030
    {
1031
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1032
        {
1033
            return $this;
1034
        }
1035
        if ( $this->value === null )
1036
        {
1037
            $message = $message ?: $this->overrideError;
1038
            $message = sprintf(
1039
                $message ?: 'Value "%s" is null, but non null value was expected.',
1040
                $this->stringify($this->value)
1041
            );
1042
1043
            throw $this->createException($message, $this->overrideCode ?: self::VALUE_NULL, $fieldName);
1044
        }
1045
1046
        return $this;
1047
    }
1048
1049
    /**
1050
     * Assert that value is a string
1051
     *
1052
     * @param string $message
1053
     * @param string $fieldName
1054
     * @return Assert
1055
     * @throws AssertionFailedException
1056
     */
1057
    public function string(string $message='', string $fieldName='') : Assert
1058
    {
1059
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1060
        {
1061
            return $this;
1062
        }
1063
        if ( !is_string($this->value) )
1064
        {
1065
            $message = $message ?: $this->overrideError;
1066
            $message = sprintf(
1067
                $message ?: 'Value "%s" expected to be string, type %s given.',
1068
                $this->stringify($this->value),
1069
                gettype($this->value)
1070
            );
1071
1072
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_STRING, $fieldName);
1073
        }
1074
1075
        return $this;
1076
    }
1077
1078
    /**
1079
     * Assert that value matches a provided Regex.
1080
     *
1081
     * @param string $pattern
1082
     * @param string $message
1083
     * @param string $fieldName
1084
     * @return Assert
1085
     * @throws AssertionFailedException
1086
     */
1087
    public function regex(string $pattern, string $message='', string $fieldName='') : Assert
1088
    {
1089
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1090
        {
1091
            return $this;
1092
        }
1093
        $this->string($message, $fieldName);
1094
        if ( ! preg_match($pattern, $this->value) )
1095
        {
1096
            $message = $message ?: $this->overrideError;
1097
            $message = sprintf(
1098
                $message ?: 'Value "%s" does not match expression.',
1099
                $this->stringify($this->value)
1100
            );
1101
1102
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_REGEX, $fieldName, ['pattern' => $pattern]);
1103
        }
1104
1105
        return $this;
1106
    }
1107
1108
    /**
1109
     * Assert that value is a valid IP address.
1110
     *
1111
     * @param string $message
1112
     * @param string $fieldName
1113
     * @return Assert
1114
     * @throws AssertionFailedException
1115
     */
1116
    public function ipAddress(string $message='', string $fieldName='') : Assert
1117
    {
1118
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1119
        {
1120
            return $this;
1121
        }
1122
        $this->string($message, $fieldName);
1123
        $pattern   = '/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/';
1124
        if ( ! preg_match($pattern, $this->value) )
1125
        {
1126
            $message = $message ?: $this->overrideError;
1127
            $message = sprintf(
1128
                $message ?: 'Value "%s" was expected to be a valid IP Address',
1129
                $this->stringify($this->value)
1130
            );
1131
1132
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_IP_ADDRESS, $fieldName);
1133
        }
1134
1135
        return $this;
1136
    }
1137
1138
    /**
1139
     * Assert that value does not match a provided Regex.
1140
     *
1141
     * @param string $pattern
1142
     * @param string $message
1143
     * @param string $fieldName
1144
     * @return Assert
1145
     * @throws AssertionFailedException
1146
     */
1147
    public function notRegex(string $pattern, string $message='', string $fieldName='') : Assert
1148
    {
1149
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1150
        {
1151
            return $this;
1152
        }
1153
        $this->string($message, $fieldName);
1154
        if ( preg_match($pattern, $this->value) )
1155
        {
1156
            $message = $message ?: $this->overrideError;
1157
            $message = sprintf(
1158
                $message ?: 'Value "%s" does not match expression.',
1159
                $this->stringify($this->value)
1160
            );
1161
1162
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_REGEX, $fieldName, ['pattern' => $pattern]);
1163
        }
1164
1165
        return $this;
1166
    }
1167
1168
    /**
1169
     * Assert that value is a string and has a character count which is equal to a given length.
1170
     *
1171
     * @param int    $length
1172
     * @param string $message
1173
     * @param string $fieldName
1174
     * @param string $encoding
1175
     * @return Assert
1176
     * @throws AssertionFailedException
1177
     */
1178 View Code Duplication
    public function length(int $length, string $message='', string $fieldName='', string $encoding='utf8') : Assert
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...
1179
    {
1180
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1181
        {
1182
            return $this;
1183
        }
1184
        $this->string($message, $fieldName);
1185
        if ( mb_strlen($this->value, $encoding) !== $length )
1186
        {
1187
            $message    = $message ?: $this->overrideError;
1188
            $message    = sprintf(
1189
                $message ?: 'Value "%s" has to be %d exactly characters long, but length is %d.',
1190
                $this->stringify($this->value),
1191
                $length,
1192
                mb_strlen($this->value, $encoding)
1193
            );
1194
            $constraints = ['length' => $length, 'encoding' => $encoding];
1195
1196
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_LENGTH, $fieldName, $constraints);
1197
        }
1198
1199
        return $this;
1200
    }
1201
1202
    /**
1203
     * Assert that value is a string and has a character count which is
1204
     * greater than or equal to a given lower limit ($minLength chars).
1205
     *
1206
     * @param int    $minLength
1207
     * @param string $message
1208
     * @param string $fieldName
1209
     * @param string $encoding
1210
     * @return Assert
1211
     * @throws AssertionFailedException
1212
     */
1213 View Code Duplication
    public function minLength(int $minLength, string $message='', string $fieldName='', string $encoding='utf8') : Assert
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...
1214
    {
1215
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1216
        {
1217
            return $this;
1218
        }
1219
        $this->string($message, $fieldName);
1220
        if ( mb_strlen($this->value, $encoding) < $minLength )
1221
        {
1222
            $message = $message ?: $this->overrideError;
1223
            $message     = sprintf(
1224
                $message
1225
                    ?: 'Value "%s" is too short, it should have more than %d characters, but only has %d characters.',
1226
                $this->stringify($this->value),
1227
                $minLength,
1228
                mb_strlen($this->value, $encoding)
1229
            );
1230
            $constraints = ['min_length' => $minLength, 'encoding' => $encoding];
1231
1232
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_MIN_LENGTH, $fieldName, $constraints);
1233
        }
1234
1235
        return $this;
1236
    }
1237
1238
    /**
1239
     * Assert that value is a string and has a character count which is
1240
     * less than or equal to given upper limit ($maxLength chars).
1241
     *
1242
     * @param int    $maxLength
1243
     * @param string $message
1244
     * @param string $fieldName
1245
     * @param string $encoding
1246
     * @return Assert
1247
     * @throws AssertionFailedException
1248
     */
1249 View Code Duplication
    public function maxLength(int $maxLength, string $message='', string $fieldName='', string $encoding='utf8') : Assert
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...
1250
    {
1251
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1252
        {
1253
            return $this;
1254
        }
1255
        $this->string($message, $fieldName);
1256
        if ( mb_strlen($this->value, $encoding) > $maxLength )
1257
        {
1258
            $message = $message ?: $this->overrideError;
1259
            $message     = sprintf(
1260
                $message ?: 'Value "%s" is too long, it should have no more than %d characters, but has %d characters.',
1261
                $this->stringify($this->value),
1262
                $maxLength,
1263
                mb_strlen($this->value, $encoding)
1264
            );
1265
            $constraints = ['max_length' => $maxLength, 'encoding' => $encoding];
1266
1267
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_MAX_LENGTH, $fieldName, $constraints);
1268
        }
1269
1270
        return $this;
1271
    }
1272
1273
    /**
1274
     * Assert that value has a length between min,max lengths (inclusive).
1275
     *
1276
     * @param int    $minLength
1277
     * @param int    $maxLength
1278
     * @param string $message
1279
     * @param string $fieldName
1280
     * @param string      $encoding
1281
     * @return Assert
1282
     * @throws AssertionFailedException
1283
     */
1284
    public function betweenLength(int $minLength, int $maxLength, string $message='', string $fieldName='', string $encoding='utf8') : Assert
1285
    {
1286
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1287
        {
1288
            return $this;
1289
        }
1290
        $this->string($message, $fieldName);
1291
        if ( mb_strlen($this->value, $encoding) < $minLength )
1292
        {
1293
            $message = $message ?: $this->overrideError;
1294
            $message     = sprintf(
1295
                $message
1296
                    ?: 'Value "%s" is too short, it should have more than %d characters, but only has %d characters.',
1297
                $this->stringify($this->value),
1298
                $minLength,
1299
                mb_strlen($this->value, $encoding)
1300
            );
1301
            $constraints = ['min_length' => $minLength, 'encoding' => $encoding];
1302
1303
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_MIN_LENGTH, $fieldName, $constraints);
1304
        }
1305
        if ( mb_strlen($this->value, $encoding) > $maxLength )
1306
        {
1307
            $message = $message ?: $this->overrideError;
1308
            $message     = sprintf(
1309
                $message ?: 'Value "%s" is too long, it should have no more than %d characters, but has %d characters.',
1310
                $this->stringify($this->value),
1311
                $maxLength,
1312
                mb_strlen($this->value, $encoding)
1313
            );
1314
            $constraints = ['max_length' => $maxLength, 'encoding' => $encoding];
1315
1316
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_MAX_LENGTH, $fieldName, $constraints);
1317
        }
1318
1319
        return $this;
1320
    }
1321
1322
    /**
1323
     * Assert that value starts with a sequence of chars.
1324
     *
1325
     * @param string $needle
1326
     * @param string $message
1327
     * @param string $fieldName
1328
     * @param string $encoding
1329
     * @return Assert
1330
     * @throws AssertionFailedException
1331
     */
1332 View Code Duplication
    public function startsWith(string $needle, string $message='', string $fieldName='', string $encoding='utf8') : Assert
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...
1333
    {
1334
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1335
        {
1336
            return $this;
1337
        }
1338
        $this->string($message, $fieldName);
1339
        if ( mb_strpos($this->value, $needle, 0, $encoding) !== 0 )
1340
        {
1341
            $message = $message ?: $this->overrideError;
1342
            $message     = sprintf(
1343
                $message ?: 'Value "%s" does not start with "%s".',
1344
                $this->stringify($this->value),
1345
                $this->stringify($needle)
1346
            );
1347
            $constraints = ['needle' => $needle, 'encoding' => $encoding];
1348
1349
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_STRING_START, $fieldName, $constraints);
1350
        }
1351
1352
        return $this;
1353
    }
1354
1355
    /**
1356
     * Assert that value ends with a sequence of chars.
1357
     *
1358
     * @param string $needle
1359
     * @param string $message
1360
     * @param string $fieldName
1361
     * @param string $encoding
1362
     * @return Assert
1363
     * @throws AssertionFailedException
1364
     */
1365
    public function endsWith(string $needle, string $message='', string $fieldName='', string $encoding='utf8') : Assert
1366
    {
1367
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1368
        {
1369
            return $this;
1370
        }
1371
        $this->string($message, $fieldName);
1372
        $stringPosition = mb_strlen($this->value, $encoding) - mb_strlen($needle, $encoding);
1373
        if ( mb_strripos($this->value, $needle, 0 , $encoding) !== $stringPosition )
1374
        {
1375
            $message = $message ?: $this->overrideError;
1376
            $message     = sprintf(
1377
                $message ?: 'Value "%s" does not end with "%s".',
1378
                $this->stringify($this->value),
1379
                $this->stringify($needle)
1380
            );
1381
            $constraints = ['needle' => $needle, 'encoding' => $encoding];
1382
1383
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_STRING_END, $fieldName, $constraints);
1384
        }
1385
1386
        return $this;
1387
    }
1388
1389
    /**
1390
     * Assert that value contains a sequence of chars.
1391
     *
1392
     * @param string $needle
1393
     * @param string $message
1394
     * @param string $fieldName
1395
     * @param string $encoding
1396
     * @return Assert
1397
     * @throws AssertionFailedException
1398
     */
1399 View Code Duplication
    public function contains(string $needle, string $message='', string $fieldName='', string $encoding='utf8') : Assert
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...
1400
    {
1401
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1402
        {
1403
            return $this;
1404
        }
1405
        $this->string($message, $fieldName);
1406
        if ( mb_strpos($this->value, $needle, 0, $encoding) === false )
1407
        {
1408
            $message = $message ?: $this->overrideError;
1409
            $message     = sprintf(
1410
                $message ?: 'Value "%s" does not contain "%s".',
1411
                $this->stringify($this->value),
1412
                $this->stringify($needle)
1413
            );
1414
            $constraints = ['needle' => $needle, 'encoding' => $encoding];
1415
1416
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_STRING_CONTAINS, $fieldName, $constraints);
1417
        }
1418
1419
        return $this;
1420
    }
1421
1422
    /**
1423
     * Assert that value is in an array of choices.
1424
     *
1425
     * @param array  $choices
1426
     * @param string $message
1427
     * @param string $fieldName
1428
     * @return Assert
1429
     * @throws AssertionFailedException
1430
     */
1431
    public function choice(array $choices, string $message='', string $fieldName='') : Assert
1432
    {
1433
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1434
        {
1435
            return $this;
1436
        }
1437
        if ( !in_array($this->value, $choices, true) )
1438
        {
1439
            $message = $message ?: $this->overrideError;
1440
            $message = sprintf(
1441
                $message ?: 'Value "%s" is not an element of the valid values: %s',
1442
                $this->stringify($this->value),
1443
                implode(", ", array_map('Terah\Assert\Assert::stringify', $choices))
1444
            );
1445
1446
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_CHOICE, $fieldName, ['choices' => $choices]);
1447
        }
1448
1449
        return $this;
1450
    }
1451
1452
    /**
1453
     * Alias of {@see choice()}
1454
     *
1455
     * @param array  $choices
1456
     * @param string $message
1457
     * @param string $fieldName
1458
     * @return Assert
1459
     * @throws AssertionFailedException
1460
     */
1461
    public function inArray(array $choices, string $message='', string $fieldName='') : Assert
1462
    {
1463
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1464
        {
1465
            return $this;
1466
        }
1467
        $this->choice($choices, $message, $fieldName);
1468
1469
        return $this;
1470
    }
1471
1472
    /**
1473
     * Assert that value is numeric.
1474
     *
1475
     * @param string $message
1476
     * @param string $fieldName
1477
     * @return Assert
1478
     * @throws AssertionFailedException
1479
     */
1480
    public function numeric(string $message='', string $fieldName='') : Assert
1481
    {
1482
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1483
        {
1484
            return $this;
1485
        }
1486
        if ( ! is_numeric($this->value) )
1487
        {
1488
            $message = $message ?: $this->overrideError;
1489
            $message = sprintf(
1490
                $message ?: 'Value "%s" is not numeric.',
1491
                $this->stringify($this->value)
1492
            );
1493
1494
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_NUMERIC, $fieldName);
1495
        }
1496
1497
        return $this;
1498
    }
1499
1500
    /**
1501
     * Assert that value is a non-empty array.
1502
     *
1503
     * @param string $message
1504
     * @param string $fieldName
1505
     * @return Assert
1506
     * @throws AssertionFailedException
1507
     */
1508
    public function nonEmptyArray(string $message='', string $fieldName='') : Assert
1509
    {
1510
        $message = $message ?: 'Value "%s" is not a non-empty array.';
1511
1512
        return $this->isArray($message, $fieldName)->notEmpty($message, $fieldName);
1513
    }
1514
1515
    /**
1516
     * Assert that value is a non-empty int.
1517
     *
1518
     * @param string $message
1519
     * @param string $fieldName
1520
     * @return Assert
1521
     * @throws AssertionFailedException
1522
     */
1523
    public function nonEmptyInt(string $message='', string $fieldName='') : Assert
1524
    {
1525
        $message = $message ?: 'Value "%s" is not a non-empty integer.';
1526
1527
        return $this->integer($message, $fieldName)->notEmpty($message, $fieldName);
1528
    }
1529
1530
    /**
1531
     * Assert that value is a non-empty string.
1532
     *
1533
     * @param string $message
1534
     * @param string $fieldName
1535
     * @return Assert
1536
     * @throws AssertionFailedException
1537
     */
1538
    public function nonEmptyString(string $message='', string $fieldName='') : Assert
1539
    {
1540
        $message = $message ?: 'Value "%s" is not a non-empty string.';
1541
1542
        return $this->string($message, $fieldName)->notEmpty($message, $fieldName);
1543
    }
1544
1545
    /**
1546
     * Assert that value is an array.
1547
     *
1548
     * @param string $message
1549
     * @param string $fieldName
1550
     * @return Assert
1551
     * @throws AssertionFailedException
1552
     */
1553
    public function isArray(string $message='', string $fieldName='') : Assert
1554
    {
1555
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1556
        {
1557
            return $this;
1558
        }
1559
        if ( !is_array($this->value) )
1560
        {
1561
            $message = $message ?: $this->overrideError;
1562
            $message = sprintf(
1563
                $message ?: 'Value "%s" is not an array.',
1564
                $this->stringify($this->value)
1565
            );
1566
1567
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_ARRAY, $fieldName);
1568
        }
1569
1570
        return $this;
1571
    }
1572
1573
    /**
1574
     * Assert that value is an array or a traversable object.
1575
     *
1576
     * @param string $message
1577
     * @param string $fieldName
1578
     * @return Assert
1579
     * @throws AssertionFailedException
1580
     */
1581
    public function isTraversable(string $message='', string $fieldName='') : Assert
1582
    {
1583
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1584
        {
1585
            return $this;
1586
        }
1587
        if ( !is_array($this->value) && !$this->value instanceof \Traversable )
1588
        {
1589
            $message = $message ?: $this->overrideError;
1590
            $message = sprintf(
1591
                $message ?: 'Value "%s" is not an array and does not implement Traversable.',
1592
                $this->stringify($this->value)
1593
            );
1594
1595
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_TRAVERSABLE, $fieldName);
1596
        }
1597
1598
        return $this;
1599
    }
1600
1601
    /**
1602
     * Assert that value is an array or an array-accessible object.
1603
     *
1604
     * @param string $message
1605
     * @param string $fieldName
1606
     * @return Assert
1607
     * @throws AssertionFailedException
1608
     */
1609
    public function isArrayAccessible(string $message='', string $fieldName='') : Assert
1610
    {
1611
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1612
        {
1613
            return $this;
1614
        }
1615
        if ( !is_array($this->value) && !$this->value instanceof \ArrayAccess )
1616
        {
1617
            $message = $message ?: $this->overrideError;
1618
            $message = sprintf(
1619
                $message ?: 'Value "%s" is not an array and does not implement ArrayAccess.',
1620
                $this->stringify($this->value)
1621
            );
1622
1623
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_ARRAY_ACCESSIBLE, $fieldName);
1624
        }
1625
1626
        return $this;
1627
    }
1628
1629
    /**
1630
     * Assert that key exists in the values array.
1631
     *
1632
     * @param string|integer $key
1633
     * @param string         $message
1634
     * @param string         $fieldName
1635
     * @return Assert
1636
     * @throws AssertionFailedException
1637
     */
1638
    public function keyExists($key, string $message='', string $fieldName='') : Assert
1639
    {
1640
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1641
        {
1642
            return $this;
1643
        }
1644
        $this->isArray($message, $fieldName);
1645
        if ( !array_key_exists($key, $this->value) )
1646
        {
1647
            $message = $message ?: $this->overrideError;
1648
            $message = sprintf(
1649
                $message ?: 'Array does not contain an element with key "%s"',
1650
                $this->stringify($key)
1651
            );
1652
1653
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_KEY_EXISTS, $fieldName, ['key' => $key]);
1654
        }
1655
1656
        return $this;
1657
    }
1658
1659
    /**
1660
     * Assert that keys exist in the values array.
1661
     *
1662
     * @param array  $keys
1663
     * @param string $message
1664
     * @param string $fieldName
1665
     * @return Assert
1666
     * @throws AssertionFailedException
1667
     */
1668
    public function keysExist(array $keys, string $message='', string $fieldName='') : Assert
1669
    {
1670
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1671
        {
1672
            return $this;
1673
        }
1674
        $this->isArray($message, $fieldName);
1675
        foreach ( $keys as $key )
1676
        {
1677
            if ( !array_key_exists($key, $this->value) )
1678
            {
1679
                $message = $message
1680
                    ?: sprintf(
1681
                        'Array does not contain an element with key "%s"',
1682
                        $this->stringify($key)
1683
                    );
1684
                throw $this->createException($message, $this->overrideCode ?: self::INVALID_KEYS_EXIST, $fieldName, ['key' => $key]);
1685
            }
1686
        }
1687
1688
        return $this;
1689
    }
1690
1691
    /**
1692
     * Assert that a property (key) exists in the values array.
1693
     *
1694
     * @param string|integer $key
1695
     * @param string|null    $message
1696
     * @param string|null    $fieldName
1697
     * @return Assert
1698
     * @throws AssertionFailedException
1699
     */
1700
    public function propertyExists($key, string $message='', string $fieldName='') : Assert
1701
    {
1702
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1703
        {
1704
            return $this;
1705
        }
1706
        $this->isObject($message, $fieldName);
1707
        if ( !property_exists($this->value, $key) && !isset( $this->value->{$key} ) )
1708
        {
1709
            $message = $message
1710
                ?: sprintf(
1711
                    'Object does not contain a property with key "%s"',
1712
                    $this->stringify($key)
1713
                );
1714
1715
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_PROPERTY_EXISTS, $fieldName, ['key' => $key]);
1716
        }
1717
1718
        return $this;
1719
    }
1720
1721
    /**
1722
     * Assert that properties (keys) exist in the values array.
1723
     *
1724
     * @param array  $keys
1725
     * @param string $message
1726
     * @param string $fieldName
1727
     * @return Assert
1728
     * @throws AssertionFailedException
1729
     */
1730
    public function propertiesExist(array $keys, string $message='', string $fieldName='') : Assert
1731
    {
1732
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1733
        {
1734
            return $this;
1735
        }
1736
        $this->isObject($message, $fieldName);
1737
        foreach ( $keys as $key )
1738
        {
1739
            // Using isset to allow resolution of magically defined properties
1740
            if ( !property_exists($this->value, $key) && !isset( $this->value->{$key} ) )
1741
            {
1742
                $message = $message
1743
                    ?: sprintf(
1744
                        'Object does not contain a property with key "%s"',
1745
                        $this->stringify($key)
1746
                    );
1747
                throw $this->createException($message, $this->overrideCode ?: self::INVALID_PROPERTIES_EXIST, $fieldName, ['key' => $key]);
1748
            }
1749
        }
1750
1751
        return $this;
1752
    }
1753
1754
    /**
1755
     * Assert that value is valid utf8.
1756
     *
1757
     * @param string $message
1758
     * @param string $fieldName
1759
     * @return Assert
1760
     * @throws AssertionFailedException
1761
     */
1762 View Code Duplication
    public function utf8(string $message='', string $fieldName='') : Assert
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...
1763
    {
1764
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1765
        {
1766
            return $this;
1767
        }
1768
        $this->string($message, $fieldName);
1769
        if ( mb_detect_encoding($this->value, 'UTF-8', true) !== 'UTF-8' )
1770
        {
1771
            $message = $message
1772
                ?: sprintf(
1773
                    'Value "%s" was expected to be a valid UTF8 string',
1774
                    $this->stringify($this->value)
1775
                );
1776
1777
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_UTF8, $fieldName);
1778
        }
1779
1780
        return $this;
1781
    }
1782
1783
1784
    /**
1785
     * Assert that value is valid ascii.
1786
     *
1787
     * @param string $message
1788
     * @param string $fieldName
1789
     * @return Assert
1790
     * @throws AssertionFailedException
1791
     */
1792 View Code Duplication
    public function ascii(string $message='', string $fieldName='') : Assert
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...
1793
    {
1794
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1795
        {
1796
            return $this;
1797
        }
1798
        $this->string($message, $fieldName);
1799
        if ( ! preg_match('/^[ -~]+$/', $this->value) )
1800
        {
1801
            $message = $message
1802
                ?: sprintf(
1803
                    'Value "%s" was expected to be a valid ASCII string',
1804
                    $this->stringify($this->value)
1805
                );
1806
1807
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_ASCII, $fieldName);
1808
        }
1809
1810
        return $this;
1811
    }
1812
1813
    /**
1814
     * Assert that key exists in an array/array-accessible object
1815
     * (using isset()).
1816
     *
1817
     * @param string|integer $key
1818
     * @param string|null    $message
1819
     * @param string|null    $fieldName
1820
     * @return Assert
1821
     * @throws AssertionFailedException
1822
     */
1823
    public function keyIsset($key, string $message='', string $fieldName='') : Assert
1824
    {
1825
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1826
        {
1827
            return $this;
1828
        }
1829
        $this->isArrayAccessible($message, $fieldName);
1830
        if ( !isset( $this->value[$key] ) )
1831
        {
1832
            $message = $message ?: $this->overrideError;
1833
            $message = sprintf(
1834
                $message ?: 'The element with key "%s" was not found',
1835
                $this->stringify($key)
1836
            );
1837
1838
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_KEY_ISSET, $fieldName, ['key' => $key]);
1839
        }
1840
1841
        return $this;
1842
    }
1843
1844
    /**
1845
     * Assert that key exists in an array/array-accessible object
1846
     * and its value is not empty.
1847
     *
1848
     * @param string|integer $key
1849
     * @param string|null    $message
1850
     * @param string|null    $fieldName
1851
     * @return Assert
1852
     * @throws AssertionFailedException
1853
     */
1854
    public function notEmptyKey($key, string $message='', string $fieldName='') : Assert
1855
    {
1856
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1857
        {
1858
            return $this;
1859
        }
1860
        $this->keyIsset($key, $message, $fieldName);
1861
        (new Assert($this->value[$key]))->setExceptionClass($this->exceptionClass)->notEmpty($message, $fieldName);
1862
1863
        return $this;
1864
    }
1865
1866
    /**
1867
     * Assert that value is not blank.
1868
     *
1869
     * @param string $message
1870
     * @param string $fieldName
1871
     * @return Assert
1872
     * @throws AssertionFailedException
1873
     */
1874 View Code Duplication
    public function notBlank(string $message='', string $fieldName='') : Assert
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...
1875
    {
1876
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1877
        {
1878
            return $this;
1879
        }
1880
        if ( false === $this->value || ( empty( $this->value ) && '0' != $this->value ) )
1881
        {
1882
            $message = $message ?: $this->overrideError;
1883
            $message = sprintf(
1884
                $message ?: 'Value "%s" is blank, but was expected to contain a value.',
1885
                $this->stringify($this->value)
1886
            );
1887
1888
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_NOT_BLANK, $fieldName);
1889
        }
1890
1891
        return $this;
1892
    }
1893
1894
    /**
1895
     * Assert that value is an instance of a given class-name.
1896
     *
1897
     * @param string $className
1898
     * @param string $message
1899
     * @param string $fieldName
1900
     * @return Assert
1901
     * @throws AssertionFailedException
1902
     */
1903
    public function isInstanceOf(string $className, string $message='', string $fieldName='') : Assert
1904
    {
1905
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1906
        {
1907
            return $this;
1908
        }
1909
        if ( !( $this->value instanceof $className ) )
1910
        {
1911
            $message = $message ?: $this->overrideError;
1912
            $message = sprintf(
1913
                $message ?: 'Class "%s" was expected to be instanceof of "%s" but is not.',
1914
                $this->stringify($this->value),
1915
                $className
1916
            );
1917
1918
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_INSTANCE_OF, $fieldName, ['class' => $className]);
1919
        }
1920
1921
        return $this;
1922
    }
1923
1924
    /**
1925
     * Assert that value is not an instance of given class-name.
1926
     *
1927
     * @param string $className
1928
     * @param string $message
1929
     * @param string $fieldName
1930
     * @return Assert
1931
     * @throws AssertionFailedException
1932
     */
1933
    public function notIsInstanceOf(string $className, string $message='', string $fieldName='') : Assert
1934
    {
1935
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1936
        {
1937
            return $this;
1938
        }
1939
        if ( $this->value instanceof $className )
1940
        {
1941
            $message = $message ?: $this->overrideError;
1942
            $message = sprintf(
1943
                $message ?: 'Class "%s" was not expected to be instanceof of "%s".',
1944
                $this->stringify($this->value),
1945
                $className
1946
            );
1947
1948
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_NOT_INSTANCE_OF, $fieldName, ['class' => $className]);
1949
        }
1950
1951
        return $this;
1952
    }
1953
1954
    /**
1955
     * Assert that value is a subclass of given class-name.
1956
     *
1957
     * @param string $className
1958
     * @param string $message
1959
     * @param string $fieldName
1960
     * @return Assert
1961
     * @throws AssertionFailedException
1962
     */
1963
    public function subclassOf(string $className, string $message='', string $fieldName='') : Assert
1964
    {
1965
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1966
        {
1967
            return $this;
1968
        }
1969
        if ( !is_subclass_of($this->value, $className) )
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if $className can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
1970
        {
1971
            $message = $message ?: $this->overrideError;
1972
            $message = sprintf(
1973
                $message ?: 'Class "%s" was expected to be subclass of "%s".',
1974
                $this->stringify($this->value),
1975
                $className
1976
            );
1977
1978
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_SUBCLASS_OF, $fieldName, ['class' => $className]);
1979
        }
1980
1981
        return $this;
1982
    }
1983
1984
    /**
1985
     * Assert that value is within a range of numbers (inclusive).
1986
     *
1987
     * @param float    $minValue
1988
     * @param float    $maxValue
1989
     * @param string $message
1990
     * @param string $fieldName
1991
     * @return Assert
1992
     * @throws AssertionFailedException
1993
     */
1994
    public function range(float $minValue, float $maxValue, string $message='', string $fieldName='') : Assert
1995
    {
1996
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1997
        {
1998
            return $this;
1999
        }
2000
        $this->numeric($message, $fieldName);
2001
        if ( $this->value < $minValue || $this->value > $maxValue )
2002
        {
2003
            $message = $message ?: $this->overrideError;
2004
            $message = sprintf(
2005
                $message ?: 'Number "%s" was expected to be at least "%d" and at most "%d".',
2006
                $this->stringify($this->value),
2007
                $this->stringify($minValue),
2008
                $this->stringify($maxValue)
2009
            );
2010
2011
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_RANGE, $fieldName, [
2012
                'min' => $minValue,
2013
                'max' => $maxValue
2014
            ]);
2015
        }
2016
2017
        return $this;
2018
    }
2019
2020
    /**
2021
     * Assert that value is larger or equal to a given lower limit.
2022
     *
2023
     * @param int    $minValue
2024
     * @param string $message
2025
     * @param string $fieldName
2026
     * @return Assert
2027
     * @throws AssertionFailedException
2028
     */
2029
    public function min(int $minValue, string $message='', string $fieldName='') : Assert
2030
    {
2031
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2032
        {
2033
            return $this;
2034
        }
2035
        $this->numeric($message, $fieldName);
2036
        if ( $this->value < $minValue )
2037
        {
2038
            $message = $message ?: $this->overrideError;
2039
            $message = sprintf(
2040
                $message ?: 'Number "%s" was expected to be at least "%d".',
2041
                $this->stringify($this->value),
2042
                $this->stringify($minValue)
2043
            );
2044
2045
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_MIN, $fieldName, ['min' => $minValue]);
2046
        }
2047
2048
        return $this;
2049
    }
2050
2051
    /**
2052
     * Assert that value is smaller than or equal to a given upper limit.
2053
     *
2054
     * @param int    $maxValue
2055
     * @param string $message
2056
     * @param string $fieldName
2057
     * @return Assert
2058
     * @throws AssertionFailedException
2059
     */
2060
    public function max(int $maxValue, string $message='', string $fieldName='') : Assert
2061
    {
2062
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2063
        {
2064
            return $this;
2065
        }
2066
        $this->numeric($message, $fieldName);
2067
        if ( $this->value > $maxValue )
2068
        {
2069
            $message = $message ?: $this->overrideError;
2070
            $message = sprintf(
2071
                $message ?: 'Number "%s" was expected to be at most "%d".',
2072
                $this->stringify($this->value),
2073
                $this->stringify($maxValue)
2074
            );
2075
2076
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_MAX, $fieldName, ['max' => $maxValue]);
2077
        }
2078
2079
        return $this;
2080
    }
2081
2082
    /**
2083
     * Assert that value is a file that exists.
2084
     *
2085
     * @param string $message
2086
     * @param string $fieldName
2087
     * @return Assert
2088
     * @throws AssertionFailedException
2089
     */
2090
    public function file(string $message='', string $fieldName='') : Assert
2091
    {
2092
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2093
        {
2094
            return $this;
2095
        }
2096
        $this->string($message, $fieldName);
2097
        $this->notEmpty($message, $fieldName);
2098
        if ( !is_file($this->value) )
2099
        {
2100
            $message = $message ?: $this->overrideError;
2101
            $message = sprintf(
2102
                $message ?: 'File "%s" was expected to exist.',
2103
                $this->stringify($this->value)
2104
            );
2105
2106
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_FILE, $fieldName);
2107
        }
2108
2109
        return $this;
2110
    }
2111
2112
    /**
2113
     * Assert that value is a file or directory that exists.
2114
     *
2115
     * @param string $message
2116
     * @param string $fieldName
2117
     * @return Assert
2118
     * @throws AssertionFailedException
2119
     */
2120
    public function fileOrDirectoryExists(string $message='', string $fieldName='') : Assert
2121
    {
2122
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2123
        {
2124
            return $this;
2125
        }
2126
        $this->string($message, $fieldName);
2127
        $this->notEmpty($message, $fieldName);
2128
        if ( ! file_exists($this->value) )
2129
        {
2130
            $message = $message ?: $this->overrideError;
2131
            $message = sprintf(
2132
                $message ?: 'File or directory "%s" was expected to exist.',
2133
                $this->stringify($this->value)
2134
            );
2135
2136
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_FILE_OR_DIR, $fieldName);
2137
        }
2138
2139
        return $this;
2140
    }
2141
2142
    /**
2143
     * Assert that value is a directory that exists.
2144
     *
2145
     * @param string $message
2146
     * @param string $fieldName
2147
     * @return Assert
2148
     * @throws AssertionFailedException
2149
     */
2150 View Code Duplication
    public function directory(string $message='', string $fieldName='') : Assert
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...
2151
    {
2152
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2153
        {
2154
            return $this;
2155
        }
2156
        $this->string($message, $fieldName);
2157
        if ( !is_dir($this->value) )
2158
        {
2159
            $message = $message ?: $this->overrideError;
2160
            $message = sprintf(
2161
                $message ?: 'Path "%s" was expected to be a directory.',
2162
                $this->stringify($this->value)
2163
            );
2164
2165
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_DIRECTORY, $fieldName);
2166
        }
2167
2168
        return $this;
2169
    }
2170
2171
    /**
2172
     * Assert that value is something readable.
2173
     *
2174
     * @param string $message
2175
     * @param string $fieldName
2176
     * @return Assert
2177
     * @throws AssertionFailedException
2178
     */
2179 View Code Duplication
    public function readable(string $message='', string $fieldName='') : Assert
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...
2180
    {
2181
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2182
        {
2183
            return $this;
2184
        }
2185
        $this->string($message, $fieldName);
2186
        if ( !is_readable($this->value) )
2187
        {
2188
            $message = $message ?: $this->overrideError;
2189
            $message = sprintf(
2190
                $message ?: 'Path "%s" was expected to be readable.',
2191
                $this->stringify($this->value)
2192
            );
2193
2194
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_READABLE, $fieldName);
2195
        }
2196
2197
        return $this;
2198
    }
2199
2200
    /**
2201
     * Assert that value is something writeable.
2202
     *
2203
     * @param string $message
2204
     * @param string $fieldName
2205
     * @return Assert
2206
     * @throws AssertionFailedException
2207
     */
2208 View Code Duplication
    public function writeable(string $message='', string $fieldName='') : Assert
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...
2209
    {
2210
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2211
        {
2212
            return $this;
2213
        }
2214
        $this->string($message, $fieldName);
2215
        if ( !is_writeable($this->value) )
2216
        {
2217
            $message = $message ?: $this->overrideError;
2218
            $message = sprintf(
2219
                $message ?: 'Path "%s" was expected to be writeable.',
2220
                $this->stringify($this->value)
2221
            );
2222
2223
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_WRITEABLE, $fieldName);
2224
        }
2225
2226
        return $this;
2227
    }
2228
2229
    /**
2230
     * Assert that value is a valid email address (using input_filter/FILTER_VALIDATE_EMAIL).
2231
     *
2232
     * @param string $message
2233
     * @param string $fieldName
2234
     * @return Assert
2235
     * @throws AssertionFailedException
2236
     */
2237
    public function email(string $message='', string $fieldName='') : Assert
2238
    {
2239
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2240
        {
2241
            return $this;
2242
        }
2243
        $this->string($message, $fieldName);
2244
        if ( ! filter_var($this->value, FILTER_VALIDATE_EMAIL) )
2245
        {
2246
            $message = $message ?: $this->overrideError;
2247
            $message = sprintf(
2248
                $message ?: 'Value "%s" was expected to be a valid e-mail address.',
2249
                $this->stringify($this->value)
2250
            );
2251
2252
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_EMAIL, $fieldName);
2253
        }
2254
        else
2255
        {
2256
            $host = substr($this->value, strpos($this->value, '@') + 1);
2257
            // Likely not a FQDN, bug in PHP FILTER_VALIDATE_EMAIL prior to PHP 5.3.3
2258
            if ( version_compare(PHP_VERSION, '5.3.3', '<') && strpos($host, '.') === false )
2259
            {
2260
                $message = $message ?: $this->overrideError;
2261
                $message = sprintf(
2262
                    $message ?: 'Value "%s" was expected to be a valid e-mail address.',
2263
                    $this->stringify($this->value)
2264
                );
2265
                throw $this->createException($message, $this->overrideCode ?: self::INVALID_EMAIL, $fieldName);
2266
            }
2267
        }
2268
2269
        return $this;
2270
    }
2271
2272
    /**
2273
     * Assert that value is a valid email prefix.
2274
     *
2275
     * @param string $message
2276
     * @param string $fieldName
2277
     * @return Assert
2278
     * @throws AssertionFailedException
2279
     */
2280
    public function emailPrefix(string $message='', string $fieldName='') : Assert
2281
    {
2282
        $this->value($this->value . '@example.com');
2283
2284
        return $this->email($message, $fieldName);
2285
    }
2286
2287
    /**
2288
     * Assert that value is a valid URL.
2289
     *
2290
     * This code snipped was taken from the Symfony project and modified to the special demands of this method.
2291
     *
2292
     * @param string $message
2293
     * @param string $fieldName
2294
     * @return Assert
2295
     * @throws AssertionFailedException
2296
     *
2297
     *
2298
     * @link https://github.com/symfony/Validator/blob/master/Constraints/UrlValidator.php
2299
     * @link https://github.com/symfony/Validator/blob/master/Constraints/Url.php
2300
     */
2301
    public function url(string $message='', string $fieldName='') : Assert
2302
    {
2303
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2304
        {
2305
            return $this;
2306
        }
2307
        $this->string($message, $fieldName);
2308
        $protocols = ['http', 'https'];
2309
        $pattern   = '~^
2310
            (%s)://                                 # protocol
2311
            (
2312
                ([\pL\pN\pS-]+\.)+[\pL]+                   # a domain name
2313
                    |                                     #  or
2314
                \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}      # a IP address
2315
                    |                                     #  or
2316
                \[
2317
                    (?:(?:(?:(?:(?:(?:(?:[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})))?::))))
2318
                \]  # a IPv6 address
2319
            )
2320
            (:[0-9]+)?                              # a port (optional)
2321
            (/?|/\S+)                               # a /, nothing or a / with something
2322
        $~ixu';
2323
        $pattern   = sprintf($pattern, implode('|', $protocols));
2324
        if ( !preg_match($pattern, $this->value) )
2325
        {
2326
            $message = $message ?: $this->overrideError;
2327
            $message = sprintf(
2328
                $message ?: 'Value "%s" was expected to be a valid URL starting with http or https',
2329
                $this->stringify($this->value)
2330
            );
2331
2332
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_URL, $fieldName);
2333
        }
2334
2335
        return $this;
2336
    }
2337
2338
    /**
2339
     * Assert that value is domain name.
2340
     *
2341
     * This code snipped was taken from the Symfony project and modified to the special demands of this method.
2342
     *
2343
     * @param string $message
2344
     * @param string $fieldName
2345
     * @return Assert
2346
     * @throws AssertionFailedException
2347
     *
2348
     */
2349
    public function domainName(string $message='', string $fieldName='') : Assert
2350
    {
2351
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2352
        {
2353
            return $this;
2354
        }
2355
        $this->string($message, $fieldName);
2356
        $pattern   = '/^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}$/';
2357
        if ( ! preg_match($pattern, $this->value) )
2358
        {
2359
            $message = $message ?: $this->overrideError;
2360
            $message = sprintf(
2361
                $message ?: 'Value "%s" was expected to be a valid domain name',
2362
                $this->stringify($this->value)
2363
            );
2364
2365
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_DOMAIN_NAME, $fieldName);
2366
        }
2367
2368
        return $this;
2369
    }
2370
2371
    /**
2372
     * Assert that string is Australian Mobile Number
2373
     *
2374
     * @param string $message
2375
     * @param string $fieldName
2376
     * @return $this
2377
     * @throws AssertionFailedException
2378
     */
2379 View Code Duplication
    public function ausMobile(string $message='', string $fieldName='') : Assert
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...
2380
    {
2381
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2382
        {
2383
            return $this;
2384
        }
2385
        $this->string($message, $fieldName);
2386
        if ( ! static::isAusMobile($this->value) )
2387
        {
2388
            $message = $message ?: $this->overrideError;
2389
            $message = sprintf(
2390
                $message
2391
                    ?: 'Value "%s" is not an australian mobile number.',
2392
                $this->stringify($this->value)
2393
            );
2394
2395
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_AUS_MOBILE, $fieldName);
2396
        }
2397
2398
        return $this;
2399
    }
2400
2401
    /**
2402
     * @param string $value
2403
     * @return bool
2404
     */
2405
    public static function isAusMobile(string $value) : bool
2406
    {
2407
        return preg_match('/^04[0-9]{8}$/', $value) ? true : false;
2408
    }
2409
2410
    /**
2411
     * Assert that value is alphanumeric.
2412
     *
2413
     * @param string $message
2414
     * @param string $fieldName
2415
     * @return Assert
2416
     * @throws AssertionFailedException
2417
     */
2418 View Code Duplication
    public function alnum(string $message='', string $fieldName='') : Assert
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...
2419
    {
2420
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2421
        {
2422
            return $this;
2423
        }
2424
        try
2425
        {
2426
            $this->regex('(^([a-zA-Z]{1}[a-zA-Z0-9]*)$)', $message, $fieldName);
2427
        }
2428
        catch (AssertionFailedException $e)
2429
        {
2430
            $message = $message ?: $this->overrideError;
2431
            $message = sprintf(
2432
                $message
2433
                    ?: 'Value "%s" is not alphanumeric, starting with letters and containing only letters and numbers.',
2434
                $this->stringify($this->value)
2435
            );
2436
2437
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_ALNUM, $fieldName);
2438
        }
2439
2440
        return $this;
2441
    }
2442
2443
    /**
2444
     * Assert that value is boolean True.
2445
     *
2446
     * @param string $message
2447
     * @param string $fieldName
2448
     * @return Assert
2449
     * @throws AssertionFailedException
2450
     */
2451
    public function true(string $message='', string $fieldName='') : Assert
2452
    {
2453
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2454
        {
2455
            return $this;
2456
        }
2457
        if ( $this->value !== true )
2458
        {
2459
            $message = $message ?: $this->overrideError;
2460
            $message = sprintf(
2461
                $message ?: 'Value "%s" is not TRUE.',
2462
                $this->stringify($this->value)
2463
            );
2464
2465
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_TRUE, $fieldName);
2466
        }
2467
2468
        return $this;
2469
    }
2470
2471
    /**
2472
     * Assert that value is boolean True.
2473
     *
2474
     * @param string $message
2475
     * @param string $fieldName
2476
     * @return Assert
2477
     * @throws AssertionFailedException
2478
     */
2479
    public function truthy(string $message='', string $fieldName='') : Assert
2480
    {
2481
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2482
        {
2483
            return $this;
2484
        }
2485
        if ( ! $this->value )
2486
        {
2487
            $message = $message ?: $this->overrideError;
2488
            $message = sprintf(
2489
                $message ?: 'Value "%s" is not truthy.',
2490
                $this->stringify($this->value)
2491
            );
2492
2493
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_TRUE, $fieldName);
2494
        }
2495
2496
        return $this;
2497
    }
2498
2499
    /**
2500
     * Assert that value is boolean False.
2501
     *
2502
     * @param string $message
2503
     * @param string $fieldName
2504
     * @return Assert
2505
     * @throws AssertionFailedException
2506
     */
2507
    public function false(string $message='', string $fieldName='') : Assert
2508
    {
2509
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2510
        {
2511
            return $this;
2512
        }
2513
        if ( $this->value !== false )
2514
        {
2515
            $message = $message ?: $this->overrideError;
2516
            $message = sprintf(
2517
                $message ?: 'Value "%s" is not FALSE.',
2518
                $this->stringify($this->value)
2519
            );
2520
2521
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_FALSE, $fieldName);
2522
        }
2523
2524
        return $this;
2525
    }
2526
2527
    /**
2528
     * Assert that value is not boolean False.
2529
     *
2530
     * @param string $message
2531
     * @param string $fieldName
2532
     * @return Assert
2533
     * @throws AssertionFailedException
2534
     */
2535
    public function notFalse(string $message='', string $fieldName='') : Assert
2536
    {
2537
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2538
        {
2539
            return $this;
2540
        }
2541
        if ( $this->value === false )
2542
        {
2543
            $message = $message ?: $this->overrideError;
2544
            $message = sprintf(
2545
                $message ?: 'Value "%s" is not FALSE.',
2546
                $this->stringify($this->value)
2547
            );
2548
2549
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_NOT_FALSE, $fieldName);
2550
        }
2551
2552
        return $this;
2553
    }
2554
2555
    /**
2556
     * Assert that the class exists.
2557
     *
2558
     * @param string $message
2559
     * @param string $fieldName
2560
     * @return Assert
2561
     * @throws AssertionFailedException
2562
     */
2563
    public function classExists(string $message='', string $fieldName='') : Assert
2564
    {
2565
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2566
        {
2567
            return $this;
2568
        }
2569
        if ( !class_exists($this->value) )
2570
        {
2571
            $message = $message ?: $this->overrideError;
2572
            $message = sprintf(
2573
                $message ?: 'Class "%s" does not exist.',
2574
                $this->stringify($this->value)
2575
            );
2576
2577
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_CLASS, $fieldName);
2578
        }
2579
2580
        return $this;
2581
    }
2582
2583
    /**
2584
     * @param string $interfaceName
2585
     * @param string $message
2586
     * @param string $fieldName
2587
     * @return Assert
2588
     * @throws AssertionFailedException
2589
     * @throws \ReflectionException
2590
     */
2591
    public function implementsInterface(string $interfaceName, string $message='', string $fieldName='') : Assert
2592
    {
2593
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2594
        {
2595
            return $this;
2596
        }
2597
        $reflection = new \ReflectionClass($this->value);
2598
        if ( !$reflection->implementsInterface($interfaceName) )
2599
        {
2600
            $message = $message ?: $this->overrideError;
2601
            $message = sprintf(
2602
                $message ?: 'Class "%s" does not implement interface "%s".',
2603
                $this->stringify($this->value),
2604
                $this->stringify($interfaceName)
2605
            );
2606
2607
            throw $this->createException($message, self::INTERFACE_NOT_IMPLEMENTED, $fieldName, ['interface' => $interfaceName]);
2608
        }
2609
2610
        return $this;
2611
    }
2612
2613
    /**
2614
     * Assert that value is a valid json string.
2615
     *
2616
     * NOTICE:
2617
     * Since this does a json_decode to determine its validity
2618
     * you probably should consider, when using the variable
2619
     * content afterwards, just to decode and check for yourself instead
2620
     * of using this assertion.
2621
     *
2622
     * @param string $message
2623
     * @param string $fieldName
2624
     * @return Assert
2625
     * @throws AssertionFailedException
2626
     */
2627
    public function isJsonString(string $message='', string $fieldName='') : Assert
2628
    {
2629
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2630
        {
2631
            return $this;
2632
        }
2633 View Code Duplication
        if ( null === json_decode($this->value) && JSON_ERROR_NONE !== json_last_error() )
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
2634
        {
2635
            $message = $message ?: $this->overrideError;
2636
            $message = sprintf(
2637
                $message ?: 'Value "%s" is not a valid JSON string.',
2638
                $this->stringify($this->value)
2639
            );
2640
2641
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_JSON_STRING, $fieldName);
2642
        }
2643
2644
        return $this;
2645
    }
2646
2647
    /**
2648
     * Assert that value is a valid UUID.
2649
     *
2650
     * Uses code from {@link https://github.com/ramsey/uuid} that is MIT licensed.
2651
     *
2652
     * @param string $message
2653
     * @param string $fieldName
2654
     * @return Assert
2655
     * @throws AssertionFailedException
2656
     */
2657
    public function uuid(string $message='', string $fieldName='') : Assert
2658
    {
2659
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2660
        {
2661
            return $this;
2662
        }
2663
        $this->value = str_replace(['urn:', 'uuid:', '{', '}'], '', $this->value);
2664
        if ( $this->value === '00000000-0000-0000-0000-000000000000' )
2665
        {
2666
            return $this;
2667
        }
2668
        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) )
2669
        {
2670
            $message = $message ?: $this->overrideError;
2671
            $message = sprintf(
2672
                $message ?: 'Value "%s" is not a valid UUID.',
2673
                $this->stringify($this->value)
2674
            );
2675
2676
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_UUID, $fieldName);
2677
        }
2678
2679
        return $this;
2680
    }
2681
    /**
2682
     * Assert that value is a valid samAccountName (in line with Active
2683
     * directory sAMAccountName restrictions for users).
2684
     *
2685
     * From: @link https://social.technet.microsoft.com/wiki/contents/articles/11216.active-directory-requirements-for-creating-objects.aspx#Objects_with_sAMAccountName_Attribute
2686
     *      The schema allows 256 characters in sAMAccountName values. However, the system limits sAMAccountName to
2687
     *      20 characters for user objects and 16 characters for computer objects. The following characters are not
2688
     *      allowed in sAMAccountName values: " [ ] : ; | = + * ? < > / \ ,
2689
     *      You cannot logon to a domain using a sAMAccountName that includes the "@" character. If a user has a
2690
     *      sAMAccountName with this character, they must logon using their userPrincipalName (UPN).
2691
     *
2692
     * @param string $message
2693
     * @param string $fieldName
2694
     * @return Assert
2695
     * @throws AssertionFailedException
2696
     */
2697 View Code Duplication
    public function samAccountName(string $message='', string $fieldName='') : Assert
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...
2698
    {
2699
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2700
        {
2701
            return $this;
2702
        }
2703
        if ( !preg_match('/^([a-z0-9]{4,20})$/', $this->value) )
2704
        {
2705
            $message = $message ?: $this->overrideError;
2706
            $message = sprintf(
2707
                $message ?: 'Value "%s" is not a valid samAccountName.',
2708
                $this->stringify($this->value)
2709
            );
2710
2711
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_SAMACCOUNTNAME, $fieldName);
2712
        }
2713
2714
        return $this;
2715
    }
2716
2717
    /**
2718
     * Assert that value is a valid userPrincipalName.
2719
     *
2720
     * @param string $message
2721
     * @param string $fieldName
2722
     * @return Assert
2723
     * @throws AssertionFailedException
2724
     */
2725 View Code Duplication
    public function userPrincipalName(string $message='', string $fieldName='') : Assert
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...
2726
    {
2727
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2728
        {
2729
            return $this;
2730
        }
2731
        try
2732
        {
2733
            $this->email($message, $fieldName);
2734
        }
2735
        catch (AssertionFailedException $e)
2736
        {
2737
            $message = $message ?: $this->overrideError;
2738
            $message = sprintf(
2739
                $message
2740
                    ?: 'Value "%s" is not a valid userPrincipalName.',
2741
                $this->stringify($this->value)
2742
            );
2743
2744
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_USERPRINCIPALNAME, $fieldName);
2745
        }
2746
2747
        return $this;
2748
    }
2749
2750
    /**
2751
     * Assert that the given string is a valid UUID
2752
     *
2753
     * Uses code from {@link https://github.com/ramsey/uuid} that is MIT licensed.
2754
     *
2755
     * @param string $message
2756
     * @param string $fieldName
2757
     * @return Assert
2758
     * @throws AssertionFailedException
2759
     */
2760
    public function isni(string $message='', string $fieldName='')
2761
    {
2762
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2763
        {
2764
            return $this;
2765
        }
2766
        if ( !preg_match('/^[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{3}[0-9X]{1}$/', $this->value) )
2767
        {
2768
            $message = $message ?: $this->overrideError;
2769
            $message = sprintf(
2770
                $message ?: 'Value "%s" is not a valid ISNI.',
2771
                $this->stringify($this->value)
2772
            );
2773
2774
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_ISNI, $fieldName);
2775
        }
2776
2777
        return $this;
2778
    }
2779
2780
    /**
2781
     * Assert that the count of countable is equal to count.
2782
     *
2783
     * @param int    $count
2784
     * @param string $message
2785
     * @param string $fieldName
2786
     * @return Assert
2787
     * @throws AssertionFailedException
2788
     */
2789
    public function count(int $count, string $message='', string $fieldName='') : Assert
2790
    {
2791
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2792
        {
2793
            return $this;
2794
        }
2795
        if ( $count !== count($this->value) )
2796
        {
2797
            $message = $message ?: $this->overrideError;
2798
            $message = sprintf(
2799
                $message ?: 'List does not contain exactly "%d" elements.',
2800
                $this->stringify($this->value),
2801
                $this->stringify($count)
2802
            );
2803
2804
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_COUNT, $fieldName, ['count' => $count]);
2805
        }
2806
2807
        return $this;
2808
    }
2809
2810
    /**
2811
     * @param $func
2812
     * @param $args
2813
     * @return bool
2814
     * @throws AssertionFailedException
2815
     */
2816
    protected function doAllOrNullOr($func, $args) : bool
2817
    {
2818
        if ( $this->nullOr && is_null($this->value) )
2819
        {
2820
            return true;
2821
        }
2822
        if ( $this->emptyOr && empty($this->value) )
2823
        {
2824
            return true;
2825
        }
2826
        if ( $this->all && (new Assert($this->value))->setExceptionClass($this->exceptionClass)->isTraversable() )
2827
        {
2828
            foreach ( $this->value as $idx => $value )
2829
            {
2830
                $object = (new Assert($value))->setExceptionClass($this->exceptionClass);
2831
                call_user_func_array([$object, $func], $args);
2832
            }
2833
            return true;
2834
        }
2835
2836
        return ( $this->nullOr && is_null($this->value) ) || ( $this->emptyOr && empty($this->value) ) ? true : false;
2837
    }
2838
2839
    /**
2840
     * Assert if values array has every choice as key and that this choice has content.
2841
     *
2842
     * @param array  $choices
2843
     * @param string $message
2844
     * @param string $fieldName
2845
     * @return Assert
2846
     * @throws AssertionFailedException
2847
     */
2848
    public function choicesNotEmpty(array $choices, string $message='', string $fieldName='') : Assert
2849
    {
2850
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2851
        {
2852
            return $this;
2853
        }
2854
        $this->notEmpty($message, $fieldName);
2855
        foreach ( $choices as $choice )
2856
        {
2857
            $this->notEmptyKey($choice, $message, $fieldName);
2858
        }
2859
2860
        return $this;
2861
    }
2862
2863
    /**
2864
     * Assert that the named method is defined in the provided object.
2865
     *
2866
     * @param mixed $object
2867
     * @param string $message
2868
     * @param string $fieldName
2869
     * @return Assert
2870
     * @throws AssertionFailedException
2871
     */
2872
    public function methodExists($object, string $message='', string $fieldName='') : Assert
2873
    {
2874
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2875
        {
2876
            return $this;
2877
        }
2878
        (new Assert($object))->setExceptionClass($this->exceptionClass)->isObject($message, $fieldName);
2879
        if ( !method_exists($object, $this->value) )
2880
        {
2881
            $message = $message ?: $this->overrideError;
2882
            $message = sprintf(
2883
                $message ?: 'Expected "%s" does not a exist in provided object.',
2884
                $this->stringify($this->value)
2885
            );
2886
2887
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_METHOD, $fieldName);
2888
        }
2889
2890
        return $this;
2891
    }
2892
2893
    /**
2894
     * Assert that value is an object.
2895
     *
2896
     * @param string $message
2897
     * @param string $fieldName
2898
     * @return Assert
2899
     * @throws AssertionFailedException
2900
     */
2901
    public function isObject(string $message='', string $fieldName='') : Assert
2902
    {
2903
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2904
        {
2905
            return $this;
2906
        }
2907
        if ( !is_object($this->value) )
2908
        {
2909
            $message = $message ?: $this->overrideError;
2910
            $message = sprintf(
2911
                $message ?: 'Provided "%s" is not a valid object.',
2912
                $this->stringify($this->value)
2913
            );
2914
2915
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_OBJECT, $fieldName);
2916
        }
2917
2918
        return $this;
2919
    }
2920
2921
    /**
2922
     * Make a string version of a value.
2923
     *
2924
     * @param $value
2925
     * @return string
2926
     */
2927
    private function stringify($value) : string
2928
    {
2929
        if ( is_bool($value) )
2930
        {
2931
            return $value ? '<TRUE>' : '<FALSE>';
2932
        }
2933
        if ( is_scalar($value) )
2934
        {
2935
            $val = (string)$value;
2936
            if ( strlen($val) > 100 )
2937
            {
2938
                $val = substr($val, 0, 97) . '...';
2939
            }
2940
2941
            return $val;
2942
        }
2943
        if ( is_array($value) )
2944
        {
2945
            return '<ARRAY>';
2946
        }
2947
        if ( is_object($value) )
2948
        {
2949
            return get_class($value);
2950
        }
2951
        if ( is_resource($value) )
2952
        {
2953
            return '<RESOURCE>';
2954
        }
2955
        if ( $value === null )
2956
        {
2957
            return '<NULL>';
2958
        }
2959
2960
        return 'unknown';
2961
    }
2962
}
2963
2964