Pull merge.
[yaffs-website] / vendor / webmozart / assert / src / Assert.php
1 <?php
2
3 /*
4  * This file is part of the webmozart/assert package.
5  *
6  * (c) Bernhard Schussek <bschussek@gmail.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Webmozart\Assert;
13
14 use ArrayAccess;
15 use BadMethodCallException;
16 use Closure;
17 use Countable;
18 use Exception;
19 use InvalidArgumentException;
20 use Throwable;
21 use Traversable;
22
23 /**
24  * Efficient assertions to validate the input/output of your methods.
25  *
26  * @method static void nullOrString($value, $message = '')
27  * @method static void nullOrStringNotEmpty($value, $message = '')
28  * @method static void nullOrInteger($value, $message = '')
29  * @method static void nullOrIntegerish($value, $message = '')
30  * @method static void nullOrFloat($value, $message = '')
31  * @method static void nullOrNumeric($value, $message = '')
32  * @method static void nullOrBoolean($value, $message = '')
33  * @method static void nullOrScalar($value, $message = '')
34  * @method static void nullOrObject($value, $message = '')
35  * @method static void nullOrResource($value, $type = null, $message = '')
36  * @method static void nullOrIsCallable($value, $message = '')
37  * @method static void nullOrIsArray($value, $message = '')
38  * @method static void nullOrIsTraversable($value, $message = '')
39  * @method static void nullOrIsArrayAccessible($value, $message = '')
40  * @method static void nullOrIsCountable($value, $message = '')
41  * @method static void nullOrIsInstanceOf($value, $class, $message = '')
42  * @method static void nullOrNotInstanceOf($value, $class, $message = '')
43  * @method static void nullOrIsInstanceOfAny($value, $classes, $message = '')
44  * @method static void nullOrIsEmpty($value, $message = '')
45  * @method static void nullOrNotEmpty($value, $message = '')
46  * @method static void nullOrTrue($value, $message = '')
47  * @method static void nullOrFalse($value, $message = '')
48  * @method static void nullOrEq($value, $value2, $message = '')
49  * @method static void nullOrNotEq($value,$value2,  $message = '')
50  * @method static void nullOrSame($value, $value2, $message = '')
51  * @method static void nullOrNotSame($value, $value2, $message = '')
52  * @method static void nullOrGreaterThan($value, $value2, $message = '')
53  * @method static void nullOrGreaterThanEq($value, $value2, $message = '')
54  * @method static void nullOrLessThan($value, $value2, $message = '')
55  * @method static void nullOrLessThanEq($value, $value2, $message = '')
56  * @method static void nullOrRange($value, $min, $max, $message = '')
57  * @method static void nullOrOneOf($value, $values, $message = '')
58  * @method static void nullOrContains($value, $subString, $message = '')
59  * @method static void nullOrNotContains($value, $subString, $message = '')
60  * @method static void nullOrNotWhitespaceOnly($value, $message = '')
61  * @method static void nullOrStartsWith($value, $prefix, $message = '')
62  * @method static void nullOrStartsWithLetter($value, $message = '')
63  * @method static void nullOrEndsWith($value, $suffix, $message = '')
64  * @method static void nullOrRegex($value, $pattern, $message = '')
65  * @method static void nullOrAlpha($value, $message = '')
66  * @method static void nullOrDigits($value, $message = '')
67  * @method static void nullOrAlnum($value, $message = '')
68  * @method static void nullOrLower($value, $message = '')
69  * @method static void nullOrUpper($value, $message = '')
70  * @method static void nullOrLength($value, $length, $message = '')
71  * @method static void nullOrMinLength($value, $min, $message = '')
72  * @method static void nullOrMaxLength($value, $max, $message = '')
73  * @method static void nullOrLengthBetween($value, $min, $max, $message = '')
74  * @method static void nullOrFileExists($value, $message = '')
75  * @method static void nullOrFile($value, $message = '')
76  * @method static void nullOrDirectory($value, $message = '')
77  * @method static void nullOrReadable($value, $message = '')
78  * @method static void nullOrWritable($value, $message = '')
79  * @method static void nullOrClassExists($value, $message = '')
80  * @method static void nullOrSubclassOf($value, $class, $message = '')
81  * @method static void nullOrImplementsInterface($value, $interface, $message = '')
82  * @method static void nullOrPropertyExists($value, $property, $message = '')
83  * @method static void nullOrPropertyNotExists($value, $property, $message = '')
84  * @method static void nullOrMethodExists($value, $method, $message = '')
85  * @method static void nullOrMethodNotExists($value, $method, $message = '')
86  * @method static void nullOrKeyExists($value, $key, $message = '')
87  * @method static void nullOrKeyNotExists($value, $key, $message = '')
88  * @method static void nullOrCount($value, $key, $message = '')
89  * @method static void nullOrMinCount($value, $min, $message = '')
90  * @method static void nullOrMaxCount($value, $max, $message = '')
91  * @method static void nullCountBetween($value, $min, $max, $message = '')
92  * @method static void nullOrUuid($values, $message = '')
93  * @method static void allString($values, $message = '')
94  * @method static void allStringNotEmpty($values, $message = '')
95  * @method static void allInteger($values, $message = '')
96  * @method static void allIntegerish($values, $message = '')
97  * @method static void allFloat($values, $message = '')
98  * @method static void allNumeric($values, $message = '')
99  * @method static void allBoolean($values, $message = '')
100  * @method static void allScalar($values, $message = '')
101  * @method static void allObject($values, $message = '')
102  * @method static void allResource($values, $type = null, $message = '')
103  * @method static void allIsCallable($values, $message = '')
104  * @method static void allIsArray($values, $message = '')
105  * @method static void allIsTraversable($values, $message = '')
106  * @method static void allIsArrayAccessible($values, $message = '')
107  * @method static void allIsCountable($values, $message = '')
108  * @method static void allIsInstanceOf($values, $class, $message = '')
109  * @method static void allNotInstanceOf($values, $class, $message = '')
110  * @method static void allIsInstanceOfAny($values, $classes, $message = '')
111  * @method static void allNull($values, $message = '')
112  * @method static void allNotNull($values, $message = '')
113  * @method static void allIsEmpty($values, $message = '')
114  * @method static void allNotEmpty($values, $message = '')
115  * @method static void allTrue($values, $message = '')
116  * @method static void allFalse($values, $message = '')
117  * @method static void allEq($values, $value2, $message = '')
118  * @method static void allNotEq($values,$value2,  $message = '')
119  * @method static void allSame($values, $value2, $message = '')
120  * @method static void allNotSame($values, $value2, $message = '')
121  * @method static void allGreaterThan($values, $value2, $message = '')
122  * @method static void allGreaterThanEq($values, $value2, $message = '')
123  * @method static void allLessThan($values, $value2, $message = '')
124  * @method static void allLessThanEq($values, $value2, $message = '')
125  * @method static void allRange($values, $min, $max, $message = '')
126  * @method static void allOneOf($values, $values, $message = '')
127  * @method static void allContains($values, $subString, $message = '')
128  * @method static void allNotContains($values, $subString, $message = '')
129  * @method static void allNotWhitespaceOnly($values, $message = '')
130  * @method static void allStartsWith($values, $prefix, $message = '')
131  * @method static void allStartsWithLetter($values, $message = '')
132  * @method static void allEndsWith($values, $suffix, $message = '')
133  * @method static void allRegex($values, $pattern, $message = '')
134  * @method static void allAlpha($values, $message = '')
135  * @method static void allDigits($values, $message = '')
136  * @method static void allAlnum($values, $message = '')
137  * @method static void allLower($values, $message = '')
138  * @method static void allUpper($values, $message = '')
139  * @method static void allLength($values, $length, $message = '')
140  * @method static void allMinLength($values, $min, $message = '')
141  * @method static void allMaxLength($values, $max, $message = '')
142  * @method static void allLengthBetween($values, $min, $max, $message = '')
143  * @method static void allFileExists($values, $message = '')
144  * @method static void allFile($values, $message = '')
145  * @method static void allDirectory($values, $message = '')
146  * @method static void allReadable($values, $message = '')
147  * @method static void allWritable($values, $message = '')
148  * @method static void allClassExists($values, $message = '')
149  * @method static void allSubclassOf($values, $class, $message = '')
150  * @method static void allImplementsInterface($values, $interface, $message = '')
151  * @method static void allPropertyExists($values, $property, $message = '')
152  * @method static void allPropertyNotExists($values, $property, $message = '')
153  * @method static void allMethodExists($values, $method, $message = '')
154  * @method static void allMethodNotExists($values, $method, $message = '')
155  * @method static void allKeyExists($values, $key, $message = '')
156  * @method static void allKeyNotExists($values, $key, $message = '')
157  * @method static void allCount($values, $key, $message = '')
158  * @method static void allMinCount($values, $min, $message = '')
159  * @method static void allMaxCount($values, $max, $message = '')
160  * @method static void allCountBetween($values, $min, $max, $message = '')
161  * @method static void allUuid($values, $message = '')
162  *
163  * @since  1.0
164  *
165  * @author Bernhard Schussek <bschussek@gmail.com>
166  */
167 class Assert
168 {
169     public static function string($value, $message = '')
170     {
171         if (!is_string($value)) {
172             static::reportInvalidArgument(sprintf(
173                 $message ?: 'Expected a string. Got: %s',
174                 static::typeToString($value)
175             ));
176         }
177     }
178
179     public static function stringNotEmpty($value, $message = '')
180     {
181         static::string($value, $message);
182         static::notEq($value, '', $message);
183     }
184
185     public static function integer($value, $message = '')
186     {
187         if (!is_int($value)) {
188             static::reportInvalidArgument(sprintf(
189                 $message ?: 'Expected an integer. Got: %s',
190                 static::typeToString($value)
191             ));
192         }
193     }
194
195     public static function integerish($value, $message = '')
196     {
197         if (!is_numeric($value) || $value != (int) $value) {
198             static::reportInvalidArgument(sprintf(
199                 $message ?: 'Expected an integerish value. Got: %s',
200                 static::typeToString($value)
201             ));
202         }
203     }
204
205     public static function float($value, $message = '')
206     {
207         if (!is_float($value)) {
208             static::reportInvalidArgument(sprintf(
209                 $message ?: 'Expected a float. Got: %s',
210                 static::typeToString($value)
211             ));
212         }
213     }
214
215     public static function numeric($value, $message = '')
216     {
217         if (!is_numeric($value)) {
218             static::reportInvalidArgument(sprintf(
219                 $message ?: 'Expected a numeric. Got: %s',
220                 static::typeToString($value)
221             ));
222         }
223     }
224
225     public static function natural($value, $message = '')
226     {
227         if (!is_int($value) || $value < 0) {
228             static::reportInvalidArgument(sprintf(
229                 $message ?: 'Expected a non-negative integer. Got %s',
230                 static::valueToString($value)
231             ));
232         }
233     }
234
235     public static function boolean($value, $message = '')
236     {
237         if (!is_bool($value)) {
238             static::reportInvalidArgument(sprintf(
239                 $message ?: 'Expected a boolean. Got: %s',
240                 static::typeToString($value)
241             ));
242         }
243     }
244
245     public static function scalar($value, $message = '')
246     {
247         if (!is_scalar($value)) {
248             static::reportInvalidArgument(sprintf(
249                 $message ?: 'Expected a scalar. Got: %s',
250                 static::typeToString($value)
251             ));
252         }
253     }
254
255     public static function object($value, $message = '')
256     {
257         if (!is_object($value)) {
258             static::reportInvalidArgument(sprintf(
259                 $message ?: 'Expected an object. Got: %s',
260                 static::typeToString($value)
261             ));
262         }
263     }
264
265     public static function resource($value, $type = null, $message = '')
266     {
267         if (!is_resource($value)) {
268             static::reportInvalidArgument(sprintf(
269                 $message ?: 'Expected a resource. Got: %s',
270                 static::typeToString($value)
271             ));
272         }
273
274         if ($type && $type !== get_resource_type($value)) {
275             static::reportInvalidArgument(sprintf(
276                 $message ?: 'Expected a resource of type %2$s. Got: %s',
277                 static::typeToString($value),
278                 $type
279             ));
280         }
281     }
282
283     public static function isCallable($value, $message = '')
284     {
285         if (!is_callable($value)) {
286             static::reportInvalidArgument(sprintf(
287                 $message ?: 'Expected a callable. Got: %s',
288                 static::typeToString($value)
289             ));
290         }
291     }
292
293     public static function isArray($value, $message = '')
294     {
295         if (!is_array($value)) {
296             static::reportInvalidArgument(sprintf(
297                 $message ?: 'Expected an array. Got: %s',
298                 static::typeToString($value)
299             ));
300         }
301     }
302
303     public static function isTraversable($value, $message = '')
304     {
305         @trigger_error(
306             sprintf(
307                 'The "%s" assertion is deprecated. You should stop using it, as it will soon be removed in 2.0 version. Use "isIterable" or "isInstanceOf" instead.',
308                 __METHOD__
309             ),
310             E_USER_DEPRECATED
311         );
312
313         if (!is_array($value) && !($value instanceof Traversable)) {
314             static::reportInvalidArgument(sprintf(
315                 $message ?: 'Expected a traversable. Got: %s',
316                 static::typeToString($value)
317             ));
318         }
319     }
320
321     public static function isArrayAccessible($value, $message = '')
322     {
323         if (!is_array($value) && !($value instanceof ArrayAccess)) {
324             static::reportInvalidArgument(sprintf(
325                 $message ?: 'Expected an array accessible. Got: %s',
326                 static::typeToString($value)
327             ));
328         }
329     }
330
331     public static function isCountable($value, $message = '')
332     {
333         if (!is_array($value) && !($value instanceof Countable)) {
334             static::reportInvalidArgument(sprintf(
335                 $message ?: 'Expected a countable. Got: %s',
336                 static::typeToString($value)
337             ));
338         }
339     }
340
341     public static function isIterable($value, $message = '')
342     {
343         if (!is_array($value) && !($value instanceof Traversable)) {
344             static::reportInvalidArgument(sprintf(
345                 $message ?: 'Expected an iterable. Got: %s',
346                 static::typeToString($value)
347             ));
348         }
349     }
350
351     public static function isInstanceOf($value, $class, $message = '')
352     {
353         if (!($value instanceof $class)) {
354             static::reportInvalidArgument(sprintf(
355                 $message ?: 'Expected an instance of %2$s. Got: %s',
356                 static::typeToString($value),
357                 $class
358             ));
359         }
360     }
361
362     public static function notInstanceOf($value, $class, $message = '')
363     {
364         if ($value instanceof $class) {
365             static::reportInvalidArgument(sprintf(
366                 $message ?: 'Expected an instance other than %2$s. Got: %s',
367                 static::typeToString($value),
368                 $class
369             ));
370         }
371     }
372
373     public static function isInstanceOfAny($value, array $classes, $message = '')
374     {
375         foreach ($classes as $class) {
376             if ($value instanceof $class) {
377                 return;
378             }
379         }
380
381         static::reportInvalidArgument(sprintf(
382             $message ?: 'Expected an instance of any of %2$s. Got: %s',
383             static::typeToString($value),
384             implode(', ', array_map(array('static', 'valueToString'), $classes))
385         ));
386     }
387
388     public static function isEmpty($value, $message = '')
389     {
390         if (!empty($value)) {
391             static::reportInvalidArgument(sprintf(
392                 $message ?: 'Expected an empty value. Got: %s',
393                 static::valueToString($value)
394             ));
395         }
396     }
397
398     public static function notEmpty($value, $message = '')
399     {
400         if (empty($value)) {
401             static::reportInvalidArgument(sprintf(
402                 $message ?: 'Expected a non-empty value. Got: %s',
403                 static::valueToString($value)
404             ));
405         }
406     }
407
408     public static function null($value, $message = '')
409     {
410         if (null !== $value) {
411             static::reportInvalidArgument(sprintf(
412                 $message ?: 'Expected null. Got: %s',
413                 static::valueToString($value)
414             ));
415         }
416     }
417
418     public static function notNull($value, $message = '')
419     {
420         if (null === $value) {
421             static::reportInvalidArgument(
422                 $message ?: 'Expected a value other than null.'
423             );
424         }
425     }
426
427     public static function true($value, $message = '')
428     {
429         if (true !== $value) {
430             static::reportInvalidArgument(sprintf(
431                 $message ?: 'Expected a value to be true. Got: %s',
432                 static::valueToString($value)
433             ));
434         }
435     }
436
437     public static function false($value, $message = '')
438     {
439         if (false !== $value) {
440             static::reportInvalidArgument(sprintf(
441                 $message ?: 'Expected a value to be false. Got: %s',
442                 static::valueToString($value)
443             ));
444         }
445     }
446
447     public static function eq($value, $value2, $message = '')
448     {
449         if ($value2 != $value) {
450             static::reportInvalidArgument(sprintf(
451                 $message ?: 'Expected a value equal to %2$s. Got: %s',
452                 static::valueToString($value),
453                 static::valueToString($value2)
454             ));
455         }
456     }
457
458     public static function notEq($value, $value2, $message = '')
459     {
460         if ($value2 == $value) {
461             static::reportInvalidArgument(sprintf(
462                 $message ?: 'Expected a different value than %s.',
463                 static::valueToString($value2)
464             ));
465         }
466     }
467
468     public static function same($value, $value2, $message = '')
469     {
470         if ($value2 !== $value) {
471             static::reportInvalidArgument(sprintf(
472                 $message ?: 'Expected a value identical to %2$s. Got: %s',
473                 static::valueToString($value),
474                 static::valueToString($value2)
475             ));
476         }
477     }
478
479     public static function notSame($value, $value2, $message = '')
480     {
481         if ($value2 === $value) {
482             static::reportInvalidArgument(sprintf(
483                 $message ?: 'Expected a value not identical to %s.',
484                 static::valueToString($value2)
485             ));
486         }
487     }
488
489     public static function greaterThan($value, $limit, $message = '')
490     {
491         if ($value <= $limit) {
492             static::reportInvalidArgument(sprintf(
493                 $message ?: 'Expected a value greater than %2$s. Got: %s',
494                 static::valueToString($value),
495                 static::valueToString($limit)
496             ));
497         }
498     }
499
500     public static function greaterThanEq($value, $limit, $message = '')
501     {
502         if ($value < $limit) {
503             static::reportInvalidArgument(sprintf(
504                 $message ?: 'Expected a value greater than or equal to %2$s. Got: %s',
505                 static::valueToString($value),
506                 static::valueToString($limit)
507             ));
508         }
509     }
510
511     public static function lessThan($value, $limit, $message = '')
512     {
513         if ($value >= $limit) {
514             static::reportInvalidArgument(sprintf(
515                 $message ?: 'Expected a value less than %2$s. Got: %s',
516                 static::valueToString($value),
517                 static::valueToString($limit)
518             ));
519         }
520     }
521
522     public static function lessThanEq($value, $limit, $message = '')
523     {
524         if ($value > $limit) {
525             static::reportInvalidArgument(sprintf(
526                 $message ?: 'Expected a value less than or equal to %2$s. Got: %s',
527                 static::valueToString($value),
528                 static::valueToString($limit)
529             ));
530         }
531     }
532
533     public static function range($value, $min, $max, $message = '')
534     {
535         if ($value < $min || $value > $max) {
536             static::reportInvalidArgument(sprintf(
537                 $message ?: 'Expected a value between %2$s and %3$s. Got: %s',
538                 static::valueToString($value),
539                 static::valueToString($min),
540                 static::valueToString($max)
541             ));
542         }
543     }
544
545     public static function oneOf($value, array $values, $message = '')
546     {
547         if (!in_array($value, $values, true)) {
548             static::reportInvalidArgument(sprintf(
549                 $message ?: 'Expected one of: %2$s. Got: %s',
550                 static::valueToString($value),
551                 implode(', ', array_map(array('static', 'valueToString'), $values))
552             ));
553         }
554     }
555
556     public static function contains($value, $subString, $message = '')
557     {
558         if (false === strpos($value, $subString)) {
559             static::reportInvalidArgument(sprintf(
560                 $message ?: 'Expected a value to contain %2$s. Got: %s',
561                 static::valueToString($value),
562                 static::valueToString($subString)
563             ));
564         }
565     }
566
567     public static function notContains($value, $subString, $message = '')
568     {
569         if (false !== strpos($value, $subString)) {
570             static::reportInvalidArgument(sprintf(
571                 $message ?: '%2$s was not expected to be contained in a value. Got: %s',
572                 static::valueToString($value),
573                 static::valueToString($subString)
574             ));
575         }
576     }
577
578     public static function notWhitespaceOnly($value, $message = '')
579     {
580         if (preg_match('/^\s*$/', $value)) {
581             static::reportInvalidArgument(sprintf(
582                 $message ?: 'Expected a non-whitespace string. Got: %s',
583                 static::valueToString($value)
584             ));
585         }
586     }
587
588     public static function startsWith($value, $prefix, $message = '')
589     {
590         if (0 !== strpos($value, $prefix)) {
591             static::reportInvalidArgument(sprintf(
592                 $message ?: 'Expected a value to start with %2$s. Got: %s',
593                 static::valueToString($value),
594                 static::valueToString($prefix)
595             ));
596         }
597     }
598
599     public static function startsWithLetter($value, $message = '')
600     {
601         $valid = isset($value[0]);
602
603         if ($valid) {
604             $locale = setlocale(LC_CTYPE, 0);
605             setlocale(LC_CTYPE, 'C');
606             $valid = ctype_alpha($value[0]);
607             setlocale(LC_CTYPE, $locale);
608         }
609
610         if (!$valid) {
611             static::reportInvalidArgument(sprintf(
612                 $message ?: 'Expected a value to start with a letter. Got: %s',
613                 static::valueToString($value)
614             ));
615         }
616     }
617
618     public static function endsWith($value, $suffix, $message = '')
619     {
620         if ($suffix !== substr($value, -static::strlen($suffix))) {
621             static::reportInvalidArgument(sprintf(
622                 $message ?: 'Expected a value to end with %2$s. Got: %s',
623                 static::valueToString($value),
624                 static::valueToString($suffix)
625             ));
626         }
627     }
628
629     public static function regex($value, $pattern, $message = '')
630     {
631         if (!preg_match($pattern, $value)) {
632             static::reportInvalidArgument(sprintf(
633                 $message ?: 'The value %s does not match the expected pattern.',
634                 static::valueToString($value)
635             ));
636         }
637     }
638
639     public static function alpha($value, $message = '')
640     {
641         $locale = setlocale(LC_CTYPE, 0);
642         setlocale(LC_CTYPE, 'C');
643         $valid = !ctype_alpha($value);
644         setlocale(LC_CTYPE, $locale);
645
646         if ($valid) {
647             static::reportInvalidArgument(sprintf(
648                 $message ?: 'Expected a value to contain only letters. Got: %s',
649                 static::valueToString($value)
650             ));
651         }
652     }
653
654     public static function digits($value, $message = '')
655     {
656         $locale = setlocale(LC_CTYPE, 0);
657         setlocale(LC_CTYPE, 'C');
658         $valid = !ctype_digit($value);
659         setlocale(LC_CTYPE, $locale);
660
661         if ($valid) {
662             static::reportInvalidArgument(sprintf(
663                 $message ?: 'Expected a value to contain digits only. Got: %s',
664                 static::valueToString($value)
665             ));
666         }
667     }
668
669     public static function alnum($value, $message = '')
670     {
671         $locale = setlocale(LC_CTYPE, 0);
672         setlocale(LC_CTYPE, 'C');
673         $valid = !ctype_alnum($value);
674         setlocale(LC_CTYPE, $locale);
675
676         if ($valid) {
677             static::reportInvalidArgument(sprintf(
678                 $message ?: 'Expected a value to contain letters and digits only. Got: %s',
679                 static::valueToString($value)
680             ));
681         }
682     }
683
684     public static function lower($value, $message = '')
685     {
686         $locale = setlocale(LC_CTYPE, 0);
687         setlocale(LC_CTYPE, 'C');
688         $valid = !ctype_lower($value);
689         setlocale(LC_CTYPE, $locale);
690
691         if ($valid) {
692             static::reportInvalidArgument(sprintf(
693                 $message ?: 'Expected a value to contain lowercase characters only. Got: %s',
694                 static::valueToString($value)
695             ));
696         }
697     }
698
699     public static function upper($value, $message = '')
700     {
701         $locale = setlocale(LC_CTYPE, 0);
702         setlocale(LC_CTYPE, 'C');
703         $valid = !ctype_upper($value);
704         setlocale(LC_CTYPE, $locale);
705
706         if ($valid) {
707             static::reportInvalidArgument(sprintf(
708                 $message ?: 'Expected a value to contain uppercase characters only. Got: %s',
709                 static::valueToString($value)
710             ));
711         }
712     }
713
714     public static function length($value, $length, $message = '')
715     {
716         if ($length !== static::strlen($value)) {
717             static::reportInvalidArgument(sprintf(
718                 $message ?: 'Expected a value to contain %2$s characters. Got: %s',
719                 static::valueToString($value),
720                 $length
721             ));
722         }
723     }
724
725     public static function minLength($value, $min, $message = '')
726     {
727         if (static::strlen($value) < $min) {
728             static::reportInvalidArgument(sprintf(
729                 $message ?: 'Expected a value to contain at least %2$s characters. Got: %s',
730                 static::valueToString($value),
731                 $min
732             ));
733         }
734     }
735
736     public static function maxLength($value, $max, $message = '')
737     {
738         if (static::strlen($value) > $max) {
739             static::reportInvalidArgument(sprintf(
740                 $message ?: 'Expected a value to contain at most %2$s characters. Got: %s',
741                 static::valueToString($value),
742                 $max
743             ));
744         }
745     }
746
747     public static function lengthBetween($value, $min, $max, $message = '')
748     {
749         $length = static::strlen($value);
750
751         if ($length < $min || $length > $max) {
752             static::reportInvalidArgument(sprintf(
753                 $message ?: 'Expected a value to contain between %2$s and %3$s characters. Got: %s',
754                 static::valueToString($value),
755                 $min,
756                 $max
757             ));
758         }
759     }
760
761     public static function fileExists($value, $message = '')
762     {
763         static::string($value);
764
765         if (!file_exists($value)) {
766             static::reportInvalidArgument(sprintf(
767                 $message ?: 'The file %s does not exist.',
768                 static::valueToString($value)
769             ));
770         }
771     }
772
773     public static function file($value, $message = '')
774     {
775         static::fileExists($value, $message);
776
777         if (!is_file($value)) {
778             static::reportInvalidArgument(sprintf(
779                 $message ?: 'The path %s is not a file.',
780                 static::valueToString($value)
781             ));
782         }
783     }
784
785     public static function directory($value, $message = '')
786     {
787         static::fileExists($value, $message);
788
789         if (!is_dir($value)) {
790             static::reportInvalidArgument(sprintf(
791                 $message ?: 'The path %s is no directory.',
792                 static::valueToString($value)
793             ));
794         }
795     }
796
797     public static function readable($value, $message = '')
798     {
799         if (!is_readable($value)) {
800             static::reportInvalidArgument(sprintf(
801                 $message ?: 'The path %s is not readable.',
802                 static::valueToString($value)
803             ));
804         }
805     }
806
807     public static function writable($value, $message = '')
808     {
809         if (!is_writable($value)) {
810             static::reportInvalidArgument(sprintf(
811                 $message ?: 'The path %s is not writable.',
812                 static::valueToString($value)
813             ));
814         }
815     }
816
817     public static function classExists($value, $message = '')
818     {
819         if (!class_exists($value)) {
820             static::reportInvalidArgument(sprintf(
821                 $message ?: 'Expected an existing class name. Got: %s',
822                 static::valueToString($value)
823             ));
824         }
825     }
826
827     public static function subclassOf($value, $class, $message = '')
828     {
829         if (!is_subclass_of($value, $class)) {
830             static::reportInvalidArgument(sprintf(
831                 $message ?: 'Expected a sub-class of %2$s. Got: %s',
832                 static::valueToString($value),
833                 static::valueToString($class)
834             ));
835         }
836     }
837
838     public static function implementsInterface($value, $interface, $message = '')
839     {
840         if (!in_array($interface, class_implements($value))) {
841             static::reportInvalidArgument(sprintf(
842                 $message ?: 'Expected an implementation of %2$s. Got: %s',
843                 static::valueToString($value),
844                 static::valueToString($interface)
845             ));
846         }
847     }
848
849     public static function propertyExists($classOrObject, $property, $message = '')
850     {
851         if (!property_exists($classOrObject, $property)) {
852             static::reportInvalidArgument(sprintf(
853                 $message ?: 'Expected the property %s to exist.',
854                 static::valueToString($property)
855             ));
856         }
857     }
858
859     public static function propertyNotExists($classOrObject, $property, $message = '')
860     {
861         if (property_exists($classOrObject, $property)) {
862             static::reportInvalidArgument(sprintf(
863                 $message ?: 'Expected the property %s to not exist.',
864                 static::valueToString($property)
865             ));
866         }
867     }
868
869     public static function methodExists($classOrObject, $method, $message = '')
870     {
871         if (!method_exists($classOrObject, $method)) {
872             static::reportInvalidArgument(sprintf(
873                 $message ?: 'Expected the method %s to exist.',
874                 static::valueToString($method)
875             ));
876         }
877     }
878
879     public static function methodNotExists($classOrObject, $method, $message = '')
880     {
881         if (method_exists($classOrObject, $method)) {
882             static::reportInvalidArgument(sprintf(
883                 $message ?: 'Expected the method %s to not exist.',
884                 static::valueToString($method)
885             ));
886         }
887     }
888
889     public static function keyExists($array, $key, $message = '')
890     {
891         if (!array_key_exists($key, $array)) {
892             static::reportInvalidArgument(sprintf(
893                 $message ?: 'Expected the key %s to exist.',
894                 static::valueToString($key)
895             ));
896         }
897     }
898
899     public static function keyNotExists($array, $key, $message = '')
900     {
901         if (array_key_exists($key, $array)) {
902             static::reportInvalidArgument(sprintf(
903                 $message ?: 'Expected the key %s to not exist.',
904                 static::valueToString($key)
905             ));
906         }
907     }
908
909     public static function count($array, $number, $message = '')
910     {
911         static::eq(
912             count($array),
913             $number,
914             $message ?: sprintf('Expected an array to contain %d elements. Got: %d.', $number, count($array))
915         );
916     }
917
918     public static function minCount($array, $min, $message = '')
919     {
920         if (count($array) < $min) {
921             static::reportInvalidArgument(sprintf(
922                 $message ?: 'Expected an array to contain at least %2$d elements. Got: %d',
923                 count($array),
924                 $min
925             ));
926         }
927     }
928
929     public static function maxCount($array, $max, $message = '')
930     {
931         if (count($array) > $max) {
932             static::reportInvalidArgument(sprintf(
933                 $message ?: 'Expected an array to contain at most %2$d elements. Got: %d',
934                 count($array),
935                 $max
936             ));
937         }
938     }
939
940     public static function countBetween($array, $min, $max, $message = '')
941     {
942         $count = count($array);
943
944         if ($count < $min || $count > $max) {
945             static::reportInvalidArgument(sprintf(
946                 $message ?: 'Expected an array to contain between %2$d and %3$d elements. Got: %d',
947                 $count,
948                 $min,
949                 $max
950             ));
951         }
952     }
953
954     public static function uuid($value, $message = '')
955     {
956         $value = str_replace(array('urn:', 'uuid:', '{', '}'), '', $value);
957
958         // The nil UUID is special form of UUID that is specified to have all
959         // 128 bits set to zero.
960         if ('00000000-0000-0000-0000-000000000000' === $value) {
961             return;
962         }
963
964         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}$/', $value)) {
965             static::reportInvalidArgument(sprintf(
966                 $message ?: 'Value %s is not a valid UUID.',
967                 static::valueToString($value)
968             ));
969         }
970     }
971
972     public static function throws(Closure $expression, $class = 'Exception', $message = '')
973     {
974         static::string($class);
975
976         $actual = 'none';
977
978         try {
979             $expression();
980         } catch (Exception $e) {
981             $actual = get_class($e);
982             if ($e instanceof $class) {
983                 return;
984             }
985         } catch (Throwable $e) {
986             $actual = get_class($e);
987             if ($e instanceof $class) {
988                 return;
989             }
990         }
991
992         static::reportInvalidArgument($message ?: sprintf(
993             'Expected to throw "%s", got "%s"',
994             $class,
995             $actual
996         ));
997     }
998
999     public static function __callStatic($name, $arguments)
1000     {
1001         if ('nullOr' === substr($name, 0, 6)) {
1002             if (null !== $arguments[0]) {
1003                 $method = lcfirst(substr($name, 6));
1004                 call_user_func_array(array('static', $method), $arguments);
1005             }
1006
1007             return;
1008         }
1009
1010         if ('all' === substr($name, 0, 3)) {
1011             static::isIterable($arguments[0]);
1012
1013             $method = lcfirst(substr($name, 3));
1014             $args = $arguments;
1015
1016             foreach ($arguments[0] as $entry) {
1017                 $args[0] = $entry;
1018
1019                 call_user_func_array(array('static', $method), $args);
1020             }
1021
1022             return;
1023         }
1024
1025         throw new BadMethodCallException('No such method: '.$name);
1026     }
1027
1028     protected static function valueToString($value)
1029     {
1030         if (null === $value) {
1031             return 'null';
1032         }
1033
1034         if (true === $value) {
1035             return 'true';
1036         }
1037
1038         if (false === $value) {
1039             return 'false';
1040         }
1041
1042         if (is_array($value)) {
1043             return 'array';
1044         }
1045
1046         if (is_object($value)) {
1047             return get_class($value);
1048         }
1049
1050         if (is_resource($value)) {
1051             return 'resource';
1052         }
1053
1054         if (is_string($value)) {
1055             return '"'.$value.'"';
1056         }
1057
1058         return (string) $value;
1059     }
1060
1061     protected static function typeToString($value)
1062     {
1063         return is_object($value) ? get_class($value) : gettype($value);
1064     }
1065
1066     protected static function strlen($value)
1067     {
1068         if (!function_exists('mb_detect_encoding')) {
1069             return strlen($value);
1070         }
1071
1072         if (false === $encoding = mb_detect_encoding($value)) {
1073             return strlen($value);
1074         }
1075
1076         return mb_strwidth($value, $encoding);
1077     }
1078
1079     protected static function reportInvalidArgument($message)
1080     {
1081         throw new InvalidArgumentException($message);
1082     }
1083
1084     private function __construct()
1085     {
1086     }
1087 }