Completed
Push — master ( 9f6592...da81a2 )
by Terry
04:12
created

Assert::runValidators()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 3
nop 1
dl 0
loc 17
rs 9.4285
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 $propertyPath             = null;
120
121
    /** @var null|string */
122
    protected $level                    = 'critical';
123
124
    /** @var int */
125
    protected $overrideCode             = null;
126
127
    /** @var string */
128
    protected $overrideError            = '';
129
    /**
130
     * Exception to throw when an assertion failed.
131
     *
132
     * @var string
133
     */
134
    protected $exceptionClass           = 'Terah\Assert\AssertionFailedException';
135
136
    /**
137
     * @param mixed $value
138
     */
139
    public function __construct($value)
140
    {
141
        $this->value($value);
142
    }
143
    
144
    
145
    /**
146
     * @param \Closure[] $validators
147
     * @return array
148
     */
149
    public static function runValidators(array $validators) : array
150
    {
151
        $errors = [];
152
        foreach ( $validators as $fieldName => $validator )
153
        {
154
            try
155
            {
156
                $validator->__invoke();
157
            }
158
            catch ( AssertionFailedException $e )
159
            {
160
                $errors[$fieldName]     = $e->getMessage();
161
            }
162
        }
163
164
        return $errors;
165
    }
166
167
    /**
168
     * @param        $value
169
     * @param string $name
170
     * @param int    $code
171
     * @param string $error
172
     * @param string $level
173
     * @return Assert
174
     */
175
    public static function that($value, $name='', $code=0, $error='', $level=Assert::WARNING)
176
    {
177
        $assert = new static($value);
178
        if ( $name )
179
        {
180
            $assert->name($name);
181
        }
182
        if ( $code )
183
        {
184
            $assert->code($code);
185
        }
186
        if ( $error )
187
        {
188
            $assert->error($error);
189
        }
190
        if ( $level )
191
        {
192
            $assert->level($level);
193
        }
194
195
        return $assert;
196
    }
197
198
    /**
199
     * @param mixed $value
200
     * @return Assert
201
     */
202
    public function reset($value)
203
    {
204
        return $this->all(false)->nullOr(false)->value($value);
205
    }
206
207
    /**
208
     * @param mixed $value
209
     * @return Assert
210
     */
211
    public function value($value)
212
    {
213
        $this->value = $value;
214
        return $this;
215
    }
216
217
    /**
218
     * @param bool $nullOr
219
     * @return Assert
220
     */
221
    public function nullOr($nullOr = true)
222
    {
223
        $this->nullOr = $nullOr;
224
        return $this;
225
    }
226
227
    /**
228
     * @param bool $emptyOr
229
     * @return Assert
230
     */
231
    public function emptyOr($emptyOr = true)
232
    {
233
        $this->emptyOr = $emptyOr;
234
        return $this;
235
    }
236
237
    /**
238
     * @param bool $all
239
     * @return Assert
240
     */
241
    public function all($all = true)
242
    {
243
        $this->all = $all;
244
        return $this;
245
    }
246
247
    /**
248
     * Helper method that handles building the assertion failure exceptions.
249
     * They are returned from this method so that the stack trace still shows
250
     * the assertions method.
251
     *
252
     * @param string $message
253
     * @param int    $code
254
     * @param string $propertyPath
255
     * @param array  $constraints
256
     * @param string $level
257
     * @return AssertionFailedException
258
     */
259
    protected function createException($message, $code, $propertyPath, array $constraints = [], $level=null)
260
    {
261
        $exceptionClass = $this->exceptionClass;
262
        $propertyPath   = is_null($propertyPath) ? $this->propertyPath : $propertyPath;
263
        $level          = is_null($level) ? $this->level : $level;
264
265
        return new $exceptionClass($message, $code, $propertyPath, $this->value, $constraints, $level);
266
    }
267
268
    /**
269
     * @param $exceptionClass
270
     * @return Assert
271
     */
272
    public function setExceptionClass($exceptionClass)
273
    {
274
        $this->exceptionClass = $exceptionClass;
275
        return $this;
276
    }
277
278
    /**
279
     * @param int $code
280
     * @return Assert
281
     */
282
    public function code($code)
283
    {
284
        $this->overrideCode = $code;
285
        return $this;
286
    }
287
288
    /**
289
     * @param int $level
290
     * @return Assert
291
     */
292
    public function level($level)
293
    {
294
        $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...
295
        return $this;
296
    }
297
298
    /**
299
     * @param string $error
300
     * @return Assert
301
     */
302
    public function error($error)
303
    {
304
        $this->overrideError = $error;
305
        return $this;
306
    }
307
308
    /**
309
     * @param string $name
310
     * @return Assert
311
     */
312
    public function name($name)
313
    {
314
        $this->propertyPath = $name;
315
        return $this;
316
    }
317
318
    /**
319
     * Assert that two values are equal (using == ).
320
     *
321
     * @param mixed       $value2
322
     * @param string|null $message
323
     * @param string|null $propertyPath
324
     * @return Assert
325
     * @throws AssertionFailedException
326
     */
327
    public function eq($value2, $message = null, $propertyPath = null)
328
    {
329
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
330
        {
331
            return $this;
332
        }
333
        if ( $this->value != $value2 )
334
        {
335
            $message = $message ?: $this->overrideError;
336
            $message = sprintf(
337
                $message ?: 'Value "%s" does not equal expected value "%s".',
338
                $this->stringify($this->value),
339
                $this->stringify($value2)
340
            );
341
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_EQ, $propertyPath, ['expected' => $value2]);
342
        }
343
        return $this;
344
    }
345
346
    /**
347
     *
348
     * @param mixed       $value2
349
     * @param string|null $message
350
     * @param string|null $propertyPath
351
     * @return Assert
352
     * @throws AssertionFailedException
353
     */
354
    public function greaterThan($value2, $message = null, $propertyPath = null)
355
    {
356
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
357
        {
358
            return $this;
359
        }
360
        if ( ! ( $this->value > $value2 ) )
361
        {
362
            $message = $message ?: $this->overrideError;
363
            $message = sprintf(
364
                $message ?: 'Value "%s" does not greater then expected value "%s".',
365
                $this->stringify($this->value),
366
                $this->stringify($value2)
367
            );
368
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_EQ, $propertyPath, ['expected' => $value2]);
369
        }
370
        return $this;
371
    }
372
373
    /**
374
     *
375
     * @param mixed       $value2
376
     * @param string|null $message
377
     * @param string|null $propertyPath
378
     * @return Assert
379
     * @throws AssertionFailedException
380
     */
381
    public function greaterThanOrEq($value2, $message = null, $propertyPath = null)
382
    {
383
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
384
        {
385
            return $this;
386
        }
387
        if ( ! ( $this->value >= $value2 ) )
388
        {
389
            $message = $message ?: $this->overrideError;
390
            $message = sprintf(
391
                $message ?: 'Value "%s" does not greater than or equal to expected value "%s".',
392
                $this->stringify($this->value),
393
                $this->stringify($value2)
394
            );
395
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_EQ, $propertyPath, ['expected' => $value2]);
396
        }
397
        return $this;
398
    }
399
400
    /**
401
     *
402
     * @param mixed       $value2
403
     * @param string|null $message
404
     * @param string|null $propertyPath
405
     * @return Assert
406
     * @throws AssertionFailedException
407
     */
408
    public function lessThan($value2, $message = null, $propertyPath = null)
409
    {
410
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
411
        {
412
            return $this;
413
        }
414
        if ( ! ( $this->value < $value2 ) )
415
        {
416
            $message = $message ?: $this->overrideError;
417
            $message = sprintf(
418
                $message ?: 'Value "%s" does not less then expected value "%s".',
419
                $this->stringify($this->value),
420
                $this->stringify($value2)
421
            );
422
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_LESS_THAN, $propertyPath, ['expected' => $value2]);
423
        }
424
        return $this;
425
    }
426
427
    /**
428
     *
429
     * @param mixed       $value2
430
     * @param string|null $message
431
     * @param string|null $propertyPath
432
     * @return Assert
433
     * @throws AssertionFailedException
434
     */
435
    public function lessThanOrEq($value2, $message = null, $propertyPath = null)
436
    {
437
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
438
        {
439
            return $this;
440
        }
441
        if ( ! ( $this->value <= $value2 ) )
442
        {
443
            $message = $message ?: $this->overrideError;
444
            $message = sprintf(
445
                $message ?: 'Value "%s" does not less than or equal to expected value "%s".',
446
                $this->stringify($this->value),
447
                $this->stringify($value2)
448
            );
449
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_LESS_THAN_OR_EQ, $propertyPath, ['expected' => $value2]);
450
        }
451
        return $this;
452
    }
453
454
    /**
455
     * Assert that two values are the same (using ===).
456
     *
457
     * @param mixed       $value2
458
     * @param string|null $message
459
     * @param string|null $propertyPath
460
     * @return Assert
461
     * @throws AssertionFailedException
462
     */
463
    public function same($value2, $message = null, $propertyPath = null)
464
    {
465
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
466
        {
467
            return $this;
468
        }
469
        if ( $this->value !== $value2 )
470
        {
471
            $message = $message ?: $this->overrideError;
472
            $message = sprintf(
473
                $message ?: 'Value "%s" is not the same as expected value "%s".',
474
                $this->stringify($this->value),
475
                $this->stringify($value2)
476
            );
477
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_SAME, $propertyPath, ['expected' => $value2]);
478
        }
479
        return $this;
480
    }
481
482
    /**
483
     * Assert that two values are not equal (using == ).
484
     *
485
     * @param mixed       $value2
486
     * @param string|null $message
487
     * @param string|null $propertyPath
488
     * @return Assert
489
     * @throws AssertionFailedException
490
     */
491
    public function notEq($value2, $message = null, $propertyPath = null)
492
    {
493
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
494
        {
495
            return $this;
496
        }
497
        if ( $this->value == $value2 )
498
        {
499
            $message = $message ?: $this->overrideError;
500
            $message = sprintf(
501
                $message ?: 'Value "%s" is equal to expected value "%s".',
502
                $this->stringify($this->value),
503
                $this->stringify($value2)
504
            );
505
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_NOT_EQ, $propertyPath, ['expected' => $value2]);
506
        }
507
        return $this;
508
    }
509
510
    /**
511
     * @param string|null $message
512
     * @param string|null        $propertyPath
513
     *
514
     * @return $this
515
     * @throws AssertionFailedException
516
     */
517
    public function isCallable($message = null, $propertyPath = null)
518
    {
519
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
520
        {
521
            return $this;
522
        }
523
        if ( !is_callable($this->value) )
524
        {
525
            $message = $message ?: $this->overrideError;
526
            $message = sprintf(
527
                $message ?: 'Value "%s" is not callable.',
528
                $this->stringify($this->value)
529
            );
530
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_NOT_EQ, $propertyPath);
531
        }
532
        return $this;
533
    }
534
535
    /**
536
     * Assert that two values are not the same (using === ).
537
     *
538
     * @param mixed       $value2
539
     * @param string|null $message
540
     * @param string|null $propertyPath
541
     * @return Assert
542
     * @throws AssertionFailedException
543
     */
544
    public function notSame($value2, $message = null, $propertyPath = null)
545
    {
546
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
547
        {
548
            return $this;
549
        }
550
        if ( $this->value === $value2 )
551
        {
552
            $message = $message ?: $this->overrideError;
553
            $message = sprintf(
554
                $message ?: 'Value "%s" is the same as expected value "%s".',
555
                $this->stringify($this->value),
556
                $this->stringify($value2)
557
            );
558
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_NOT_SAME, $propertyPath, ['expected' => $value2]);
559
        }
560
        return $this;
561
    }
562
563
    /**
564
     * @param string|null $message
565
     * @param string|null $propertyPath
566
     * @return Assert
567
     * @throws AssertionFailedException
568
     */
569
    public function id($message = null, $propertyPath = null)
570
    {
571
        $message = $message ?: $this->overrideError;
572
        $message = $message ?: 'Value "%s" is not an integer id.';
573
        return $this->nonEmptyInt($message, $propertyPath)->range(1, PHP_INT_MAX);
574
    }
575
576
    /**
577
     * @param string|null $message
578
     * @param string|null $propertyPath
579
     * @return Assert
580
     * @throws AssertionFailedException
581
     */
582
    public function unsignedInt($message=null, $propertyPath=null)
583
    {
584
        $message = $message ?: $this->overrideError;
585
        $message = $message ?: 'Value "%s" is not an integer id.';
586
587
        return $this->int($message, $propertyPath)->range(0, PHP_INT_MAX);
588
    }
589
590
    /**
591
     * @param string|null $message
592
     * @param string|null $propertyPath
593
     * @return Assert
594
     * @throws AssertionFailedException
595
     */
596
    public function flag($message = null, $propertyPath = null)
597
    {
598
        $message = $message ?: $this->overrideError;
599
        $message = $message ?: 'Value "%s" is not a 0 or 1.';
600
        return $this->range(0, 1, $message, $propertyPath);
601
    }
602
603
    /**
604
     * @param string|null $message
605
     * @param string|null $propertyPath
606
     * @return Assert
607
     * @throws AssertionFailedException
608
     */
609
    public function status($message = null, $propertyPath = null)
610
    {
611
        $message = $message ?: $this->overrideError;
612
        $message = $message ?: 'Value "%s" is not a valid status.';
613
        return $this->integer($message, $propertyPath)->inArray([-1, 0, 1]);
614
    }
615
616
    /**
617
     * @param string|null $message
618
     * @param string|null $propertyPath
619
     * @return Assert
620
     */
621
    public function nullOrId($message = null, $propertyPath = null)
622
    {
623
        return $this->nullOr()->id($message, $propertyPath);
624
    }
625
626
    /**
627
     * @param string|null $message
628
     * @param string|null $propertyPath
629
     * @return Assert
630
     */
631
    public function allIds($message = null, $propertyPath = null)
632
    {
633
        return $this->all()->id($message, $propertyPath);
634
    }
635
636
    /**
637
     * @param string|null $message
638
     * @param string|null $propertyPath
639
     * @return Assert
640
     * @throws AssertionFailedException
641
     */
642
    public function int($message = null, $propertyPath = null)
643
    {
644
        return $this->integer($message, $propertyPath);
645
    }
646
647
    /**
648
     * Assert that value is a php integer.
649
     *
650
     * @param string|null $message
651
     * @param string|null $propertyPath
652
     * @return Assert
653
     * @throws AssertionFailedException
654
     */
655
    public function integer($message = null, $propertyPath = null)
656
    {
657
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
658
        {
659
            return $this;
660
        }
661
        if ( !is_int($this->value) )
662
        {
663
            $message = $message ?: $this->overrideError;
664
            $message = sprintf(
665
                $message ?: 'Value "%s" is not an integer.',
666
                $this->stringify($this->value)
667
            );
668
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_INTEGER, $propertyPath);
669
        }
670
        return $this;
671
    }
672
673
    /**
674
     * Assert that value is a php float.
675
     *
676
     * @param string|null $message
677
     * @param string|null $propertyPath
678
     * @return Assert
679
     * @throws AssertionFailedException
680
     */
681
    public function float($message = null, $propertyPath = null)
682
    {
683
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
684
        {
685
            return $this;
686
        }
687
        if ( ! is_float($this->value) )
688
        {
689
            $message = $message ?: $this->overrideError;
690
            $message = sprintf(
691
                $message ?: 'Value "%s" is not a float.',
692
                $this->stringify($this->value)
693
            );
694
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_FLOAT, $propertyPath);
695
        }
696
        return $this;
697
    }
698
699
    /**
700
     * Validates if an integer or integerish is a digit.
701
     *
702
     * @param string|null $message
703
     * @param string|null $propertyPath
704
     * @return Assert
705
     * @throws AssertionFailedException
706
     */
707
    public function digit($message = null, $propertyPath = null)
708
    {
709
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
710
        {
711
            return $this;
712
        }
713
        if ( ! ctype_digit((string)$this->value) )
714
        {
715
            $message = $message ?: $this->overrideError;
716
            $message = sprintf(
717
                $message ?: 'Value "%s" is not a digit.',
718
                $this->stringify($this->value)
719
            );
720
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_DIGIT, $propertyPath);
721
        }
722
        return $this;
723
    }
724
725
    /**
726
     * Validates if an string is a date .
727
     *
728
     * @param string|null $message
729
     * @param string|null $propertyPath
730
     * @return Assert
731
     * @throws AssertionFailedException
732
     */
733
    public function date($message = null, $propertyPath = null)
734
    {
735
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
736
        {
737
            return $this;
738
        }
739
        $this->notEmpty($message, $propertyPath);
740
        if ( strtotime($this->value) === false )
741
        {
742
            $message = $message ?: $this->overrideError;
743
            $message = sprintf(
744
                $message ?: 'Value "%s" is not a date.',
745
                $this->stringify($this->value)
746
            );
747
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_DATE, $propertyPath);
748
        }
749
        return $this;
750
    }
751
752
    /**
753
     * Assert that value is a php integer'ish.
754
     *
755
     * @param string|null $message
756
     * @param string|null $propertyPath
757
     * @return Assert
758
     * @throws AssertionFailedException
759
     */
760
    public function integerish($message = null, $propertyPath = null)
761
    {
762
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
763
        {
764
            return $this;
765
        }
766
        if ( is_object($this->value) || strval(intval($this->value)) != $this->value || is_bool($this->value) || is_null($this->value) )
767
        {
768
            $message = $message ?: $this->overrideError;
769
            $message = sprintf(
770
                $message ?: 'Value "%s" is not an integer or a number castable to integer.',
771
                $this->stringify($this->value)
772
            );
773
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_INTEGERISH, $propertyPath);
774
        }
775
        return $this;
776
    }
777
778
    /**
779
     * Assert that value is php boolean
780
     *
781
     * @param string|null $message
782
     * @param string|null $propertyPath
783
     * @return Assert
784
     * @throws AssertionFailedException
785
     */
786
    public function boolean($message = null, $propertyPath = null)
787
    {
788
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
789
        {
790
            return $this;
791
        }
792
        if ( ! is_bool($this->value) )
793
        {
794
            $message = $message ?: $this->overrideError;
795
            $message = sprintf(
796
                $message ?: 'Value "%s" is not a boolean.',
797
                $this->stringify($this->value)
798
            );
799
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_BOOLEAN, $propertyPath);
800
        }
801
        return $this;
802
    }
803
804
    /**
805
     * Assert that value is a PHP scalar
806
     *
807
     * @param string|null $message
808
     * @param string|null $propertyPath
809
     * @return Assert
810
     * @throws AssertionFailedException
811
     */
812
    public function scalar($message = null, $propertyPath = null)
813
    {
814
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
815
        {
816
            return $this;
817
        }
818
        if ( ! is_scalar($this->value) )
819
        {
820
            $message = $message ?: $this->overrideError;
821
            $message = sprintf(
822
                $message ?: 'Value "%s" is not a scalar.',
823
                $this->stringify($this->value)
824
            );
825
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_SCALAR, $propertyPath);
826
        }
827
        return $this;
828
    }
829
830
    /**
831
     * Assert that value is not empty
832
     *
833
     * @param string|null $message
834
     * @param string|null $propertyPath
835
     * @return Assert
836
     * @throws AssertionFailedException
837
     */
838 View Code Duplication
    public function notEmpty($message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
839
    {
840
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
841
        {
842
            return $this;
843
        }
844
        if ( ( is_object($this->value) && empty((array)$this->value) ) || empty($this->value) )
845
        {
846
            $message = $message ?: $this->overrideError;
847
            $message = sprintf(
848
                $message ?: 'Value "%s" is empty, but non empty value was expected.',
849
                $this->stringify($this->value)
850
            );
851
            throw $this->createException($message, $this->overrideCode ?: self::VALUE_EMPTY, $propertyPath);
852
        }
853
        return $this;
854
    }
855
856
    /**
857
     * Assert that value is empty
858
     *
859
     * @param string|null $message
860
     * @param string|null $propertyPath
861
     * @return Assert
862
     * @throws AssertionFailedException
863
     */
864
    public function noContent($message = null, $propertyPath = null)
865
    {
866
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
867
        {
868
            return $this;
869
        }
870
        if ( !empty( $this->value ) )
871
        {
872
            $message = $message ?: $this->overrideError;
873
            $message = sprintf(
874
                $message ?: 'Value "%s" is not empty, but empty value was expected.',
875
                $this->stringify($this->value)
876
            );
877
            throw $this->createException($message, $this->overrideCode ?: self::VALUE_NOT_EMPTY, $propertyPath);
878
        }
879
        return $this;
880
    }
881
882
    /**
883
     * Assert that value is not null
884
     *
885
     * @param string|null $message
886
     * @param string|null $propertyPath
887
     * @return Assert
888
     * @throws AssertionFailedException
889
     */
890
    public function notNull($message = null, $propertyPath = null)
891
    {
892
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
893
        {
894
            return $this;
895
        }
896
        if ( $this->value === null )
897
        {
898
            $message = $message ?: $this->overrideError;
899
            $message = sprintf(
900
                $message ?: 'Value "%s" is null, but non null value was expected.',
901
                $this->stringify($this->value)
902
            );
903
            throw $this->createException($message, $this->overrideCode ?: self::VALUE_NULL, $propertyPath);
904
        }
905
        return $this;
906
    }
907
908
    /**
909
     * Assert that value is a string
910
     *
911
     * @param string|null $message
912
     * @param string|null $propertyPath
913
     * @return Assert
914
     * @throws AssertionFailedException
915
     */
916
    public function string($message = null, $propertyPath = null)
917
    {
918
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
919
        {
920
            return $this;
921
        }
922
        if ( !is_string($this->value) )
923
        {
924
            $message = $message ?: $this->overrideError;
925
            $message = sprintf(
926
                $message ?: 'Value "%s" expected to be string, type %s given.',
927
                $this->stringify($this->value),
928
                gettype($this->value)
929
            );
930
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_STRING, $propertyPath);
931
        }
932
        return $this;
933
    }
934
935
    /**
936
     * Assert that value matches a regex
937
     *
938
     * @param string      $pattern
939
     * @param string|null $message
940
     * @param string|null $propertyPath
941
     * @return Assert
942
     * @throws AssertionFailedException
943
     */
944
    public function regex($pattern, $message=null, $propertyPath=null)
945
    {
946
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
947
        {
948
            return $this;
949
        }
950
        $this->string($message, $propertyPath);
951
        if ( ! preg_match($pattern, $this->value) )
952
        {
953
            $message = $message ?: $this->overrideError;
954
            $message = sprintf(
955
                $message ?: 'Value "%s" does not match expression.',
956
                $this->stringify($this->value)
957
            );
958
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_REGEX, $propertyPath, ['pattern' => $pattern]);
959
        }
960
        return $this;
961
    }
962
963
    /**
964
     * @param string $message
965
     * @param string $propertyPath
966
     * @return $this
967
     * @throws AssertionFailedException
968
     */
969
    public function ipAddress($message = null, $propertyPath = null)
970
    {
971
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
972
        {
973
            return $this;
974
        }
975
        $this->string($message, $propertyPath);
976
        $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])$/';
977
        if ( ! preg_match($pattern, $this->value) )
978
        {
979
            $message = $message ?: $this->overrideError;
980
            $message = sprintf(
981
                $message ?: 'Value "%s" was expected to be a valid IP Address',
982
                $this->stringify($this->value)
983
            );
984
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_IP_ADDRESS, $propertyPath);
985
        }
986
        return $this;
987
    }
988
989
    /**
990
     * @param string $pattern
991
     * @param string|null $message
992
     * @param string|null $propertyPath
993
     * @return $this
994
     * @throws AssertionFailedException
995
     */
996
    public function notRegex($pattern, $message = null, $propertyPath = null)
997
    {
998
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
999
        {
1000
            return $this;
1001
        }
1002
        $this->string($message, $propertyPath);
1003
        if ( preg_match($pattern, $this->value) )
1004
        {
1005
            $message = $message ?: $this->overrideError;
1006
            $message = sprintf(
1007
                $message ?: 'Value "%s" does not match expression.',
1008
                $this->stringify($this->value)
1009
            );
1010
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_REGEX, $propertyPath, ['pattern' => $pattern]);
1011
        }
1012
        return $this;
1013
    }
1014
1015
    /**
1016
     * Assert that string has a given length.
1017
     *
1018
     * @param int         $length
1019
     * @param string|null $message
1020
     * @param string|null $propertyPath
1021
     * @param string      $encoding
1022
     * @return Assert
1023
     * @throws AssertionFailedException
1024
     */
1025
    public function length($length, $message = null, $propertyPath = null, $encoding = 'utf8')
1026
    {
1027
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1028
        {
1029
            return $this;
1030
        }
1031
        $this->string($message, $propertyPath);
1032
        if ( mb_strlen($this->value, $encoding) !== $length )
1033
        {
1034
            $message    = $message ?: $this->overrideError;
1035
            $message    = sprintf(
1036
                $message ?: 'Value "%s" has to be %d exactly characters long, but length is %d.',
1037
                $this->stringify($this->value),
1038
                $length,
1039
                mb_strlen($this->value, $encoding)
1040
            );
1041
            $constraints = ['length' => $length, 'encoding' => $encoding];
1042
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_LENGTH, $propertyPath, $constraints);
1043
        }
1044
        return $this;
1045
    }
1046
1047
    /**
1048
     * Assert that a string is at least $minLength chars long.
1049
     *
1050
     * @param int         $minLength
1051
     * @param string|null $message
1052
     * @param string|null $propertyPath
1053
     * @param string      $encoding
1054
     * @return Assert
1055
     * @throws AssertionFailedException
1056
     */
1057
    public function minLength($minLength, $message = null, $propertyPath = null, $encoding = 'utf8')
1058
    {
1059
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1060
        {
1061
            return $this;
1062
        }
1063
        $this->string($message, $propertyPath);
1064
        if ( mb_strlen($this->value, $encoding) < $minLength )
1065
        {
1066
            $message = $message ?: $this->overrideError;
1067
            $message     = sprintf(
1068
                $message
1069
                    ?: 'Value "%s" is too short, it should have more than %d characters, but only has %d characters.',
1070
                $this->stringify($this->value),
1071
                $minLength,
1072
                mb_strlen($this->value, $encoding)
1073
            );
1074
            $constraints = ['min_length' => $minLength, 'encoding' => $encoding];
1075
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_MIN_LENGTH, $propertyPath, $constraints);
1076
        }
1077
        return $this;
1078
    }
1079
1080
    /**
1081
     * Assert that string value is not longer than $maxLength chars.
1082
     *
1083
     * @param integer     $maxLength
1084
     * @param string|null $message
1085
     * @param string|null $propertyPath
1086
     * @param string      $encoding
1087
     * @return Assert
1088
     * @throws AssertionFailedException
1089
     */
1090
    public function maxLength($maxLength, $message = null, $propertyPath = null, $encoding = 'utf8')
1091
    {
1092
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1093
        {
1094
            return $this;
1095
        }
1096
        $this->string($message, $propertyPath);
1097
        if ( mb_strlen($this->value, $encoding) > $maxLength )
1098
        {
1099
            $message = $message ?: $this->overrideError;
1100
            $message     = sprintf(
1101
                $message ?: 'Value "%s" is too long, it should have no more than %d characters, but has %d characters.',
1102
                $this->stringify($this->value),
1103
                $maxLength,
1104
                mb_strlen($this->value, $encoding)
1105
            );
1106
            $constraints = ['max_length' => $maxLength, 'encoding' => $encoding];
1107
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_MAX_LENGTH, $propertyPath, $constraints);
1108
        }
1109
        return $this;
1110
    }
1111
1112
    /**
1113
     * Assert that string length is between min,max lengths.
1114
     *
1115
     * @param integer     $minLength
1116
     * @param integer     $maxLength
1117
     * @param string|null $message
1118
     * @param string|null $propertyPath
1119
     * @param string      $encoding
1120
     * @return Assert
1121
     * @throws AssertionFailedException
1122
     */
1123
    public function betweenLength($minLength, $maxLength, $message = null, $propertyPath = null, $encoding = 'utf8')
1124
    {
1125
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1126
        {
1127
            return $this;
1128
        }
1129
        $this->string($message, $propertyPath);
1130
        if ( mb_strlen($this->value, $encoding) < $minLength )
1131
        {
1132
            $message = $message ?: $this->overrideError;
1133
            $message     = sprintf(
1134
                $message
1135
                    ?: 'Value "%s" is too short, it should have more than %d characters, but only has %d characters.',
1136
                $this->stringify($this->value),
1137
                $minLength,
1138
                mb_strlen($this->value, $encoding)
1139
            );
1140
            $constraints = ['min_length' => $minLength, 'encoding' => $encoding];
1141
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_MIN_LENGTH, $propertyPath, $constraints);
1142
        }
1143
        if ( mb_strlen($this->value, $encoding) > $maxLength )
1144
        {
1145
            $message = $message ?: $this->overrideError;
1146
            $message     = sprintf(
1147
                $message ?: 'Value "%s" is too long, it should have no more than %d characters, but has %d characters.',
1148
                $this->stringify($this->value),
1149
                $maxLength,
1150
                mb_strlen($this->value, $encoding)
1151
            );
1152
            $constraints = ['max_length' => $maxLength, 'encoding' => $encoding];
1153
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_MAX_LENGTH, $propertyPath, $constraints);
1154
        }
1155
        return $this;
1156
    }
1157
1158
    /**
1159
     * Assert that string starts with a sequence of chars.
1160
     *
1161
     * @param string      $needle
1162
     * @param string|null $message
1163
     * @param string|null $propertyPath
1164
     * @param string      $encoding
1165
     * @return Assert
1166
     * @throws AssertionFailedException
1167
     */
1168 View Code Duplication
    public function startsWith($needle, $message = null, $propertyPath = null, $encoding = 'utf8')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1169
    {
1170
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1171
        {
1172
            return $this;
1173
        }
1174
        $this->string($message, $propertyPath);
1175
        if ( mb_strpos($this->value, $needle, null, $encoding) !== 0 )
1176
        {
1177
            $message = $message ?: $this->overrideError;
1178
            $message     = sprintf(
1179
                $message ?: 'Value "%s" does not start with "%s".',
1180
                $this->stringify($this->value),
1181
                $this->stringify($needle)
1182
            );
1183
            $constraints = ['needle' => $needle, 'encoding' => $encoding];
1184
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_STRING_START, $propertyPath, $constraints);
1185
        }
1186
        return $this;
1187
    }
1188
1189
    /**
1190
     * Assert that string ends with a sequence of chars.
1191
     *
1192
     * @param string      $needle
1193
     * @param string|null $message
1194
     * @param string|null $propertyPath
1195
     * @param string      $encoding
1196
     * @return Assert
1197
     * @throws AssertionFailedException
1198
     */
1199
    public function endsWith($needle, $message = null, $propertyPath = null, $encoding = 'utf8')
1200
    {
1201
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1202
        {
1203
            return $this;
1204
        }
1205
        $this->string($message, $propertyPath);
1206
        $stringPosition = mb_strlen($this->value, $encoding) - mb_strlen($needle, $encoding);
1207
        if ( mb_strripos($this->value, $needle, null, $encoding) !== $stringPosition )
1208
        {
1209
            $message = $message ?: $this->overrideError;
1210
            $message     = sprintf(
1211
                $message ?: 'Value "%s" does not end with "%s".',
1212
                $this->stringify($this->value),
1213
                $this->stringify($needle)
1214
            );
1215
            $constraints = ['needle' => $needle, 'encoding' => $encoding];
1216
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_STRING_END, $propertyPath, $constraints);
1217
        }
1218
        return $this;
1219
    }
1220
1221
    /**
1222
     * Assert that string contains a sequence of chars.
1223
     *
1224
     * @param string      $needle
1225
     * @param string|null $message
1226
     * @param string|null $propertyPath
1227
     * @param string      $encoding
1228
     * @return Assert
1229
     * @throws AssertionFailedException
1230
     */
1231 View Code Duplication
    public function contains($needle, $message = null, $propertyPath = null, $encoding = 'utf8')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1232
    {
1233
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1234
        {
1235
            return $this;
1236
        }
1237
        $this->string($message, $propertyPath);
1238
        if ( mb_strpos($this->value, $needle, null, $encoding) === false )
1239
        {
1240
            $message = $message ?: $this->overrideError;
1241
            $message     = sprintf(
1242
                $message ?: 'Value "%s" does not contain "%s".',
1243
                $this->stringify($this->value),
1244
                $this->stringify($needle)
1245
            );
1246
            $constraints = ['needle' => $needle, 'encoding' => $encoding];
1247
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_STRING_CONTAINS, $propertyPath, $constraints);
1248
        }
1249
        return $this;
1250
    }
1251
1252
    /**
1253
     * Assert that value is in array of choices.
1254
     *
1255
     * @param array       $choices
1256
     * @param string|null $message
1257
     * @param string|null $propertyPath
1258
     * @return Assert
1259
     * @throws AssertionFailedException
1260
     */
1261
    public function choice(array $choices, $message = null, $propertyPath = null)
1262
    {
1263
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1264
        {
1265
            return $this;
1266
        }
1267
        if ( !in_array($this->value, $choices, true) )
1268
        {
1269
            $message = $message ?: $this->overrideError;
1270
            $message = sprintf(
1271
                $message ?: 'Value "%s" is not an element of the valid values: %s',
1272
                $this->stringify($this->value),
1273
                implode(", ", array_map('Terah\Assert\Assert::stringify', $choices))
1274
            );
1275
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_CHOICE, $propertyPath, ['choices' => $choices]);
1276
        }
1277
        return $this;
1278
    }
1279
1280
    /**
1281
     * Alias of {@see choice()}
1282
     *
1283
     * @throws AssertionFailedException
1284
     *
1285
     * @param array $choices
1286
     * @param string|null $message
1287
     * @param string|null $propertyPath
1288
     * @return $this
1289
     */
1290
    public function inArray(array $choices, $message = null, $propertyPath = null)
1291
    {
1292
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1293
        {
1294
            return $this;
1295
        }
1296
        $this->choice($choices, $message, $propertyPath);
1297
        return $this;
1298
    }
1299
1300
    /**
1301
     * Assert that value is numeric.
1302
     *
1303
     * @param string|null $message
1304
     * @param string|null $propertyPath
1305
     * @return Assert
1306
     * @throws AssertionFailedException
1307
     */
1308
    public function numeric($message = null, $propertyPath = null)
1309
    {
1310
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1311
        {
1312
            return $this;
1313
        }
1314
        if ( ! is_numeric($this->value) )
1315
        {
1316
            $message = $message ?: $this->overrideError;
1317
            $message = sprintf(
1318
                $message ?: 'Value "%s" is not numeric.',
1319
                $this->stringify($this->value)
1320
            );
1321
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_NUMERIC, $propertyPath);
1322
        }
1323
        return $this;
1324
    }
1325
1326
    /**
1327
     * @param string|null $message
1328
     * @param string|null $propertyPath
1329
     * @return Assert
1330
     * @throws AssertionFailedException
1331
     */
1332
    public function nonEmptyArray($message = null, $propertyPath = null)
1333
    {
1334
        $message = $message ?: 'Value "%s" is not a non-empty array.';
1335
        return $this->isArray($message, $propertyPath)->notEmpty($message, $propertyPath);
1336
    }
1337
1338
    /**
1339
     * @param string|null $message
1340
     * @param string|null $propertyPath
1341
     * @return Assert
1342
     * @throws AssertionFailedException
1343
     */
1344
    public function nonEmptyInt($message = null, $propertyPath = null)
1345
    {
1346
        $message = $message ?: 'Value "%s" is not a non-empty integer.';
1347
        return $this->integer($message, $propertyPath)->notEmpty($message, $propertyPath);
1348
    }
1349
1350
    /**
1351
     * @param string|null $message
1352
     * @param string|null $propertyPath
1353
     * @return Assert
1354
     * @throws AssertionFailedException
1355
     */
1356
    public function nonEmptyString($message = null, $propertyPath = null)
1357
    {
1358
        $message = $message ?: 'Value "%s" is not a non-empty string.';
1359
        return $this->string($message, $propertyPath)->notEmpty($message, $propertyPath);
1360
    }
1361
1362
    /**
1363
     * Assert that value is an array.
1364
     *
1365
     * @param string|null $message
1366
     * @param string|null $propertyPath
1367
     * @return Assert
1368
     * @throws AssertionFailedException
1369
     */
1370
    public function isArray($message = null, $propertyPath = null)
1371
    {
1372
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1373
        {
1374
            return $this;
1375
        }
1376
        if ( !is_array($this->value) )
1377
        {
1378
            $message = $message ?: $this->overrideError;
1379
            $message = sprintf(
1380
                $message ?: 'Value "%s" is not an array.',
1381
                $this->stringify($this->value)
1382
            );
1383
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_ARRAY, $propertyPath);
1384
        }
1385
        return $this;
1386
    }
1387
1388
    /**
1389
     * Assert that value is an array or a traversable object.
1390
     *
1391
     * @param string|null $message
1392
     * @param string|null $propertyPath
1393
     * @return Assert
1394
     * @throws AssertionFailedException
1395
     */
1396
    public function isTraversable($message = null, $propertyPath = null)
1397
    {
1398
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1399
        {
1400
            return $this;
1401
        }
1402
        if ( !is_array($this->value) && !$this->value instanceof \Traversable )
1403
        {
1404
            $message = $message ?: $this->overrideError;
1405
            $message = sprintf(
1406
                $message ?: 'Value "%s" is not an array and does not implement Traversable.',
1407
                $this->stringify($this->value)
1408
            );
1409
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_TRAVERSABLE, $propertyPath);
1410
        }
1411
        return $this;
1412
    }
1413
1414
    /**
1415
     * Assert that value is an array or an array-accessible object.
1416
     *
1417
     * @param string|null $message
1418
     * @param string|null $propertyPath
1419
     * @return Assert
1420
     * @throws AssertionFailedException
1421
     */
1422
    public function isArrayAccessible($message = null, $propertyPath = null)
1423
    {
1424
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1425
        {
1426
            return $this;
1427
        }
1428
        if ( !is_array($this->value) && !$this->value instanceof \ArrayAccess )
1429
        {
1430
            $message = $message ?: $this->overrideError;
1431
            $message = sprintf(
1432
                $message ?: 'Value "%s" is not an array and does not implement ArrayAccess.',
1433
                $this->stringify($this->value)
1434
            );
1435
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_ARRAY_ACCESSIBLE, $propertyPath);
1436
        }
1437
        return $this;
1438
    }
1439
1440
    /**
1441
     * Assert that key exists in an array
1442
     *
1443
     * @param string|integer $key
1444
     * @param string|null    $message
1445
     * @param string|null    $propertyPath
1446
     * @return Assert
1447
     * @throws AssertionFailedException
1448
     */
1449
    public function keyExists($key, $message = null, $propertyPath = null)
1450
    {
1451
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1452
        {
1453
            return $this;
1454
        }
1455
        $this->isArray($message, $propertyPath);
1456
        if ( !array_key_exists($key, $this->value) )
1457
        {
1458
            $message = $message ?: $this->overrideError;
1459
            $message = sprintf(
1460
                $message ?: 'Array does not contain an element with key "%s"',
1461
                $this->stringify($key)
1462
            );
1463
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_KEY_EXISTS, $propertyPath, ['key' => $key]);
1464
        }
1465
        return $this;
1466
    }
1467
1468
    /**
1469
     * Assert that keys exist in array
1470
     *
1471
     * @param array       $keys
1472
     * @param string|null $message
1473
     * @param string|null $propertyPath
1474
     * @return Assert
1475
     * @throws AssertionFailedException
1476
     */
1477
    public function keysExist($keys, $message = null, $propertyPath = null)
1478
    {
1479
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1480
        {
1481
            return $this;
1482
        }
1483
        $this->isArray($message, $propertyPath);
1484
        foreach ( $keys as $key )
1485
        {
1486
            if ( !array_key_exists($key, $this->value) )
1487
            {
1488
                $message = $message
1489
                    ?: sprintf(
1490
                        'Array does not contain an element with key "%s"',
1491
                        $this->stringify($key)
1492
                    );
1493
                throw $this->createException($message, $this->overrideCode ?: self::INVALID_KEYS_EXIST, $propertyPath, ['key' => $key]);
1494
            }
1495
        }
1496
        return $this;
1497
    }
1498
1499
    /**
1500
     * Assert that property exists in array
1501
     *
1502
     * @param string|integer $key
1503
     * @param string|null    $message
1504
     * @param string|null    $propertyPath
1505
     * @return Assert
1506
     * @throws AssertionFailedException
1507
     */
1508
    public function propertyExists($key, $message = null, $propertyPath = null)
1509
    {
1510
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1511
        {
1512
            return $this;
1513
        }
1514
        $this->isObject($message, $propertyPath);
1515
        if ( !property_exists($this->value, $key) && !isset( $this->value->{$key} ) )
1516
        {
1517
            $message = $message
1518
                ?: sprintf(
1519
                    'Object does not contain a property with key "%s"',
1520
                    $this->stringify($key)
1521
                );
1522
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_PROPERTY_EXISTS, $propertyPath, ['key' => $key]);
1523
        }
1524
        return $this;
1525
    }
1526
1527
    /**
1528
     * Assert that properties exists in array
1529
     *
1530
     * @param array       $keys
1531
     * @param string|null $message
1532
     * @param string|null $propertyPath
1533
     * @return Assert
1534
     * @throws AssertionFailedException
1535
     */
1536
    public function propertiesExist(array $keys, $message = null, $propertyPath = null)
1537
    {
1538
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1539
        {
1540
            return $this;
1541
        }
1542
        $this->isObject($message, $propertyPath);
1543
        foreach ( $keys as $key )
1544
        {
1545
            // Using isset to allow resolution of magically defined properties
1546
            if ( !property_exists($this->value, $key) && !isset( $this->value->{$key} ) )
1547
            {
1548
                $message = $message
1549
                    ?: sprintf(
1550
                        'Object does not contain a property with key "%s"',
1551
                        $this->stringify($key)
1552
                    );
1553
                throw $this->createException($message, $this->overrideCode ?: self::INVALID_PROPERTIES_EXIST, $propertyPath, ['key' => $key]);
1554
            }
1555
        }
1556
        return $this;
1557
    }
1558
1559
    /**
1560
     * Assert that string is valid utf8
1561
     *
1562
     * @param string|null $message
1563
     * @param string|null $propertyPath
1564
     * @return Assert
1565
     * @throws AssertionFailedException
1566
     */
1567 View Code Duplication
    public function utf8($message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1568
    {
1569
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1570
        {
1571
            return $this;
1572
        }
1573
        $this->string($message, $propertyPath);
1574
        if ( mb_detect_encoding($this->value, 'UTF-8', true) !== 'UTF-8' )
1575
        {
1576
            $message = $message
1577
                ?: sprintf(
1578
                    'Value "%s" was expected to be a valid UTF8 string',
1579
                    $this->stringify($this->value)
1580
                );
1581
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_UTF8, $propertyPath);
1582
        }
1583
        return $this;
1584
    }
1585
1586
1587
    /**
1588
     * Assert that string is valid utf8
1589
     *
1590
     * @param string|null $message
1591
     * @param string|null $propertyPath
1592
     * @return Assert
1593
     * @throws AssertionFailedException
1594
     */
1595 View Code Duplication
    public function ascii($message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1596
    {
1597
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1598
        {
1599
            return $this;
1600
        }
1601
        $this->string($message, $propertyPath);
1602
        if ( ! preg_match('/^[ -~]+$/', $this->value) )
1603
        {
1604
            $message = $message
1605
                ?: sprintf(
1606
                    'Value "%s" was expected to be a valid ASCII string',
1607
                    $this->stringify($this->value)
1608
                );
1609
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_ASCII, $propertyPath);
1610
        }
1611
        return $this;
1612
    }
1613
1614
    /**
1615
     * Assert that key exists in an array/array-accessible object using isset()
1616
     *
1617
     * @param string|integer $key
1618
     * @param string|null    $message
1619
     * @param string|null    $propertyPath
1620
     * @return Assert
1621
     * @throws AssertionFailedException
1622
     */
1623
    public function keyIsset($key, $message = null, $propertyPath = null)
1624
    {
1625
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1626
        {
1627
            return $this;
1628
        }
1629
        $this->isArrayAccessible($message, $propertyPath);
1630
        if ( !isset( $this->value[$key] ) )
1631
        {
1632
            $message = $message ?: $this->overrideError;
1633
            $message = sprintf(
1634
                $message ?: 'The element with key "%s" was not found',
1635
                $this->stringify($key)
1636
            );
1637
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_KEY_ISSET, $propertyPath, ['key' => $key]);
1638
        }
1639
        return $this;
1640
    }
1641
1642
    /**
1643
     * Assert that key exists in an array/array-accessible object and it's value is not empty.
1644
     *
1645
     * @param string|integer $key
1646
     * @param string|null    $message
1647
     * @param string|null    $propertyPath
1648
     * @return Assert
1649
     * @throws AssertionFailedException
1650
     */
1651
    public function notEmptyKey($key, $message = null, $propertyPath = null)
1652
    {
1653
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1654
        {
1655
            return $this;
1656
        }
1657
        $this->keyIsset($key, $message, $propertyPath);
1658
        (new Assert($this->value[$key]))->setExceptionClass($this->exceptionClass)->notEmpty($message, $propertyPath);
1659
        return $this;
1660
    }
1661
1662
    /**
1663
     * Assert that value is not blank
1664
     *
1665
     * @param string|null $message
1666
     * @param string|null $propertyPath
1667
     * @return Assert
1668
     * @throws AssertionFailedException
1669
     */
1670 View Code Duplication
    public function notBlank($message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1671
    {
1672
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1673
        {
1674
            return $this;
1675
        }
1676
        if ( false === $this->value || ( empty( $this->value ) && '0' != $this->value ) )
1677
        {
1678
            $message = $message ?: $this->overrideError;
1679
            $message = sprintf(
1680
                $message ?: 'Value "%s" is blank, but was expected to contain a value.',
1681
                $this->stringify($this->value)
1682
            );
1683
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_NOT_BLANK, $propertyPath);
1684
        }
1685
        return $this;
1686
    }
1687
1688
    /**
1689
     * Assert that value is instance of given class-name.
1690
     *
1691
     * @param string      $className
1692
     * @param string|null $message
1693
     * @param string|null $propertyPath
1694
     * @return Assert
1695
     * @throws AssertionFailedException
1696
     */
1697
    public function isInstanceOf($className, $message = null, $propertyPath = null)
1698
    {
1699
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1700
        {
1701
            return $this;
1702
        }
1703
        if ( !( $this->value instanceof $className ) )
1704
        {
1705
            $message = $message ?: $this->overrideError;
1706
            $message = sprintf(
1707
                $message ?: 'Class "%s" was expected to be instanceof of "%s" but is not.',
1708
                $this->stringify($this->value),
1709
                $className
1710
            );
1711
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_INSTANCE_OF, $propertyPath, ['class' => $className]);
1712
        }
1713
        return $this;
1714
    }
1715
1716
    /**
1717
     * Assert that value is not instance of given class-name.
1718
     *
1719
     * @param string      $className
1720
     * @param string|null $message
1721
     * @param string|null $propertyPath
1722
     * @return Assert
1723
     * @throws AssertionFailedException
1724
     */
1725
    public function notIsInstanceOf($className, $message = null, $propertyPath = null)
1726
    {
1727
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1728
        {
1729
            return $this;
1730
        }
1731
        if ( $this->value instanceof $className )
1732
        {
1733
            $message = $message ?: $this->overrideError;
1734
            $message = sprintf(
1735
                $message ?: 'Class "%s" was not expected to be instanceof of "%s".',
1736
                $this->stringify($this->value),
1737
                $className
1738
            );
1739
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_NOT_INSTANCE_OF, $propertyPath, ['class' => $className]);
1740
        }
1741
        return $this;
1742
    }
1743
1744
    /**
1745
     * Assert that value is subclass of given class-name.
1746
     *
1747
     * @param string      $className
1748
     * @param string|null $message
1749
     * @param string|null $propertyPath
1750
     * @return Assert
1751
     * @throws AssertionFailedException
1752
     */
1753
    public function subclassOf($className, $message = null, $propertyPath = null)
1754
    {
1755
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1756
        {
1757
            return $this;
1758
        }
1759
        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...
1760
        {
1761
            $message = $message ?: $this->overrideError;
1762
            $message = sprintf(
1763
                $message ?: 'Class "%s" was expected to be subclass of "%s".',
1764
                $this->stringify($this->value),
1765
                $className
1766
            );
1767
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_SUBCLASS_OF, $propertyPath, ['class' => $className]);
1768
        }
1769
        return $this;
1770
    }
1771
1772
    /**
1773
     * Assert that value is in range of numbers.
1774
     *
1775
     * @param integer     $minValue
1776
     * @param integer     $maxValue
1777
     * @param string|null $message
1778
     * @param string|null $propertyPath
1779
     * @return Assert
1780
     * @throws AssertionFailedException
1781
     */
1782
    public function range($minValue, $maxValue, $message = null, $propertyPath = null)
1783
    {
1784
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1785
        {
1786
            return $this;
1787
        }
1788
        $this->numeric($message, $propertyPath);
1789
        if ( $this->value < $minValue || $this->value > $maxValue )
1790
        {
1791
            $message = $message ?: $this->overrideError;
1792
            $message = sprintf(
1793
                $message ?: 'Number "%s" was expected to be at least "%d" and at most "%d".',
1794
                $this->stringify($this->value),
1795
                $this->stringify($minValue),
1796
                $this->stringify($maxValue)
1797
            );
1798
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_RANGE, $propertyPath, [
1799
                'min' => $minValue,
1800
                'max' => $maxValue
1801
            ]);
1802
        }
1803
        return $this;
1804
    }
1805
1806
    /**
1807
     * Assert that a value is at least as big as a given limit
1808
     *
1809
     * @param mixed       $minValue
1810
     * @param string|null $message
1811
     * @param string|null $propertyPath
1812
     * @return Assert
1813
     * @throws AssertionFailedException
1814
     */
1815
    public function min($minValue, $message = null, $propertyPath = null)
1816
    {
1817
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1818
        {
1819
            return $this;
1820
        }
1821
        $this->numeric($message, $propertyPath);
1822
        if ( $this->value < $minValue )
1823
        {
1824
            $message = $message ?: $this->overrideError;
1825
            $message = sprintf(
1826
                $message ?: 'Number "%s" was expected to be at least "%d".',
1827
                $this->stringify($this->value),
1828
                $this->stringify($minValue)
1829
            );
1830
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_MIN, $propertyPath, ['min' => $minValue]);
1831
        }
1832
        return $this;
1833
    }
1834
1835
    /**
1836
     * Assert that a number is smaller as a given limit
1837
     *
1838
     * @param mixed       $maxValue
1839
     * @param string|null $message
1840
     * @param string|null $propertyPath
1841
     * @return Assert
1842
     * @throws AssertionFailedException
1843
     */
1844
    public function max($maxValue, $message = null, $propertyPath = null)
1845
    {
1846
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1847
        {
1848
            return $this;
1849
        }
1850
        $this->numeric($message, $propertyPath);
1851
        if ( $this->value > $maxValue )
1852
        {
1853
            $message = $message ?: $this->overrideError;
1854
            $message = sprintf(
1855
                $message ?: 'Number "%s" was expected to be at most "%d".',
1856
                $this->stringify($this->value),
1857
                $this->stringify($maxValue)
1858
            );
1859
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_MAX, $propertyPath, ['max' => $maxValue]);
1860
        }
1861
        return $this;
1862
    }
1863
1864
    /**
1865
     * Assert that a file exists
1866
     *
1867
     * @param string|null $message
1868
     * @param string|null $propertyPath
1869
     * @return Assert
1870
     * @throws AssertionFailedException
1871
     */
1872
    public function file($message = null, $propertyPath = null)
1873
    {
1874
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1875
        {
1876
            return $this;
1877
        }
1878
        $this->string($message, $propertyPath);
1879
        $this->notEmpty($message, $propertyPath);
1880
        if ( !is_file($this->value) )
1881
        {
1882
            $message = $message ?: $this->overrideError;
1883
            $message = sprintf(
1884
                $message ?: 'File "%s" was expected to exist.',
1885
                $this->stringify($this->value)
1886
            );
1887
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_FILE, $propertyPath);
1888
        }
1889
        return $this;
1890
    }
1891
1892
    /**
1893
     * @param string|null $message
1894
     * @param string|null $propertyPath
1895
     * @return $this
1896
     * @throws AssertionFailedException
1897
     */
1898
    public function fileExists($message = null, $propertyPath = null)
1899
    {
1900
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1901
        {
1902
            return $this;
1903
        }
1904
        $this->string($message, $propertyPath);
1905
        $this->notEmpty($message, $propertyPath);
1906
        if ( ! file_exists($this->value) )
1907
        {
1908
            $message = $message ?: $this->overrideError;
1909
            $message = sprintf(
1910
                $message ?: 'File or directory "%s" was expected to exist.',
1911
                $this->stringify($this->value)
1912
            );
1913
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_FILE_OR_DIR, $propertyPath);
1914
        }
1915
        return $this;
1916
    }
1917
1918
    /**
1919
     * Assert that a directory exists
1920
     *
1921
     * @param string|null $message
1922
     * @param string|null $propertyPath
1923
     * @return Assert
1924
     * @throws AssertionFailedException
1925
     */
1926 View Code Duplication
    public function directory($message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1927
    {
1928
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1929
        {
1930
            return $this;
1931
        }
1932
        $this->string($message, $propertyPath);
1933
        if ( !is_dir($this->value) )
1934
        {
1935
            $message = $message ?: $this->overrideError;
1936
            $message = sprintf(
1937
                $message ?: 'Path "%s" was expected to be a directory.',
1938
                $this->stringify($this->value)
1939
            );
1940
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_DIRECTORY, $propertyPath);
1941
        }
1942
        return $this;
1943
    }
1944
1945
    /**
1946
     * Assert that the value is something readable
1947
     *
1948
     * @param string|null $message
1949
     * @param string|null $propertyPath
1950
     * @return Assert
1951
     * @throws AssertionFailedException
1952
     */
1953 View Code Duplication
    public function readable($message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1954
    {
1955
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1956
        {
1957
            return $this;
1958
        }
1959
        $this->string($message, $propertyPath);
1960
        if ( !is_readable($this->value) )
1961
        {
1962
            $message = $message ?: $this->overrideError;
1963
            $message = sprintf(
1964
                $message ?: 'Path "%s" was expected to be readable.',
1965
                $this->stringify($this->value)
1966
            );
1967
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_READABLE, $propertyPath);
1968
        }
1969
        return $this;
1970
    }
1971
1972
    /**
1973
     * Assert that the value is something writeable
1974
     *
1975
     * @param string|null $message
1976
     * @param string|null $propertyPath
1977
     * @return Assert
1978
     * @throws AssertionFailedException
1979
     */
1980 View Code Duplication
    public function writeable($message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1981
    {
1982
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1983
        {
1984
            return $this;
1985
        }
1986
        $this->string($message, $propertyPath);
1987
        if ( !is_writeable($this->value) )
1988
        {
1989
            $message = $message ?: $this->overrideError;
1990
            $message = sprintf(
1991
                $message ?: 'Path "%s" was expected to be writeable.',
1992
                $this->stringify($this->value)
1993
            );
1994
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_WRITEABLE, $propertyPath);
1995
        }
1996
        return $this;
1997
    }
1998
1999
    /**
2000
     * Assert that value is an email adress (using
2001
     * input_filter/FILTER_VALIDATE_EMAIL).
2002
     *
2003
     * @param string|null $message
2004
     * @param string|null $propertyPath
2005
     * @return Assert
2006
     * @throws AssertionFailedException
2007
     */
2008
    public function email($message = null, $propertyPath = null)
2009
    {
2010
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2011
        {
2012
            return $this;
2013
        }
2014
        $this->string($message, $propertyPath);
2015
        if ( ! filter_var($this->value, FILTER_VALIDATE_EMAIL) )
2016
        {
2017
            $message = $message ?: $this->overrideError;
2018
            $message = sprintf(
2019
                $message ?: 'Value "%s" was expected to be a valid e-mail address.',
2020
                $this->stringify($this->value)
2021
            );
2022
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_EMAIL, $propertyPath);
2023
        }
2024
        else
2025
        {
2026
            $host = substr($this->value, strpos($this->value, '@') + 1);
2027
            // Likely not a FQDN, bug in PHP FILTER_VALIDATE_EMAIL prior to PHP 5.3.3
2028
            if ( version_compare(PHP_VERSION, '5.3.3', '<') && strpos($host, '.') === false )
2029
            {
2030
                $message = $message ?: $this->overrideError;
2031
                $message = sprintf(
2032
                    $message ?: 'Value "%s" was expected to be a valid e-mail address.',
2033
                    $this->stringify($this->value)
2034
                );
2035
                throw $this->createException($message, $this->overrideCode ?: self::INVALID_EMAIL, $propertyPath);
2036
            }
2037
        }
2038
        return $this;
2039
    }
2040
2041
    /**
2042
     * @param null $message
2043
     * @param null $propertyPath
2044
     * @return Assert
2045
     * @throws AssertionFailedException
2046
     */
2047
    public function emailPrefix($message = null, $propertyPath = null)
2048
    {
2049
        $this->value($this->value . '@example.com');
2050
        return $this->email($message, $propertyPath);
2051
    }
2052
2053
    /**
2054
     * Assert that value is an URL.
2055
     *
2056
     * This code snipped was taken from the Symfony project and modified to the special demands of this method.
2057
     *
2058
     * @param string|null $message
2059
     * @param string|null $propertyPath
2060
     * @return Assert
2061
     * @throws AssertionFailedException
2062
     *
2063
     *
2064
     * @link https://github.com/symfony/Validator/blob/master/Constraints/UrlValidator.php
2065
     * @link https://github.com/symfony/Validator/blob/master/Constraints/Url.php
2066
     */
2067
    public function url($message = null, $propertyPath = null)
2068
    {
2069
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2070
        {
2071
            return $this;
2072
        }
2073
        $this->string($message, $propertyPath);
2074
        $protocols = ['http', 'https'];
2075
        $pattern   = '~^
2076
            (%s)://                                 # protocol
2077
            (
2078
                ([\pL\pN\pS-]+\.)+[\pL]+                   # a domain name
2079
                    |                                     #  or
2080
                \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}      # a IP address
2081
                    |                                     #  or
2082
                \[
2083
                    (?:(?:(?:(?:(?:(?:(?:[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})))?::))))
2084
                \]  # a IPv6 address
2085
            )
2086
            (:[0-9]+)?                              # a port (optional)
2087
            (/?|/\S+)                               # a /, nothing or a / with something
2088
        $~ixu';
2089
        $pattern   = sprintf($pattern, implode('|', $protocols));
2090
        if ( !preg_match($pattern, $this->value) )
2091
        {
2092
            $message = $message ?: $this->overrideError;
2093
            $message = sprintf(
2094
                $message ?: 'Value "%s" was expected to be a valid URL starting with http or https',
2095
                $this->stringify($this->value)
2096
            );
2097
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_URL, $propertyPath);
2098
        }
2099
        return $this;
2100
    }
2101
2102
    /**
2103
     * Assert that value is domain name.
2104
     *
2105
     * This code snipped was taken from the Symfony project and modified to the special demands of this method.
2106
     *
2107
     * @param string|null $message
2108
     * @param string|null $propertyPath
2109
     * @return Assert
2110
     * @throws AssertionFailedException
2111
     *
2112
     */
2113
    public function domainName($message = null, $propertyPath = null)
2114
    {
2115
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2116
        {
2117
            return $this;
2118
        }
2119
        $this->string($message, $propertyPath);
2120
        $pattern   = '/^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}$/';
2121
        if ( ! preg_match($pattern, $this->value) )
2122
        {
2123
            $message = $message ?: $this->overrideError;
2124
            $message = sprintf(
2125
                $message ?: 'Value "%s" was expected to be a valid domain name',
2126
                $this->stringify($this->value)
2127
            );
2128
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_DOMAIN_NAME, $propertyPath);
2129
        }
2130
        return $this;
2131
    }
2132
2133
    /**
2134
     * Assert that value is alphanumeric.
2135
     *
2136
     * @param string|null $message
2137
     * @param string|null $propertyPath
2138
     * @return Assert
2139
     * @throws AssertionFailedException
2140
     */
2141 View Code Duplication
    public function ausMobile($message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2142
    {
2143
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2144
        {
2145
            return $this;
2146
        }
2147
        try
2148
        {
2149
            $this->regex('/^04[0-9]{8})$/', $message, $propertyPath);
2150
        }
2151
        catch ( AssertionFailedException $e )
2152
        {
2153
            $message = $message ?: $this->overrideError;
2154
            $message = sprintf(
2155
                $message
2156
                    ?: 'Value "%s" is not an australian mobile number.',
2157
                $this->stringify($this->value)
2158
            );
2159
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_AUS_MOBILE, $propertyPath);
2160
        }
2161
        return $this;
2162
    }
2163
2164
    /**
2165
     * Assert that value is alphanumeric.
2166
     *
2167
     * @param string|null $message
2168
     * @param string|null $propertyPath
2169
     * @return Assert
2170
     * @throws AssertionFailedException
2171
     */
2172 View Code Duplication
    public function alnum($message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2173
    {
2174
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2175
        {
2176
            return $this;
2177
        }
2178
        try
2179
        {
2180
            $this->regex('(^([a-zA-Z]{1}[a-zA-Z0-9]*)$)', $message, $propertyPath);
2181
        }
2182
        catch (AssertionFailedException $e)
2183
        {
2184
            $message = $message ?: $this->overrideError;
2185
            $message = sprintf(
2186
                $message
2187
                    ?: 'Value "%s" is not alphanumeric, starting with letters and containing only letters and numbers.',
2188
                $this->stringify($this->value)
2189
            );
2190
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_ALNUM, $propertyPath);
2191
        }
2192
        return $this;
2193
    }
2194
2195
    /**
2196
     * Assert that the value is boolean True.
2197
     *
2198
     * @param string|null $message
2199
     * @param string|null $propertyPath
2200
     * @return Assert
2201
     * @throws AssertionFailedException
2202
     */
2203
    public function true($message = null, $propertyPath = null)
2204
    {
2205
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2206
        {
2207
            return $this;
2208
        }
2209
        if ( $this->value !== true )
2210
        {
2211
            $message = $message ?: $this->overrideError;
2212
            $message = sprintf(
2213
                $message ?: 'Value "%s" is not TRUE.',
2214
                $this->stringify($this->value)
2215
            );
2216
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_TRUE, $propertyPath);
2217
        }
2218
2219
        return $this;
2220
    }
2221
2222
    /**
2223
     * Assert that the value is boolean True.
2224
     *
2225
     * @param string|null $message
2226
     * @param string|null $propertyPath
2227
     * @return Assert
2228
     * @throws AssertionFailedException
2229
     */
2230
    public function truthy($message = null, $propertyPath = null)
2231
    {
2232
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2233
        {
2234
            return $this;
2235
        }
2236
        if ( ! $this->value )
2237
        {
2238
            $message = $message ?: $this->overrideError;
2239
            $message = sprintf(
2240
                $message ?: 'Value "%s" is not truthy.',
2241
                $this->stringify($this->value)
2242
            );
2243
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_TRUE, $propertyPath);
2244
        }
2245
        return $this;
2246
    }
2247
2248
    /**
2249
     * Assert that the value is boolean False.
2250
     *
2251
     * @param string|null $message
2252
     * @param string|null $propertyPath
2253
     * @return Assert
2254
     * @throws AssertionFailedException
2255
     */
2256
    public function false($message = null, $propertyPath = null)
2257
    {
2258
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2259
        {
2260
            return $this;
2261
        }
2262
        if ( $this->value !== false )
2263
        {
2264
            $message = $message ?: $this->overrideError;
2265
            $message = sprintf(
2266
                $message ?: 'Value "%s" is not FALSE.',
2267
                $this->stringify($this->value)
2268
            );
2269
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_FALSE, $propertyPath);
2270
        }
2271
        return $this;
2272
    }
2273
2274
    /**
2275
     * Assert that the value is not boolean False.
2276
     *
2277
     * @param string|null $message
2278
     * @param string|null $propertyPath
2279
     * @return Assert
2280
     * @throws AssertionFailedException
2281
     */
2282
    public function notFalse($message = null, $propertyPath = null)
2283
    {
2284
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2285
        {
2286
            return $this;
2287
        }
2288
        if ( $this->value === false )
2289
        {
2290
            $message = $message ?: $this->overrideError;
2291
            $message = sprintf(
2292
                $message ?: 'Value "%s" is not FALSE.',
2293
                $this->stringify($this->value)
2294
            );
2295
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_NOT_FALSE, $propertyPath);
2296
        }
2297
        return $this;
2298
    }
2299
2300
    /**
2301
     * Assert that the class exists.
2302
     *
2303
     * @param string|null $message
2304
     * @param string|null $propertyPath
2305
     * @return Assert
2306
     * @throws AssertionFailedException
2307
     */
2308
    public function classExists($message = null, $propertyPath = null)
2309
    {
2310
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2311
        {
2312
            return $this;
2313
        }
2314
        if ( !class_exists($this->value) )
2315
        {
2316
            $message = $message ?: $this->overrideError;
2317
            $message = sprintf(
2318
                $message ?: 'Class "%s" does not exist.',
2319
                $this->stringify($this->value)
2320
            );
2321
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_CLASS, $propertyPath);
2322
        }
2323
        return $this;
2324
    }
2325
2326
    /**
2327
     * Assert that the class implements the interface
2328
     *
2329
     * @param string      $interfaceName
2330
     * @param string|null $message
2331
     * @param string|null $propertyPath
2332
     * @return Assert
2333
     * @throws AssertionFailedException
2334
     */
2335
    public function implementsInterface($interfaceName, $message = null, $propertyPath = null)
2336
    {
2337
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2338
        {
2339
            return $this;
2340
        }
2341
        $reflection = new \ReflectionClass($this->value);
2342
        if ( !$reflection->implementsInterface($interfaceName) )
2343
        {
2344
            $message = $message ?: $this->overrideError;
2345
            $message = sprintf(
2346
                $message ?: 'Class "%s" does not implement interface "%s".',
2347
                $this->stringify($this->value),
2348
                $this->stringify($interfaceName)
2349
            );
2350
            throw $this->createException($message, self::INTERFACE_NOT_IMPLEMENTED, $propertyPath, ['interface' => $interfaceName]);
2351
        }
2352
        return $this;
2353
    }
2354
2355
    /**
2356
     * Assert that the given string is a valid json string.
2357
     *
2358
     * NOTICE:
2359
     * Since this does a json_decode to determine its validity
2360
     * you probably should consider, when using the variable
2361
     * content afterwards, just to decode and check for yourself instead
2362
     * of using this assertion.
2363
     *
2364
     * @param string|null $message
2365
     * @param string|null $propertyPath
2366
     * @return Assert
2367
     * @throws AssertionFailedException
2368
     */
2369
    public function isJsonString($message = null, $propertyPath = null)
2370
    {
2371
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2372
        {
2373
            return $this;
2374
        }
2375
        if ( null === json_decode($this->value) && JSON_ERROR_NONE !== json_last_error() )
2376
        {
2377
            $message = $message ?: $this->overrideError;
2378
            $message = sprintf(
2379
                $message ?: 'Value "%s" is not a valid JSON string.',
2380
                $this->stringify($this->value)
2381
            );
2382
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_JSON_STRING, $propertyPath);
2383
        }
2384
        return $this;
2385
    }
2386
2387
    /**
2388
     * Assert that the given string is a valid UUID
2389
     *
2390
     * Uses code from {@link https://github.com/ramsey/uuid} that is MIT licensed.
2391
     *
2392
     * @param string|null $message
2393
     * @param string|null $propertyPath
2394
     * @return Assert
2395
     * @throws AssertionFailedException
2396
     */
2397
    public function uuid($message = null, $propertyPath = null)
2398
    {
2399
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2400
        {
2401
            return $this;
2402
        }
2403
        $this->value = str_replace(['urn:', 'uuid:', '{', '}'], '', $this->value);
2404
        if ( $this->value === '00000000-0000-0000-0000-000000000000' )
2405
        {
2406
            return $this;
2407
        }
2408
        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) )
2409
        {
2410
            $message = $message ?: $this->overrideError;
2411
            $message = sprintf(
2412
                $message ?: 'Value "%s" is not a valid UUID.',
2413
                $this->stringify($this->value)
2414
            );
2415
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_UUID, $propertyPath);
2416
        }
2417
        return $this;
2418
    }
2419
    /**
2420
     * Assert that the given string is a valid samAccountName (in line with Active directory sAMAccountName restrictions for users)
2421
     *
2422
     * From: https://social.technet.microsoft.com/wiki/contents/articles/11216.active-directory-requirements-for-creating-objects.aspx#Objects_with_sAMAccountName_Attribute
2423
     *      The schema allows 256 characters in sAMAccountName values. However, the system limits sAMAccountName to 20 characters for user objects and 16 characters for computer objects.
2424
     *      The following characters are not allowed in sAMAccountName values: " [ ] : ; | = + * ? < > / \ ,
2425
     *      you cannot logon to a domain using a sAMAccountName that includes the "@" character. If a user has a sAMAccountName with this character, they must logon using their userPrincipalName (UPN).
2426
     *
2427
     * @param string|null $message
2428
     * @param string|null $propertyPath
2429
     * @return Assert
2430
     * @throws AssertionFailedException
2431
     */
2432 View Code Duplication
    public function samAccountName($message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2433
    {
2434
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2435
        {
2436
            return $this;
2437
        }
2438
        if ( !preg_match('/^([a-z0-9]{4,20})$/', $this->value) )
2439
        {
2440
            $message = $message ?: $this->overrideError;
2441
            $message = sprintf(
2442
                $message ?: 'Value "%s" is not a valid samAccountName.',
2443
                $this->stringify($this->value)
2444
            );
2445
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_SAMACCOUNTNAME, $propertyPath);
2446
        }
2447
        return $this;
2448
    }
2449
2450
    /**
2451
     * Assert that the given string is a valid userPrincipalName
2452
     *
2453
     * @param string|null $message
2454
     * @param string|null $propertyPath
2455
     * @return Assert
2456
     * @throws AssertionFailedException
2457
     */
2458 View Code Duplication
    public function userPrincipalName($message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2459
    {
2460
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2461
        {
2462
            return $this;
2463
        }
2464
        try {
2465
            $this->email($message, $propertyPath);
2466
        } catch (AssertionFailedException $e) {
2467
            $message = $message ?: $this->overrideError;
2468
            $message = sprintf(
2469
                $message
2470
                    ?: 'Value "%s" is not a valid userPrincipalName.',
2471
                $this->stringify($this->value)
2472
            );
2473
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_USERPRINCIPALNAME, $propertyPath);
2474
        }
2475
        return $this;
2476
    }
2477
2478
    /**
2479
     * Assert that the count of countable is equal to count.
2480
     *
2481
     * @param int    $count
2482
     * @param string $message
2483
     * @param string $propertyPath
2484
     * @return Assert
2485
     * @throws AssertionFailedException
2486
     */
2487
    public function count($count, $message = null, $propertyPath = null)
2488
    {
2489
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2490
        {
2491
            return $this;
2492
        }
2493
        if ( $count !== count($this->value) )
2494
        {
2495
            $message = $message ?: $this->overrideError;
2496
            $message = sprintf(
2497
                $message ?: 'List does not contain exactly "%d" elements.',
2498
                $this->stringify($this->value),
2499
                $this->stringify($count)
2500
            );
2501
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_COUNT, $propertyPath, ['count' => $count]);
2502
        }
2503
        return $this;
2504
    }
2505
2506
    /**
2507
     * @param $func
2508
     * @param $args
2509
     * @return bool
2510
     * @throws AssertionFailedException
2511
     */
2512
    protected function doAllOrNullOr($func, $args)
2513
    {
2514
        if ( $this->nullOr && is_null($this->value) )
2515
        {
2516
            return true;
2517
        }
2518
        if ( $this->emptyOr && empty($this->value) )
2519
        {
2520
            return true;
2521
        }
2522
        if ( $this->all && (new Assert($this->value))->setExceptionClass($this->exceptionClass)->isTraversable() )
2523
        {
2524
            foreach ( $this->value as $idx => $value )
2525
            {
2526
                $object = (new Assert($value))->setExceptionClass($this->exceptionClass);
2527
                call_user_func_array([$object, $func], $args);
2528
            }
2529
            return true;
2530
        }
2531
        return ( $this->nullOr && is_null($this->value) ) || ( $this->emptyOr && empty($this->value) ) ? true : false;
2532
    }
2533
2534
    /**
2535
     * Determines if the values array has every choice as key and that this choice has content.
2536
     *
2537
     * @param array $choices
2538
     * @param string|null $message
2539
     * @param string|null $propertyPath
2540
     * @return $this
2541
     */
2542
    public function choicesNotEmpty(array $choices, $message = null, $propertyPath = null)
2543
    {
2544
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2545
        {
2546
            return $this;
2547
        }
2548
        $this->notEmpty($message, $propertyPath);
2549
        foreach ( $choices as $choice )
2550
        {
2551
            $this->notEmptyKey($choice, $message, $propertyPath);
2552
        }
2553
        return $this;
2554
    }
2555
2556
    /**
2557
     * Determines that the named method is defined in the provided object.
2558
     *
2559
     * @param mixed $object
2560
     * @param string|null $message
2561
     * @param string|null $propertyPath
2562
     * @returns Assert
2563
     * @throws
2564
     */
2565
    public function methodExists($object, $message = null, $propertyPath = null)
2566
    {
2567
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2568
        {
2569
            return $this;
2570
        }
2571
        (new Assert($object))->setExceptionClass($this->exceptionClass)->isObject($message, $propertyPath);
2572
        if ( !method_exists($object, $this->value) )
2573
        {
2574
            $message = $message ?: $this->overrideError;
2575
            $message = sprintf(
2576
                $message ?: 'Expected "%s" does not a exist in provided object.',
2577
                $this->stringify($this->value)
2578
            );
2579
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_METHOD, $propertyPath);
2580
        }
2581
        return $this;
2582
    }
2583
2584
    /**
2585
     * Determines that the provided value is an object.
2586
     *
2587
     * @param string|null $message
2588
     * @param string|null $propertyPath
2589
     * @return $this
2590
     * @throws AssertionFailedException
2591
     */
2592
    public function isObject($message = null, $propertyPath = null)
2593
    {
2594
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2595
        {
2596
            return $this;
2597
        }
2598
        if ( !is_object($this->value) )
2599
        {
2600
            $message = $message ?: $this->overrideError;
2601
            $message = sprintf(
2602
                $message ?: 'Provided "%s" is not a valid object.',
2603
                $this->stringify($this->value)
2604
            );
2605
            throw $this->createException($message, $this->overrideCode ?: self::INVALID_OBJECT, $propertyPath);
2606
        }
2607
        return $this;
2608
    }
2609
2610
    /**
2611
     * Make a string version of a value.
2612
     *
2613
     * @param $value
2614
     * @return string
2615
     */
2616
    private function stringify($value)
2617
    {
2618
        if ( is_bool($value) )
2619
        {
2620
            return $value ? '<TRUE>' : '<FALSE>';
2621
        }
2622
        if ( is_scalar($value) )
2623
        {
2624
            $val = (string)$value;
2625
            if ( strlen($val) > 100 )
2626
            {
2627
                $val = substr($val, 0, 97) . '...';
2628
            }
2629
            return $val;
2630
        }
2631
        if ( is_array($value) )
2632
        {
2633
            return '<ARRAY>';
2634
        }
2635
        if ( is_object($value) )
2636
        {
2637
            return get_class($value);
2638
        }
2639
        if ( is_resource($value) )
2640
        {
2641
            return '<RESOURCE>';
2642
        }
2643
        if ( $value === null )
2644
        {
2645
            return '<NULL>';
2646
        }
2647
        return 'unknown';
2648
    }
2649
}
2650
2651