Completed
Pull Request — master (#5)
by
unknown
01:38
created

Assert::startsWith()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 20
Code Lines 13

Duplication

Lines 20
Ratio 100 %

Importance

Changes 0
Metric Value
cc 6
eloc 13
nc 4
nop 4
dl 20
loc 20
rs 8.8571
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                 = 9;
27
    const INVALID_INTEGER               = 10;
28
    const INVALID_DIGIT                 = 11;
29
    const INVALID_INTEGERISH            = 12;
30
    const INVALID_BOOLEAN               = 13;
31
    const VALUE_EMPTY                   = 14;
32
    const VALUE_NULL                    = 15;
33
    const INVALID_STRING                = 16;
34
    const INVALID_REGEX                 = 17;
35
    const INVALID_MIN_LENGTH            = 18;
36
    const INVALID_MAX_LENGTH            = 19;
37
    const INVALID_STRING_START          = 20;
38
    const INVALID_STRING_CONTAINS       = 21;
39
    const INVALID_CHOICE                = 22;
40
    const INVALID_NUMERIC               = 23;
41
    const INVALID_ARRAY                 = 24;
42
    const INVALID_KEY_EXISTS            = 26;
43
    const INVALID_NOT_BLANK             = 27;
44
    const INVALID_INSTANCE_OF           = 28;
45
    const INVALID_SUBCLASS_OF           = 29;
46
    const INVALID_RANGE                 = 30;
47
    const INVALID_ALNUM                 = 31;
48
    const INVALID_TRUE                  = 32;
49
    const INVALID_EQ                    = 33;
50
    const INVALID_SAME                  = 34;
51
    const INVALID_MIN                   = 35;
52
    const INVALID_MAX                   = 36;
53
    const INVALID_LENGTH                = 37;
54
    const INVALID_FALSE                 = 38;
55
    const INVALID_STRING_END            = 39;
56
    const INVALID_UUID                  = 40;
57
    const INVALID_COUNT                 = 41;
58
    const INVALID_NOT_EQ                = 42;
59
    const INVALID_NOT_SAME              = 43;
60
    const INVALID_TRAVERSABLE           = 44;
61
    const INVALID_ARRAY_ACCESSIBLE      = 45;
62
    const INVALID_KEY_ISSET             = 46;
63
    const INVALID_SAMACCOUNTNAME        = 47;
64
    const INVALID_USERPRINCIPALNAME     = 48;
65
    const INVALID_DIRECTORY             = 101;
66
    const INVALID_FILE                  = 102;
67
    const INVALID_READABLE              = 103;
68
    const INVALID_WRITEABLE             = 104;
69
    const INVALID_CLASS                 = 105;
70
    const INVALID_EMAIL                 = 201;
71
    const INTERFACE_NOT_IMPLEMENTED     = 202;
72
    const INVALID_URL                   = 203;
73
    const INVALID_NOT_INSTANCE_OF       = 204;
74
    const VALUE_NOT_EMPTY               = 205;
75
    const INVALID_JSON_STRING           = 206;
76
    const INVALID_OBJECT                = 207;
77
    const INVALID_METHOD                = 208;
78
    const INVALID_SCALAR                = 209;
79
    const INVALID_DATE                  = 210;
80
    const INVALID_CALLABLE              = 211;
81
    const INVALID_KEYS_EXIST            = 300;
82
    const INVALID_PROPERTY_EXISTS       = 301;
83
    const INVALID_PROPERTIES_EXIST      = 302;
84
    const INVALID_UTF8                  = 303;
85
    const INVALID_DOMAIN_NAME           = 304;
86
    const INVALID_NOT_FALSE             = 305;
87
    const INVALID_FILE_OR_DIR           = 306;
88
    const INVALID_ASCII                 = 307;
89
    const INVALID_NOT_REGEX             = 308;
90
    const INVALID_GREATER_THAN          = 309;
91
    const INVALID_LESS_THAN             = 310;
92
    const INVALID_GREATER_THAN_OR_EQ    = 311;
93
    const INVALID_LESS_THAN_OR_EQ       = 312;
94
    const INVALID_IP_ADDRESS            = 313;
95
    const INVALID_AUS_MOBILE            = 314;
96
97
    const EMERGENCY                     = 'emergency';
98
    const ALERT                         = 'alert';
99
    const CRITICAL                      = 'critical';
100
    const ERROR                         = 'error';
101
    const WARNING                       = 'warning';
102
    const NOTICE                        = 'notice';
103
    const INFO                          = 'info';
104
    const DEBUG                         = 'debug';
105
106
    /** @var bool */
107
    protected $nullOr                   = false;
108
109
    /** @var bool */
110
    protected $emptyOr                  = false;
111
112
    /** @var mixed */
113
    protected $value                    = null;
114
115
    /** @var bool */
116
    protected $all                      = false;
117
118
    /** @var null|string */
119
    protected $fieldName                = null;
120
121
    /** @var null|string */
122
    protected $propertyPath             = null;
123
124
    /** @var null|string */
125
    protected $level                    = 'critical';
126
127
    /** @var int */
128
    protected $overrideCode             = null;
129
130
    /** @var string */
131
    protected $overrideError            = '';
132
    /**
133
     * Exception to throw when an assertion failed.
134
     *
135
     * @var string
136
     */
137
    protected $exceptionClass           = 'Terah\Assert\AssertionFailedException';
138
139
    /**
140
     * @param mixed $value
141
     */
142
    public function __construct($value)
143
    {
144
        $this->value($value);
145
    }
146
    
147
    
148
    /**
149
     * @param \Closure[] $validators
150
     * @return array
151
     */
152 View Code Duplication
    public static function runValidators(array $validators) : array
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...
153
    {
154
        $errors = [];
155
        foreach ( $validators as $fieldName => $validator )
156
        {
157
            try
158
            {
159
                $validator->__invoke();
160
            }
161
            catch ( AssertionFailedException $e )
162
            {
163
                $errors[$fieldName]     = $e->getMessage();
164
            }
165
        }
166
167
        return $errors;
168
    }
169
170
    /**
171
     * @param \Closure[] $validators
172
     * @return array
173
     */
174 View Code Duplication
    public static function runValidators(array $validators) : array
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...
175
    {
176
        $errors = [];
177
        foreach ( $validators as $fieldName => $validator )
178
        {
179
            try
180
            {
181
                $validator->__invoke();
182
            }
183
            catch ( AssertionFailedException $e )
184
            {
185
                $errors[$fieldName]     = $e->getMessage();
186
            }
187
        }
188
189
        return $errors;
190
    }
191
    
192
    /**
193
     * @param        $value
194
     * @param string $fieldName
195
     * @param int    $code
196
     * @param string $error
197
     * @param string $level
198
     * @return Assert
199
     */
200 View Code Duplication
    public static function that($value, $fieldName='', $code=0, $error='', $level=Assert::WARNING)
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...
201
    {
202
        $assert = new static($value);
203
        if ( $fieldName )
204
        {
205
            $assert->fieldName($fieldName);
206
        }
207
        if ( $code )
208
        {
209
            $assert->code($code);
210
        }
211
        if ( $error )
212
        {
213
            $assert->error($error);
214
        }
215
        if ( $level )
216
        {
217
            $assert->level($level);
218
        }
219
220
        return $assert;
221
    }
222
223
    /**
224
     * @param mixed $value
225
     * @return Assert
226
     */
227
    public function reset($value)
228
    {
229
        return $this->all(false)->nullOr(false)->value($value);
230
    }
231
232
    /**
233
     * @param mixed $value
234
     * @return Assert
235
     */
236
    public function value($value)
237
    {
238
        $this->value = $value;
239
        return $this;
240
    }
241
242
    /**
243
     * Allow value to pass assertion if it is null.
244
     *
245
     * @param bool $nullOr
246
     * @return Assert
247
     */
248
    public function nullOr($nullOr = true)
249
    {
250
        $this->nullOr = $nullOr;
251
        return $this;
252
    }
253
254
    /**
255
     * Allow value to pass assertion if it is empty.
256
     *
257
     * @param bool $emptyOr
258
     * @return Assert
259
     */
260
    public function emptyOr($emptyOr = true)
261
    {
262
        $this->emptyOr = $emptyOr;
263
        return $this;
264
    }
265
266
    /**
267
     * Assert all values in the value array.
268
     *
269
     * @param bool $all
270
     * @return Assert
271
     */
272
    public function all($all = true)
273
    {
274
        $this->all = $all;
275
        return $this;
276
    }
277
278
    /**
279
     * Helper method that handles building the assertion failure exceptions.
280
     * They are returned from this method so that the stack trace still shows
281
     * the assertions method.
282
     *
283
     * @param string $message
284
     * @param int    $code
285
     * @param string $fieldName
286
     * @param array  $constraints
287
     * @param string $level
288
     * @return AssertionFailedException
289
     */
290
    protected function createException($message, $code, $fieldName, array $constraints = [], $level=null)
291
    {
292
        $exceptionClass = $this->exceptionClass;
293
        $fieldName      = is_null($fieldName) ? $this->fieldName : $fieldName;
294
        $level          = is_null($level) ? $this->level : $level;
295
296
        return new $exceptionClass($message, $code, $fieldName, $this->value, $constraints, $level, $this->propertyPath);
297
    }
298
299
    /**
300
     * @param $exceptionClass
301
     * @return Assert
302
     */
303
    public function setExceptionClass($exceptionClass)
304
    {
305
        $this->exceptionClass = $exceptionClass;
306
        return $this;
307
    }
308
309
    /**
310
     * @param int $code
311
     * @return Assert
312
     */
313
    public function code($code)
314
    {
315
        $this->overrideCode = $code;
316
        return $this;
317
    }
318
319
    /**
320
     * @param string $fieldName
321
     * @return Assert
322
     */
323
    public function fieldName($fieldName)
324
    {
325
        $this->fieldName = $fieldName;
326
        return $this;
327
    }
328
329
    /**
330
     * @param int $level
331
     * @return Assert
332
     */
333
    public function level($level)
334
    {
335
        $this->level = $level;
0 ignored issues
show
Documentation Bug introduced by
It seems like $level of type integer is incompatible with the declared type null|string of property $level.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
336
        return $this;
337
    }
338
339
    /**
340
     * @param string $error
341
     * @return Assert
342
     */
343
    public function error($error)
344
    {
345
        $this->overrideError = $error;
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($propertyPath)
360
    {
361
        $this->propertyPath = $propertyPath;
362
        return $this;
363
    }
364
365
    /**
366
     * Assert that value is equal to a provided value (using == ).
367
     *
368
     * @param mixed       $value2
369
     * @param string|null $message
370
     * @param string|null $fieldName
371
     * @return Assert
372
     * @throws AssertionFailedException
373
     */
374
    public function eq($value2, $message = null, $fieldName = null)
375
    {
376
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
377
        {
378
            return $this;
379
        }
380
        if ( $this->value != $value2 )
381
        {
382
            $message = $message ?: $this->overrideError;
383
            $message = sprintf(
384
                $message ?: 'Value "%s" does not equal expected value "%s".',
385
                $this->stringify($this->value),
386
                $this->stringify($value2)
387
            );
388
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_EQ, $fieldName, ['expected' => $value2]);
389
        }
390
        return $this;
391
    }
392
393
    /**
394
     * Assert that value is greater than a provided value (exclusive).
395
     *
396
     * @param mixed       $value2
397
     * @param string|null $message
398
     * @param string|null $fieldName
399
     * @return Assert
400
     * @throws AssertionFailedException
401
     */
402
    public function greaterThan($value2, $message = null, $fieldName = null)
403
    {
404
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
405
        {
406
            return $this;
407
        }
408
        if ( ! ( $this->value > $value2 ) )
409
        {
410
            $message = $message ?: $this->overrideError;
411
            $message = sprintf(
412
                $message ?: 'Value "%s" does not greater then expected value "%s".',
413
                $this->stringify($this->value),
414
                $this->stringify($value2)
415
            );
416
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_EQ, $fieldName, ['expected' => $value2]);
417
        }
418
        return $this;
419
    }
420
421
    /**
422
     * Assert that value is greater than or equal to a provided value (inclusive).
423
     *
424
     * @param mixed       $value2
425
     * @param string|null $message
426
     * @param string|null $fieldName
427
     * @return Assert
428
     * @throws AssertionFailedException
429
     */
430
    public function greaterThanOrEq($value2, $message = null, $fieldName = null)
431
    {
432
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
433
        {
434
            return $this;
435
        }
436
        if ( ! ( $this->value >= $value2 ) )
437
        {
438
            $message = $message ?: $this->overrideError;
439
            $message = sprintf(
440
                $message ?: 'Value "%s" does not greater than or equal to expected value "%s".',
441
                $this->stringify($this->value),
442
                $this->stringify($value2)
443
            );
444
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_EQ, $fieldName, ['expected' => $value2]);
445
        }
446
        return $this;
447
    }
448
449
    /**
450
     * Assert that value is less than a provided value (exclusive).
451
     *
452
     * @param mixed       $value2
453
     * @param string|null $message
454
     * @param string|null $fieldName
455
     * @return Assert
456
     * @throws AssertionFailedException
457
     */
458
    public function lessThan($value2, $message = null, $fieldName = null)
459
    {
460
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
461
        {
462
            return $this;
463
        }
464
        if ( ! ( $this->value < $value2 ) )
465
        {
466
            $message = $message ?: $this->overrideError;
467
            $message = sprintf(
468
                $message ?: 'Value "%s" does not less then expected value "%s".',
469
                $this->stringify($this->value),
470
                $this->stringify($value2)
471
            );
472
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_LESS_THAN, $fieldName, ['expected' => $value2]);
473
        }
474
        return $this;
475
    }
476
477
    /**
478
     * Assert that value is less than or equal to a provided value (inclusive).
479
     *
480
     * @param mixed       $value2
481
     * @param string|null $message
482
     * @param string|null $fieldName
483
     * @return Assert
484
     * @throws AssertionFailedException
485
     */
486
    public function lessThanOrEq($value2, $message = null, $fieldName = null)
487
    {
488
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
489
        {
490
            return $this;
491
        }
492
        if ( ! ( $this->value <= $value2 ) )
493
        {
494
            $message = $message ?: $this->overrideError;
495
            $message = sprintf(
496
                $message ?: 'Value "%s" does not less than or equal to expected value "%s".',
497
                $this->stringify($this->value),
498
                $this->stringify($value2)
499
            );
500
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_LESS_THAN_OR_EQ, $fieldName, ['expected' => $value2]);
501
        }
502
        return $this;
503
    }
504
505
    /**
506
     * Assert that value is the same as a provided value (using === ).
507
     *
508
     * @param mixed       $value2
509
     * @param string|null $message
510
     * @param string|null $fieldName
511
     * @return Assert
512
     * @throws AssertionFailedException
513
     */
514
    public function same($value2, $message = null, $fieldName = null)
515
    {
516
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
517
        {
518
            return $this;
519
        }
520
        if ( $this->value !== $value2 )
521
        {
522
            $message = $message ?: $this->overrideError;
523
            $message = sprintf(
524
                $message ?: 'Value "%s" is not the same as expected value "%s".',
525
                $this->stringify($this->value),
526
                $this->stringify($value2)
527
            );
528
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_SAME, $fieldName, ['expected' => $value2]);
529
        }
530
        return $this;
531
    }
532
533
    /**
534
     * Assert that value is not equal to a provided value (using == ).
535
     *
536
     * @param mixed       $value2
537
     * @param string|null $message
538
     * @param string|null $fieldName
539
     * @return Assert
540
     * @throws AssertionFailedException
541
     */
542
    public function notEq($value2, $message = null, $fieldName = null)
543
    {
544
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
545
        {
546
            return $this;
547
        }
548
        if ( $this->value == $value2 )
549
        {
550
            $message = $message ?: $this->overrideError;
551
            $message = sprintf(
552
                $message ?: 'Value "%s" is equal to expected value "%s".',
553
                $this->stringify($this->value),
554
                $this->stringify($value2)
555
            );
556
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_NOT_EQ, $fieldName, ['expected' => $value2]);
557
        }
558
        return $this;
559
    }
560
561
    /**
562
     * Assert that value can be called as a function (using is_callable()).
563
     *
564
     * @param string|null $message
565
     * @param string|null $fieldName
566
     * @return $this
567
     * @throws AssertionFailedException
568
     */
569
    public function isCallable($message = null, $fieldName = null)
570
    {
571
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
572
        {
573
            return $this;
574
        }
575
        if ( !is_callable($this->value) )
576
        {
577
            $message = $message ?: $this->overrideError;
578
            $message = sprintf(
579
                $message ?: 'Value "%s" is not callable.',
580
                $this->stringify($this->value)
581
            );
582
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_NOT_EQ, $fieldName);
583
        }
584
        return $this;
585
    }
586
587
    /**
588
     * Assert that value is not the same as a provided value (using === ).
589
     *
590
     * @param mixed       $value2
591
     * @param string|null $message
592
     * @param string|null $fieldName
593
     * @return Assert
594
     * @throws AssertionFailedException
595
     */
596
    public function notSame($value2, $message = null, $fieldName = null)
597
    {
598
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
599
        {
600
            return $this;
601
        }
602
        if ( $this->value === $value2 )
603
        {
604
            $message = $message ?: $this->overrideError;
605
            $message = sprintf(
606
                $message ?: 'Value "%s" is the same as expected value "%s".',
607
                $this->stringify($this->value),
608
                $this->stringify($value2)
609
            );
610
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_NOT_SAME, $fieldName, ['expected' => $value2]);
611
        }
612
        return $this;
613
    }
614
615
    /**
616
     * Assert that value is a valid ID (non-empty, non-zero, valid integer).
617
     *
618
     * @param string|null $message
619
     * @param string|null $fieldName
620
     * @return Assert
621
     * @throws AssertionFailedException
622
     */
623
    public function id($message = null, $fieldName = null)
624
    {
625
        $message = $message ?: $this->overrideError;
626
        $message = $message ?: 'Value "%s" is not an integer id.';
627
        return $this->nonEmptyInt($message, $fieldName)->range(1, PHP_INT_MAX, $message, $fieldName);
628
    }
629
630
    /**
631
     * Assert that value is a unsigned int (non-empty valid integer, can be zero).
632
     * @param string|null $message
633
     * @param string|null $fieldName
634
     * @return Assert
635
     * @throws AssertionFailedException
636
     */
637
    public function unsignedInt($message=null, $fieldName=null)
638
    {
639
        $message = $message ?: $this->overrideError;
640
        $message = $message ?: 'Value "%s" is not an integer id.';
641
642
        return $this->int($message, $fieldName)->range(0, PHP_INT_MAX, $message, $fieldName);
643
    }
644
645
    /**
646
     * Assert that value is a valid flag (0 or 1).
647
     *
648
     * @param string|null $message
649
     * @param string|null $fieldName
650
     * @return Assert
651
     * @throws AssertionFailedException
652
     */
653
    public function flag($message = null, $fieldName = null)
654
    {
655
        $message = $message ?: $this->overrideError;
656
        $message = $message ?: 'Value "%s" is not a 0 or 1.';
657
        return $this->range(0, 1, $message, $fieldName);
658
    }
659
660
    /**
661
     * Assert that value is a valid status (-1, 0, or 1).
662
     *
663
     * @param string|null $message
664
     * @param string|null $fieldName
665
     * @return Assert
666
     * @throws AssertionFailedException
667
     */
668
    public function status($message = null, $fieldName = null)
669
    {
670
        $message = $message ?: $this->overrideError;
671
        $message = $message ?: 'Value "%s" is not a valid status.';
672
        return $this->integer($message, $fieldName)->inArray([-1, 0, 1]);
673
    }
674
675
    /**
676
     * Assert that value is null or a valid ID.
677
     *
678
     * @param string|null $message
679
     * @param string|null $fieldName
680
     * @return Assert
681
     * @throws AssertionFailedException
682
     */
683
    public function nullOrId($message = null, $fieldName = null)
684
    {
685
        return $this->nullOr()->id($message, $fieldName);
686
    }
687
688
    /**
689
     * Assert that values are all valid IDs.
690
     *
691
     * @param string|null $message
692
     * @param string|null $fieldName
693
     * @return Assert
694
     * @throws AssertionFailedException
695
     */
696
    public function allIds($message = null, $fieldName = null)
697
    {
698
        return $this->all()->id($message, $fieldName);
699
    }
700
701
    /**
702
     * Alias of {@see integer()}.
703
     *
704
     * @param string|null $message
705
     * @param string|null $fieldName
706
     * @return Assert
707
     * @throws AssertionFailedException
708
     */
709
    public function int($message = null, $fieldName = null)
710
    {
711
        return $this->integer($message, $fieldName);
712
    }
713
714
    /**
715
     * Assert that value is a valid PHP integer.
716
     *
717
     * @param string|null $message
718
     * @param string|null $fieldName
719
     * @return Assert
720
     * @throws AssertionFailedException
721
     */
722
    public function integer($message = null, $fieldName = null)
723
    {
724
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
725
        {
726
            return $this;
727
        }
728
        if ( !is_int($this->value) )
729
        {
730
            $message = $message ?: $this->overrideError;
731
            $message = sprintf(
732
                $message ?: 'Value "%s" is not an integer.',
733
                $this->stringify($this->value)
734
            );
735
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_INTEGER, $fieldName);
736
        }
737
        return $this;
738
    }
739
740
    /**
741
     * Assert that value is a valid PHP float.
742
     *
743
     * @param string|null $message
744
     * @param string|null $fieldName
745
     * @return Assert
746
     * @throws AssertionFailedException
747
     */
748
    public function float($message = null, $fieldName = null)
749
    {
750
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
751
        {
752
            return $this;
753
        }
754
        if ( ! is_float($this->value) )
755
        {
756
            $message = $message ?: $this->overrideError;
757
            $message = sprintf(
758
                $message ?: 'Value "%s" is not a float.',
759
                $this->stringify($this->value)
760
            );
761
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_FLOAT, $fieldName);
762
        }
763
        return $this;
764
    }
765
766
    /**
767
     * Assert that value (integer or integer'ish) is a digit.
768
     *
769
     * @param string|null $message
770
     * @param string|null $fieldName
771
     * @return Assert
772
     * @throws AssertionFailedException
773
     */
774
    public function digit($message = null, $fieldName = null)
775
    {
776
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
777
        {
778
            return $this;
779
        }
780
        if ( ! ctype_digit((string)$this->value) )
781
        {
782
            $message = $message ?: $this->overrideError;
783
            $message = sprintf(
784
                $message ?: 'Value "%s" is not a digit.',
785
                $this->stringify($this->value)
786
            );
787
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_DIGIT, $fieldName);
788
        }
789
        return $this;
790
    }
791
792
    /**
793
     * Assert that value is a valid date.
794
     *
795
     * @param string|null $message
796
     * @param string|null $fieldName
797
     * @return Assert
798
     * @throws AssertionFailedException
799
     */
800
    public function date($message = null, $fieldName = null)
801
    {
802
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
803
        {
804
            return $this;
805
        }
806
        $this->notEmpty($message, $fieldName);
807
        if ( strtotime($this->value) === false )
808
        {
809
            $message = $message ?: $this->overrideError;
810
            $message = sprintf(
811
                $message ?: 'Value "%s" is not a date.',
812
                $this->stringify($this->value)
813
            );
814
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_DATE, $fieldName);
815
        }
816
        return $this;
817
    }
818
819
    /**
820
     * Assert that value is a PHP integer'ish.
821
     *
822
     * @param string|null $message
823
     * @param string|null $fieldName
824
     * @return Assert
825
     * @throws AssertionFailedException
826
     */
827
    public function integerish($message = null, $fieldName = null)
828
    {
829
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
830
        {
831
            return $this;
832
        }
833
        if ( is_object($this->value) || strval(intval($this->value)) != $this->value || is_bool($this->value) || is_null($this->value) )
834
        {
835
            $message = $message ?: $this->overrideError;
836
            $message = sprintf(
837
                $message ?: 'Value "%s" is not an integer or a number castable to integer.',
838
                $this->stringify($this->value)
839
            );
840
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_INTEGERISH, $fieldName);
841
        }
842
        return $this;
843
    }
844
845
    /**
846
     * Assert that value is a valid PHP boolean.
847
     *
848
     * @param string|null $message
849
     * @param string|null $fieldName
850
     * @return Assert
851
     * @throws AssertionFailedException
852
     */
853
    public function boolean($message = null, $fieldName = null)
854
    {
855
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
856
        {
857
            return $this;
858
        }
859
        if ( ! is_bool($this->value) )
860
        {
861
            $message = $message ?: $this->overrideError;
862
            $message = sprintf(
863
                $message ?: 'Value "%s" is not a boolean.',
864
                $this->stringify($this->value)
865
            );
866
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_BOOLEAN, $fieldName);
867
        }
868
        return $this;
869
    }
870
871
    /**
872
     * Assert that value is a valid PHP scalar.
873
     *
874
     * @param string|null $message
875
     * @param string|null $fieldName
876
     * @return Assert
877
     * @throws AssertionFailedException
878
     */
879
    public function scalar($message = null, $fieldName = null)
880
    {
881
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
882
        {
883
            return $this;
884
        }
885
        if ( ! is_scalar($this->value) )
886
        {
887
            $message = $message ?: $this->overrideError;
888
            $message = sprintf(
889
                $message ?: 'Value "%s" is not a scalar.',
890
                $this->stringify($this->value)
891
            );
892
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_SCALAR, $fieldName);
893
        }
894
        return $this;
895
    }
896
897
    /**
898
     * Assert that value is not empty.
899
     *
900
     * @param string|null $message
901
     * @param string|null $fieldName
902
     * @return Assert
903
     * @throws AssertionFailedException
904
     */
905 View Code Duplication
    public function notEmpty($message = null, $fieldName = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
906
    {
907
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
908
        {
909
            return $this;
910
        }
911
        if ( ( is_object($this->value) && empty((array)$this->value) ) || empty($this->value) )
912
        {
913
            $message = $message ?: $this->overrideError;
914
            $message = sprintf(
915
                $message ?: 'Value "%s" is empty, but non empty value was expected.',
916
                $this->stringify($this->value)
917
            );
918
            throw $this->createException($message, $this->overrideCode ?: self::VALUE_EMPTY, $fieldName);
919
        }
920
        return $this;
921
    }
922
923
    /**
924
     * Assert that value is empty.
925
     *
926
     * @param string|null $message
927
     * @param string|null $fieldName
928
     * @return Assert
929
     * @throws AssertionFailedException
930
     */
931
    public function noContent($message = null, $fieldName = null)
932
    {
933
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
934
        {
935
            return $this;
936
        }
937
        if ( !empty( $this->value ) )
938
        {
939
            $message = $message ?: $this->overrideError;
940
            $message = sprintf(
941
                $message ?: 'Value "%s" is not empty, but empty value was expected.',
942
                $this->stringify($this->value)
943
            );
944
            throw $this->createException($message, $this->overrideCode ?: self::VALUE_NOT_EMPTY, $fieldName);
945
        }
946
        return $this;
947
    }
948
949
    /**
950
     * Assert that value is not null.
951
     *
952
     * @param string|null $message
953
     * @param string|null $fieldName
954
     * @return Assert
955
     * @throws AssertionFailedException
956
     */
957
    public function notNull($message = null, $fieldName = null)
958
    {
959
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
960
        {
961
            return $this;
962
        }
963
        if ( $this->value === null )
964
        {
965
            $message = $message ?: $this->overrideError;
966
            $message = sprintf(
967
                $message ?: 'Value "%s" is null, but non null value was expected.',
968
                $this->stringify($this->value)
969
            );
970
            throw $this->createException($message, $this->overrideCode ?: self::VALUE_NULL, $fieldName);
971
        }
972
        return $this;
973
    }
974
975
    /**
976
     * Assert that value is a string
977
     *
978
     * @param string|null $message
979
     * @param string|null $fieldName
980
     * @return Assert
981
     * @throws AssertionFailedException
982
     */
983
    public function string($message = null, $fieldName = null)
984
    {
985
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
986
        {
987
            return $this;
988
        }
989
        if ( !is_string($this->value) )
990
        {
991
            $message = $message ?: $this->overrideError;
992
            $message = sprintf(
993
                $message ?: 'Value "%s" expected to be string, type %s given.',
994
                $this->stringify($this->value),
995
                gettype($this->value)
996
            );
997
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_STRING, $fieldName);
998
        }
999
        return $this;
1000
    }
1001
1002
    /**
1003
     * Assert that value matches a provided Regex.
1004
     *
1005
     * @param string      $pattern
1006
     * @param string|null $message
1007
     * @param string|null $fieldName
1008
     * @return Assert
1009
     * @throws AssertionFailedException
1010
     */
1011
    public function regex($pattern, $message=null, $fieldName=null)
1012
    {
1013
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1014
        {
1015
            return $this;
1016
        }
1017
        $this->string($message, $fieldName);
1018
        if ( ! preg_match($pattern, $this->value) )
1019
        {
1020
            $message = $message ?: $this->overrideError;
1021
            $message = sprintf(
1022
                $message ?: 'Value "%s" does not match expression.',
1023
                $this->stringify($this->value)
1024
            );
1025
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_REGEX, $fieldName, ['pattern' => $pattern]);
1026
        }
1027
        return $this;
1028
    }
1029
1030
    /**
1031
     * Assert that value is a valid IP address.
1032
     *
1033
     * @param string $message
1034
     * @param string|null $fieldName
1035
     * @return $this
1036
     * @throws AssertionFailedException
1037
     */
1038
    public function ipAddress($message = null, $fieldName = null)
1039
    {
1040
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1041
        {
1042
            return $this;
1043
        }
1044
        $this->string($message, $fieldName);
1045
        $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])$/';
1046
        if ( ! preg_match($pattern, $this->value) )
1047
        {
1048
            $message = $message ?: $this->overrideError;
1049
            $message = sprintf(
1050
                $message ?: 'Value "%s" was expected to be a valid IP Address',
1051
                $this->stringify($this->value)
1052
            );
1053
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_IP_ADDRESS, $fieldName);
1054
        }
1055
        return $this;
1056
    }
1057
1058
    /**
1059
     * Assert that value does not match a provided Regex.
1060
     *
1061
     * @param string $pattern
1062
     * @param string|null $message
1063
     * @param string|null $fieldName
1064
     * @return $this
1065
     * @throws AssertionFailedException
1066
     */
1067
    public function notRegex($pattern, $message = null, $fieldName = null)
1068
    {
1069
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1070
        {
1071
            return $this;
1072
        }
1073
        $this->string($message, $fieldName);
1074
        if ( preg_match($pattern, $this->value) )
1075
        {
1076
            $message = $message ?: $this->overrideError;
1077
            $message = sprintf(
1078
                $message ?: 'Value "%s" does not match expression.',
1079
                $this->stringify($this->value)
1080
            );
1081
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_REGEX, $fieldName, ['pattern' => $pattern]);
1082
        }
1083
        return $this;
1084
    }
1085
1086
    /**
1087
     * Assert that value is a string and has a character count which is equal to a given length.
1088
     *
1089
     * @param int         $length
1090
     * @param string|null $message
1091
     * @param string|null $fieldName
1092
     * @param string      $encoding
1093
     * @return Assert
1094
     * @throws AssertionFailedException
1095
     */
1096
    public function length($length, $message = null, $fieldName = null, $encoding = 'utf8')
1097
    {
1098
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1099
        {
1100
            return $this;
1101
        }
1102
        $this->string($message, $fieldName);
1103
        if ( mb_strlen($this->value, $encoding) !== $length )
1104
        {
1105
            $message    = $message ?: $this->overrideError;
1106
            $message    = sprintf(
1107
                $message ?: 'Value "%s" has to be %d exactly characters long, but length is %d.',
1108
                $this->stringify($this->value),
1109
                $length,
1110
                mb_strlen($this->value, $encoding)
1111
            );
1112
            $constraints = ['length' => $length, 'encoding' => $encoding];
1113
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_LENGTH, $fieldName, $constraints);
1114
        }
1115
        return $this;
1116
    }
1117
1118
    /**
1119
     * Assert that value is a string and has a character count which is
1120
     * greater than or equal to a given lower limit ($minLength chars).
1121
     *
1122
     * @param int         $minLength
1123
     * @param string|null $message
1124
     * @param string|null $fieldName
1125
     * @param string      $encoding
1126
     * @return Assert
1127
     * @throws AssertionFailedException
1128
     */
1129
    public function minLength($minLength, $message = null, $fieldName = null, $encoding = 'utf8')
1130
    {
1131
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1132
        {
1133
            return $this;
1134
        }
1135
        $this->string($message, $fieldName);
1136
        if ( mb_strlen($this->value, $encoding) < $minLength )
1137
        {
1138
            $message = $message ?: $this->overrideError;
1139
            $message     = sprintf(
1140
                $message
1141
                    ?: 'Value "%s" is too short, it should have more than %d characters, but only has %d characters.',
1142
                $this->stringify($this->value),
1143
                $minLength,
1144
                mb_strlen($this->value, $encoding)
1145
            );
1146
            $constraints = ['min_length' => $minLength, 'encoding' => $encoding];
1147
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_MIN_LENGTH, $fieldName, $constraints);
1148
        }
1149
        return $this;
1150
    }
1151
1152
    /**
1153
     * Assert that value is a string and has a character count which is
1154
     * less than or equal to given upper limit ($maxLength chars).
1155
     *
1156
     * @param integer     $maxLength
1157
     * @param string|null $message
1158
     * @param string|null $fieldName
1159
     * @param string      $encoding
1160
     * @return Assert
1161
     * @throws AssertionFailedException
1162
     */
1163
    public function maxLength($maxLength, $message = null, $fieldName = null, $encoding = 'utf8')
1164
    {
1165
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1166
        {
1167
            return $this;
1168
        }
1169
        $this->string($message, $fieldName);
1170
        if ( mb_strlen($this->value, $encoding) > $maxLength )
1171
        {
1172
            $message = $message ?: $this->overrideError;
1173
            $message     = sprintf(
1174
                $message ?: 'Value "%s" is too long, it should have no more than %d characters, but has %d characters.',
1175
                $this->stringify($this->value),
1176
                $maxLength,
1177
                mb_strlen($this->value, $encoding)
1178
            );
1179
            $constraints = ['max_length' => $maxLength, 'encoding' => $encoding];
1180
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_MAX_LENGTH, $fieldName, $constraints);
1181
        }
1182
        return $this;
1183
    }
1184
1185
    /**
1186
     * Assert that value has a length between min,max lengths (inclusive).
1187
     *
1188
     * @param integer     $minLength
1189
     * @param integer     $maxLength
1190
     * @param string|null $message
1191
     * @param string|null $fieldName
1192
     * @param string      $encoding
1193
     * @return Assert
1194
     * @throws AssertionFailedException
1195
     */
1196
    public function betweenLength($minLength, $maxLength, $message = null, $fieldName = null, $encoding = 'utf8')
1197
    {
1198
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1199
        {
1200
            return $this;
1201
        }
1202
        $this->string($message, $fieldName);
1203
        if ( mb_strlen($this->value, $encoding) < $minLength )
1204
        {
1205
            $message = $message ?: $this->overrideError;
1206
            $message     = sprintf(
1207
                $message
1208
                    ?: 'Value "%s" is too short, it should have more than %d characters, but only has %d characters.',
1209
                $this->stringify($this->value),
1210
                $minLength,
1211
                mb_strlen($this->value, $encoding)
1212
            );
1213
            $constraints = ['min_length' => $minLength, 'encoding' => $encoding];
1214
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_MIN_LENGTH, $fieldName, $constraints);
1215
        }
1216
        if ( mb_strlen($this->value, $encoding) > $maxLength )
1217
        {
1218
            $message = $message ?: $this->overrideError;
1219
            $message     = sprintf(
1220
                $message ?: 'Value "%s" is too long, it should have no more than %d characters, but has %d characters.',
1221
                $this->stringify($this->value),
1222
                $maxLength,
1223
                mb_strlen($this->value, $encoding)
1224
            );
1225
            $constraints = ['max_length' => $maxLength, 'encoding' => $encoding];
1226
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_MAX_LENGTH, $fieldName, $constraints);
1227
        }
1228
        return $this;
1229
    }
1230
1231
    /**
1232
     * Assert that value starts with a sequence of chars.
1233
     *
1234
     * @param string      $needle
1235
     * @param string|null $message
1236
     * @param string|null $fieldName
1237
     * @param string      $encoding
1238
     * @return Assert
1239
     * @throws AssertionFailedException
1240
     */
1241 View Code Duplication
    public function startsWith($needle, $message = null, $fieldName = null, $encoding = 'utf8')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1242
    {
1243
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1244
        {
1245
            return $this;
1246
        }
1247
        $this->string($message, $fieldName);
1248
        if ( mb_strpos($this->value, $needle, null, $encoding) !== 0 )
1249
        {
1250
            $message = $message ?: $this->overrideError;
1251
            $message     = sprintf(
1252
                $message ?: 'Value "%s" does not start with "%s".',
1253
                $this->stringify($this->value),
1254
                $this->stringify($needle)
1255
            );
1256
            $constraints = ['needle' => $needle, 'encoding' => $encoding];
1257
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_STRING_START, $fieldName, $constraints);
1258
        }
1259
        return $this;
1260
    }
1261
1262
    /**
1263
     * Assert that value ends with a sequence of chars.
1264
     *
1265
     * @param string      $needle
1266
     * @param string|null $message
1267
     * @param string|null $fieldName
1268
     * @param string      $encoding
1269
     * @return Assert
1270
     * @throws AssertionFailedException
1271
     */
1272
    public function endsWith($needle, $message = null, $fieldName = null, $encoding = 'utf8')
1273
    {
1274
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1275
        {
1276
            return $this;
1277
        }
1278
        $this->string($message, $fieldName);
1279
        $stringPosition = mb_strlen($this->value, $encoding) - mb_strlen($needle, $encoding);
1280
        if ( mb_strripos($this->value, $needle, null, $encoding) !== $stringPosition )
1281
        {
1282
            $message = $message ?: $this->overrideError;
1283
            $message     = sprintf(
1284
                $message ?: 'Value "%s" does not end with "%s".',
1285
                $this->stringify($this->value),
1286
                $this->stringify($needle)
1287
            );
1288
            $constraints = ['needle' => $needle, 'encoding' => $encoding];
1289
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_STRING_END, $fieldName, $constraints);
1290
        }
1291
        return $this;
1292
    }
1293
1294
    /**
1295
     * Assert that value contains a sequence of chars.
1296
     *
1297
     * @param string      $needle
1298
     * @param string|null $message
1299
     * @param string|null $fieldName
1300
     * @param string      $encoding
1301
     * @return Assert
1302
     * @throws AssertionFailedException
1303
     */
1304 View Code Duplication
    public function contains($needle, $message = null, $fieldName = null, $encoding = 'utf8')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1305
    {
1306
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1307
        {
1308
            return $this;
1309
        }
1310
        $this->string($message, $fieldName);
1311
        if ( mb_strpos($this->value, $needle, null, $encoding) === false )
1312
        {
1313
            $message = $message ?: $this->overrideError;
1314
            $message     = sprintf(
1315
                $message ?: 'Value "%s" does not contain "%s".',
1316
                $this->stringify($this->value),
1317
                $this->stringify($needle)
1318
            );
1319
            $constraints = ['needle' => $needle, 'encoding' => $encoding];
1320
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_STRING_CONTAINS, $fieldName, $constraints);
1321
        }
1322
        return $this;
1323
    }
1324
1325
    /**
1326
     * Assert that value is in an array of choices.
1327
     *
1328
     * @param array       $choices
1329
     * @param string|null $message
1330
     * @param string|null $fieldName
1331
     * @return Assert
1332
     * @throws AssertionFailedException
1333
     */
1334
    public function choice(array $choices, $message = null, $fieldName = null)
1335
    {
1336
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1337
        {
1338
            return $this;
1339
        }
1340
        if ( !in_array($this->value, $choices, true) )
1341
        {
1342
            $message = $message ?: $this->overrideError;
1343
            $message = sprintf(
1344
                $message ?: 'Value "%s" is not an element of the valid values: %s',
1345
                $this->stringify($this->value),
1346
                implode(", ", array_map('Terah\Assert\Assert::stringify', $choices))
1347
            );
1348
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_CHOICE, $fieldName, ['choices' => $choices]);
1349
        }
1350
        return $this;
1351
    }
1352
1353
    /**
1354
     * Alias of {@see choice()}
1355
     *
1356
     * @param array       $choices
1357
     * @param string|null $message
1358
     * @param string|null $fieldName
1359
     * @return Assert
1360
     * @throws AssertionFailedException
1361
     */
1362
    public function inArray(array $choices, $message = null, $fieldName = null)
1363
    {
1364
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1365
        {
1366
            return $this;
1367
        }
1368
        $this->choice($choices, $message, $fieldName);
1369
        return $this;
1370
    }
1371
1372
    /**
1373
     * Assert that value is numeric.
1374
     *
1375
     * @param string|null $message
1376
     * @param string|null $fieldName
1377
     * @return Assert
1378
     * @throws AssertionFailedException
1379
     */
1380
    public function numeric($message = null, $fieldName = null)
1381
    {
1382
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1383
        {
1384
            return $this;
1385
        }
1386
        if ( ! is_numeric($this->value) )
1387
        {
1388
            $message = $message ?: $this->overrideError;
1389
            $message = sprintf(
1390
                $message ?: 'Value "%s" is not numeric.',
1391
                $this->stringify($this->value)
1392
            );
1393
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_NUMERIC, $fieldName);
1394
        }
1395
        return $this;
1396
    }
1397
1398
    /**
1399
     * Assert that value is a non-empty array.
1400
     *
1401
     * @param string|null $message
1402
     * @param string|null $fieldName
1403
     * @return Assert
1404
     * @throws AssertionFailedException
1405
     */
1406
    public function nonEmptyArray($message = null, $fieldName = null)
1407
    {
1408
        $message = $message ?: 'Value "%s" is not a non-empty array.';
1409
        return $this->isArray($message, $fieldName)->notEmpty($message, $fieldName);
1410
    }
1411
1412
    /**
1413
     * Assert that value is a non-empty int.
1414
     *
1415
     * @param string|null $message
1416
     * @param string|null $fieldName
1417
     * @return Assert
1418
     * @throws AssertionFailedException
1419
     */
1420
    public function nonEmptyInt($message = null, $fieldName = null)
1421
    {
1422
        $message = $message ?: 'Value "%s" is not a non-empty integer.';
1423
        return $this->integer($message, $fieldName)->notEmpty($message, $fieldName);
1424
    }
1425
1426
    /**
1427
     * Assert that value is a non-empty string.
1428
     *
1429
     * @param string|null $message
1430
     * @param string|null $fieldName
1431
     * @return Assert
1432
     * @throws AssertionFailedException
1433
     */
1434
    public function nonEmptyString($message = null, $fieldName = null)
1435
    {
1436
        $message = $message ?: 'Value "%s" is not a non-empty string.';
1437
        return $this->string($message, $fieldName)->notEmpty($message, $fieldName);
1438
    }
1439
1440
    /**
1441
     * Assert that value is an array.
1442
     *
1443
     * @param string|null $message
1444
     * @param string|null $fieldName
1445
     * @return Assert
1446
     * @throws AssertionFailedException
1447
     */
1448
    public function isArray($message = null, $fieldName = null)
1449
    {
1450
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1451
        {
1452
            return $this;
1453
        }
1454
        if ( !is_array($this->value) )
1455
        {
1456
            $message = $message ?: $this->overrideError;
1457
            $message = sprintf(
1458
                $message ?: 'Value "%s" is not an array.',
1459
                $this->stringify($this->value)
1460
            );
1461
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_ARRAY, $fieldName);
1462
        }
1463
        return $this;
1464
    }
1465
1466
    /**
1467
     * Assert that value is an array or a traversable object.
1468
     *
1469
     * @param string|null $message
1470
     * @param string|null $fieldName
1471
     * @return Assert
1472
     * @throws AssertionFailedException
1473
     */
1474
    public function isTraversable($message = null, $fieldName = null)
1475
    {
1476
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1477
        {
1478
            return $this;
1479
        }
1480
        if ( !is_array($this->value) && !$this->value instanceof \Traversable )
1481
        {
1482
            $message = $message ?: $this->overrideError;
1483
            $message = sprintf(
1484
                $message ?: 'Value "%s" is not an array and does not implement Traversable.',
1485
                $this->stringify($this->value)
1486
            );
1487
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_TRAVERSABLE, $fieldName);
1488
        }
1489
        return $this;
1490
    }
1491
1492
    /**
1493
     * Assert that value is an array or an array-accessible object.
1494
     *
1495
     * @param string|null $message
1496
     * @param string|null $fieldName
1497
     * @return Assert
1498
     * @throws AssertionFailedException
1499
     */
1500
    public function isArrayAccessible($message = null, $fieldName = null)
1501
    {
1502
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1503
        {
1504
            return $this;
1505
        }
1506
        if ( !is_array($this->value) && !$this->value instanceof \ArrayAccess )
1507
        {
1508
            $message = $message ?: $this->overrideError;
1509
            $message = sprintf(
1510
                $message ?: 'Value "%s" is not an array and does not implement ArrayAccess.',
1511
                $this->stringify($this->value)
1512
            );
1513
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_ARRAY_ACCESSIBLE, $fieldName);
1514
        }
1515
        return $this;
1516
    }
1517
1518
    /**
1519
     * Assert that key exists in the values array.
1520
     *
1521
     * @param string|integer $key
1522
     * @param string|null    $message
1523
     * @param string|null    $fieldName
1524
     * @return Assert
1525
     * @throws AssertionFailedException
1526
     */
1527
    public function keyExists($key, $message = null, $fieldName = null)
1528
    {
1529
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1530
        {
1531
            return $this;
1532
        }
1533
        $this->isArray($message, $fieldName);
1534
        if ( !array_key_exists($key, $this->value) )
1535
        {
1536
            $message = $message ?: $this->overrideError;
1537
            $message = sprintf(
1538
                $message ?: 'Array does not contain an element with key "%s"',
1539
                $this->stringify($key)
1540
            );
1541
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_KEY_EXISTS, $fieldName, ['key' => $key]);
1542
        }
1543
        return $this;
1544
    }
1545
1546
    /**
1547
     * Assert that keys exist in the values array.
1548
     *
1549
     * @param array       $keys
1550
     * @param string|null $message
1551
     * @param string|null $fieldName
1552
     * @return Assert
1553
     * @throws AssertionFailedException
1554
     */
1555
    public function keysExist($keys, $message = null, $fieldName = null)
1556
    {
1557
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1558
        {
1559
            return $this;
1560
        }
1561
        $this->isArray($message, $fieldName);
1562
        foreach ( $keys as $key )
1563
        {
1564
            if ( !array_key_exists($key, $this->value) )
1565
            {
1566
                $message = $message
1567
                    ?: sprintf(
1568
                        'Array does not contain an element with key "%s"',
1569
                        $this->stringify($key)
1570
                    );
1571
                throw $this->createException($message, $this->overrideCode ?: self::INVALID_KEYS_EXIST, $fieldName, ['key' => $key]);
1572
            }
1573
        }
1574
        return $this;
1575
    }
1576
1577
    /**
1578
     * Assert that a property (key) exists in the values array.
1579
     *
1580
     * @param string|integer $key
1581
     * @param string|null    $message
1582
     * @param string|null    $fieldName
1583
     * @return Assert
1584
     * @throws AssertionFailedException
1585
     */
1586
    public function propertyExists($key, $message = null, $fieldName = null)
1587
    {
1588
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1589
        {
1590
            return $this;
1591
        }
1592
        $this->isObject($message, $fieldName);
1593
        if ( !property_exists($this->value, $key) && !isset( $this->value->{$key} ) )
1594
        {
1595
            $message = $message
1596
                ?: sprintf(
1597
                    'Object does not contain a property with key "%s"',
1598
                    $this->stringify($key)
1599
                );
1600
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_PROPERTY_EXISTS, $fieldName, ['key' => $key]);
1601
        }
1602
        return $this;
1603
    }
1604
1605
    /**
1606
     * Assert that properties (keys) exist in the values array.
1607
     *
1608
     * @param array       $keys
1609
     * @param string|null $message
1610
     * @param string|null $fieldName
1611
     * @return Assert
1612
     * @throws AssertionFailedException
1613
     */
1614
    public function propertiesExist(array $keys, $message = null, $fieldName = null)
1615
    {
1616
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1617
        {
1618
            return $this;
1619
        }
1620
        $this->isObject($message, $fieldName);
1621
        foreach ( $keys as $key )
1622
        {
1623
            // Using isset to allow resolution of magically defined properties
1624
            if ( !property_exists($this->value, $key) && !isset( $this->value->{$key} ) )
1625
            {
1626
                $message = $message
1627
                    ?: sprintf(
1628
                        'Object does not contain a property with key "%s"',
1629
                        $this->stringify($key)
1630
                    );
1631
                throw $this->createException($message, $this->overrideCode ?: self::INVALID_PROPERTIES_EXIST, $fieldName, ['key' => $key]);
1632
            }
1633
        }
1634
        return $this;
1635
    }
1636
1637
    /**
1638
     * Assert that value is valid utf8.
1639
     *
1640
     * @param string|null $message
1641
     * @param string|null $fieldName
1642
     * @return Assert
1643
     * @throws AssertionFailedException
1644
     */
1645 View Code Duplication
    public function utf8($message = null, $fieldName = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1646
    {
1647
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1648
        {
1649
            return $this;
1650
        }
1651
        $this->string($message, $fieldName);
1652
        if ( mb_detect_encoding($this->value, 'UTF-8', true) !== 'UTF-8' )
1653
        {
1654
            $message = $message
1655
                ?: sprintf(
1656
                    'Value "%s" was expected to be a valid UTF8 string',
1657
                    $this->stringify($this->value)
1658
                );
1659
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_UTF8, $fieldName);
1660
        }
1661
        return $this;
1662
    }
1663
1664
1665
    /**
1666
     * Assert that value is valid ascii.
1667
     *
1668
     * @param string|null $message
1669
     * @param string|null $fieldName
1670
     * @return Assert
1671
     * @throws AssertionFailedException
1672
     */
1673 View Code Duplication
    public function ascii($message = null, $fieldName = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1674
    {
1675
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1676
        {
1677
            return $this;
1678
        }
1679
        $this->string($message, $fieldName);
1680
        if ( ! preg_match('/^[ -~]+$/', $this->value) )
1681
        {
1682
            $message = $message
1683
                ?: sprintf(
1684
                    'Value "%s" was expected to be a valid ASCII string',
1685
                    $this->stringify($this->value)
1686
                );
1687
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_ASCII, $fieldName);
1688
        }
1689
        return $this;
1690
    }
1691
1692
    /**
1693
     * Assert that key exists in an array/array-accessible object
1694
     * (using isset()).
1695
     *
1696
     * @param string|integer $key
1697
     * @param string|null    $message
1698
     * @param string|null    $fieldName
1699
     * @return Assert
1700
     * @throws AssertionFailedException
1701
     */
1702
    public function keyIsset($key, $message = null, $fieldName = null)
1703
    {
1704
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1705
        {
1706
            return $this;
1707
        }
1708
        $this->isArrayAccessible($message, $fieldName);
1709
        if ( !isset( $this->value[$key] ) )
1710
        {
1711
            $message = $message ?: $this->overrideError;
1712
            $message = sprintf(
1713
                $message ?: 'The element with key "%s" was not found',
1714
                $this->stringify($key)
1715
            );
1716
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_KEY_ISSET, $fieldName, ['key' => $key]);
1717
        }
1718
        return $this;
1719
    }
1720
1721
    /**
1722
     * Assert that key exists in an array/array-accessible object
1723
     * and its value is not empty.
1724
     *
1725
     * @param string|integer $key
1726
     * @param string|null    $message
1727
     * @param string|null    $fieldName
1728
     * @return Assert
1729
     * @throws AssertionFailedException
1730
     */
1731
    public function notEmptyKey($key, $message = null, $fieldName = null)
1732
    {
1733
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1734
        {
1735
            return $this;
1736
        }
1737
        $this->keyIsset($key, $message, $fieldName);
1738
        (new Assert($this->value[$key]))->setExceptionClass($this->exceptionClass)->notEmpty($message, $fieldName);
1739
        return $this;
1740
    }
1741
1742
    /**
1743
     * Assert that value is not blank.
1744
     *
1745
     * @param string|null $message
1746
     * @param string|null $fieldName
1747
     * @return Assert
1748
     * @throws AssertionFailedException
1749
     */
1750 View Code Duplication
    public function notBlank($message = null, $fieldName = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1751
    {
1752
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1753
        {
1754
            return $this;
1755
        }
1756
        if ( false === $this->value || ( empty( $this->value ) && '0' != $this->value ) )
1757
        {
1758
            $message = $message ?: $this->overrideError;
1759
            $message = sprintf(
1760
                $message ?: 'Value "%s" is blank, but was expected to contain a value.',
1761
                $this->stringify($this->value)
1762
            );
1763
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_NOT_BLANK, $fieldName);
1764
        }
1765
        return $this;
1766
    }
1767
1768
    /**
1769
     * Assert that value is an instance of a given class-name.
1770
     *
1771
     * @param string      $className
1772
     * @param string|null $message
1773
     * @param string|null $fieldName
1774
     * @return Assert
1775
     * @throws AssertionFailedException
1776
     */
1777
    public function isInstanceOf($className, $message = null, $fieldName = null)
1778
    {
1779
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1780
        {
1781
            return $this;
1782
        }
1783
        if ( !( $this->value instanceof $className ) )
1784
        {
1785
            $message = $message ?: $this->overrideError;
1786
            $message = sprintf(
1787
                $message ?: 'Class "%s" was expected to be instanceof of "%s" but is not.',
1788
                $this->stringify($this->value),
1789
                $className
1790
            );
1791
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_INSTANCE_OF, $fieldName, ['class' => $className]);
1792
        }
1793
        return $this;
1794
    }
1795
1796
    /**
1797
     * Assert that value is not an instance of given class-name.
1798
     *
1799
     * @param string      $className
1800
     * @param string|null $message
1801
     * @param string|null $fieldName
1802
     * @return Assert
1803
     * @throws AssertionFailedException
1804
     */
1805
    public function notIsInstanceOf($className, $message = null, $fieldName = null)
1806
    {
1807
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1808
        {
1809
            return $this;
1810
        }
1811
        if ( $this->value instanceof $className )
1812
        {
1813
            $message = $message ?: $this->overrideError;
1814
            $message = sprintf(
1815
                $message ?: 'Class "%s" was not expected to be instanceof of "%s".',
1816
                $this->stringify($this->value),
1817
                $className
1818
            );
1819
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_NOT_INSTANCE_OF, $fieldName, ['class' => $className]);
1820
        }
1821
        return $this;
1822
    }
1823
1824
    /**
1825
     * Assert that value is a subclass of given class-name.
1826
     *
1827
     * @param string      $className
1828
     * @param string|null $message
1829
     * @param string|null $fieldName
1830
     * @return Assert
1831
     * @throws AssertionFailedException
1832
     */
1833
    public function subclassOf($className, $message = null, $fieldName = null)
1834
    {
1835
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1836
        {
1837
            return $this;
1838
        }
1839
        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...
1840
        {
1841
            $message = $message ?: $this->overrideError;
1842
            $message = sprintf(
1843
                $message ?: 'Class "%s" was expected to be subclass of "%s".',
1844
                $this->stringify($this->value),
1845
                $className
1846
            );
1847
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_SUBCLASS_OF, $fieldName, ['class' => $className]);
1848
        }
1849
        return $this;
1850
    }
1851
1852
    /**
1853
     * Assert that value is within a range of numbers (inclusive).
1854
     *
1855
     * @param integer     $minValue
1856
     * @param integer     $maxValue
1857
     * @param string|null $message
1858
     * @param string|null $fieldName
1859
     * @return Assert
1860
     * @throws AssertionFailedException
1861
     */
1862
    public function range($minValue, $maxValue, $message = null, $fieldName = null)
1863
    {
1864
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1865
        {
1866
            return $this;
1867
        }
1868
        $this->numeric($message, $fieldName);
1869
        if ( $this->value < $minValue || $this->value > $maxValue )
1870
        {
1871
            $message = $message ?: $this->overrideError;
1872
            $message = sprintf(
1873
                $message ?: 'Number "%s" was expected to be at least "%d" and at most "%d".',
1874
                $this->stringify($this->value),
1875
                $this->stringify($minValue),
1876
                $this->stringify($maxValue)
1877
            );
1878
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_RANGE, $fieldName, [
1879
                'min' => $minValue,
1880
                'max' => $maxValue
1881
            ]);
1882
        }
1883
        return $this;
1884
    }
1885
1886
    /**
1887
     * Assert that value is larger or equal to a given lower limit.
1888
     *
1889
     * @param mixed       $minValue
1890
     * @param string|null $message
1891
     * @param string|null $fieldName
1892
     * @return Assert
1893
     * @throws AssertionFailedException
1894
     */
1895
    public function min($minValue, $message = null, $fieldName = null)
1896
    {
1897
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1898
        {
1899
            return $this;
1900
        }
1901
        $this->numeric($message, $fieldName);
1902
        if ( $this->value < $minValue )
1903
        {
1904
            $message = $message ?: $this->overrideError;
1905
            $message = sprintf(
1906
                $message ?: 'Number "%s" was expected to be at least "%d".',
1907
                $this->stringify($this->value),
1908
                $this->stringify($minValue)
1909
            );
1910
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_MIN, $fieldName, ['min' => $minValue]);
1911
        }
1912
        return $this;
1913
    }
1914
1915
    /**
1916
     * Assert that value is smaller than or equal to a given upper limit.
1917
     *
1918
     * @param mixed       $maxValue
1919
     * @param string|null $message
1920
     * @param string|null $fieldName
1921
     * @return Assert
1922
     * @throws AssertionFailedException
1923
     */
1924
    public function max($maxValue, $message = null, $fieldName = null)
1925
    {
1926
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1927
        {
1928
            return $this;
1929
        }
1930
        $this->numeric($message, $fieldName);
1931
        if ( $this->value > $maxValue )
1932
        {
1933
            $message = $message ?: $this->overrideError;
1934
            $message = sprintf(
1935
                $message ?: 'Number "%s" was expected to be at most "%d".',
1936
                $this->stringify($this->value),
1937
                $this->stringify($maxValue)
1938
            );
1939
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_MAX, $fieldName, ['max' => $maxValue]);
1940
        }
1941
        return $this;
1942
    }
1943
1944
    /**
1945
     * Assert that value is a file that exists.
1946
     *
1947
     * @param string|null $message
1948
     * @param string|null $fieldName
1949
     * @return Assert
1950
     * @throws AssertionFailedException
1951
     */
1952
    public function file($message = null, $fieldName = null)
1953
    {
1954
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1955
        {
1956
            return $this;
1957
        }
1958
        $this->string($message, $fieldName);
1959
        $this->notEmpty($message, $fieldName);
1960
        if ( !is_file($this->value) )
1961
        {
1962
            $message = $message ?: $this->overrideError;
1963
            $message = sprintf(
1964
                $message ?: 'File "%s" was expected to exist.',
1965
                $this->stringify($this->value)
1966
            );
1967
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_FILE, $fieldName);
1968
        }
1969
        return $this;
1970
    }
1971
1972
    /**
1973
     * Assert that value is a file or directory that exists.
1974
     *
1975
     * @param string|null $message
1976
     * @param string|null $fieldName
1977
     * @return $this
1978
     * @throws AssertionFailedException
1979
     */
1980
    public function fileOrDirectoryExists($message = null, $fieldName = null)
1981
    {
1982
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1983
        {
1984
            return $this;
1985
        }
1986
        $this->string($message, $fieldName);
1987
        $this->notEmpty($message, $fieldName);
1988
        if ( ! file_exists($this->value) )
1989
        {
1990
            $message = $message ?: $this->overrideError;
1991
            $message = sprintf(
1992
                $message ?: 'File or directory "%s" was expected to exist.',
1993
                $this->stringify($this->value)
1994
            );
1995
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_FILE_OR_DIR, $fieldName);
1996
        }
1997
        return $this;
1998
    }
1999
2000
    /**
2001
     * Assert that value is a directory that exists.
2002
     *
2003
     * @param string|null $message
2004
     * @param string|null $fieldName
2005
     * @return Assert
2006
     * @throws AssertionFailedException
2007
     */
2008 View Code Duplication
    public function directory($message = null, $fieldName = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2009
    {
2010
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2011
        {
2012
            return $this;
2013
        }
2014
        $this->string($message, $fieldName);
2015
        if ( !is_dir($this->value) )
2016
        {
2017
            $message = $message ?: $this->overrideError;
2018
            $message = sprintf(
2019
                $message ?: 'Path "%s" was expected to be a directory.',
2020
                $this->stringify($this->value)
2021
            );
2022
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_DIRECTORY, $fieldName);
2023
        }
2024
        return $this;
2025
    }
2026
2027
    /**
2028
     * Assert that value is something readable.
2029
     *
2030
     * @param string|null $message
2031
     * @param string|null $fieldName
2032
     * @return Assert
2033
     * @throws AssertionFailedException
2034
     */
2035 View Code Duplication
    public function readable($message = null, $fieldName = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2036
    {
2037
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2038
        {
2039
            return $this;
2040
        }
2041
        $this->string($message, $fieldName);
2042
        if ( !is_readable($this->value) )
2043
        {
2044
            $message = $message ?: $this->overrideError;
2045
            $message = sprintf(
2046
                $message ?: 'Path "%s" was expected to be readable.',
2047
                $this->stringify($this->value)
2048
            );
2049
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_READABLE, $fieldName);
2050
        }
2051
        return $this;
2052
    }
2053
2054
    /**
2055
     * Assert that value is something writeable.
2056
     *
2057
     * @param string|null $message
2058
     * @param string|null $fieldName
2059
     * @return Assert
2060
     * @throws AssertionFailedException
2061
     */
2062 View Code Duplication
    public function writeable($message = null, $fieldName = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2063
    {
2064
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2065
        {
2066
            return $this;
2067
        }
2068
        $this->string($message, $fieldName);
2069
        if ( !is_writeable($this->value) )
2070
        {
2071
            $message = $message ?: $this->overrideError;
2072
            $message = sprintf(
2073
                $message ?: 'Path "%s" was expected to be writeable.',
2074
                $this->stringify($this->value)
2075
            );
2076
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_WRITEABLE, $fieldName);
2077
        }
2078
        return $this;
2079
    }
2080
2081
    /**
2082
     * Assert that value is a valid email address (using input_filter/FILTER_VALIDATE_EMAIL).
2083
     *
2084
     * @param string|null $message
2085
     * @param string|null $fieldName
2086
     * @return Assert
2087
     * @throws AssertionFailedException
2088
     */
2089
    public function email($message = null, $fieldName = null)
2090
    {
2091
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2092
        {
2093
            return $this;
2094
        }
2095
        $this->string($message, $fieldName);
2096
        if ( ! filter_var($this->value, FILTER_VALIDATE_EMAIL) )
2097
        {
2098
            $message = $message ?: $this->overrideError;
2099
            $message = sprintf(
2100
                $message ?: 'Value "%s" was expected to be a valid e-mail address.',
2101
                $this->stringify($this->value)
2102
            );
2103
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_EMAIL, $fieldName);
2104
        }
2105
        else
2106
        {
2107
            $host = substr($this->value, strpos($this->value, '@') + 1);
2108
            // Likely not a FQDN, bug in PHP FILTER_VALIDATE_EMAIL prior to PHP 5.3.3
2109
            if ( version_compare(PHP_VERSION, '5.3.3', '<') && strpos($host, '.') === false )
2110
            {
2111
                $message = $message ?: $this->overrideError;
2112
                $message = sprintf(
2113
                    $message ?: 'Value "%s" was expected to be a valid e-mail address.',
2114
                    $this->stringify($this->value)
2115
                );
2116
                throw $this->createException($message, $this->overrideCode ?: self::INVALID_EMAIL, $fieldName);
2117
            }
2118
        }
2119
        return $this;
2120
    }
2121
2122
    /**
2123
     * Assert that value is a valid email prefix.
2124
     *
2125
     * @param string|null $message
2126
     * @param string|null $fieldName
2127
     * @return Assert
2128
     * @throws AssertionFailedException
2129
     */
2130
    public function emailPrefix($message = null, $fieldName = null)
2131
    {
2132
        $this->value($this->value . '@example.com');
2133
        return $this->email($message, $fieldName);
2134
    }
2135
2136
    /**
2137
     * Assert that value is a valid URL.
2138
     *
2139
     * This code snipped was taken from the Symfony project and modified to the special demands of this method.
2140
     *
2141
     * @param string|null $message
2142
     * @param string|null $fieldName
2143
     * @return Assert
2144
     * @throws AssertionFailedException
2145
     *
2146
     *
2147
     * @link https://github.com/symfony/Validator/blob/master/Constraints/UrlValidator.php
2148
     * @link https://github.com/symfony/Validator/blob/master/Constraints/Url.php
2149
     */
2150
    public function url($message = null, $fieldName = null)
2151
    {
2152
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2153
        {
2154
            return $this;
2155
        }
2156
        $this->string($message, $fieldName);
2157
        $protocols = ['http', 'https'];
2158
        $pattern   = '~^
2159
            (%s)://                                 # protocol
2160
            (
2161
                ([\pL\pN\pS-]+\.)+[\pL]+                   # a domain name
2162
                    |                                     #  or
2163
                \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}      # a IP address
2164
                    |                                     #  or
2165
                \[
2166
                    (?:(?:(?:(?:(?:(?:(?:[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})))?::))))
2167
                \]  # a IPv6 address
2168
            )
2169
            (:[0-9]+)?                              # a port (optional)
2170
            (/?|/\S+)                               # a /, nothing or a / with something
2171
        $~ixu';
2172
        $pattern   = sprintf($pattern, implode('|', $protocols));
2173
        if ( !preg_match($pattern, $this->value) )
2174
        {
2175
            $message = $message ?: $this->overrideError;
2176
            $message = sprintf(
2177
                $message ?: 'Value "%s" was expected to be a valid URL starting with http or https',
2178
                $this->stringify($this->value)
2179
            );
2180
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_URL, $fieldName);
2181
        }
2182
        return $this;
2183
    }
2184
2185
    /**
2186
     * Assert that value is domain name.
2187
     *
2188
     * This code snipped was taken from the Symfony project and modified to the special demands of this method.
2189
     *
2190
     * @param string|null $message
2191
     * @param string|null $fieldName
2192
     * @return Assert
2193
     * @throws AssertionFailedException
2194
     *
2195
     */
2196
    public function domainName($message = null, $fieldName = null)
2197
    {
2198
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2199
        {
2200
            return $this;
2201
        }
2202
        $this->string($message, $fieldName);
2203
        $pattern   = '/^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}$/';
2204
        if ( ! preg_match($pattern, $this->value) )
2205
        {
2206
            $message = $message ?: $this->overrideError;
2207
            $message = sprintf(
2208
                $message ?: 'Value "%s" was expected to be a valid domain name',
2209
                $this->stringify($this->value)
2210
            );
2211
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_DOMAIN_NAME, $fieldName);
2212
        }
2213
        return $this;
2214
    }
2215
2216
    /**
2217
     * Assert that value is alphanumeric.
2218
     *
2219
     * @param string|null $message
2220
     * @param string|null $fieldName
2221
     * @return Assert
2222
     * @throws AssertionFailedException
2223
     */
2224 View Code Duplication
    public function ausMobile($message = null, $fieldName = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2225
    {
2226
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2227
        {
2228
            return $this;
2229
        }
2230
        try
2231
        {
2232
            $this->regex('/^04[0-9]{8})$/', $message, $fieldName);
2233
        }
2234
        catch ( AssertionFailedException $e )
2235
        {
2236
            $message = $message ?: $this->overrideError;
2237
            $message = sprintf(
2238
                $message
2239
                    ?: 'Value "%s" is not an australian mobile number.',
2240
                $this->stringify($this->value)
2241
            );
2242
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_AUS_MOBILE, $fieldName);
2243
        }
2244
        return $this;
2245
    }
2246
2247
    /**
2248
     * Assert that value is alphanumeric.
2249
     *
2250
     * @param string|null $message
2251
     * @param string|null $fieldName
2252
     * @return Assert
2253
     * @throws AssertionFailedException
2254
     */
2255 View Code Duplication
    public function alnum($message = null, $fieldName = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2256
    {
2257
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2258
        {
2259
            return $this;
2260
        }
2261
        try
2262
        {
2263
            $this->regex('(^([a-zA-Z]{1}[a-zA-Z0-9]*)$)', $message, $fieldName);
2264
        }
2265
        catch (AssertionFailedException $e)
2266
        {
2267
            $message = $message ?: $this->overrideError;
2268
            $message = sprintf(
2269
                $message
2270
                    ?: 'Value "%s" is not alphanumeric, starting with letters and containing only letters and numbers.',
2271
                $this->stringify($this->value)
2272
            );
2273
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_ALNUM, $fieldName);
2274
        }
2275
        return $this;
2276
    }
2277
2278
    /**
2279
     * Assert that value is boolean True.
2280
     *
2281
     * @param string|null $message
2282
     * @param string|null $fieldName
2283
     * @return Assert
2284
     * @throws AssertionFailedException
2285
     */
2286
    public function true($message = null, $fieldName = null)
2287
    {
2288
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2289
        {
2290
            return $this;
2291
        }
2292
        if ( $this->value !== true )
2293
        {
2294
            $message = $message ?: $this->overrideError;
2295
            $message = sprintf(
2296
                $message ?: 'Value "%s" is not TRUE.',
2297
                $this->stringify($this->value)
2298
            );
2299
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_TRUE, $fieldName);
2300
        }
2301
2302
        return $this;
2303
    }
2304
2305
    /**
2306
     * Assert that value is boolean True.
2307
     *
2308
     * @param string|null $message
2309
     * @param string|null $fieldName
2310
     * @return Assert
2311
     * @throws AssertionFailedException
2312
     */
2313
    public function truthy($message = null, $fieldName = null)
2314
    {
2315
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2316
        {
2317
            return $this;
2318
        }
2319
        if ( ! $this->value )
2320
        {
2321
            $message = $message ?: $this->overrideError;
2322
            $message = sprintf(
2323
                $message ?: 'Value "%s" is not truthy.',
2324
                $this->stringify($this->value)
2325
            );
2326
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_TRUE, $fieldName);
2327
        }
2328
        return $this;
2329
    }
2330
2331
    /**
2332
     * Assert that value is boolean False.
2333
     *
2334
     * @param string|null $message
2335
     * @param string|null $fieldName
2336
     * @return Assert
2337
     * @throws AssertionFailedException
2338
     */
2339
    public function false($message = null, $fieldName = null)
2340
    {
2341
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2342
        {
2343
            return $this;
2344
        }
2345
        if ( $this->value !== false )
2346
        {
2347
            $message = $message ?: $this->overrideError;
2348
            $message = sprintf(
2349
                $message ?: 'Value "%s" is not FALSE.',
2350
                $this->stringify($this->value)
2351
            );
2352
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_FALSE, $fieldName);
2353
        }
2354
        return $this;
2355
    }
2356
2357
    /**
2358
     * Assert that value is not boolean False.
2359
     *
2360
     * @param string|null $message
2361
     * @param string|null $fieldName
2362
     * @return Assert
2363
     * @throws AssertionFailedException
2364
     */
2365
    public function notFalse($message = null, $fieldName = null)
2366
    {
2367
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2368
        {
2369
            return $this;
2370
        }
2371
        if ( $this->value === false )
2372
        {
2373
            $message = $message ?: $this->overrideError;
2374
            $message = sprintf(
2375
                $message ?: 'Value "%s" is not FALSE.',
2376
                $this->stringify($this->value)
2377
            );
2378
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_NOT_FALSE, $fieldName);
2379
        }
2380
        return $this;
2381
    }
2382
2383
    /**
2384
     * Assert that the class exists.
2385
     *
2386
     * @param string|null $message
2387
     * @param string|null $fieldName
2388
     * @return Assert
2389
     * @throws AssertionFailedException
2390
     */
2391
    public function classExists($message = null, $fieldName = null)
2392
    {
2393
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2394
        {
2395
            return $this;
2396
        }
2397
        if ( !class_exists($this->value) )
2398
        {
2399
            $message = $message ?: $this->overrideError;
2400
            $message = sprintf(
2401
                $message ?: 'Class "%s" does not exist.',
2402
                $this->stringify($this->value)
2403
            );
2404
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_CLASS, $fieldName);
2405
        }
2406
        return $this;
2407
    }
2408
2409
    /**
2410
     * Assert that the class implements the interface
2411
     *
2412
     * @param string      $interfaceName
2413
     * @param string|null $message
2414
     * @param string|null $fieldName
2415
     * @return Assert
2416
     * @throws AssertionFailedException
2417
     */
2418
    public function implementsInterface($interfaceName, $message = null, $fieldName = null)
2419
    {
2420
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2421
        {
2422
            return $this;
2423
        }
2424
        $reflection = new \ReflectionClass($this->value);
2425
        if ( !$reflection->implementsInterface($interfaceName) )
2426
        {
2427
            $message = $message ?: $this->overrideError;
2428
            $message = sprintf(
2429
                $message ?: 'Class "%s" does not implement interface "%s".',
2430
                $this->stringify($this->value),
2431
                $this->stringify($interfaceName)
2432
            );
2433
            throw $this->createException($message, self::INTERFACE_NOT_IMPLEMENTED, $fieldName, ['interface' => $interfaceName]);
2434
        }
2435
        return $this;
2436
    }
2437
2438
    /**
2439
     * Assert that value is a valid json string.
2440
     *
2441
     * NOTICE:
2442
     * Since this does a json_decode to determine its validity
2443
     * you probably should consider, when using the variable
2444
     * content afterwards, just to decode and check for yourself instead
2445
     * of using this assertion.
2446
     *
2447
     * @param string|null $message
2448
     * @param string|null $fieldName
2449
     * @return Assert
2450
     * @throws AssertionFailedException
2451
     */
2452
    public function isJsonString($message = null, $fieldName = null)
2453
    {
2454
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2455
        {
2456
            return $this;
2457
        }
2458
        if ( null === json_decode($this->value) && JSON_ERROR_NONE !== json_last_error() )
2459
        {
2460
            $message = $message ?: $this->overrideError;
2461
            $message = sprintf(
2462
                $message ?: 'Value "%s" is not a valid JSON string.',
2463
                $this->stringify($this->value)
2464
            );
2465
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_JSON_STRING, $fieldName);
2466
        }
2467
        return $this;
2468
    }
2469
2470
    /**
2471
     * Assert that value is a valid UUID.
2472
     *
2473
     * Uses code from {@link https://github.com/ramsey/uuid} that is MIT licensed.
2474
     *
2475
     * @param string|null $message
2476
     * @param string|null $fieldName
2477
     * @return Assert
2478
     * @throws AssertionFailedException
2479
     */
2480
    public function uuid($message = null, $fieldName = null)
2481
    {
2482
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2483
        {
2484
            return $this;
2485
        }
2486
        $this->value = str_replace(['urn:', 'uuid:', '{', '}'], '', $this->value);
2487
        if ( $this->value === '00000000-0000-0000-0000-000000000000' )
2488
        {
2489
            return $this;
2490
        }
2491
        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) )
2492
        {
2493
            $message = $message ?: $this->overrideError;
2494
            $message = sprintf(
2495
                $message ?: 'Value "%s" is not a valid UUID.',
2496
                $this->stringify($this->value)
2497
            );
2498
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_UUID, $fieldName);
2499
        }
2500
        return $this;
2501
    }
2502
    /**
2503
     * Assert that value is a valid samAccountName (in line with Active
2504
     * directory sAMAccountName restrictions for users).
2505
     *
2506
     * From: @link https://social.technet.microsoft.com/wiki/contents/articles/11216.active-directory-requirements-for-creating-objects.aspx#Objects_with_sAMAccountName_Attribute
2507
     *      The schema allows 256 characters in sAMAccountName values. However, the system limits sAMAccountName to
2508
     *      20 characters for user objects and 16 characters for computer objects. The following characters are not
2509
     *      allowed in sAMAccountName values: " [ ] : ; | = + * ? < > / \ ,
2510
     *      You cannot logon to a domain using a sAMAccountName that includes the "@" character. If a user has a
2511
     *      sAMAccountName with this character, they must logon using their userPrincipalName (UPN).
2512
     *
2513
     * @param string|null $message
2514
     * @param string|null $fieldName
2515
     * @return Assert
2516
     * @throws AssertionFailedException
2517
     */
2518 View Code Duplication
    public function samAccountName($message = null, $fieldName = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2519
    {
2520
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2521
        {
2522
            return $this;
2523
        }
2524
        if ( !preg_match('/^([a-z0-9]{4,20})$/', $this->value) )
2525
        {
2526
            $message = $message ?: $this->overrideError;
2527
            $message = sprintf(
2528
                $message ?: 'Value "%s" is not a valid samAccountName.',
2529
                $this->stringify($this->value)
2530
            );
2531
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_SAMACCOUNTNAME, $fieldName);
2532
        }
2533
        return $this;
2534
    }
2535
2536
    /**
2537
     * Assert that value is a valid userPrincipalName.
2538
     *
2539
     * @param string|null $message
2540
     * @param string|null $fieldName
2541
     * @return Assert
2542
     * @throws AssertionFailedException
2543
     */
2544 View Code Duplication
    public function userPrincipalName($message = null, $fieldName = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2545
    {
2546
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2547
        {
2548
            return $this;
2549
        }
2550
        try
2551
        {
2552
            $this->email($message, $fieldName);
2553
        }
2554
        catch (AssertionFailedException $e)
2555
        {
2556
            $message = $message ?: $this->overrideError;
2557
            $message = sprintf(
2558
                $message
2559
                    ?: 'Value "%s" is not a valid userPrincipalName.',
2560
                $this->stringify($this->value)
2561
            );
2562
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_USERPRINCIPALNAME, $fieldName);
2563
        }
2564
        return $this;
2565
    }
2566
2567
    /**
2568
     * Assert that the count of countable is equal to count.
2569
     *
2570
     * @param int         $count
2571
     * @param string|null $message
2572
     * @param string|null $fieldName
2573
     * @return Assert
2574
     * @throws AssertionFailedException
2575
     */
2576
    public function count($count, $message = null, $fieldName = null)
2577
    {
2578
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2579
        {
2580
            return $this;
2581
        }
2582
        if ( $count !== count($this->value) )
2583
        {
2584
            $message = $message ?: $this->overrideError;
2585
            $message = sprintf(
2586
                $message ?: 'List does not contain exactly "%d" elements.',
2587
                $this->stringify($this->value),
2588
                $this->stringify($count)
2589
            );
2590
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_COUNT, $fieldName, ['count' => $count]);
2591
        }
2592
        return $this;
2593
    }
2594
2595
    /**
2596
     * @param $func
2597
     * @param $args
2598
     * @return bool
2599
     * @throws AssertionFailedException
2600
     */
2601
    protected function doAllOrNullOr($func, $args)
2602
    {
2603
        if ( $this->nullOr && is_null($this->value) )
2604
        {
2605
            return true;
2606
        }
2607
        if ( $this->emptyOr && empty($this->value) )
2608
        {
2609
            return true;
2610
        }
2611
        if ( $this->all && (new Assert($this->value))->setExceptionClass($this->exceptionClass)->isTraversable() )
2612
        {
2613
            foreach ( $this->value as $idx => $value )
2614
            {
2615
                $object = (new Assert($value))->setExceptionClass($this->exceptionClass);
2616
                call_user_func_array([$object, $func], $args);
2617
            }
2618
            return true;
2619
        }
2620
        return ( $this->nullOr && is_null($this->value) ) || ( $this->emptyOr && empty($this->value) ) ? true : false;
2621
    }
2622
2623
    /**
2624
     * Assert if values array has every choice as key and that this choice has content.
2625
     *
2626
     * @param array $choices
2627
     * @param string|null $message
2628
     * @param string|null $fieldName
2629
     * @return $this
2630
     * @throws AssertionFailedException
2631
     */
2632
    public function choicesNotEmpty(array $choices, $message = null, $fieldName = null)
2633
    {
2634
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2635
        {
2636
            return $this;
2637
        }
2638
        $this->notEmpty($message, $fieldName);
2639
        foreach ( $choices as $choice )
2640
        {
2641
            $this->notEmptyKey($choice, $message, $fieldName);
2642
        }
2643
        return $this;
2644
    }
2645
2646
    /**
2647
     * Assert that the named method is defined in the provided object.
2648
     *
2649
     * @param mixed $object
2650
     * @param string|null $message
2651
     * @param string|null $fieldName
2652
     * @returns Assert
2653
     * @throws AssertionFailedException
2654
     */
2655
    public function methodExists($object, $message = null, $fieldName = null)
2656
    {
2657
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2658
        {
2659
            return $this;
2660
        }
2661
        (new Assert($object))->setExceptionClass($this->exceptionClass)->isObject($message, $fieldName);
2662
        if ( !method_exists($object, $this->value) )
2663
        {
2664
            $message = $message ?: $this->overrideError;
2665
            $message = sprintf(
2666
                $message ?: 'Expected "%s" does not a exist in provided object.',
2667
                $this->stringify($this->value)
2668
            );
2669
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_METHOD, $fieldName);
2670
        }
2671
        return $this;
2672
    }
2673
2674
    /**
2675
     * Assert that value is an object.
2676
     *
2677
     * @param string|null $message
2678
     * @param string|null $fieldName
2679
     * @return $this
2680
     * @throws AssertionFailedException
2681
     */
2682
    public function isObject($message = null, $fieldName = null)
2683
    {
2684
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2685
        {
2686
            return $this;
2687
        }
2688
        if ( !is_object($this->value) )
2689
        {
2690
            $message = $message ?: $this->overrideError;
2691
            $message = sprintf(
2692
                $message ?: 'Provided "%s" is not a valid object.',
2693
                $this->stringify($this->value)
2694
            );
2695
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_OBJECT, $fieldName);
2696
        }
2697
        return $this;
2698
    }
2699
2700
    /**
2701
     * Make a string version of a value.
2702
     *
2703
     * @param $value
2704
     * @return string
2705
     */
2706
    private function stringify($value)
2707
    {
2708
        if ( is_bool($value) )
2709
        {
2710
            return $value ? '<TRUE>' : '<FALSE>';
2711
        }
2712
        if ( is_scalar($value) )
2713
        {
2714
            $val = (string)$value;
2715
            if ( strlen($val) > 100 )
2716
            {
2717
                $val = substr($val, 0, 97) . '...';
2718
            }
2719
            return $val;
2720
        }
2721
        if ( is_array($value) )
2722
        {
2723
            return '<ARRAY>';
2724
        }
2725
        if ( is_object($value) )
2726
        {
2727
            return get_class($value);
2728
        }
2729
        if ( is_resource($value) )
2730
        {
2731
            return '<RESOURCE>';
2732
        }
2733
        if ( $value === null )
2734
        {
2735
            return '<NULL>';
2736
        }
2737
        return 'unknown';
2738
    }
2739
}
2740
2741