Completed
Push — master ( 21fe8c...5a3cd6 )
by Terry
03:39
created

Assert::greaterThan()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 17
Code Lines 10

Duplication

Lines 17
Ratio 100 %

Importance

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

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

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

Loading history...
241
    {
242
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
243
        {
244
            return $this;
245
        }
246
        if ( ! ( $this->value > $value2 ) )
247
        {
248
            $message = sprintf(
249
                $message ?: 'Value "%s" does not greater then expected value "%s".',
250
                $this->stringify($this->value),
251
                $this->stringify($value2)
252
            );
253
            throw $this->createException($message, self::INVALID_EQ, $propertyPath, ['expected' => $value2]);
254
        }
255
        return $this;
256
    }
257
258
    /**
259
     *
260
     * @param mixed       $value2
261
     * @param string|null $message
262
     * @param string|null $propertyPath
263
     * @return Assert
264
     * @throws AssertionFailedException
265
     */
266 View Code Duplication
    public function greaterThanOrEq($value2, $message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
267
    {
268
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
269
        {
270
            return $this;
271
        }
272
        if ( ! ( $this->value >= $value2 ) )
273
        {
274
            $message = sprintf(
275
                $message ?: 'Value "%s" does not greater than or equal to expected value "%s".',
276
                $this->stringify($this->value),
277
                $this->stringify($value2)
278
            );
279
            throw $this->createException($message, self::INVALID_EQ, $propertyPath, ['expected' => $value2]);
280
        }
281
        return $this;
282
    }
283
284
    /**
285
     *
286
     * @param mixed       $value2
287
     * @param string|null $message
288
     * @param string|null $propertyPath
289
     * @return Assert
290
     * @throws AssertionFailedException
291
     */
292 View Code Duplication
    public function lessThan($value2, $message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
293
    {
294
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
295
        {
296
            return $this;
297
        }
298
        if ( ! ( $this->value < $value2 ) )
299
        {
300
            $message = sprintf(
301
                $message ?: 'Value "%s" does not less then expected value "%s".',
302
                $this->stringify($this->value),
303
                $this->stringify($value2)
304
            );
305
            throw $this->createException($message, self::INVALID_LESS_THAN, $propertyPath, ['expected' => $value2]);
306
        }
307
        return $this;
308
    }
309
310
    /**
311
     *
312
     * @param mixed       $value2
313
     * @param string|null $message
314
     * @param string|null $propertyPath
315
     * @return Assert
316
     * @throws AssertionFailedException
317
     */
318 View Code Duplication
    public function lessThanOrEq($value2, $message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
319
    {
320
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
321
        {
322
            return $this;
323
        }
324
        if ( ! ( $this->value <= $value2 ) )
325
        {
326
            $message = sprintf(
327
                $message ?: 'Value "%s" does not less than or equal to expected value "%s".',
328
                $this->stringify($this->value),
329
                $this->stringify($value2)
330
            );
331
            throw $this->createException($message, self::INVALID_LESS_THAN_OR_EQ, $propertyPath, ['expected' => $value2]);
332
        }
333
        return $this;
334
    }
335
336
    /**
337
     * Assert that two values are the same (using ===).
338
     *
339
     * @param mixed       $value2
340
     * @param string|null $message
341
     * @param string|null $propertyPath
342
     * @return Assert
343
     * @throws AssertionFailedException
344
     */
345
    public function same($value2, $message = null, $propertyPath = null)
346
    {
347
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
348
        {
349
            return $this;
350
        }
351
        if ( $this->value !== $value2 )
352
        {
353
            $message = sprintf(
354
                $message ?: 'Value "%s" is not the same as expected value "%s".',
355
                $this->stringify($this->value),
356
                $this->stringify($value2)
357
            );
358
            throw $this->createException($message, self::INVALID_SAME, $propertyPath, ['expected' => $value2]);
359
        }
360
        return $this;
361
    }
362
363
    /**
364
     * Assert that two values are not equal (using == ).
365
     *
366
     * @param mixed       $value2
367
     * @param string|null $message
368
     * @param string|null $propertyPath
369
     * @return Assert
370
     * @throws AssertionFailedException
371
     */
372
    public function notEq($value2, $message = null, $propertyPath = null)
373
    {
374
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
375
        {
376
            return $this;
377
        }
378
        if ( $this->value == $value2 )
379
        {
380
            $message = sprintf(
381
                $message ?: 'Value "%s" is equal to expected value "%s".',
382
                $this->stringify($this->value),
383
                $this->stringify($value2)
384
            );
385
            throw $this->createException($message, self::INVALID_NOT_EQ, $propertyPath, ['expected' => $value2]);
386
        }
387
        return $this;
388
    }
389
390
    /**
391
     * @param string|null $message
392
     * @param string|null        $propertyPath
393
     *
394
     * @return $this
395
     * @throws AssertionFailedException
396
     */
397
    public function isCallable($message = null, $propertyPath = null)
398
    {
399
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
400
        {
401
            return $this;
402
        }
403
        if ( !is_callable($this->value) )
404
        {
405
            $message = sprintf(
406
                $message ?: 'Value "%s" is not callable.',
407
                $this->stringify($this->value)
408
            );
409
            throw $this->createException($message, self::INVALID_NOT_EQ, $propertyPath);
410
        }
411
        return $this;
412
    }
413
414
    /**
415
     * Assert that two values are not the same (using === ).
416
     *
417
     * @param mixed       $value2
418
     * @param string|null $message
419
     * @param string|null $propertyPath
420
     * @return Assert
421
     * @throws AssertionFailedException
422
     */
423
    public function notSame($value2, $message = null, $propertyPath = null)
424
    {
425
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
426
        {
427
            return $this;
428
        }
429
        if ( $this->value === $value2 )
430
        {
431
            $message = sprintf(
432
                $message ?: 'Value "%s" is the same as expected value "%s".',
433
                $this->stringify($this->value),
434
                $this->stringify($value2)
435
            );
436
            throw $this->createException($message, self::INVALID_NOT_SAME, $propertyPath, ['expected' => $value2]);
437
        }
438
        return $this;
439
    }
440
441
    /**
442
     * @param string|null $message
443
     * @param string|null $propertyPath
444
     * @return Assert
445
     * @throws AssertionFailedException
446
     */
447
    public function id($message = null, $propertyPath = null)
448
    {
449
        $message = $message ?: 'Value "%s" is not an integer id.';
450
        return $this->nonEmptyInt($message, $propertyPath)->range(1, PHP_INT_MAX);
451
    }
452
453
    /**
454
     * @param string|null $message
455
     * @param string|null $propertyPath
456
     * @return Assert
457
     * @throws AssertionFailedException
458
     */
459
    public function flag($message = null, $propertyPath = null)
460
    {
461
        $message = $message ?: 'Value "%s" is not a 0 or 1.';
462
        return $this->range(0, 1, $message, $propertyPath);
463
    }
464
465
    /**
466
     * @param string|null $message
467
     * @param string|null $propertyPath
468
     * @return Assert
469
     * @throws AssertionFailedException
470
     */
471
    public function status($message = null, $propertyPath = null)
472
    {
473
        $message = $message ?: 'Value "%s" is not a valid status.';
474
        return $this->integer($message, $propertyPath)->inArray([-1, 0, 1]);
475
    }
476
477
    /**
478
     * @param string|null $message
479
     * @param string|null $propertyPath
480
     * @return Assert
481
     */
482
    public function nullOrId($message = null, $propertyPath = null)
483
    {
484
        return $this->nullOr()->id($message, $propertyPath);
485
    }
486
487
    /**
488
     * @param string|null $message
489
     * @param string|null $propertyPath
490
     * @return Assert
491
     */
492
    public function allIds($message = null, $propertyPath = null)
493
    {
494
        return $this->all()->id($message, $propertyPath);
495
    }
496
497
    /**
498
     * @param string|null $message
499
     * @param string|null $propertyPath
500
     * @return Assert
501
     * @throws AssertionFailedException
502
     */
503
    public function int($message = null, $propertyPath = null)
504
    {
505
        return $this->integer($message, $propertyPath);
506
    }
507
508
    /**
509
     * Assert that value is a php integer.
510
     *
511
     * @param string|null $message
512
     * @param string|null $propertyPath
513
     * @return Assert
514
     * @throws AssertionFailedException
515
     */
516
    public function integer($message = null, $propertyPath = null)
517
    {
518
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
519
        {
520
            return $this;
521
        }
522
        if ( !is_int($this->value) )
523
        {
524
            $message = sprintf(
525
                $message ?: 'Value "%s" is not an integer.',
526
                $this->stringify($this->value)
527
            );
528
            throw $this->createException($message, self::INVALID_INTEGER, $propertyPath);
529
        }
530
        return $this;
531
    }
532
533
    /**
534
     * Assert that value is a php float.
535
     *
536
     * @param string|null $message
537
     * @param string|null $propertyPath
538
     * @return Assert
539
     * @throws AssertionFailedException
540
     */
541
    public function float($message = null, $propertyPath = null)
542
    {
543
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
544
        {
545
            return $this;
546
        }
547
        if ( !is_float($this->value) )
548
        {
549
            $message = sprintf(
550
                $message ?: 'Value "%s" is not a float.',
551
                $this->stringify($this->value)
552
            );
553
            throw $this->createException($message, self::INVALID_FLOAT, $propertyPath);
554
        }
555
        return $this;
556
    }
557
558
    /**
559
     * Validates if an integer or integerish is a digit.
560
     *
561
     * @param string|null $message
562
     * @param string|null $propertyPath
563
     * @return Assert
564
     * @throws AssertionFailedException
565
     */
566
    public function digit($message = null, $propertyPath = null)
567
    {
568
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
569
        {
570
            return $this;
571
        }
572
        if ( !ctype_digit((string)$this->value) )
573
        {
574
            $message = sprintf(
575
                $message ?: 'Value "%s" is not a digit.',
576
                $this->stringify($this->value)
577
            );
578
            throw $this->createException($message, self::INVALID_DIGIT, $propertyPath);
579
        }
580
        return $this;
581
    }
582
583
    /**
584
     * Validates if an string is a date .
585
     *
586
     * @param string|null $message
587
     * @param string|null $propertyPath
588
     * @return Assert
589
     * @throws AssertionFailedException
590
     */
591
    public function date($message = null, $propertyPath = null)
592
    {
593
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
594
        {
595
            return $this;
596
        }
597
        $this->notEmpty($message, $propertyPath);
598
        if ( strtotime($this->value) === false )
599
        {
600
            $message = sprintf(
601
                $message ?: 'Value "%s" is not a date.',
602
                $this->stringify($this->value)
603
            );
604
            throw $this->createException($message, self::INVALID_DATE, $propertyPath);
605
        }
606
        return $this;
607
    }
608
609
    /**
610
     * Assert that value is a php integer'ish.
611
     *
612
     * @param string|null $message
613
     * @param string|null $propertyPath
614
     * @return Assert
615
     * @throws AssertionFailedException
616
     */
617
    public function integerish($message = null, $propertyPath = null)
618
    {
619
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
620
        {
621
            return $this;
622
        }
623
        if ( is_object($this->value) || strval(intval($this->value)) != $this->value || is_bool($this->value) || is_null($this->value) )
624
        {
625
            $message = sprintf(
626
                $message ?: 'Value "%s" is not an integer or a number castable to integer.',
627
                $this->stringify($this->value)
628
            );
629
            throw $this->createException($message, self::INVALID_INTEGERISH, $propertyPath);
630
        }
631
        return $this;
632
    }
633
634
    /**
635
     * Assert that value is php boolean
636
     *
637
     * @param string|null $message
638
     * @param string|null $propertyPath
639
     * @return Assert
640
     * @throws AssertionFailedException
641
     */
642
    public function boolean($message = null, $propertyPath = null)
643
    {
644
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
645
        {
646
            return $this;
647
        }
648
        if ( !is_bool($this->value) )
649
        {
650
            $message = sprintf(
651
                $message ?: 'Value "%s" is not a boolean.',
652
                $this->stringify($this->value)
653
            );
654
            throw $this->createException($message, self::INVALID_BOOLEAN, $propertyPath);
655
        }
656
        return $this;
657
    }
658
659
    /**
660
     * Assert that value is a PHP scalar
661
     *
662
     * @param string|null $message
663
     * @param string|null $propertyPath
664
     * @return Assert
665
     * @throws AssertionFailedException
666
     */
667
    public function scalar($message = null, $propertyPath = null)
668
    {
669
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
670
        {
671
            return $this;
672
        }
673
        if ( !is_scalar($this->value) )
674
        {
675
            $message = sprintf(
676
                $message ?: 'Value "%s" is not a scalar.',
677
                $this->stringify($this->value)
678
            );
679
            throw $this->createException($message, self::INVALID_SCALAR, $propertyPath);
680
        }
681
        return $this;
682
    }
683
684
    /**
685
     * Assert that value is not empty
686
     *
687
     * @param string|null $message
688
     * @param string|null $propertyPath
689
     * @return Assert
690
     * @throws AssertionFailedException
691
     */
692 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...
693
    {
694
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
695
        {
696
            return $this;
697
        }
698
        if ( ( is_object($this->value) && empty((array)$this->value) ) || empty($this->value) )
699
        {
700
            $message = sprintf(
701
                $message ?: 'Value "%s" is empty, but non empty value was expected.',
702
                $this->stringify($this->value)
703
            );
704
            throw $this->createException($message, self::VALUE_EMPTY, $propertyPath);
705
        }
706
        return $this;
707
    }
708
709
    /**
710
     * Assert that value is empty
711
     *
712
     * @param string|null $message
713
     * @param string|null $propertyPath
714
     * @return Assert
715
     * @throws AssertionFailedException
716
     */
717
    public function noContent($message = null, $propertyPath = null)
718
    {
719
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
720
        {
721
            return $this;
722
        }
723
        if ( !empty( $this->value ) )
724
        {
725
            $message = sprintf(
726
                $message ?: 'Value "%s" is not empty, but empty value was expected.',
727
                $this->stringify($this->value)
728
            );
729
            throw $this->createException($message, self::VALUE_NOT_EMPTY, $propertyPath);
730
        }
731
        return $this;
732
    }
733
734
    /**
735
     * Assert that value is not null
736
     *
737
     * @param string|null $message
738
     * @param string|null $propertyPath
739
     * @return Assert
740
     * @throws AssertionFailedException
741
     */
742
    public function notNull($message = null, $propertyPath = null)
743
    {
744
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
745
        {
746
            return $this;
747
        }
748
        if ( $this->value === null )
749
        {
750
            $message = sprintf(
751
                $message ?: 'Value "%s" is null, but non null value was expected.',
752
                $this->stringify($this->value)
753
            );
754
            throw $this->createException($message, self::VALUE_NULL, $propertyPath);
755
        }
756
        return $this;
757
    }
758
759
    /**
760
     * Assert that value is a string
761
     *
762
     * @param string|null $message
763
     * @param string|null $propertyPath
764
     * @return Assert
765
     * @throws AssertionFailedException
766
     */
767
    public function string($message = null, $propertyPath = null)
768
    {
769
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
770
        {
771
            return $this;
772
        }
773
        if ( !is_string($this->value) )
774
        {
775
            $message = sprintf(
776
                $message ?: 'Value "%s" expected to be string, type %s given.',
777
                $this->stringify($this->value),
778
                gettype($this->value)
779
            );
780
            throw $this->createException($message, self::INVALID_STRING, $propertyPath);
781
        }
782
        return $this;
783
    }
784
785
    /**
786
     * Assert that value matches a regex
787
     *
788
     * @param string      $pattern
789
     * @param string|null $message
790
     * @param string|null $propertyPath
791
     * @return Assert
792
     * @throws AssertionFailedException
793
     */
794
    public function regex($pattern, $message = null, $propertyPath = null)
795
    {
796
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
797
        {
798
            return $this;
799
        }
800
        $this->string($message, $propertyPath);
801
        if ( !preg_match($pattern, $this->value) )
802
        {
803
            $message = sprintf(
804
                $message ?: 'Value "%s" does not match expression.',
805
                $this->stringify($this->value)
806
            );
807
            throw $this->createException($message, self::INVALID_REGEX, $propertyPath, ['pattern' => $pattern]);
808
        }
809
        return $this;
810
    }
811
812
    /**
813
     * @param string $pattern
814
     * @param string|null $message
815
     * @param string|null $propertyPath
816
     * @return $this
817
     * @throws AssertionFailedException
818
     */
819
    public function notRegex($pattern, $message = null, $propertyPath = null)
820
    {
821
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
822
        {
823
            return $this;
824
        }
825
        $this->string($message, $propertyPath);
826
        if ( preg_match($pattern, $this->value) )
827
        {
828
            $message = sprintf(
829
                $message ?: 'Value "%s" does not match expression.',
830
                $this->stringify($this->value)
831
            );
832
            throw $this->createException($message, self::INVALID_REGEX, $propertyPath, ['pattern' => $pattern]);
833
        }
834
        return $this;
835
    }
836
837
    /**
838
     * Assert that string has a given length.
839
     *
840
     * @param int         $length
841
     * @param string|null $message
842
     * @param string|null $propertyPath
843
     * @param string      $encoding
844
     * @return Assert
845
     * @throws AssertionFailedException
846
     */
847
    public function length($length, $message = null, $propertyPath = null, $encoding = 'utf8')
848
    {
849
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
850
        {
851
            return $this;
852
        }
853
        $this->string($message, $propertyPath);
854
        if ( mb_strlen($this->value, $encoding) !== $length )
855
        {
856
            $message     = sprintf(
857
                $message ?: 'Value "%s" has to be %d exactly characters long, but length is %d.',
858
                $this->stringify($this->value),
859
                $length,
860
                mb_strlen($this->value, $encoding)
861
            );
862
            $constraints = ['length' => $length, 'encoding' => $encoding];
863
            throw $this->createException($message, self::INVALID_LENGTH, $propertyPath, $constraints);
864
        }
865
        return $this;
866
    }
867
868
    /**
869
     * Assert that a string is at least $minLength chars long.
870
     *
871
     * @param int         $minLength
872
     * @param string|null $message
873
     * @param string|null $propertyPath
874
     * @param string      $encoding
875
     * @return Assert
876
     * @throws AssertionFailedException
877
     */
878
    public function minLength($minLength, $message = null, $propertyPath = null, $encoding = 'utf8')
879
    {
880
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
881
        {
882
            return $this;
883
        }
884
        $this->string($message, $propertyPath);
885
        if ( mb_strlen($this->value, $encoding) < $minLength )
886
        {
887
            $message     = sprintf(
888
                $message
889
                    ?: 'Value "%s" is too short, it should have more than %d characters, but only has %d characters.',
890
                $this->stringify($this->value),
891
                $minLength,
892
                mb_strlen($this->value, $encoding)
893
            );
894
            $constraints = ['min_length' => $minLength, 'encoding' => $encoding];
895
            throw $this->createException($message, self::INVALID_MIN_LENGTH, $propertyPath, $constraints);
896
        }
897
        return $this;
898
    }
899
900
    /**
901
     * Assert that string value is not longer than $maxLength chars.
902
     *
903
     * @param integer     $maxLength
904
     * @param string|null $message
905
     * @param string|null $propertyPath
906
     * @param string      $encoding
907
     * @return Assert
908
     * @throws AssertionFailedException
909
     */
910
    public function maxLength($maxLength, $message = null, $propertyPath = null, $encoding = 'utf8')
911
    {
912
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
913
        {
914
            return $this;
915
        }
916
        $this->string($message, $propertyPath);
917
        if ( mb_strlen($this->value, $encoding) > $maxLength )
918
        {
919
            $message     = sprintf(
920
                $message ?: 'Value "%s" is too long, it should have no more than %d characters, but has %d characters.',
921
                $this->stringify($this->value),
922
                $maxLength,
923
                mb_strlen($this->value, $encoding)
924
            );
925
            $constraints = ['max_length' => $maxLength, 'encoding' => $encoding];
926
            throw $this->createException($message, self::INVALID_MAX_LENGTH, $propertyPath, $constraints);
927
        }
928
        return $this;
929
    }
930
931
    /**
932
     * Assert that string length is between min,max lengths.
933
     *
934
     * @param integer     $minLength
935
     * @param integer     $maxLength
936
     * @param string|null $message
937
     * @param string|null $propertyPath
938
     * @param string      $encoding
939
     * @return Assert
940
     * @throws AssertionFailedException
941
     */
942
    public function betweenLength($minLength, $maxLength, $message = null, $propertyPath = null, $encoding = 'utf8')
943
    {
944
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
945
        {
946
            return $this;
947
        }
948
        $this->string($message, $propertyPath);
949
        if ( mb_strlen($this->value, $encoding) < $minLength )
950
        {
951
            $message     = sprintf(
952
                $message
953
                    ?: 'Value "%s" is too short, it should have more than %d characters, but only has %d characters.',
954
                $this->stringify($this->value),
955
                $minLength,
956
                mb_strlen($this->value, $encoding)
957
            );
958
            $constraints = ['min_length' => $minLength, 'encoding' => $encoding];
959
            throw $this->createException($message, self::INVALID_MIN_LENGTH, $propertyPath, $constraints);
960
        }
961
        if ( mb_strlen($this->value, $encoding) > $maxLength )
962
        {
963
            $message     = sprintf(
964
                $message ?: 'Value "%s" is too long, it should have no more than %d characters, but has %d characters.',
965
                $this->stringify($this->value),
966
                $maxLength,
967
                mb_strlen($this->value, $encoding)
968
            );
969
            $constraints = ['max_length' => $maxLength, 'encoding' => $encoding];
970
            throw $this->createException($message, self::INVALID_MAX_LENGTH, $propertyPath, $constraints);
971
        }
972
        return $this;
973
    }
974
975
    /**
976
     * Assert that string starts with a sequence of chars.
977
     *
978
     * @param string      $needle
979
     * @param string|null $message
980
     * @param string|null $propertyPath
981
     * @param string      $encoding
982
     * @return Assert
983
     * @throws AssertionFailedException
984
     */
985 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...
986
    {
987
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
988
        {
989
            return $this;
990
        }
991
        $this->string($message, $propertyPath);
992
        if ( mb_strpos($this->value, $needle, null, $encoding) !== 0 )
993
        {
994
            $message     = sprintf(
995
                $message ?: 'Value "%s" does not start with "%s".',
996
                $this->stringify($this->value),
997
                $this->stringify($needle)
998
            );
999
            $constraints = ['needle' => $needle, 'encoding' => $encoding];
1000
            throw $this->createException($message, self::INVALID_STRING_START, $propertyPath, $constraints);
1001
        }
1002
        return $this;
1003
    }
1004
1005
    /**
1006
     * Assert that string ends with a sequence of chars.
1007
     *
1008
     * @param string      $needle
1009
     * @param string|null $message
1010
     * @param string|null $propertyPath
1011
     * @param string      $encoding
1012
     * @return Assert
1013
     * @throws AssertionFailedException
1014
     */
1015
    public function endsWith($needle, $message = null, $propertyPath = null, $encoding = 'utf8')
1016
    {
1017
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1018
        {
1019
            return $this;
1020
        }
1021
        $this->string($message, $propertyPath);
1022
        $stringPosition = mb_strlen($this->value, $encoding) - mb_strlen($needle, $encoding);
1023
        if ( mb_strripos($this->value, $needle, null, $encoding) !== $stringPosition )
1024
        {
1025
            $message     = sprintf(
1026
                $message ?: 'Value "%s" does not end with "%s".',
1027
                $this->stringify($this->value),
1028
                $this->stringify($needle)
1029
            );
1030
            $constraints = ['needle' => $needle, 'encoding' => $encoding];
1031
            throw $this->createException($message, self::INVALID_STRING_END, $propertyPath, $constraints);
1032
        }
1033
        return $this;
1034
    }
1035
1036
    /**
1037
     * Assert that string contains a sequence of chars.
1038
     *
1039
     * @param string      $needle
1040
     * @param string|null $message
1041
     * @param string|null $propertyPath
1042
     * @param string      $encoding
1043
     * @return Assert
1044
     * @throws AssertionFailedException
1045
     */
1046 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...
1047
    {
1048
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1049
        {
1050
            return $this;
1051
        }
1052
        $this->string($message, $propertyPath);
1053
        if ( mb_strpos($this->value, $needle, null, $encoding) === false )
1054
        {
1055
            $message     = sprintf(
1056
                $message ?: 'Value "%s" does not contain "%s".',
1057
                $this->stringify($this->value),
1058
                $this->stringify($needle)
1059
            );
1060
            $constraints = ['needle' => $needle, 'encoding' => $encoding];
1061
            throw $this->createException($message, self::INVALID_STRING_CONTAINS, $propertyPath, $constraints);
1062
        }
1063
        return $this;
1064
    }
1065
1066
    /**
1067
     * Assert that value is in array of choices.
1068
     *
1069
     * @param array       $choices
1070
     * @param string|null $message
1071
     * @param string|null $propertyPath
1072
     * @return Assert
1073
     * @throws AssertionFailedException
1074
     */
1075
    public function choice(array $choices, $message = null, $propertyPath = null)
1076
    {
1077
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1078
        {
1079
            return $this;
1080
        }
1081
        if ( !in_array($this->value, $choices, true) )
1082
        {
1083
            $message = sprintf(
1084
                $message ?: 'Value "%s" is not an element of the valid values: %s',
1085
                $this->stringify($this->value),
1086
                implode(", ", array_map('Terah\Assert\Assert::stringify', $choices))
1087
            );
1088
            throw $this->createException($message, self::INVALID_CHOICE, $propertyPath, ['choices' => $choices]);
1089
        }
1090
        return $this;
1091
    }
1092
1093
    /**
1094
     * Alias of {@see choice()}
1095
     *
1096
     * @throws AssertionFailedException
1097
     *
1098
     * @param array $choices
1099
     * @param string|null $message
1100
     * @param string|null $propertyPath
1101
     * @return $this
1102
     */
1103
    public function inArray(array $choices, $message = null, $propertyPath = null)
1104
    {
1105
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1106
        {
1107
            return $this;
1108
        }
1109
        $this->choice($choices, $message, $propertyPath);
1110
        return $this;
1111
    }
1112
1113
    /**
1114
     * Assert that value is numeric.
1115
     *
1116
     * @param string|null $message
1117
     * @param string|null $propertyPath
1118
     * @return Assert
1119
     * @throws AssertionFailedException
1120
     */
1121
    public function numeric($message = null, $propertyPath = null)
1122
    {
1123
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1124
        {
1125
            return $this;
1126
        }
1127
        if ( ! is_numeric($this->value) )
1128
        {
1129
            $message = sprintf(
1130
                $message ?: 'Value "%s" is not numeric.',
1131
                $this->stringify($this->value)
1132
            );
1133
            throw $this->createException($message, self::INVALID_NUMERIC, $propertyPath);
1134
        }
1135
        return $this;
1136
    }
1137
1138
    /**
1139
     * @param string|null $message
1140
     * @param string|null $propertyPath
1141
     * @return Assert
1142
     * @throws AssertionFailedException
1143
     */
1144
    public function nonEmptyArray($message = null, $propertyPath = null)
1145
    {
1146
        $message = $message ?: 'Value "%s" is not a non-empty array.';
1147
        return $this->isArray($message, $propertyPath)->notEmpty($message, $propertyPath);
1148
    }
1149
1150
    /**
1151
     * @param string|null $message
1152
     * @param string|null $propertyPath
1153
     * @return Assert
1154
     * @throws AssertionFailedException
1155
     */
1156
    public function nonEmptyInt($message = null, $propertyPath = null)
1157
    {
1158
        $message = $message ?: 'Value "%s" is not a non-empty integer.';
1159
        return $this->integer($message, $propertyPath)->notEmpty($message, $propertyPath);
1160
    }
1161
1162
    /**
1163
     * @param string|null $message
1164
     * @param string|null $propertyPath
1165
     * @return Assert
1166
     * @throws AssertionFailedException
1167
     */
1168
    public function nonEmptyString($message = null, $propertyPath = null)
1169
    {
1170
        $message = $message ?: 'Value "%s" is not a non-empty string.';
1171
        return $this->integer($message, $propertyPath)->notEmpty($message, $propertyPath);
1172
    }
1173
1174
    /**
1175
     * Assert that value is an array.
1176
     *
1177
     * @param string|null $message
1178
     * @param string|null $propertyPath
1179
     * @return Assert
1180
     * @throws AssertionFailedException
1181
     */
1182
    public function isArray($message = null, $propertyPath = null)
1183
    {
1184
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1185
        {
1186
            return $this;
1187
        }
1188
        if ( !is_array($this->value) )
1189
        {
1190
            $message = sprintf(
1191
                $message ?: 'Value "%s" is not an array.',
1192
                $this->stringify($this->value)
1193
            );
1194
            throw $this->createException($message, self::INVALID_ARRAY, $propertyPath);
1195
        }
1196
        return $this;
1197
    }
1198
1199
    /**
1200
     * Assert that value is an array or a traversable object.
1201
     *
1202
     * @param string|null $message
1203
     * @param string|null $propertyPath
1204
     * @return Assert
1205
     * @throws AssertionFailedException
1206
     */
1207
    public function isTraversable($message = null, $propertyPath = null)
1208
    {
1209
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1210
        {
1211
            return $this;
1212
        }
1213
        if ( !is_array($this->value) && !$this->value instanceof \Traversable )
1214
        {
1215
            $message = sprintf(
1216
                $message ?: 'Value "%s" is not an array and does not implement Traversable.',
1217
                $this->stringify($this->value)
1218
            );
1219
            throw $this->createException($message, self::INVALID_TRAVERSABLE, $propertyPath);
1220
        }
1221
        return $this;
1222
    }
1223
1224
    /**
1225
     * Assert that value is an array or an array-accessible object.
1226
     *
1227
     * @param string|null $message
1228
     * @param string|null $propertyPath
1229
     * @return Assert
1230
     * @throws AssertionFailedException
1231
     */
1232
    public function isArrayAccessible($message = null, $propertyPath = null)
1233
    {
1234
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1235
        {
1236
            return $this;
1237
        }
1238
        if ( !is_array($this->value) && !$this->value instanceof \ArrayAccess )
1239
        {
1240
            $message = sprintf(
1241
                $message ?: 'Value "%s" is not an array and does not implement ArrayAccess.',
1242
                $this->stringify($this->value)
1243
            );
1244
            throw $this->createException($message, self::INVALID_ARRAY_ACCESSIBLE, $propertyPath);
1245
        }
1246
        return $this;
1247
    }
1248
1249
    /**
1250
     * Assert that key exists in an array
1251
     *
1252
     * @param string|integer $key
1253
     * @param string|null    $message
1254
     * @param string|null    $propertyPath
1255
     * @return Assert
1256
     * @throws AssertionFailedException
1257
     */
1258
    public function keyExists($key, $message = null, $propertyPath = null)
1259
    {
1260
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1261
        {
1262
            return $this;
1263
        }
1264
        $this->isArray($message, $propertyPath);
1265
        if ( !array_key_exists($key, $this->value) )
1266
        {
1267
            $message = sprintf(
1268
                $message ?: 'Array does not contain an element with key "%s"',
1269
                $this->stringify($key)
1270
            );
1271
            throw $this->createException($message, self::INVALID_KEY_EXISTS, $propertyPath, ['key' => $key]);
1272
        }
1273
        return $this;
1274
    }
1275
1276
    /**
1277
     * Assert that keys exist in array
1278
     *
1279
     * @param array       $keys
1280
     * @param string|null $message
1281
     * @param string|null $propertyPath
1282
     * @return Assert
1283
     * @throws AssertionFailedException
1284
     */
1285 View Code Duplication
    public function keysExist($keys, $message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

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

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

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

Loading history...
1317
    {
1318
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1319
        {
1320
            return $this;
1321
        }
1322
        $this->isObject($message, $propertyPath);
1323
        if ( !property_exists($this->value, $key) && !isset( $this->value->{$key} ) )
1324
        {
1325
            $message = $message
1326
                ?: sprintf(
1327
                    'Object does not contain a property with key "%s"',
1328
                    $this->stringify($key)
1329
                );
1330
            throw $this->createException($message, self::INVALID_PROPERTY_EXISTS, $propertyPath, ['key' => $key]);
1331
        }
1332
        return $this;
1333
    }
1334
1335
    /**
1336
     * Assert that properties exists in array
1337
     *
1338
     * @param array       $keys
1339
     * @param string|null $message
1340
     * @param string|null $propertyPath
1341
     * @return Assert
1342
     * @throws AssertionFailedException
1343
     */
1344 View Code Duplication
    public function propertiesExist(array $keys, $message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1345
    {
1346
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1347
        {
1348
            return $this;
1349
        }
1350
        $this->isObject($message, $propertyPath);
1351
        foreach ( $keys as $key )
1352
        {
1353
            // Using isset to allow resolution of magically defined properties
1354
            if ( !property_exists($this->value, $key) && !isset( $this->value->{$key} ) )
1355
            {
1356
                $message = $message
1357
                    ?: sprintf(
1358
                        'Object does not contain a property with key "%s"',
1359
                        $this->stringify($key)
1360
                    );
1361
                throw $this->createException($message, self::INVALID_PROPERTIES_EXIST, $propertyPath, ['key' => $key]);
1362
            }
1363
        }
1364
        return $this;
1365
    }
1366
1367
    /**
1368
     * Assert that string is valid utf8
1369
     *
1370
     * @param string|null $message
1371
     * @param string|null $propertyPath
1372
     * @return Assert
1373
     * @throws AssertionFailedException
1374
     */
1375
    public function utf8($message = null, $propertyPath = null)
1376
    {
1377
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1378
        {
1379
            return $this;
1380
        }
1381
        $this->string($message, $propertyPath);
1382
        if ( mb_detect_encoding($this->value, 'UTF-8', true) !== 'UTF-8' )
1383
        {
1384
            $message = $message
1385
                ?: sprintf(
1386
                    'Value "%s" was expected to be a valid UTF8 string',
1387
                    $this->stringify($this->value)
1388
                );
1389
            throw $this->createException($message, self::INVALID_UTF8, $propertyPath);
1390
        }
1391
        return $this;
1392
    }
1393
1394
1395
    /**
1396
     * Assert that string is valid utf8
1397
     *
1398
     * @param string|null $message
1399
     * @param string|null $propertyPath
1400
     * @return Assert
1401
     * @throws AssertionFailedException
1402
     */
1403 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...
1404
    {
1405
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1406
        {
1407
            return $this;
1408
        }
1409
        $this->string($message, $propertyPath);
1410
        if ( ! preg_match('/^[ -~]+$/', $this->value) )
1411
        {
1412
            $message = $message
1413
                ?: sprintf(
1414
                    'Value "%s" was expected to be a valid ASCII string',
1415
                    $this->stringify($this->value)
1416
                );
1417
            throw $this->createException($message, self::INVALID_ASCII, $propertyPath);
1418
        }
1419
        return $this;
1420
    }
1421
1422
    /**
1423
     * Assert that key exists in an array/array-accessible object using isset()
1424
     *
1425
     * @param string|integer $key
1426
     * @param string|null    $message
1427
     * @param string|null    $propertyPath
1428
     * @return Assert
1429
     * @throws AssertionFailedException
1430
     */
1431
    public function keyIsset($key, $message = null, $propertyPath = null)
1432
    {
1433
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1434
        {
1435
            return $this;
1436
        }
1437
        $this->isArrayAccessible($message, $propertyPath);
1438
        if ( !isset( $this->value[$key] ) )
1439
        {
1440
            $message = sprintf(
1441
                $message ?: 'The element with key "%s" was not found',
1442
                $this->stringify($key)
1443
            );
1444
            throw $this->createException($message, self::INVALID_KEY_ISSET, $propertyPath, ['key' => $key]);
1445
        }
1446
        return $this;
1447
    }
1448
1449
    /**
1450
     * Assert that key exists in an array/array-accessible object and it's value is not empty.
1451
     *
1452
     * @param string|integer $key
1453
     * @param string|null    $message
1454
     * @param string|null    $propertyPath
1455
     * @return Assert
1456
     * @throws AssertionFailedException
1457
     */
1458
    public function notEmptyKey($key, $message = null, $propertyPath = null)
1459
    {
1460
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1461
        {
1462
            return $this;
1463
        }
1464
        $this->keyIsset($key, $message, $propertyPath);
1465
        (new Assert($this->value[$key]))->setExceptionClass($this->exceptionClass)->notEmpty($message, $propertyPath);
1466
        return $this;
1467
    }
1468
1469
    /**
1470
     * Assert that value is not blank
1471
     *
1472
     * @param string|null $message
1473
     * @param string|null $propertyPath
1474
     * @return Assert
1475
     * @throws AssertionFailedException
1476
     */
1477 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...
1478
    {
1479
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1480
        {
1481
            return $this;
1482
        }
1483
        if ( false === $this->value || ( empty( $this->value ) && '0' != $this->value ) )
1484
        {
1485
            $message = sprintf(
1486
                $message ?: 'Value "%s" is blank, but was expected to contain a value.',
1487
                $this->stringify($this->value)
1488
            );
1489
            throw $this->createException($message, self::INVALID_NOT_BLANK, $propertyPath);
1490
        }
1491
        return $this;
1492
    }
1493
1494
    /**
1495
     * Assert that value is instance of given class-name.
1496
     *
1497
     * @param string      $className
1498
     * @param string|null $message
1499
     * @param string|null $propertyPath
1500
     * @return Assert
1501
     * @throws AssertionFailedException
1502
     */
1503
    public function isInstanceOf($className, $message = null, $propertyPath = null)
1504
    {
1505
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1506
        {
1507
            return $this;
1508
        }
1509
        if ( !( $this->value instanceof $className ) )
1510
        {
1511
            $message = sprintf(
1512
                $message ?: 'Class "%s" was expected to be instanceof of "%s" but is not.',
1513
                $this->stringify($this->value),
1514
                $className
1515
            );
1516
            throw $this->createException($message, self::INVALID_INSTANCE_OF, $propertyPath, ['class' => $className]);
1517
        }
1518
        return $this;
1519
    }
1520
1521
    /**
1522
     * Assert that value is not instance of given class-name.
1523
     *
1524
     * @param string      $className
1525
     * @param string|null $message
1526
     * @param string|null $propertyPath
1527
     * @return Assert
1528
     * @throws AssertionFailedException
1529
     */
1530
    public function notIsInstanceOf($className, $message = null, $propertyPath = null)
1531
    {
1532
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1533
        {
1534
            return $this;
1535
        }
1536
        if ( $this->value instanceof $className )
1537
        {
1538
            $message = sprintf(
1539
                $message ?: 'Class "%s" was not expected to be instanceof of "%s".',
1540
                $this->stringify($this->value),
1541
                $className
1542
            );
1543
            throw $this->createException($message, self::INVALID_NOT_INSTANCE_OF, $propertyPath, ['class' => $className]);
1544
        }
1545
        return $this;
1546
    }
1547
1548
    /**
1549
     * Assert that value is subclass of given class-name.
1550
     *
1551
     * @param string      $className
1552
     * @param string|null $message
1553
     * @param string|null $propertyPath
1554
     * @return Assert
1555
     * @throws AssertionFailedException
1556
     */
1557
    public function subclassOf($className, $message = null, $propertyPath = null)
1558
    {
1559
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1560
        {
1561
            return $this;
1562
        }
1563
        if ( !is_subclass_of($this->value, $className) )
1564
        {
1565
            $message = sprintf(
1566
                $message ?: 'Class "%s" was expected to be subclass of "%s".',
1567
                $this->stringify($this->value),
1568
                $className
1569
            );
1570
            throw $this->createException($message, self::INVALID_SUBCLASS_OF, $propertyPath, ['class' => $className]);
1571
        }
1572
        return $this;
1573
    }
1574
1575
    /**
1576
     * Assert that value is in range of numbers.
1577
     *
1578
     * @param integer     $minValue
1579
     * @param integer     $maxValue
1580
     * @param string|null $message
1581
     * @param string|null $propertyPath
1582
     * @return Assert
1583
     * @throws AssertionFailedException
1584
     */
1585
    public function range($minValue, $maxValue, $message = null, $propertyPath = null)
1586
    {
1587
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1588
        {
1589
            return $this;
1590
        }
1591
        $this->numeric($message, $propertyPath);
1592
        if ( $this->value < $minValue || $this->value > $maxValue )
1593
        {
1594
            $message = sprintf(
1595
                $message ?: 'Number "%s" was expected to be at least "%d" and at most "%d".',
1596
                $this->stringify($this->value),
1597
                $this->stringify($minValue),
1598
                $this->stringify($maxValue)
1599
            );
1600
            throw $this->createException($message, self::INVALID_RANGE, $propertyPath, [
1601
                'min' => $minValue,
1602
                'max' => $maxValue
1603
            ]);
1604
        }
1605
        return $this;
1606
    }
1607
1608
    /**
1609
     * Assert that a value is at least as big as a given limit
1610
     *
1611
     * @param mixed       $minValue
1612
     * @param string|null $message
1613
     * @param string|null $propertyPath
1614
     * @return Assert
1615
     * @throws AssertionFailedException
1616
     */
1617
    public function min($minValue, $message = null, $propertyPath = null)
1618
    {
1619
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1620
        {
1621
            return $this;
1622
        }
1623
        $this->numeric($message, $propertyPath);
1624
        if ( $this->value < $minValue )
1625
        {
1626
            $message = sprintf(
1627
                $message ?: 'Number "%s" was expected to be at least "%d".',
1628
                $this->stringify($this->value),
1629
                $this->stringify($minValue)
1630
            );
1631
            throw $this->createException($message, self::INVALID_MIN, $propertyPath, ['min' => $minValue]);
1632
        }
1633
        return $this;
1634
    }
1635
1636
    /**
1637
     * Assert that a number is smaller as a given limit
1638
     *
1639
     * @param mixed       $maxValue
1640
     * @param string|null $message
1641
     * @param string|null $propertyPath
1642
     * @return Assert
1643
     * @throws AssertionFailedException
1644
     */
1645
    public function max($maxValue, $message = null, $propertyPath = null)
1646
    {
1647
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1648
        {
1649
            return $this;
1650
        }
1651
        $this->numeric($message, $propertyPath);
1652
        if ( $this->value > $maxValue )
1653
        {
1654
            $message = sprintf(
1655
                $message ?: 'Number "%s" was expected to be at most "%d".',
1656
                $this->stringify($this->value),
1657
                $this->stringify($maxValue)
1658
            );
1659
            throw $this->createException($message, self::INVALID_MAX, $propertyPath, ['max' => $maxValue]);
1660
        }
1661
        return $this;
1662
    }
1663
1664
    /**
1665
     * Assert that a file exists
1666
     *
1667
     * @param string|null $message
1668
     * @param string|null $propertyPath
1669
     * @return Assert
1670
     * @throws AssertionFailedException
1671
     */
1672
    public function file($message = null, $propertyPath = null)
1673
    {
1674
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1675
        {
1676
            return $this;
1677
        }
1678
        $this->string($message, $propertyPath);
1679
        $this->notEmpty($message, $propertyPath);
1680
        if ( !is_file($this->value) )
1681
        {
1682
            $message = sprintf(
1683
                $message ?: 'File "%s" was expected to exist.',
1684
                $this->stringify($this->value)
1685
            );
1686
            throw $this->createException($message, self::INVALID_FILE, $propertyPath);
1687
        }
1688
        return $this;
1689
    }
1690
1691
    /**
1692
     * @param string|null $message
1693
     * @param string|null $propertyPath
1694
     * @return $this
1695
     * @throws AssertionFailedException
1696
     */
1697
    public function fileExists($message = null, $propertyPath = null)
1698
    {
1699
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1700
        {
1701
            return $this;
1702
        }
1703
        $this->string($message, $propertyPath);
1704
        $this->notEmpty($message, $propertyPath);
1705
        if ( ! file_exists($this->value) )
1706
        {
1707
            $message = sprintf(
1708
                $message ?: 'File or directory "%s" was expected to exist.',
1709
                $this->stringify($this->value)
1710
            );
1711
            throw $this->createException($message, self::INVALID_FILE_OR_DIR, $propertyPath);
1712
        }
1713
        return $this;
1714
    }
1715
1716
    /**
1717
     * Assert that a directory exists
1718
     *
1719
     * @param string|null $message
1720
     * @param string|null $propertyPath
1721
     * @return Assert
1722
     * @throws AssertionFailedException
1723
     */
1724 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...
1725
    {
1726
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1727
        {
1728
            return $this;
1729
        }
1730
        $this->string($message, $propertyPath);
1731
        if ( !is_dir($this->value) )
1732
        {
1733
            $message = sprintf(
1734
                $message ?: 'Path "%s" was expected to be a directory.',
1735
                $this->stringify($this->value)
1736
            );
1737
            throw $this->createException($message, self::INVALID_DIRECTORY, $propertyPath);
1738
        }
1739
        return $this;
1740
    }
1741
1742
    /**
1743
     * Assert that the value is something readable
1744
     *
1745
     * @param string|null $message
1746
     * @param string|null $propertyPath
1747
     * @return Assert
1748
     * @throws AssertionFailedException
1749
     */
1750 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...
1751
    {
1752
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1753
        {
1754
            return $this;
1755
        }
1756
        $this->string($message, $propertyPath);
1757
        if ( !is_readable($this->value) )
1758
        {
1759
            $message = sprintf(
1760
                $message ?: 'Path "%s" was expected to be readable.',
1761
                $this->stringify($this->value)
1762
            );
1763
            throw $this->createException($message, self::INVALID_READABLE, $propertyPath);
1764
        }
1765
        return $this;
1766
    }
1767
1768
    /**
1769
     * Assert that the value is something writeable
1770
     *
1771
     * @param string|null $message
1772
     * @param string|null $propertyPath
1773
     * @return Assert
1774
     * @throws AssertionFailedException
1775
     */
1776 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...
1777
    {
1778
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1779
        {
1780
            return $this;
1781
        }
1782
        $this->string($message, $propertyPath);
1783
        if ( !is_writeable($this->value) )
1784
        {
1785
            $message = sprintf(
1786
                $message ?: 'Path "%s" was expected to be writeable.',
1787
                $this->stringify($this->value)
1788
            );
1789
            throw $this->createException($message, self::INVALID_WRITEABLE, $propertyPath);
1790
        }
1791
        return $this;
1792
    }
1793
1794
    /**
1795
     * Assert that value is an email adress (using
1796
     * input_filter/FILTER_VALIDATE_EMAIL).
1797
     *
1798
     * @param string|null $message
1799
     * @param string|null $propertyPath
1800
     * @return Assert
1801
     * @throws AssertionFailedException
1802
     */
1803
    public function email($message = null, $propertyPath = null)
1804
    {
1805
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1806
        {
1807
            return $this;
1808
        }
1809
        $this->string($message, $propertyPath);
1810
        if ( ! filter_var($this->value, FILTER_VALIDATE_EMAIL) )
1811
        {
1812
            $message = sprintf(
1813
                $message ?: 'Value "%s" was expected to be a valid e-mail address.',
1814
                $this->stringify($this->value)
1815
            );
1816
            throw $this->createException($message, self::INVALID_EMAIL, $propertyPath);
1817
        }
1818
        else
1819
        {
1820
            $host = substr($this->value, strpos($this->value, '@') + 1);
1821
            // Likely not a FQDN, bug in PHP FILTER_VALIDATE_EMAIL prior to PHP 5.3.3
1822
            if ( version_compare(PHP_VERSION, '5.3.3', '<') && strpos($host, '.') === false )
1823
            {
1824
                $message = sprintf(
1825
                    $message ?: 'Value "%s" was expected to be a valid e-mail address.',
1826
                    $this->stringify($this->value)
1827
                );
1828
                throw $this->createException($message, self::INVALID_EMAIL, $propertyPath);
1829
            }
1830
        }
1831
        return $this;
1832
    }
1833
1834
    /**
1835
     * @param null $message
1836
     * @param null $propertyPath
1837
     * @return Assert
1838
     * @throws AssertionFailedException
1839
     */
1840
    public function emailPrefix($message = null, $propertyPath = null)
1841
    {
1842
        $this->value($this->value . '@example.com');
1843
        return $this->email($message, $propertyPath);
1844
    }
1845
1846
    /**
1847
     * Assert that value is an URL.
1848
     *
1849
     * This code snipped was taken from the Symfony project and modified to the special demands of this method.
1850
     *
1851
     * @param string|null $message
1852
     * @param string|null $propertyPath
1853
     * @return Assert
1854
     * @throws AssertionFailedException
1855
     *
1856
     *
1857
     * @link https://github.com/symfony/Validator/blob/master/Constraints/UrlValidator.php
1858
     * @link https://github.com/symfony/Validator/blob/master/Constraints/Url.php
1859
     */
1860
    public function url($message = null, $propertyPath = null)
1861
    {
1862
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1863
        {
1864
            return $this;
1865
        }
1866
        $this->string($message, $propertyPath);
1867
        $protocols = ['http', 'https'];
1868
        $pattern   = '~^
1869
            (%s)://                                 # protocol
1870
            (
1871
                ([\pL\pN\pS-]+\.)+[\pL]+                   # a domain name
1872
                    |                                     #  or
1873
                \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}      # a IP address
1874
                    |                                     #  or
1875
                \[
1876
                    (?:(?:(?:(?:(?:(?:(?:[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})))?::))))
1877
                \]  # a IPv6 address
1878
            )
1879
            (:[0-9]+)?                              # a port (optional)
1880
            (/?|/\S+)                               # a /, nothing or a / with something
1881
        $~ixu';
1882
        $pattern   = sprintf($pattern, implode('|', $protocols));
1883
        if ( !preg_match($pattern, $this->value) )
1884
        {
1885
            $message = sprintf(
1886
                $message ?: 'Value "%s" was expected to be a valid URL starting with http or https',
1887
                $this->stringify($this->value)
1888
            );
1889
            throw $this->createException($message, self::INVALID_URL, $propertyPath);
1890
        }
1891
        return $this;
1892
    }
1893
1894
    /**
1895
     * Assert that value is domain name.
1896
     *
1897
     * This code snipped was taken from the Symfony project and modified to the special demands of this method.
1898
     *
1899
     * @param string|null $message
1900
     * @param string|null $propertyPath
1901
     * @return Assert
1902
     * @throws AssertionFailedException
1903
     *
1904
     */
1905
    public function domainName($message = null, $propertyPath = null)
1906
    {
1907
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1908
        {
1909
            return $this;
1910
        }
1911
        $this->string($message, $propertyPath);
1912
        $pattern   = '/^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}$/';
1913
        if ( ! preg_match($pattern, $this->value) )
1914
        {
1915
            $message = sprintf(
1916
                $message ?: 'Value "%s" was expected to be a valid domain name',
1917
                $this->stringify($this->value)
1918
            );
1919
            throw $this->createException($message, self::INVALID_DOMAIN_NAME, $propertyPath);
1920
        }
1921
        return $this;
1922
    }
1923
1924
    /**
1925
     * Assert that value is alphanumeric.
1926
     *
1927
     * @param string|null $message
1928
     * @param string|null $propertyPath
1929
     * @return Assert
1930
     * @throws AssertionFailedException
1931
     */
1932 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...
1933
    {
1934
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1935
        {
1936
            return $this;
1937
        }
1938
        try
1939
        {
1940
            $this->regex('(^([a-zA-Z]{1}[a-zA-Z0-9]*)$)', $message, $propertyPath);
1941
        }
1942
        catch (AssertionFailedException $e)
1943
        {
1944
            $message = sprintf(
1945
                $message
1946
                    ?: 'Value "%s" is not alphanumeric, starting with letters and containing only letters and numbers.',
1947
                $this->stringify($this->value)
1948
            );
1949
            throw $this->createException($message, self::INVALID_ALNUM, $propertyPath);
1950
        }
1951
        return $this;
1952
    }
1953
1954
    /**
1955
     * Assert that the value is boolean True.
1956
     *
1957
     * @param string|null $message
1958
     * @param string|null $propertyPath
1959
     * @return Assert
1960
     * @throws AssertionFailedException
1961
     */
1962
    public function true($message = null, $propertyPath = null)
1963
    {
1964
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1965
        {
1966
            return $this;
1967
        }
1968
        if ( $this->value !== true )
1969
        {
1970
            $message = sprintf(
1971
                $message ?: 'Value "%s" is not TRUE.',
1972
                $this->stringify($this->value)
1973
            );
1974
            throw $this->createException($message, self::INVALID_TRUE, $propertyPath);
1975
        }
1976
        return $this;
1977
    }
1978
1979
    /**
1980
     * Assert that the value is boolean True.
1981
     *
1982
     * @param string|null $message
1983
     * @param string|null $propertyPath
1984
     * @return Assert
1985
     * @throws AssertionFailedException
1986
     */
1987
    public function truthy($message = null, $propertyPath = null)
1988
    {
1989
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
1990
        {
1991
            return $this;
1992
        }
1993
        if ( ! $this->value )
1994
        {
1995
            $message = sprintf(
1996
                $message ?: 'Value "%s" is not truthy.',
1997
                $this->stringify($this->value)
1998
            );
1999
            throw $this->createException($message, self::INVALID_TRUE, $propertyPath);
2000
        }
2001
        return $this;
2002
    }
2003
2004
    /**
2005
     * Assert that the value is boolean False.
2006
     *
2007
     * @param string|null $message
2008
     * @param string|null $propertyPath
2009
     * @return Assert
2010
     * @throws AssertionFailedException
2011
     */
2012
    public function false($message = null, $propertyPath = null)
2013
    {
2014
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2015
        {
2016
            return $this;
2017
        }
2018
        if ( $this->value !== false )
2019
        {
2020
            $message = sprintf(
2021
                $message ?: 'Value "%s" is not FALSE.',
2022
                $this->stringify($this->value)
2023
            );
2024
            throw $this->createException($message, self::INVALID_FALSE, $propertyPath);
2025
        }
2026
        return $this;
2027
    }
2028
2029
    /**
2030
     * Assert that the value is not boolean False.
2031
     *
2032
     * @param string|null $message
2033
     * @param string|null $propertyPath
2034
     * @return Assert
2035
     * @throws AssertionFailedException
2036
     */
2037
    public function notFalse($message = null, $propertyPath = null)
2038
    {
2039
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2040
        {
2041
            return $this;
2042
        }
2043
        if ( $this->value === false )
2044
        {
2045
            $message = sprintf(
2046
                $message ?: 'Value "%s" is not FALSE.',
2047
                $this->stringify($this->value)
2048
            );
2049
            throw $this->createException($message, self::INVALID_NOT_FALSE, $propertyPath);
2050
        }
2051
        return $this;
2052
    }
2053
2054
    /**
2055
     * Assert that the class exists.
2056
     *
2057
     * @param string|null $message
2058
     * @param string|null $propertyPath
2059
     * @return Assert
2060
     * @throws AssertionFailedException
2061
     */
2062
    public function classExists($message = null, $propertyPath = null)
2063
    {
2064
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2065
        {
2066
            return $this;
2067
        }
2068
        if ( !class_exists($this->value) )
2069
        {
2070
            $message = sprintf(
2071
                $message ?: 'Class "%s" does not exist.',
2072
                $this->stringify($this->value)
2073
            );
2074
            throw $this->createException($message, self::INVALID_CLASS, $propertyPath);
2075
        }
2076
        return $this;
2077
    }
2078
2079
    /**
2080
     * Assert that the class implements the interface
2081
     *
2082
     * @param string      $interfaceName
2083
     * @param string|null $message
2084
     * @param string|null $propertyPath
2085
     * @return Assert
2086
     * @throws AssertionFailedException
2087
     */
2088
    public function implementsInterface($interfaceName, $message = null, $propertyPath = null)
2089
    {
2090
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2091
        {
2092
            return $this;
2093
        }
2094
        $reflection = new \ReflectionClass($this->value);
2095
        if ( !$reflection->implementsInterface($interfaceName) )
2096
        {
2097
            $message = sprintf(
2098
                $message ?: 'Class "%s" does not implement interface "%s".',
2099
                $this->stringify($this->value),
2100
                $this->stringify($interfaceName)
2101
            );
2102
            throw $this->createException($message, self::INTERFACE_NOT_IMPLEMENTED, $propertyPath, ['interface' => $interfaceName]);
2103
        }
2104
        return $this;
2105
    }
2106
2107
    /**
2108
     * Assert that the given string is a valid json string.
2109
     *
2110
     * NOTICE:
2111
     * Since this does a json_decode to determine its validity
2112
     * you probably should consider, when using the variable
2113
     * content afterwards, just to decode and check for yourself instead
2114
     * of using this assertion.
2115
     *
2116
     * @param string|null $message
2117
     * @param string|null $propertyPath
2118
     * @return Assert
2119
     * @throws AssertionFailedException
2120
     */
2121
    public function isJsonString($message = null, $propertyPath = null)
2122
    {
2123
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2124
        {
2125
            return $this;
2126
        }
2127
        if ( null === json_decode($this->value) && JSON_ERROR_NONE !== json_last_error() )
2128
        {
2129
            $message = sprintf(
2130
                $message ?: 'Value "%s" is not a valid JSON string.',
2131
                $this->stringify($this->value)
2132
            );
2133
            throw $this->createException($message, self::INVALID_JSON_STRING, $propertyPath);
2134
        }
2135
        return $this;
2136
    }
2137
2138
    /**
2139
     * Assert that the given string is a valid UUID
2140
     *
2141
     * Uses code from {@link https://github.com/ramsey/uuid} that is MIT licensed.
2142
     *
2143
     * @param string|null $message
2144
     * @param string|null $propertyPath
2145
     * @return Assert
2146
     * @throws AssertionFailedException
2147
     */
2148
    public function uuid($message = null, $propertyPath = null)
2149
    {
2150
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2151
        {
2152
            return $this;
2153
        }
2154
        $this->value = str_replace(['urn:', 'uuid:', '{', '}'], '', $this->value);
2155
        if ( $this->value === '00000000-0000-0000-0000-000000000000' )
2156
        {
2157
            return $this;
2158
        }
2159
        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) )
2160
        {
2161
            $message = sprintf(
2162
                $message ?: 'Value "%s" is not a valid UUID.',
2163
                $this->stringify($this->value)
2164
            );
2165
            throw $this->createException($message, self::INVALID_UUID, $propertyPath);
2166
        }
2167
        return $this;
2168
    }
2169
2170
    /**
2171
     * Assert that the count of countable is equal to count.
2172
     *
2173
     * @param int    $count
2174
     * @param string $message
2175
     * @param string $propertyPath
2176
     * @return Assert
2177
     * @throws AssertionFailedException
2178
     */
2179
    public function count($count, $message = null, $propertyPath = null)
2180
    {
2181
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2182
        {
2183
            return $this;
2184
        }
2185
        if ( $count !== count($this->value) )
2186
        {
2187
            $message = sprintf(
2188
                $message ?: 'List does not contain exactly "%d" elements.',
2189
                $this->stringify($this->value),
2190
                $this->stringify($count)
2191
            );
2192
            throw $this->createException($message, self::INVALID_COUNT, $propertyPath, ['count' => $count]);
2193
        }
2194
        return $this;
2195
    }
2196
2197
    /**
2198
     * @param $func
2199
     * @param $args
2200
     * @return bool
2201
     * @throws AssertionFailedException
2202
     */
2203
    protected function doAllOrNullOr($func, $args)
2204
    {
2205
        if ( $this->nullOr && is_null($this->value) )
2206
        {
2207
            return true;
2208
        }
2209
        if ( $this->all && (new Assert($this->value))->setExceptionClass($this->exceptionClass)->isTraversable() )
2210
        {
2211
            foreach ( $this->value as $idx => $value )
2212
            {
2213
                $object = (new Assert($value))->setExceptionClass($this->exceptionClass);
2214
                call_user_func_array([$object, $func], $args);
2215
            }
2216
            return true;
2217
        }
2218
        return $this->nullOr && is_null($this->value) ? true : false;
2219
    }
2220
2221
    /**
2222
     * Determines if the values array has every choice as key and that this choice has content.
2223
     *
2224
     * @param array $choices
2225
     * @param string|null $message
2226
     * @param string|null $propertyPath
2227
     * @return $this
2228
     */
2229
    public function choicesNotEmpty(array $choices, $message = null, $propertyPath = null)
2230
    {
2231
        if ( $this->doAllOrNullOr(__FUNCTION__, func_get_args()) )
2232
        {
2233
            return $this;
2234
        }
2235
        $this->notEmpty($message, $propertyPath);
2236
        foreach ( $choices as $choice )
2237
        {
2238
            $this->notEmptyKey($choice, $message, $propertyPath);
2239
        }
2240
        return $this;
2241
    }
2242
2243
    /**
2244
     * Determines that the named method is defined in the provided object.
2245
     *
2246
     * @param mixed $object
2247
     * @param string|null $message
2248
     * @param string|null $propertyPath
2249
     * @returns Assert
2250
     * @throws
2251
     */
2252 View Code Duplication
    public function methodExists($object, $message = null, $propertyPath = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

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