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