Completed
Push — master ( b46744...9f6592 )
by Terry
12s
created

Assert::keyIsset()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 11
nc 4
nop 3
dl 0
loc 18
rs 8.8571
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
namespace Terah\Assert;
4
5
    /**
6
     * Assert
7
     *
8
     * LICENSE
9
     *
10
     * This source file is subject to the new BSD license that is bundled
11
     * with this package in the file LICENSE.txt.
12
     * If you did not receive a copy of the license and are unable to
13
     * obtain it through the world-wide-web, please send an email
14
     * to [email protected] so I can send you a copy immediately.
15
     */
16
17
/**
18
 * Assert library
19
 *
20
 * @author Benjamin Eberlei <[email protected]>
21
 * @author Terry Cullen <[email protected]>
22
 *
23
 */
24
class Assert
25
{
26
    const INVALID_FLOAT                 = 9;
27
    const INVALID_INTEGER               = 10;
28
    const INVALID_DIGIT                 = 11;
29
    const INVALID_INTEGERISH            = 12;
30
    const INVALID_BOOLEAN               = 13;
31
    const VALUE_EMPTY                   = 14;
32
    const VALUE_NULL                    = 15;
33
    const INVALID_STRING                = 16;
34
    const INVALID_REGEX                 = 17;
35
    const INVALID_MIN_LENGTH            = 18;
36
    const INVALID_MAX_LENGTH            = 19;
37
    const INVALID_STRING_START          = 20;
38
    const INVALID_STRING_CONTAINS       = 21;
39
    const INVALID_CHOICE                = 22;
40
    const INVALID_NUMERIC               = 23;
41
    const INVALID_ARRAY                 = 24;
42
    const INVALID_KEY_EXISTS            = 26;
43
    const INVALID_NOT_BLANK             = 27;
44
    const INVALID_INSTANCE_OF           = 28;
45
    const INVALID_SUBCLASS_OF           = 29;
46
    const INVALID_RANGE                 = 30;
47
    const INVALID_ALNUM                 = 31;
48
    const INVALID_TRUE                  = 32;
49
    const INVALID_EQ                    = 33;
50
    const INVALID_SAME                  = 34;
51
    const INVALID_MIN                   = 35;
52
    const INVALID_MAX                   = 36;
53
    const INVALID_LENGTH                = 37;
54
    const INVALID_FALSE                 = 38;
55
    const INVALID_STRING_END            = 39;
56
    const INVALID_UUID                  = 40;
57
    const INVALID_COUNT                 = 41;
58
    const INVALID_NOT_EQ                = 42;
59
    const INVALID_NOT_SAME              = 43;
60
    const INVALID_TRAVERSABLE           = 44;
61
    const INVALID_ARRAY_ACCESSIBLE      = 45;
62
    const INVALID_KEY_ISSET             = 46;
63
    const INVALID_SAMACCOUNTNAME        = 47;
64
    const INVALID_USERPRINCIPALNAME     = 48;
65
    const INVALID_DIRECTORY             = 101;
66
    const INVALID_FILE                  = 102;
67
    const INVALID_READABLE              = 103;
68
    const INVALID_WRITEABLE             = 104;
69
    const INVALID_CLASS                 = 105;
70
    const INVALID_EMAIL                 = 201;
71
    const INTERFACE_NOT_IMPLEMENTED     = 202;
72
    const INVALID_URL                   = 203;
73
    const INVALID_NOT_INSTANCE_OF       = 204;
74
    const VALUE_NOT_EMPTY               = 205;
75
    const INVALID_JSON_STRING           = 206;
76
    const INVALID_OBJECT                = 207;
77
    const INVALID_METHOD                = 208;
78
    const INVALID_SCALAR                = 209;
79
    const INVALID_DATE                  = 210;
80
    const INVALID_CALLABLE              = 211;
81
    const INVALID_KEYS_EXIST            = 300;
82
    const INVALID_PROPERTY_EXISTS       = 301;
83
    const INVALID_PROPERTIES_EXIST      = 302;
84
    const INVALID_UTF8                  = 303;
85
    const INVALID_DOMAIN_NAME           = 304;
86
    const INVALID_NOT_FALSE             = 305;
87
    const INVALID_FILE_OR_DIR           = 306;
88
    const INVALID_ASCII                 = 307;
89
    const INVALID_NOT_REGEX             = 308;
90
    const INVALID_GREATER_THAN          = 309;
91
    const INVALID_LESS_THAN             = 310;
92
    const INVALID_GREATER_THAN_OR_EQ    = 311;
93
    const INVALID_LESS_THAN_OR_EQ       = 312;
94
    const INVALID_IP_ADDRESS            = 313;
95
    const INVALID_AUS_MOBILE            = 314;
96
97
    const EMERGENCY                     = 'emergency';
98
    const ALERT                         = 'alert';
99
    const CRITICAL                      = 'critical';
100
    const ERROR                         = 'error';
101
    const WARNING                       = 'warning';
102
    const NOTICE                        = 'notice';
103
    const INFO                          = 'info';
104
    const DEBUG                         = 'debug';
105
106
    /** @var bool */
107
    protected $nullOr                   = false;
108
109
    /** @var bool */
110
    protected $emptyOr                  = false;
111
112
    /** @var mixed */
113
    protected $value                    = null;
114
115
    /** @var bool */
116
    protected $all                      = false;
117
118
    /** @var null|string */
119
    protected $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
     * @param        $value
146
     * @param string $name
147
     * @param int    $code
148
     * @param string $error
149
     * @param string $level
150
     * @return Assert
151
     */
152 View Code Duplication
    public static function that($value, $name='', $code=0, $error='', $level=Assert::WARNING)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

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