├── .gitignore ├── LICENSE ├── composer.json ├── composer.lock ├── phpunit.xml ├── readme.md ├── src └── Validation │ ├── Validation.php │ └── ValidationException.php └── tests └── Validation ├── CustomCaseValidation.php ├── CustomDemoValidation.php ├── CustomExampleValidation.php ├── CustomSampleValidation.php ├── CustomValidation.php ├── MyValidation.php ├── MyValidation2.php ├── MyValidation3.php └── ValidationTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | # ======os====== 2 | #.* 3 | *.DS_Store 4 | Thumbs.db 5 | 6 | # ======temp files====== 7 | *~ 8 | ~$*.xls* 9 | ~$*.doc* 10 | *.swp 11 | *.bak 12 | 13 | # ======git====== 14 | !.gitignore 15 | !.gitattributes 16 | !.gitkeep 17 | !.gitkeeper 18 | 19 | # ======apache====== 20 | !.htaccess 21 | 22 | # ============================== IDEs ============================== 23 | 24 | # ======eclipse====== 25 | !.project 26 | !.cproject 27 | 28 | # ======netbeans====== 29 | nbproject/ 30 | nbproject/private/ 31 | 32 | # ======phpstorm====== 33 | .idea/ 34 | 35 | # ======visual studio====== 36 | *.suo 37 | *.ncb 38 | *.opensdf 39 | *.filters 40 | *.sdf 41 | *.vspscc 42 | *.user 43 | ipch/ 44 | #Debug/ 45 | #Release/ 46 | 47 | # ======xcode====== 48 | *.mode1v3 49 | xcuserdata/ 50 | xcshareddata/ 51 | 52 | # ============================== package manage ============================== 53 | 54 | # ======nodejs====== 55 | # /node_modules 56 | npm-debug.log 57 | npm-debug.log.* 58 | 59 | # ======composer====== 60 | /vendor 61 | 62 | # ============================== frameworks ============================== 63 | 64 | # ======laravel====== 65 | # .env 66 | !.env.example 67 | 68 | # ======vue-webpack====== 69 | /dist/ 70 | test/unit/coverage 71 | test/e2e/reports 72 | selenium-debug.log 73 | 74 | # ============================== my project ============================== 75 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 photondragon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webgeeker/validation", 3 | "description": "Validation for WebGeeker", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "photondragon", 8 | "email": "photondragon@163.com" 9 | } 10 | ], 11 | "autoload": { 12 | "psr-4": { 13 | "WebGeeker\\Validation\\": "src/Validation/" 14 | } 15 | }, 16 | "autoload-dev": { 17 | "psr-4": { 18 | "WebGeeker\\ValidationTest\\": "tests/Validation/" 19 | } 20 | }, 21 | "minimum-stability": "stable", 22 | "require": { 23 | "php": ">=5.6" 24 | }, 25 | "require-dev": { 26 | "phpunit/phpunit": "^5.7", 27 | "ext-mbstring": "*" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "a5a957c1cedc9fd999020d44116f00da", 8 | "packages": [], 9 | "packages-dev": [ 10 | { 11 | "name": "doctrine/instantiator", 12 | "version": "1.0.5", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/doctrine/instantiator.git", 16 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", 21 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "php": ">=5.3,<8.0-DEV" 26 | }, 27 | "require-dev": { 28 | "athletic/athletic": "~0.1.8", 29 | "ext-pdo": "*", 30 | "ext-phar": "*", 31 | "phpunit/phpunit": "~4.0", 32 | "squizlabs/php_codesniffer": "~2.0" 33 | }, 34 | "type": "library", 35 | "extra": { 36 | "branch-alias": { 37 | "dev-master": "1.0.x-dev" 38 | } 39 | }, 40 | "autoload": { 41 | "psr-4": { 42 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 43 | } 44 | }, 45 | "notification-url": "https://packagist.org/downloads/", 46 | "license": [ 47 | "MIT" 48 | ], 49 | "authors": [ 50 | { 51 | "name": "Marco Pivetta", 52 | "email": "ocramius@gmail.com", 53 | "homepage": "http://ocramius.github.com/" 54 | } 55 | ], 56 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 57 | "homepage": "https://github.com/doctrine/instantiator", 58 | "keywords": [ 59 | "constructor", 60 | "instantiate" 61 | ], 62 | "time": "2015-06-14T21:17:01+00:00" 63 | }, 64 | { 65 | "name": "myclabs/deep-copy", 66 | "version": "1.7.0", 67 | "source": { 68 | "type": "git", 69 | "url": "https://github.com/myclabs/DeepCopy.git", 70 | "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" 71 | }, 72 | "dist": { 73 | "type": "zip", 74 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", 75 | "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", 76 | "shasum": "" 77 | }, 78 | "require": { 79 | "php": "^5.6 || ^7.0" 80 | }, 81 | "require-dev": { 82 | "doctrine/collections": "^1.0", 83 | "doctrine/common": "^2.6", 84 | "phpunit/phpunit": "^4.1" 85 | }, 86 | "type": "library", 87 | "autoload": { 88 | "psr-4": { 89 | "DeepCopy\\": "src/DeepCopy/" 90 | }, 91 | "files": [ 92 | "src/DeepCopy/deep_copy.php" 93 | ] 94 | }, 95 | "notification-url": "https://packagist.org/downloads/", 96 | "license": [ 97 | "MIT" 98 | ], 99 | "description": "Create deep copies (clones) of your objects", 100 | "keywords": [ 101 | "clone", 102 | "copy", 103 | "duplicate", 104 | "object", 105 | "object graph" 106 | ], 107 | "time": "2017-10-19T19:58:43+00:00" 108 | }, 109 | { 110 | "name": "phpdocumentor/reflection-common", 111 | "version": "1.0.1", 112 | "source": { 113 | "type": "git", 114 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git", 115 | "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" 116 | }, 117 | "dist": { 118 | "type": "zip", 119 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", 120 | "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", 121 | "shasum": "" 122 | }, 123 | "require": { 124 | "php": ">=5.5" 125 | }, 126 | "require-dev": { 127 | "phpunit/phpunit": "^4.6" 128 | }, 129 | "type": "library", 130 | "extra": { 131 | "branch-alias": { 132 | "dev-master": "1.0.x-dev" 133 | } 134 | }, 135 | "autoload": { 136 | "psr-4": { 137 | "phpDocumentor\\Reflection\\": [ 138 | "src" 139 | ] 140 | } 141 | }, 142 | "notification-url": "https://packagist.org/downloads/", 143 | "license": [ 144 | "MIT" 145 | ], 146 | "authors": [ 147 | { 148 | "name": "Jaap van Otterdijk", 149 | "email": "opensource@ijaap.nl" 150 | } 151 | ], 152 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure", 153 | "homepage": "http://www.phpdoc.org", 154 | "keywords": [ 155 | "FQSEN", 156 | "phpDocumentor", 157 | "phpdoc", 158 | "reflection", 159 | "static analysis" 160 | ], 161 | "time": "2017-09-11T18:02:19+00:00" 162 | }, 163 | { 164 | "name": "phpdocumentor/reflection-docblock", 165 | "version": "3.3.2", 166 | "source": { 167 | "type": "git", 168 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 169 | "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2" 170 | }, 171 | "dist": { 172 | "type": "zip", 173 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bf329f6c1aadea3299f08ee804682b7c45b326a2", 174 | "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2", 175 | "shasum": "" 176 | }, 177 | "require": { 178 | "php": "^5.6 || ^7.0", 179 | "phpdocumentor/reflection-common": "^1.0.0", 180 | "phpdocumentor/type-resolver": "^0.4.0", 181 | "webmozart/assert": "^1.0" 182 | }, 183 | "require-dev": { 184 | "mockery/mockery": "^0.9.4", 185 | "phpunit/phpunit": "^4.4" 186 | }, 187 | "type": "library", 188 | "autoload": { 189 | "psr-4": { 190 | "phpDocumentor\\Reflection\\": [ 191 | "src/" 192 | ] 193 | } 194 | }, 195 | "notification-url": "https://packagist.org/downloads/", 196 | "license": [ 197 | "MIT" 198 | ], 199 | "authors": [ 200 | { 201 | "name": "Mike van Riel", 202 | "email": "me@mikevanriel.com" 203 | } 204 | ], 205 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", 206 | "time": "2017-11-10T14:09:06+00:00" 207 | }, 208 | { 209 | "name": "phpdocumentor/type-resolver", 210 | "version": "0.4.0", 211 | "source": { 212 | "type": "git", 213 | "url": "https://github.com/phpDocumentor/TypeResolver.git", 214 | "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" 215 | }, 216 | "dist": { 217 | "type": "zip", 218 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", 219 | "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", 220 | "shasum": "" 221 | }, 222 | "require": { 223 | "php": "^5.5 || ^7.0", 224 | "phpdocumentor/reflection-common": "^1.0" 225 | }, 226 | "require-dev": { 227 | "mockery/mockery": "^0.9.4", 228 | "phpunit/phpunit": "^5.2||^4.8.24" 229 | }, 230 | "type": "library", 231 | "extra": { 232 | "branch-alias": { 233 | "dev-master": "1.0.x-dev" 234 | } 235 | }, 236 | "autoload": { 237 | "psr-4": { 238 | "phpDocumentor\\Reflection\\": [ 239 | "src/" 240 | ] 241 | } 242 | }, 243 | "notification-url": "https://packagist.org/downloads/", 244 | "license": [ 245 | "MIT" 246 | ], 247 | "authors": [ 248 | { 249 | "name": "Mike van Riel", 250 | "email": "me@mikevanriel.com" 251 | } 252 | ], 253 | "time": "2017-07-14T14:27:02+00:00" 254 | }, 255 | { 256 | "name": "phpspec/prophecy", 257 | "version": "1.8.0", 258 | "source": { 259 | "type": "git", 260 | "url": "https://github.com/phpspec/prophecy.git", 261 | "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" 262 | }, 263 | "dist": { 264 | "type": "zip", 265 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", 266 | "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", 267 | "shasum": "" 268 | }, 269 | "require": { 270 | "doctrine/instantiator": "^1.0.2", 271 | "php": "^5.3|^7.0", 272 | "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", 273 | "sebastian/comparator": "^1.1|^2.0|^3.0", 274 | "sebastian/recursion-context": "^1.0|^2.0|^3.0" 275 | }, 276 | "require-dev": { 277 | "phpspec/phpspec": "^2.5|^3.2", 278 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" 279 | }, 280 | "type": "library", 281 | "extra": { 282 | "branch-alias": { 283 | "dev-master": "1.8.x-dev" 284 | } 285 | }, 286 | "autoload": { 287 | "psr-0": { 288 | "Prophecy\\": "src/" 289 | } 290 | }, 291 | "notification-url": "https://packagist.org/downloads/", 292 | "license": [ 293 | "MIT" 294 | ], 295 | "authors": [ 296 | { 297 | "name": "Konstantin Kudryashov", 298 | "email": "ever.zet@gmail.com", 299 | "homepage": "http://everzet.com" 300 | }, 301 | { 302 | "name": "Marcello Duarte", 303 | "email": "marcello.duarte@gmail.com" 304 | } 305 | ], 306 | "description": "Highly opinionated mocking framework for PHP 5.3+", 307 | "homepage": "https://github.com/phpspec/prophecy", 308 | "keywords": [ 309 | "Double", 310 | "Dummy", 311 | "fake", 312 | "mock", 313 | "spy", 314 | "stub" 315 | ], 316 | "time": "2018-08-05T17:53:17+00:00" 317 | }, 318 | { 319 | "name": "phpunit/php-code-coverage", 320 | "version": "4.0.8", 321 | "source": { 322 | "type": "git", 323 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 324 | "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d" 325 | }, 326 | "dist": { 327 | "type": "zip", 328 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef7b2f56815df854e66ceaee8ebe9393ae36a40d", 329 | "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d", 330 | "shasum": "" 331 | }, 332 | "require": { 333 | "ext-dom": "*", 334 | "ext-xmlwriter": "*", 335 | "php": "^5.6 || ^7.0", 336 | "phpunit/php-file-iterator": "^1.3", 337 | "phpunit/php-text-template": "^1.2", 338 | "phpunit/php-token-stream": "^1.4.2 || ^2.0", 339 | "sebastian/code-unit-reverse-lookup": "^1.0", 340 | "sebastian/environment": "^1.3.2 || ^2.0", 341 | "sebastian/version": "^1.0 || ^2.0" 342 | }, 343 | "require-dev": { 344 | "ext-xdebug": "^2.1.4", 345 | "phpunit/phpunit": "^5.7" 346 | }, 347 | "suggest": { 348 | "ext-xdebug": "^2.5.1" 349 | }, 350 | "type": "library", 351 | "extra": { 352 | "branch-alias": { 353 | "dev-master": "4.0.x-dev" 354 | } 355 | }, 356 | "autoload": { 357 | "classmap": [ 358 | "src/" 359 | ] 360 | }, 361 | "notification-url": "https://packagist.org/downloads/", 362 | "license": [ 363 | "BSD-3-Clause" 364 | ], 365 | "authors": [ 366 | { 367 | "name": "Sebastian Bergmann", 368 | "email": "sb@sebastian-bergmann.de", 369 | "role": "lead" 370 | } 371 | ], 372 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 373 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 374 | "keywords": [ 375 | "coverage", 376 | "testing", 377 | "xunit" 378 | ], 379 | "time": "2017-04-02T07:44:40+00:00" 380 | }, 381 | { 382 | "name": "phpunit/php-file-iterator", 383 | "version": "1.4.5", 384 | "source": { 385 | "type": "git", 386 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 387 | "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" 388 | }, 389 | "dist": { 390 | "type": "zip", 391 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", 392 | "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", 393 | "shasum": "" 394 | }, 395 | "require": { 396 | "php": ">=5.3.3" 397 | }, 398 | "type": "library", 399 | "extra": { 400 | "branch-alias": { 401 | "dev-master": "1.4.x-dev" 402 | } 403 | }, 404 | "autoload": { 405 | "classmap": [ 406 | "src/" 407 | ] 408 | }, 409 | "notification-url": "https://packagist.org/downloads/", 410 | "license": [ 411 | "BSD-3-Clause" 412 | ], 413 | "authors": [ 414 | { 415 | "name": "Sebastian Bergmann", 416 | "email": "sb@sebastian-bergmann.de", 417 | "role": "lead" 418 | } 419 | ], 420 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 421 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 422 | "keywords": [ 423 | "filesystem", 424 | "iterator" 425 | ], 426 | "time": "2017-11-27T13:52:08+00:00" 427 | }, 428 | { 429 | "name": "phpunit/php-text-template", 430 | "version": "1.2.1", 431 | "source": { 432 | "type": "git", 433 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 434 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 435 | }, 436 | "dist": { 437 | "type": "zip", 438 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 439 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 440 | "shasum": "" 441 | }, 442 | "require": { 443 | "php": ">=5.3.3" 444 | }, 445 | "type": "library", 446 | "autoload": { 447 | "classmap": [ 448 | "src/" 449 | ] 450 | }, 451 | "notification-url": "https://packagist.org/downloads/", 452 | "license": [ 453 | "BSD-3-Clause" 454 | ], 455 | "authors": [ 456 | { 457 | "name": "Sebastian Bergmann", 458 | "email": "sebastian@phpunit.de", 459 | "role": "lead" 460 | } 461 | ], 462 | "description": "Simple template engine.", 463 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 464 | "keywords": [ 465 | "template" 466 | ], 467 | "time": "2015-06-21T13:50:34+00:00" 468 | }, 469 | { 470 | "name": "phpunit/php-timer", 471 | "version": "1.0.9", 472 | "source": { 473 | "type": "git", 474 | "url": "https://github.com/sebastianbergmann/php-timer.git", 475 | "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" 476 | }, 477 | "dist": { 478 | "type": "zip", 479 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", 480 | "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", 481 | "shasum": "" 482 | }, 483 | "require": { 484 | "php": "^5.3.3 || ^7.0" 485 | }, 486 | "require-dev": { 487 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" 488 | }, 489 | "type": "library", 490 | "extra": { 491 | "branch-alias": { 492 | "dev-master": "1.0-dev" 493 | } 494 | }, 495 | "autoload": { 496 | "classmap": [ 497 | "src/" 498 | ] 499 | }, 500 | "notification-url": "https://packagist.org/downloads/", 501 | "license": [ 502 | "BSD-3-Clause" 503 | ], 504 | "authors": [ 505 | { 506 | "name": "Sebastian Bergmann", 507 | "email": "sb@sebastian-bergmann.de", 508 | "role": "lead" 509 | } 510 | ], 511 | "description": "Utility class for timing", 512 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 513 | "keywords": [ 514 | "timer" 515 | ], 516 | "time": "2017-02-26T11:10:40+00:00" 517 | }, 518 | { 519 | "name": "phpunit/php-token-stream", 520 | "version": "1.4.12", 521 | "source": { 522 | "type": "git", 523 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 524 | "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16" 525 | }, 526 | "dist": { 527 | "type": "zip", 528 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/1ce90ba27c42e4e44e6d8458241466380b51fa16", 529 | "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16", 530 | "shasum": "" 531 | }, 532 | "require": { 533 | "ext-tokenizer": "*", 534 | "php": ">=5.3.3" 535 | }, 536 | "require-dev": { 537 | "phpunit/phpunit": "~4.2" 538 | }, 539 | "type": "library", 540 | "extra": { 541 | "branch-alias": { 542 | "dev-master": "1.4-dev" 543 | } 544 | }, 545 | "autoload": { 546 | "classmap": [ 547 | "src/" 548 | ] 549 | }, 550 | "notification-url": "https://packagist.org/downloads/", 551 | "license": [ 552 | "BSD-3-Clause" 553 | ], 554 | "authors": [ 555 | { 556 | "name": "Sebastian Bergmann", 557 | "email": "sebastian@phpunit.de" 558 | } 559 | ], 560 | "description": "Wrapper around PHP's tokenizer extension.", 561 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 562 | "keywords": [ 563 | "tokenizer" 564 | ], 565 | "time": "2017-12-04T08:55:13+00:00" 566 | }, 567 | { 568 | "name": "phpunit/phpunit", 569 | "version": "5.7.27", 570 | "source": { 571 | "type": "git", 572 | "url": "https://github.com/sebastianbergmann/phpunit.git", 573 | "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c" 574 | }, 575 | "dist": { 576 | "type": "zip", 577 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c", 578 | "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c", 579 | "shasum": "" 580 | }, 581 | "require": { 582 | "ext-dom": "*", 583 | "ext-json": "*", 584 | "ext-libxml": "*", 585 | "ext-mbstring": "*", 586 | "ext-xml": "*", 587 | "myclabs/deep-copy": "~1.3", 588 | "php": "^5.6 || ^7.0", 589 | "phpspec/prophecy": "^1.6.2", 590 | "phpunit/php-code-coverage": "^4.0.4", 591 | "phpunit/php-file-iterator": "~1.4", 592 | "phpunit/php-text-template": "~1.2", 593 | "phpunit/php-timer": "^1.0.6", 594 | "phpunit/phpunit-mock-objects": "^3.2", 595 | "sebastian/comparator": "^1.2.4", 596 | "sebastian/diff": "^1.4.3", 597 | "sebastian/environment": "^1.3.4 || ^2.0", 598 | "sebastian/exporter": "~2.0", 599 | "sebastian/global-state": "^1.1", 600 | "sebastian/object-enumerator": "~2.0", 601 | "sebastian/resource-operations": "~1.0", 602 | "sebastian/version": "^1.0.6|^2.0.1", 603 | "symfony/yaml": "~2.1|~3.0|~4.0" 604 | }, 605 | "conflict": { 606 | "phpdocumentor/reflection-docblock": "3.0.2" 607 | }, 608 | "require-dev": { 609 | "ext-pdo": "*" 610 | }, 611 | "suggest": { 612 | "ext-xdebug": "*", 613 | "phpunit/php-invoker": "~1.1" 614 | }, 615 | "bin": [ 616 | "phpunit" 617 | ], 618 | "type": "library", 619 | "extra": { 620 | "branch-alias": { 621 | "dev-master": "5.7.x-dev" 622 | } 623 | }, 624 | "autoload": { 625 | "classmap": [ 626 | "src/" 627 | ] 628 | }, 629 | "notification-url": "https://packagist.org/downloads/", 630 | "license": [ 631 | "BSD-3-Clause" 632 | ], 633 | "authors": [ 634 | { 635 | "name": "Sebastian Bergmann", 636 | "email": "sebastian@phpunit.de", 637 | "role": "lead" 638 | } 639 | ], 640 | "description": "The PHP Unit Testing framework.", 641 | "homepage": "https://phpunit.de/", 642 | "keywords": [ 643 | "phpunit", 644 | "testing", 645 | "xunit" 646 | ], 647 | "time": "2018-02-01T05:50:59+00:00" 648 | }, 649 | { 650 | "name": "phpunit/phpunit-mock-objects", 651 | "version": "3.4.4", 652 | "source": { 653 | "type": "git", 654 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 655 | "reference": "a23b761686d50a560cc56233b9ecf49597cc9118" 656 | }, 657 | "dist": { 658 | "type": "zip", 659 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/a23b761686d50a560cc56233b9ecf49597cc9118", 660 | "reference": "a23b761686d50a560cc56233b9ecf49597cc9118", 661 | "shasum": "" 662 | }, 663 | "require": { 664 | "doctrine/instantiator": "^1.0.2", 665 | "php": "^5.6 || ^7.0", 666 | "phpunit/php-text-template": "^1.2", 667 | "sebastian/exporter": "^1.2 || ^2.0" 668 | }, 669 | "conflict": { 670 | "phpunit/phpunit": "<5.4.0" 671 | }, 672 | "require-dev": { 673 | "phpunit/phpunit": "^5.4" 674 | }, 675 | "suggest": { 676 | "ext-soap": "*" 677 | }, 678 | "type": "library", 679 | "extra": { 680 | "branch-alias": { 681 | "dev-master": "3.2.x-dev" 682 | } 683 | }, 684 | "autoload": { 685 | "classmap": [ 686 | "src/" 687 | ] 688 | }, 689 | "notification-url": "https://packagist.org/downloads/", 690 | "license": [ 691 | "BSD-3-Clause" 692 | ], 693 | "authors": [ 694 | { 695 | "name": "Sebastian Bergmann", 696 | "email": "sb@sebastian-bergmann.de", 697 | "role": "lead" 698 | } 699 | ], 700 | "description": "Mock Object library for PHPUnit", 701 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 702 | "keywords": [ 703 | "mock", 704 | "xunit" 705 | ], 706 | "time": "2017-06-30T09:13:00+00:00" 707 | }, 708 | { 709 | "name": "sebastian/code-unit-reverse-lookup", 710 | "version": "1.0.1", 711 | "source": { 712 | "type": "git", 713 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 714 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" 715 | }, 716 | "dist": { 717 | "type": "zip", 718 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", 719 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", 720 | "shasum": "" 721 | }, 722 | "require": { 723 | "php": "^5.6 || ^7.0" 724 | }, 725 | "require-dev": { 726 | "phpunit/phpunit": "^5.7 || ^6.0" 727 | }, 728 | "type": "library", 729 | "extra": { 730 | "branch-alias": { 731 | "dev-master": "1.0.x-dev" 732 | } 733 | }, 734 | "autoload": { 735 | "classmap": [ 736 | "src/" 737 | ] 738 | }, 739 | "notification-url": "https://packagist.org/downloads/", 740 | "license": [ 741 | "BSD-3-Clause" 742 | ], 743 | "authors": [ 744 | { 745 | "name": "Sebastian Bergmann", 746 | "email": "sebastian@phpunit.de" 747 | } 748 | ], 749 | "description": "Looks up which function or method a line of code belongs to", 750 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 751 | "time": "2017-03-04T06:30:41+00:00" 752 | }, 753 | { 754 | "name": "sebastian/comparator", 755 | "version": "1.2.4", 756 | "source": { 757 | "type": "git", 758 | "url": "https://github.com/sebastianbergmann/comparator.git", 759 | "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" 760 | }, 761 | "dist": { 762 | "type": "zip", 763 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", 764 | "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", 765 | "shasum": "" 766 | }, 767 | "require": { 768 | "php": ">=5.3.3", 769 | "sebastian/diff": "~1.2", 770 | "sebastian/exporter": "~1.2 || ~2.0" 771 | }, 772 | "require-dev": { 773 | "phpunit/phpunit": "~4.4" 774 | }, 775 | "type": "library", 776 | "extra": { 777 | "branch-alias": { 778 | "dev-master": "1.2.x-dev" 779 | } 780 | }, 781 | "autoload": { 782 | "classmap": [ 783 | "src/" 784 | ] 785 | }, 786 | "notification-url": "https://packagist.org/downloads/", 787 | "license": [ 788 | "BSD-3-Clause" 789 | ], 790 | "authors": [ 791 | { 792 | "name": "Jeff Welch", 793 | "email": "whatthejeff@gmail.com" 794 | }, 795 | { 796 | "name": "Volker Dusch", 797 | "email": "github@wallbash.com" 798 | }, 799 | { 800 | "name": "Bernhard Schussek", 801 | "email": "bschussek@2bepublished.at" 802 | }, 803 | { 804 | "name": "Sebastian Bergmann", 805 | "email": "sebastian@phpunit.de" 806 | } 807 | ], 808 | "description": "Provides the functionality to compare PHP values for equality", 809 | "homepage": "http://www.github.com/sebastianbergmann/comparator", 810 | "keywords": [ 811 | "comparator", 812 | "compare", 813 | "equality" 814 | ], 815 | "time": "2017-01-29T09:50:25+00:00" 816 | }, 817 | { 818 | "name": "sebastian/diff", 819 | "version": "1.4.3", 820 | "source": { 821 | "type": "git", 822 | "url": "https://github.com/sebastianbergmann/diff.git", 823 | "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" 824 | }, 825 | "dist": { 826 | "type": "zip", 827 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", 828 | "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", 829 | "shasum": "" 830 | }, 831 | "require": { 832 | "php": "^5.3.3 || ^7.0" 833 | }, 834 | "require-dev": { 835 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" 836 | }, 837 | "type": "library", 838 | "extra": { 839 | "branch-alias": { 840 | "dev-master": "1.4-dev" 841 | } 842 | }, 843 | "autoload": { 844 | "classmap": [ 845 | "src/" 846 | ] 847 | }, 848 | "notification-url": "https://packagist.org/downloads/", 849 | "license": [ 850 | "BSD-3-Clause" 851 | ], 852 | "authors": [ 853 | { 854 | "name": "Kore Nordmann", 855 | "email": "mail@kore-nordmann.de" 856 | }, 857 | { 858 | "name": "Sebastian Bergmann", 859 | "email": "sebastian@phpunit.de" 860 | } 861 | ], 862 | "description": "Diff implementation", 863 | "homepage": "https://github.com/sebastianbergmann/diff", 864 | "keywords": [ 865 | "diff" 866 | ], 867 | "time": "2017-05-22T07:24:03+00:00" 868 | }, 869 | { 870 | "name": "sebastian/environment", 871 | "version": "2.0.0", 872 | "source": { 873 | "type": "git", 874 | "url": "https://github.com/sebastianbergmann/environment.git", 875 | "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac" 876 | }, 877 | "dist": { 878 | "type": "zip", 879 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac", 880 | "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac", 881 | "shasum": "" 882 | }, 883 | "require": { 884 | "php": "^5.6 || ^7.0" 885 | }, 886 | "require-dev": { 887 | "phpunit/phpunit": "^5.0" 888 | }, 889 | "type": "library", 890 | "extra": { 891 | "branch-alias": { 892 | "dev-master": "2.0.x-dev" 893 | } 894 | }, 895 | "autoload": { 896 | "classmap": [ 897 | "src/" 898 | ] 899 | }, 900 | "notification-url": "https://packagist.org/downloads/", 901 | "license": [ 902 | "BSD-3-Clause" 903 | ], 904 | "authors": [ 905 | { 906 | "name": "Sebastian Bergmann", 907 | "email": "sebastian@phpunit.de" 908 | } 909 | ], 910 | "description": "Provides functionality to handle HHVM/PHP environments", 911 | "homepage": "http://www.github.com/sebastianbergmann/environment", 912 | "keywords": [ 913 | "Xdebug", 914 | "environment", 915 | "hhvm" 916 | ], 917 | "time": "2016-11-26T07:53:53+00:00" 918 | }, 919 | { 920 | "name": "sebastian/exporter", 921 | "version": "2.0.0", 922 | "source": { 923 | "type": "git", 924 | "url": "https://github.com/sebastianbergmann/exporter.git", 925 | "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4" 926 | }, 927 | "dist": { 928 | "type": "zip", 929 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", 930 | "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", 931 | "shasum": "" 932 | }, 933 | "require": { 934 | "php": ">=5.3.3", 935 | "sebastian/recursion-context": "~2.0" 936 | }, 937 | "require-dev": { 938 | "ext-mbstring": "*", 939 | "phpunit/phpunit": "~4.4" 940 | }, 941 | "type": "library", 942 | "extra": { 943 | "branch-alias": { 944 | "dev-master": "2.0.x-dev" 945 | } 946 | }, 947 | "autoload": { 948 | "classmap": [ 949 | "src/" 950 | ] 951 | }, 952 | "notification-url": "https://packagist.org/downloads/", 953 | "license": [ 954 | "BSD-3-Clause" 955 | ], 956 | "authors": [ 957 | { 958 | "name": "Jeff Welch", 959 | "email": "whatthejeff@gmail.com" 960 | }, 961 | { 962 | "name": "Volker Dusch", 963 | "email": "github@wallbash.com" 964 | }, 965 | { 966 | "name": "Bernhard Schussek", 967 | "email": "bschussek@2bepublished.at" 968 | }, 969 | { 970 | "name": "Sebastian Bergmann", 971 | "email": "sebastian@phpunit.de" 972 | }, 973 | { 974 | "name": "Adam Harvey", 975 | "email": "aharvey@php.net" 976 | } 977 | ], 978 | "description": "Provides the functionality to export PHP variables for visualization", 979 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 980 | "keywords": [ 981 | "export", 982 | "exporter" 983 | ], 984 | "time": "2016-11-19T08:54:04+00:00" 985 | }, 986 | { 987 | "name": "sebastian/global-state", 988 | "version": "1.1.1", 989 | "source": { 990 | "type": "git", 991 | "url": "https://github.com/sebastianbergmann/global-state.git", 992 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" 993 | }, 994 | "dist": { 995 | "type": "zip", 996 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", 997 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", 998 | "shasum": "" 999 | }, 1000 | "require": { 1001 | "php": ">=5.3.3" 1002 | }, 1003 | "require-dev": { 1004 | "phpunit/phpunit": "~4.2" 1005 | }, 1006 | "suggest": { 1007 | "ext-uopz": "*" 1008 | }, 1009 | "type": "library", 1010 | "extra": { 1011 | "branch-alias": { 1012 | "dev-master": "1.0-dev" 1013 | } 1014 | }, 1015 | "autoload": { 1016 | "classmap": [ 1017 | "src/" 1018 | ] 1019 | }, 1020 | "notification-url": "https://packagist.org/downloads/", 1021 | "license": [ 1022 | "BSD-3-Clause" 1023 | ], 1024 | "authors": [ 1025 | { 1026 | "name": "Sebastian Bergmann", 1027 | "email": "sebastian@phpunit.de" 1028 | } 1029 | ], 1030 | "description": "Snapshotting of global state", 1031 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1032 | "keywords": [ 1033 | "global state" 1034 | ], 1035 | "time": "2015-10-12T03:26:01+00:00" 1036 | }, 1037 | { 1038 | "name": "sebastian/object-enumerator", 1039 | "version": "2.0.1", 1040 | "source": { 1041 | "type": "git", 1042 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1043 | "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7" 1044 | }, 1045 | "dist": { 1046 | "type": "zip", 1047 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7", 1048 | "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7", 1049 | "shasum": "" 1050 | }, 1051 | "require": { 1052 | "php": ">=5.6", 1053 | "sebastian/recursion-context": "~2.0" 1054 | }, 1055 | "require-dev": { 1056 | "phpunit/phpunit": "~5" 1057 | }, 1058 | "type": "library", 1059 | "extra": { 1060 | "branch-alias": { 1061 | "dev-master": "2.0.x-dev" 1062 | } 1063 | }, 1064 | "autoload": { 1065 | "classmap": [ 1066 | "src/" 1067 | ] 1068 | }, 1069 | "notification-url": "https://packagist.org/downloads/", 1070 | "license": [ 1071 | "BSD-3-Clause" 1072 | ], 1073 | "authors": [ 1074 | { 1075 | "name": "Sebastian Bergmann", 1076 | "email": "sebastian@phpunit.de" 1077 | } 1078 | ], 1079 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 1080 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 1081 | "time": "2017-02-18T15:18:39+00:00" 1082 | }, 1083 | { 1084 | "name": "sebastian/recursion-context", 1085 | "version": "2.0.0", 1086 | "source": { 1087 | "type": "git", 1088 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1089 | "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a" 1090 | }, 1091 | "dist": { 1092 | "type": "zip", 1093 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2c3ba150cbec723aa057506e73a8d33bdb286c9a", 1094 | "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a", 1095 | "shasum": "" 1096 | }, 1097 | "require": { 1098 | "php": ">=5.3.3" 1099 | }, 1100 | "require-dev": { 1101 | "phpunit/phpunit": "~4.4" 1102 | }, 1103 | "type": "library", 1104 | "extra": { 1105 | "branch-alias": { 1106 | "dev-master": "2.0.x-dev" 1107 | } 1108 | }, 1109 | "autoload": { 1110 | "classmap": [ 1111 | "src/" 1112 | ] 1113 | }, 1114 | "notification-url": "https://packagist.org/downloads/", 1115 | "license": [ 1116 | "BSD-3-Clause" 1117 | ], 1118 | "authors": [ 1119 | { 1120 | "name": "Jeff Welch", 1121 | "email": "whatthejeff@gmail.com" 1122 | }, 1123 | { 1124 | "name": "Sebastian Bergmann", 1125 | "email": "sebastian@phpunit.de" 1126 | }, 1127 | { 1128 | "name": "Adam Harvey", 1129 | "email": "aharvey@php.net" 1130 | } 1131 | ], 1132 | "description": "Provides functionality to recursively process PHP variables", 1133 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1134 | "time": "2016-11-19T07:33:16+00:00" 1135 | }, 1136 | { 1137 | "name": "sebastian/resource-operations", 1138 | "version": "1.0.0", 1139 | "source": { 1140 | "type": "git", 1141 | "url": "https://github.com/sebastianbergmann/resource-operations.git", 1142 | "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" 1143 | }, 1144 | "dist": { 1145 | "type": "zip", 1146 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", 1147 | "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", 1148 | "shasum": "" 1149 | }, 1150 | "require": { 1151 | "php": ">=5.6.0" 1152 | }, 1153 | "type": "library", 1154 | "extra": { 1155 | "branch-alias": { 1156 | "dev-master": "1.0.x-dev" 1157 | } 1158 | }, 1159 | "autoload": { 1160 | "classmap": [ 1161 | "src/" 1162 | ] 1163 | }, 1164 | "notification-url": "https://packagist.org/downloads/", 1165 | "license": [ 1166 | "BSD-3-Clause" 1167 | ], 1168 | "authors": [ 1169 | { 1170 | "name": "Sebastian Bergmann", 1171 | "email": "sebastian@phpunit.de" 1172 | } 1173 | ], 1174 | "description": "Provides a list of PHP built-in functions that operate on resources", 1175 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations", 1176 | "time": "2015-07-28T20:34:47+00:00" 1177 | }, 1178 | { 1179 | "name": "sebastian/version", 1180 | "version": "2.0.1", 1181 | "source": { 1182 | "type": "git", 1183 | "url": "https://github.com/sebastianbergmann/version.git", 1184 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" 1185 | }, 1186 | "dist": { 1187 | "type": "zip", 1188 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", 1189 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", 1190 | "shasum": "" 1191 | }, 1192 | "require": { 1193 | "php": ">=5.6" 1194 | }, 1195 | "type": "library", 1196 | "extra": { 1197 | "branch-alias": { 1198 | "dev-master": "2.0.x-dev" 1199 | } 1200 | }, 1201 | "autoload": { 1202 | "classmap": [ 1203 | "src/" 1204 | ] 1205 | }, 1206 | "notification-url": "https://packagist.org/downloads/", 1207 | "license": [ 1208 | "BSD-3-Clause" 1209 | ], 1210 | "authors": [ 1211 | { 1212 | "name": "Sebastian Bergmann", 1213 | "email": "sebastian@phpunit.de", 1214 | "role": "lead" 1215 | } 1216 | ], 1217 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1218 | "homepage": "https://github.com/sebastianbergmann/version", 1219 | "time": "2016-10-03T07:35:21+00:00" 1220 | }, 1221 | { 1222 | "name": "symfony/polyfill-ctype", 1223 | "version": "v1.9.0", 1224 | "source": { 1225 | "type": "git", 1226 | "url": "https://github.com/symfony/polyfill-ctype.git", 1227 | "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" 1228 | }, 1229 | "dist": { 1230 | "type": "zip", 1231 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", 1232 | "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", 1233 | "shasum": "" 1234 | }, 1235 | "require": { 1236 | "php": ">=5.3.3" 1237 | }, 1238 | "suggest": { 1239 | "ext-ctype": "For best performance" 1240 | }, 1241 | "type": "library", 1242 | "extra": { 1243 | "branch-alias": { 1244 | "dev-master": "1.9-dev" 1245 | } 1246 | }, 1247 | "autoload": { 1248 | "psr-4": { 1249 | "Symfony\\Polyfill\\Ctype\\": "" 1250 | }, 1251 | "files": [ 1252 | "bootstrap.php" 1253 | ] 1254 | }, 1255 | "notification-url": "https://packagist.org/downloads/", 1256 | "license": [ 1257 | "MIT" 1258 | ], 1259 | "authors": [ 1260 | { 1261 | "name": "Symfony Community", 1262 | "homepage": "https://symfony.com/contributors" 1263 | }, 1264 | { 1265 | "name": "Gert de Pagter", 1266 | "email": "BackEndTea@gmail.com" 1267 | } 1268 | ], 1269 | "description": "Symfony polyfill for ctype functions", 1270 | "homepage": "https://symfony.com", 1271 | "keywords": [ 1272 | "compatibility", 1273 | "ctype", 1274 | "polyfill", 1275 | "portable" 1276 | ], 1277 | "time": "2018-08-06T14:22:27+00:00" 1278 | }, 1279 | { 1280 | "name": "symfony/yaml", 1281 | "version": "v3.4.15", 1282 | "source": { 1283 | "type": "git", 1284 | "url": "https://github.com/symfony/yaml.git", 1285 | "reference": "c2f4812ead9f847cb69e90917ca7502e6892d6b8" 1286 | }, 1287 | "dist": { 1288 | "type": "zip", 1289 | "url": "https://api.github.com/repos/symfony/yaml/zipball/c2f4812ead9f847cb69e90917ca7502e6892d6b8", 1290 | "reference": "c2f4812ead9f847cb69e90917ca7502e6892d6b8", 1291 | "shasum": "" 1292 | }, 1293 | "require": { 1294 | "php": "^5.5.9|>=7.0.8", 1295 | "symfony/polyfill-ctype": "~1.8" 1296 | }, 1297 | "conflict": { 1298 | "symfony/console": "<3.4" 1299 | }, 1300 | "require-dev": { 1301 | "symfony/console": "~3.4|~4.0" 1302 | }, 1303 | "suggest": { 1304 | "symfony/console": "For validating YAML files using the lint command" 1305 | }, 1306 | "type": "library", 1307 | "extra": { 1308 | "branch-alias": { 1309 | "dev-master": "3.4-dev" 1310 | } 1311 | }, 1312 | "autoload": { 1313 | "psr-4": { 1314 | "Symfony\\Component\\Yaml\\": "" 1315 | }, 1316 | "exclude-from-classmap": [ 1317 | "/Tests/" 1318 | ] 1319 | }, 1320 | "notification-url": "https://packagist.org/downloads/", 1321 | "license": [ 1322 | "MIT" 1323 | ], 1324 | "authors": [ 1325 | { 1326 | "name": "Fabien Potencier", 1327 | "email": "fabien@symfony.com" 1328 | }, 1329 | { 1330 | "name": "Symfony Community", 1331 | "homepage": "https://symfony.com/contributors" 1332 | } 1333 | ], 1334 | "description": "Symfony Yaml Component", 1335 | "homepage": "https://symfony.com", 1336 | "time": "2018-08-10T07:34:36+00:00" 1337 | }, 1338 | { 1339 | "name": "webmozart/assert", 1340 | "version": "1.3.0", 1341 | "source": { 1342 | "type": "git", 1343 | "url": "https://github.com/webmozart/assert.git", 1344 | "reference": "0df1908962e7a3071564e857d86874dad1ef204a" 1345 | }, 1346 | "dist": { 1347 | "type": "zip", 1348 | "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", 1349 | "reference": "0df1908962e7a3071564e857d86874dad1ef204a", 1350 | "shasum": "" 1351 | }, 1352 | "require": { 1353 | "php": "^5.3.3 || ^7.0" 1354 | }, 1355 | "require-dev": { 1356 | "phpunit/phpunit": "^4.6", 1357 | "sebastian/version": "^1.0.1" 1358 | }, 1359 | "type": "library", 1360 | "extra": { 1361 | "branch-alias": { 1362 | "dev-master": "1.3-dev" 1363 | } 1364 | }, 1365 | "autoload": { 1366 | "psr-4": { 1367 | "Webmozart\\Assert\\": "src/" 1368 | } 1369 | }, 1370 | "notification-url": "https://packagist.org/downloads/", 1371 | "license": [ 1372 | "MIT" 1373 | ], 1374 | "authors": [ 1375 | { 1376 | "name": "Bernhard Schussek", 1377 | "email": "bschussek@gmail.com" 1378 | } 1379 | ], 1380 | "description": "Assertions to validate method input/output with nice error messages.", 1381 | "keywords": [ 1382 | "assert", 1383 | "check", 1384 | "validate" 1385 | ], 1386 | "time": "2018-01-29T19:49:41+00:00" 1387 | } 1388 | ], 1389 | "aliases": [], 1390 | "minimum-stability": "stable", 1391 | "stability-flags": [], 1392 | "prefer-stable": false, 1393 | "prefer-lowest": false, 1394 | "platform": { 1395 | "php": ">=5.6" 1396 | }, 1397 | "platform-dev": { 1398 | "ext-mbstring": "*" 1399 | } 1400 | } 1401 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ./tests/Validation 5 | 6 | 7 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # WebGeeker-Validation: 一个强大的 PHP 参数验证器 2 | 3 | 用于对API接口的请求参数进行合法性检查。 4 | 5 | 在实现服务端的API接口时,对于每一个接口的每一个参数,都应该检测其取值是否合法,以免错误的数据输入到系统中。这个工作可以说是费时费力,但又不得不做。而且PHP本身是弱类型语言,不但要验证取值,还要验证数据的类型是否符合,这就更复杂了。 6 | 7 | 本工具就是针对这个工作而设计的,能够有效地减少编码量,代码可读性好。 8 | 9 | 看看下面这段代码,可以对用法有个大概印象,应该不难看懂: 10 | ```php 11 | $params = $request->query(); // 获取GET参数 12 | 13 | // 验证(如果验证不通过,会抛出异常) 14 | Validation::validate($params, [ 15 | "offset" => "IntGe:0", // 参数"offset"必须是大于等于0的整数 16 | "count" => "Required|IntGeLe:1,200", // 参数"count"是必需的且取值在 1 - 200 之间 17 | ]); 18 | ``` 19 | 20 | 支持多种数据类型的校验:整型、浮点型、bool型、字符串、数组、对象、文件、日期时间,能够验证嵌套的数据结构中的参数,还支持带条件判断的验证。 21 | 22 | - 目录 23 | * [1 简介](#1-%E7%AE%80%E4%BB%8B) 24 | + [1.1 为什么要写这样一个工具?](#11-%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E5%86%99%E8%BF%99%E6%A0%B7%E4%B8%80%E4%B8%AA%E5%B7%A5%E5%85%B7) 25 | + [1.2 特点](#12-%E7%89%B9%E7%82%B9) 26 | + [1.3 一个简单示例](#13-%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%A4%BA%E4%BE%8B) 27 | * [2 安装](#2-%E5%AE%89%E8%A3%85) 28 | * [3 快速上手](#3-%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B) 29 | + [3.1 一个完整的示例(不使用任何框架)](#31-%E4%B8%80%E4%B8%AA%E5%AE%8C%E6%95%B4%E7%9A%84%E7%A4%BA%E4%BE%8B%E4%B8%8D%E4%BD%BF%E7%94%A8%E4%BB%BB%E4%BD%95%E6%A1%86%E6%9E%B6) 30 | + [3.2 验证不通过的错误处理](#32-%E9%AA%8C%E8%AF%81%E4%B8%8D%E9%80%9A%E8%BF%87%E7%9A%84%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86) 31 | + [3.3 在第三方框架中的用法](#33-%E5%9C%A8%E7%AC%AC%E4%B8%89%E6%96%B9%E6%A1%86%E6%9E%B6%E4%B8%AD%E7%9A%84%E7%94%A8%E6%B3%95) 32 | * [4 详细使用方法](#4-%E8%AF%A6%E7%BB%86%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95) 33 | + [4.1 验证整型参数](#41-%E9%AA%8C%E8%AF%81%E6%95%B4%E5%9E%8B%E5%8F%82%E6%95%B0) 34 | + [4.2 验证浮点型参数](#42-%E9%AA%8C%E8%AF%81%E6%B5%AE%E7%82%B9%E5%9E%8B%E5%8F%82%E6%95%B0) 35 | + [4.3 验证bool型参数](#43-%E9%AA%8C%E8%AF%81bool%E5%9E%8B%E5%8F%82%E6%95%B0) 36 | + [4.4 验证字符串型参数](#44-%E9%AA%8C%E8%AF%81%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%9E%8B%E5%8F%82%E6%95%B0) 37 | + [4.5 验证数组型、对象型、文件型、日期时间型参数](#45-%E9%AA%8C%E8%AF%81%E6%95%B0%E7%BB%84%E5%9E%8B%E5%AF%B9%E8%B1%A1%E5%9E%8B%E6%96%87%E4%BB%B6%E5%9E%8B%E6%97%A5%E6%9C%9F%E6%97%B6%E9%97%B4%E5%9E%8B%E5%8F%82%E6%95%B0) 38 | + [4.6 验证器串联(与)](#46-%E9%AA%8C%E8%AF%81%E5%99%A8%E4%B8%B2%E8%81%94%E4%B8%8E) 39 | + [4.7 Required 验证器](#47-required-%E9%AA%8C%E8%AF%81%E5%99%A8) 40 | + [4.8 忽略所有 Required 验证器](#48-%E5%BF%BD%E7%95%A5%E6%89%80%E6%9C%89-required-%E9%AA%8C%E8%AF%81%E5%99%A8) 41 | + [4.9 嵌套参数的验证](#49-%E5%B5%8C%E5%A5%97%E5%8F%82%E6%95%B0%E7%9A%84%E9%AA%8C%E8%AF%81) 42 | + [4.10 条件判断型验证器](#410-%E6%9D%A1%E4%BB%B6%E5%88%A4%E6%96%AD%E5%9E%8B%E9%AA%8C%E8%AF%81%E5%99%A8) 43 | + [4.11 验证规则并联(或)](#411-%E9%AA%8C%E8%AF%81%E8%A7%84%E5%88%99%E5%B9%B6%E8%81%94%E6%88%96) 44 | + [4.12 关于特殊值`null`, `""`,`0`,`false`的问题](#412-%E5%85%B3%E4%BA%8E%E7%89%B9%E6%AE%8A%E5%80%BCnull-0false%E7%9A%84%E9%97%AE%E9%A2%98) 45 | + [4.13 关于基本数据类型与字符串的关系](#413-%E5%85%B3%E4%BA%8E%E5%9F%BA%E6%9C%AC%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E4%B8%8E%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E5%85%B3%E7%B3%BB) 46 | + [4.14 自定义错误信息输出文本](#414-%E8%87%AA%E5%AE%9A%E4%B9%89%E9%94%99%E8%AF%AF%E4%BF%A1%E6%81%AF%E8%BE%93%E5%87%BA%E6%96%87%E6%9C%AC) 47 | + [4.15 国际化](#415-%E5%9B%BD%E9%99%85%E5%8C%96) 48 | + [4.16 国际化(0.4版之前)](#416-%E5%9B%BD%E9%99%85%E5%8C%9604%E7%89%88%E4%B9%8B%E5%89%8D) 49 | + [4.17 自定义验证器](#417-%E8%87%AA%E5%AE%9A%E4%B9%89%E9%AA%8C%E8%AF%81%E5%99%A8) 50 | * [A 附录 - 验证器列表](#a-%E9%99%84%E5%BD%95---%E9%AA%8C%E8%AF%81%E5%99%A8%E5%88%97%E8%A1%A8) 51 | + [A.1 整型](#a1-%E6%95%B4%E5%9E%8B) 52 | + [A.2 浮点型](#a2-%E6%B5%AE%E7%82%B9%E5%9E%8B) 53 | + [A.3 bool型](#a3-bool%E5%9E%8B) 54 | + [A.4 字符串型](#a4-%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%9E%8B) 55 | + [A.5 数组型](#a5-%E6%95%B0%E7%BB%84%E5%9E%8B) 56 | + [A.6 对象型](#a6-%E5%AF%B9%E8%B1%A1%E5%9E%8B) 57 | + [A.7 文件型](#a7-%E6%96%87%E4%BB%B6%E5%9E%8B) 58 | + [A.8 日期和时间型](#a8-%E6%97%A5%E6%9C%9F%E5%92%8C%E6%97%B6%E9%97%B4%E5%9E%8B) 59 | + [A.9 条件判断型](#a9-%E6%9D%A1%E4%BB%B6%E5%88%A4%E6%96%AD%E5%9E%8B) 60 | + [A.10 其它验证器](#a10-%E5%85%B6%E5%AE%83%E9%AA%8C%E8%AF%81%E5%99%A8) 61 | 62 | ## 1 简介 63 | 64 | ### 1.1 为什么要写这样一个工具? 65 | 66 | 我在使用Laravel框架的时候,Laravel提供了一个参数验证工具,不过用起来不怎么顺畅: 67 | * 每一个验证都写一个验证类(继承XXX),这样太麻烦,而且系统中会多出许多许多的类;如果这些类在多处被复用,或者为了“更加”复用(减少重复代码),再在这些类之间搞出很多的继承关系,那么这些类的维护本身就是一个大问题; 68 | * 验证器有“一词多义”的问题。比如它有一个`size`验证器,它同时支持验证字符串、整型、文件等多种类型的参数,针对不同数据类型`size`的含义不一样。这就好比你去背英语单词,有那么一些英语单词,它有很多很多意思,不同的语境下有不同的含义。比如"present"这个单词,它既有“呈现”、“出席”的意思,也有“礼物”的意思。这种一词多义的单词最让人头疼了,搞不清它到底什么意思,而且记不住啊。 69 | 70 | 为了解决这些问题,所以才写了这么一个工具。 71 | 72 | ### 1.2 特点 73 | 1. 每个功能特性都有单元测试(共有 44 tests, 700+ assertions) 74 | 1. 支持无限嵌套的数据结构的验证(参考 1.3 节的例子) 75 | 1. 支持条件验证,根据参数取值不同,应用不同的验证规则(参考 1.3 节的例子) 76 | 1. 支持正则表达式验证 77 | 1. 简洁,验证逻辑一目了然 78 | 1. 轻量,不需要定义和维护各种验证classes 79 | 1. 验证器语义明确,没有“一词多义”的问题 80 | 1. 易学易记。比如整型验证器都是以"Int"开头,浮点型验证器都是以"Float"开头,等等。唯一不符合这一规则的是字符串型验证器,它们一部分以"Str"开头的,但也有一部分不以"Str"开头,比如`Regexp`, `Ip`, `Email`, `Url`等。 81 | 1. 不绑定任何一个框架,无任何依赖。你可以在任何一个框架中使用这个工具,就算你不使用框架,也可以使用本工具。 82 | 1. 支持**自定义验证器**,可以实现各种自定义验证功能 83 | 84 | ### 1.3 一个简单示例 85 | 86 | 下面这个示例展示了一个查询获取用户投诉列表的Request参数的验证(注意其中的*条件验证*和针对*嵌套数据结构的验证*): 87 | 88 | ```php 89 | //验证规则 90 | $validations = [ 91 | "offset" => "IntGe:0", // 参数offset应该大于等于0 92 | "count" => "Required|IntGeLe:1,200", // 参数count是必需的且大于等于1小于等于200 93 | "type" => "IntIn:1,2", // 参数type可取值为: 1, 2 94 | "state" => [ 95 | 'IfIntEq:type,1|IntEq:0', // 如果type==1(批评建议),那么参数state只能是0 96 | 'IfIntEq:type,2|IntIn:0,1,2', // 如果type==2(用户投诉),那么参数state可取值为: 1, 2, 3 97 | ], 98 | "search.keyword" => "StrLenGeLe:1,100", // search.keyword 应该是一个长度在[1, 100]之间的字符串 99 | "search.start_time" => "Date", // search.start_time 应该是一个包含合法日期的字符串 100 | "search.end_time" => "Date", // search.end_time 应该是一个包含合法日期的字符串 101 | ]; 102 | 103 | // 待验证参数 104 | $params = [ 105 | "offset" => 0, // 从第0条记录开始 106 | "count" => 10, // 最多返回10条记录 107 | "type" => 2, // 1-批评建议, 2-用户投诉 108 | "state" => 0, // 0-待处理, 1-处理中, 2-已处理 109 | "search" => [ // 搜索条件 110 | "keyword" => '硬件故障', // 关键字 111 | "start_time" => "2018-01-01", // 起始日期 112 | "end_time" => "2018-01-31", // 结束日期 113 | ], 114 | ]; 115 | 116 | // 验证(如果验证不通过,会抛出异常) 117 | Validation::validate($params, $validations); 118 | ``` 119 | 120 | ## 2 安装 121 | 122 | 通过Composer安装 123 | ``` 124 | composer require webgeeker/validation:^0.5.2 125 | ``` 126 | 127 | ## 3 快速上手 128 | 129 | ### 3.1 一个完整的示例(不使用任何框架) 130 | 131 | 这个例子直接验证`$_POST`(POST表单)中的参数,展示了最基本的用法 132 | 133 | ```php 134 | "IntGe:0", // 参数offset应该大于等于0 142 | "count" => "Required|IntGeLe:1,200", // 参数count是必需的且大于等于1小于等于200 143 | ]); 144 | } catch (\Exception $e) { 145 | echo $e->getMessage(); 146 | } 147 | ``` 148 | *注意*:验证不通过会抛出异常,该异常中包含有错误描述信息 149 | 150 | ### 3.2 验证不通过的错误处理 151 | 152 | 如果验证不通过,`Validation::validate(...)`方法会抛出异常,建议在框架层面统一捕获这些异常,提取错误描述信息并返回给客户端。 153 | 154 | ### 3.3 在第三方框架中的用法 155 | 156 | 第三方框架一般会提供Request对象,可以取到GET, POST参数(以Laravel为例) 157 | 158 | ```php 159 | //$params = $request->query(); // 获取GET参数 160 | $params = $request->request->all(); // 获取POST参数 161 | 162 | // 验证(如果验证不通过,会抛出异常) 163 | Validation::validate($params, [ 164 | // 此处省略验证规则 165 | ]); 166 | ``` 167 | 168 | ## 4 详细使用方法 169 | 170 | ### 4.1 验证整型参数 171 | 172 | 整型验证器全部以"Int"开头,用于验证整型数值(如`123`)或整型字符串(如`"123"`)。其它数据类型均不匹配。 173 | 174 | ```php 175 | "size" => "IntGeLe:1,100" 176 | ``` 177 | 这条验证要求参数"size"是整数,并且大于等于1,小于等于100。 178 | 179 | 完整的整型验证器的列表参考附录 A.1 。 180 | 181 | ### 4.2 验证浮点型参数 182 | 183 | 浮点型验证器全部以"Float"开头,用于验证浮点型数值(如`1.0`)、浮点型字符串(如`"1.0"`)、整型数值(如`123`)或整型字符串(如`"123"`)。其它数据类型均不匹配。 184 | 185 | ```php 186 | "height" => "FloatGeLe:0.0,100.0" 187 | ``` 188 | 这条验证要求参数"height"是浮点数,并且大于等于0,小于等于100.0。 189 | 190 | 完整的浮点型验证器的列表参考附录 A.2 。 191 | 192 | ### 4.3 验证bool型参数 193 | 194 | bool型验证器: 195 | * Bool: 合法的取值为: `true`, `false`, `"true"`, `"false"`(字符串忽略大小写)。 196 | * BoolTrue: 合法的取值为: `true`, `"true"`(字符串忽略大小写)。 197 | * BoolFalse: 合法的取值为: `false`, `"false"`(字符串忽略大小写)。 198 | * BoolSmart: 合法的取值为: `true`, `false`, `"true"`, `"false"`, `1`, `0`, `"1"`, `"0"`, `"yes"`, `"no"`, `"y"`, `"n"`(字符串忽略大小写) 199 | * BoolSmartTrue: 合法的取值为: `true`, `"true"`, `1`, `"1"`, `"yes"`, `"y"`(字符串忽略大小写) 200 | * BoolSmartFalse: 合法的取值为: `false`, `"false"`, `0`, `"0"`, `"no"`, `"n"`(字符串忽略大小写) 201 | 202 | 例 203 | ```php 204 | "accept" => "BoolSmart" 205 | ``` 206 | 207 | 完整的bool型验证器的列表参考附录 A.3 。 208 | 209 | ### 4.4 验证字符串型参数 210 | 211 | 字符串型验证器不全以"Str"开头。只接收字符串型数据,其它数据类型均不匹配。 212 | 213 | 例1: 214 | ```php 215 | "name" => "StrLenGeLe:2,20" 216 | ``` 217 | 这条验证要求参数"name"是字符串,长度在2-20之间(字符串长度是用`mb_strlen()`来计算的)。 218 | 219 | 例2: 220 | ```php 221 | "comment" => "ByteLenLe:1048576" 222 | ``` 223 | 这条验证要求参数"comment"是字符串,字节长度不超过1048576(字节长度是用`strlen()`来计算的)。 224 | 225 | 例3: 226 | ```php 227 | "email" => "Email" 228 | ``` 229 | 这条验证要求参数"email"是必须是合法的电子邮件地址。 230 | 231 | 例4(正则表达式验证): 232 | ```php 233 | "phone" => "Regexp:/^1(3[0-9]|4[579]|5[0-35-9]|7[0135678]|8[0-9]|66|9[89])\d{8}$/" 234 | ``` 235 | 这条验证要求参数"phone"是合法的手机号。 236 | 237 | 关于正则表达式中的哪些特殊字符需要转义的问题,只需要用 `preg_match()` 函数验证好,如: 238 | ``` 239 | preg_match('/^string$/', $string); 240 | ``` 241 | 然后把两个`'/'`号及其中间的部分拷贝出来,放在`Regexp:`后面即可,不需要再做额外的转义,即使正则中有`'|'`这种特殊符号,也不需要再转义。 242 | 243 | 关于正则匹配中文的问题 244 | 用户 preq_match() 匹配中文的代码如下: 245 | ``` 246 | $matched = preg_match("/^[\x{4e00}-\x{9fa5}]+$/u", "你好"); 247 | ``` 248 | 注意正则表达式结尾需要加一个'u',意思是开启UTF8模式。 249 | 250 | 用我们的验证库代码应该这样写: 251 | ``` 252 | Validation::validate(["param" => "你好"], ["param" => "Regexp:/^[\x{4e00}-\x{9fa5}]+$/"]); 253 | ``` 254 | 注意正则表达式结尾不需要加'u'。 255 | 256 | 完整的字符串型验证器的列表参考附录 A.4 。 257 | 258 | ### 4.5 验证数组型、对象型、文件型、日期时间型参数 259 | 260 | 参考附录A.5-A.8 261 | 262 | ### 4.6 验证器串联(与) 263 | 264 | 一条规则中可以有多个验证器前后串联,它们之间是“AND”的关系,如: 265 | ```php 266 | "file" => "FileMaxSize:10m|FileImage" 267 | ``` 268 | 这个验证要求参数"file"是一个图像文件,并且文件大小不超过10m 269 | 270 | 271 | ### 4.7 Required 验证器 272 | 273 | * Required验证器要求参数必须存在,且其值不能为`null`(这个是PHP的`null`值,而不是字符串"null")(参数值为`null`等价于参数不存在)。 274 | * 如果多个验证器串联,Required验证器必须在其它验证器前面。 275 | * 如果还有条件验证器,Required必须串联在条件验证器后面。 276 | * 如果验证规则中没有 Required,当参数存在时才进行验证,验证不通过会抛异常;如果参数不存在,那么就不验证(相当于验证通过) 277 | 278 | 例: 279 | ```php 280 | "size" => "Required|StrIn:small,middle,large" 281 | ``` 282 | 该验证要求参数"size"必须是字符串的"small", "middle"或者"large"。 283 | 284 | ### 4.8 忽略所有 Required 验证器 285 | 286 | 比如当创建一个用户时,要求姓名、性别、年龄全部都要提供;但是当更新用户信息时,不需要提供全部信息,提供哪个信息就更新哪个信息。 287 | 288 | ```php 289 | $validations = [ 290 | "name" => "Required|StrLenGeLe:2,20", 291 | "sex" => "Required|IntIn:0,1", 292 | "age" => "Required|IntGeLe:1,200", 293 | ]; 294 | 295 | $userInfo = [ 296 | "name" => "tom", 297 | "sex" => "0", 298 | "age" => "10", 299 | ]; 300 | Validation::validate($userInfo, $validations); // 创建用户时的验证 301 | 302 | unset($userInfo["age"]); // 删除age字段 303 | Validation::validate($userInfo, $validations, true); // 更新用户信息时的验证 304 | ``` 305 | 注意上面代码的最后一行:`validate()`函数的第三个参数为true表示忽略所有的 Required 验证器。 306 | 307 | 这样我们就只需要写一份验证规则,就可以同时用于创建用户和更新用户信息这两个接口。 308 | 309 | ### 4.9 嵌套参数的验证 310 | 311 | 下面这个例子展示了包含数组和对象的嵌套的参数的验证: 312 | ```php 313 | $params = [ 314 | "comments" => [ 315 | [ 316 | "title" => "title 1", 317 | "content" => "content 1", 318 | ], 319 | [ 320 | "title" => "title 1", 321 | "content" => "content 1", 322 | ], 323 | [ 324 | "title" => "title 1", 325 | "content" => "content 1", 326 | ], 327 | ] 328 | ]; 329 | 330 | $validations = [ 331 | "comments[*].title" => "Required|StrLenGeLe:2,50", 332 | "comments[*].content" => "Required|StrLenGeLe:2,500", 333 | ]; 334 | 335 | Validation::validate($params, $validations); 336 | ``` 337 | 338 | ### 4.10 条件判断型验证器 339 | 340 | 条件判断型验证器都以"If"开头。 341 | 342 | 如果条件不满足,则条件验证器后面的规则都不检测,忽略当前这条验证规则。 343 | 344 | 比如你想招聘一批模特,男的要求180以上,女的要求170以上,验证可以这样写: 345 | ```php 346 | $validations = [ 347 | "sex" => "StrIn:male,female", 348 | "height" => [ 349 | "IfStrEq:sex,male|IntGe:180", 350 | "IfStrEq:sex,female|IntGe:170", 351 | ], 352 | ]; 353 | ``` 354 | 参数"sex"的值不同,参数"height"的验证规则也不一样。 355 | 356 | 除了`IfExist`和`IfNotExist`,其它的条件验证器 IfXxx 都要求*条件参数*必须存在。如果希望*条件参数*是可选的,那么可以结合`IfExist`或`IfNotExist`一起使用, 如: 357 | ```php 358 | "IfExist:sex|IfStrEq:sex,male|IntGe:180" 359 | ``` 360 | 361 | 注意: 362 | 设计条件验证器的主要目的是根据一个参数的取值不同,对另外一个参数应用不同的验证规则。 363 | "IfXxx:"的后面应该是另一个参数的名称,而不是当前参数,这一点一定要注意。 364 | 比如上面的例子中,是根据参数"sex"的取值不同,对参数"height"应用了不同的验证规则,"IfXxx:"后面跟的是"sex"。 365 | 366 | 完整的条件判断型验证器的列表参考附录 A.9 。 367 | 368 | ### 4.11 验证规则并联(或) 369 | 370 | 多条验证规则可以并联,它们之间是“或”的关系,如 371 | ```php 372 | "type" => [ 373 | "StrIn:small,middle,large", 374 | "IntIn:1,2,3", 375 | ] 376 | ``` 377 | 上面这条验证要求参数"type"既可以是字符串"small", "middle"或"large",也可以整型的1, 2或3 378 | 379 | 验证规则并联不是简单的“或”的关系,具体验证流程如下: 380 | 1. 按顺序验证这些规则,如果有一条验证规则通过, 则该参数验证通过。 381 | 2. 如果全部验证规则都被忽略(If验证器条件不满足,或者没有Required验证器并且该参数不存在,或者有0条验证规则),也算参数验证通过。 382 | 3. 上面两条都不满足, 则该参数验证失败。 383 | 384 | 这些规则如果要完全理清并不是一件容易的事,所以不建议使用验证规则并联,也尽量不要设计需要这种验证方式的参数。 385 | 386 | ### 4.12 关于特殊值`null`, `""`,`0`,`false`的问题 387 | 388 | 这些特殊的值是不等价的,它们是不同的数据类型(需要用不同的验证器去验证): 389 | * `""`是字符串。 390 | * `0`是整型。 391 | * `false`是bool型。 392 | * `null`是PHP的空。在本工具中它有特殊的含义。 393 | 394 | 如果某个参数的值为`null`,则本工具会视为该参数不存在。 395 | 396 | 比如下面两个array对于本工具来说是等价的. 397 | ```php 398 | $params = [ 399 | "name" => "hello", 400 | ]; 401 | ``` 402 | 与 403 | ```php 404 | $params = [ 405 | "name" => "hello", 406 | "comment" => null, 407 | ]; 408 | ``` 409 | 是等价的。 410 | 411 | ### 4.13 关于基本数据类型与字符串的关系 412 | 413 | 对于以下url地址 414 | ``` 415 | http://abc.com/index.php?p1=&&p2=hello&&p3=123 416 | ``` 417 | 我们将得到的参数数组: 418 | ```php 419 | $params = [ 420 | "p1" => "", 421 | "p2" => "hello", 422 | "p3" => "123", 423 | ]; 424 | ``` 425 | *注意*: 426 | * 参数"p1"的值为空字符串`""`,而不是`null`。 427 | * 参数"p3"的值为字符串`"123"`,而不是整型`123`。 428 | * GET方式的HTTP请求是传递不了`null`值的。 429 | 430 | 本工具的所有验证器都是**强类型**的,"Int*"验证的是整型,"Float*"验证的是浮点型,"Str*"验证的是字符串型,数据类型不匹配,验证是通不过的。但是字符串类型是个例外。 431 | 432 | 因为常规的HTTP请求,所有的基本数据类型最终都会转换成字符串,所以: 433 | * 整型`123`和字符串`"123"`均可以通过验证器"Int"的验证; 434 | * 浮点型`123.0`和字符串`"123.0"`均可以通过验证器"Float"的验证; 435 | * bool型`true`和字符串`"true"`均可以通过验证器"Bool"的验证; 436 | * 但是`null`值和字符串`"null"`永远不等价,字符串`"null"`就只是普通的字符串。 437 | 438 | ### 4.14 自定义错误信息输出文本 439 | 440 | 如果参数验证不通过,`Validation::validate()`方法会抛出异常,这个异常会包含验证不通过的错误信息描述的文本。 441 | 442 | 但是这个描述文本对用户来说可能不那么友好,我们可以通过两个伪验证器来自定义这些文本: 443 | * `Alias` 用于自定义参数名称(这个名称会与内部的错误信息模版相结合,生成最终的错误信息描述文本) 444 | * `>>>` 用于自定义错误描述文本(这个文本会完全取代模版生成的错误描述文本)。 445 | 446 | 看下面的例子: 447 | 448 | ```php 449 | $params = [ 450 | "title" => "a", 451 | ]; 452 | 453 | Validation::validate($params, [ 454 | "title" => "Required|StrLenGeLe:2,50", 455 | ]); // 抛出异常的错误描述为:“title”长度必须在 2 - 50 之间 456 | 457 | Validation::validate($params, [ 458 | "title" => "Required|StrLenGeLe:2,50|Alias:标题", // 自定义参数名称 459 | ]); // 抛出异常的错误描述为:“标题”长度必须在 2 - 50 之间 460 | 461 | Validation::validate($params, [ 462 | "title" => "Required|StrLenGeLe:2,50|>>>:标题长度应在2~50之间", // 自定义错误信息描述文本 463 | ]); // 抛出异常的错误描述为:标题长度应在2~50之间 464 | ``` 465 | 参考附录A.10获取更详细的信息 466 | 467 | ### 4.15 国际化 468 | 469 | 从0.4版开始: 470 | * 使用新的静态成员变量 `$langCode2ErrorTemplates` 来进行“错误提示信息模版”的翻译,主要目的是简化格式(感谢 @gitHusband 的建议)。 471 | * 旧的翻译表 `$langCodeToErrorTemplates` 仍然有效,已有代码无需修改(参考下一节)。如果新旧翻译表同时提供,优先新的,新表中查不到再使用旧的。 472 | 473 | 要支持国际化,需要自定义一个类,继承`\WebGeeker\Validation\Validation`,重载两个静态成员变量: 474 | * `$langCode2ErrorTemplates`用于提供“错误提示信息模版”的翻译对照表。完整的错误提示信息模版列表可以在`\WebGeeker\Validation\Validation::$errorTemplates`成员变量中找到 475 | * `$langCodeToTranslations`用于提供“自定义参数名称”(由`Alias`指定)和“自定义错误描述文本”(由`>>>`指定)的翻译对照表。 476 | 477 | 下面提供一个示例类: 478 | ```php 479 | class MyValidation extends Validation 480 | { 481 | // “错误提示信息模版”翻译对照表 482 | protected static $langCodeToErrorTemplates = [ 483 | "zh-tw" => [ 484 | 'Int' => '“{{param}}”必須是整數', // 🌝 485 | 'IntGt' => '“{{param}}”必須大於 {{min}}', 486 | 'Str' => '“{{param}}”必須是字符串', 487 | ], 488 | "en-us" => [ 489 | 'Int' => '{{param}} must be an integer', 490 | 'IntGt' => '{{param}} must be greater than {{min}}', 491 | 'Str' => '{{param}} must be a string', 492 | ], 493 | ]; 494 | 495 | // 文本翻译对照表 496 | protected static $langCodeToTranslations = [ 497 | "zh-tw" => [ 498 | "变量" => "變量", // 🌙 499 | "变量必须是整数" => "變量必須是整數", // ⭐ 500 | ], 501 | "en-us" => [ 502 | "变量" => "variable", 503 | "变量必须是整数" => "variable must be an integer", 504 | ], 505 | ]; 506 | } 507 | ``` 508 | 注意: 509 | * 语言代码是区分大小写的,建议全部用小写,如"zh-cn", "en-us"等。 510 | * 语言代码的名称是自定义的,你可以随便起名,比如"abc"(建议使用标准的语言代码)。 511 | 512 | 使用这个`MyValidation`类来进行验证,就可以实现文本的翻译了。 513 | ```php 514 | MyValidation::setLangCode("zh-tw"); // 设置语言代码 515 | 516 | MyValidation::validate(["var" => 1.0], [ 517 | "var" => "Int", // 既没有Alias,也没有>>>,只会翻译错误提示信息模版(对应🌝那行) 518 | ]); // 会抛出异常:“var”必須是整數 519 | 520 | MyValidation::validate(["var" => 1.0], [ 521 | "var" => "Int|Alias:变量", // 有Alias,除了翻译错误提示信息模版外,还会翻译参数名称(对应🌙那行) 522 | ]); // 会抛出异常:“變量”必須是整數 523 | 524 | MyValidation::validate(["var" => 1.0], [ 525 | "var" => "Int|>>>:变量必须是整数", // 有>>>,会翻译自定义错误描述文本(对应⭐那行) 526 | ]); // 会抛出异常:變量必須是整數 527 | ``` 528 | 如果提供了错误的语言代码,或者没有找到翻译的文本,那么就不翻译,输出原始的文本。 529 | 530 | ### 4.16 国际化(0.4版之前) 531 | 532 | *(如果你使用的是0.4及之后的版本,建议使用新的国际化方案(参考上一节),更简洁一点)*。 533 | 534 | 要支持国际化,需要自定义一个类,继承`\WebGeeker\Validation\Validation`,重载两个静态成员变量: 535 | * `$langCodeToErrorTemplates`用于提供“错误提示信息模版”的翻译对照表。完整的错误提示信息模版列表可以在`\WebGeeker\Validation\Validation::$errorTemplates`成员变量中找到 536 | * `$langCodeToTranslations`用于提供“自定义参数名称”(由`Alias`指定)和“自定义错误描述文本”(由`>>>`指定)的翻译对照表。 537 | 538 | 下面提供一个示例类: 539 | ```php 540 | class MyValidation extends Validation 541 | { 542 | // “错误提示信息模版”翻译对照表 543 | protected static $langCodeToErrorTemplates = [ 544 | "zh-tw" => [ 545 | "“{{param}}”必须是整数" => "“{{param}}”必須是整數", // 🌝 546 | "“{{param}}”必须是字符串" => "“{{param}}”必須是字符串", 547 | ], 548 | "en-us" => [ 549 | "“{{param}}”必须是整数" => "{{param}} must be a integer", 550 | "“{{param}}”必须是字符串" => "{{param}} must be a string", 551 | ], 552 | ]; 553 | 554 | // 文本翻译对照表 555 | protected static $langCodeToTranslations = [ 556 | "zh-tw" => [ 557 | "变量" => "變量", // 🌙 558 | "变量必须是整数" => "變量必須是整數", // ⭐ 559 | ], 560 | "en-us" => [ 561 | "变量" => "variable", 562 | "变量必须是整数" => "variable must be an integer", 563 | ], 564 | ]; 565 | } 566 | ``` 567 | 注意: 568 | * 语言代码是区分大小写的,建议全部用小写,如"zh-cn", "en-us"等。 569 | * 语言代码的名称是自定义的,你可以随便起名,比如"abc"(建议使用标准的语言代码)。 570 | 571 | 使用这个`MyValidation`类来进行验证,就可以实现文本的翻译了。 572 | ```php 573 | MyValidation::setLangCode("zh-tw"); // 设置语言代码 574 | 575 | MyValidation::validate(["var" => 1.0], [ 576 | "var" => "Int", // 既没有Alias,也没有>>>,只会翻译错误提示信息模版(对应🌝那行) 577 | ]); // 会抛出异常:“var”必須是整數 578 | 579 | MyValidation::validate(["var" => 1.0], [ 580 | "var" => "Int|Alias:变量", // 有Alias,除了翻译错误提示信息模版外,还会翻译参数名称(对应🌙那行) 581 | ]); // 会抛出异常:“變量”必須是整數 582 | 583 | MyValidation::validate(["var" => 1.0], [ 584 | "var" => "Int|>>>:变量必须是整数", // 有>>>,会翻译自定义错误描述文本(对应⭐那行) 585 | ]); // 会抛出异常:變量必須是整數 586 | ``` 587 | 如果提供了错误的语言代码,或者没有找到翻译的文本,那么就不翻译,输出原始的文本。 588 | 589 | ### 4.17 自定义验证器 590 | 591 | 这里先强调一下,**不要滥用自定义验证器功能**。跟业务相关强相关的一些验证,如果用的地方很少,可以考虑直接用PHP代码来验证,或者封装成PHP函数直接调用,不一定非要纳入到 webgeeker-validation 验证体系中。 592 | 593 | #### 4.17.1 一个简单的自定义验证器示例 594 | 595 | 下面例子中定义了一个类`CustomCaseValidation`,类中提供一个方法`validateCustomStartWith()`和一个错误提示信息模版`$errorTemplates`,就实现了自定义验证器`CustomStartWith`。 596 | (不要被代码行数吓到了,实际没多少代码,有一半是注释) 597 | 598 | ```php 599 | // 本类实现了自定义验证器 CustomStartWith 用于验证参数是否以指定前缀开头 600 | class CustomCaseValidation extends Validation 601 | { 602 | // 验证失败时的错误提示信息的模版。子类的模版在运行时会与父类的模版 $errorTemplates 合并,如果出现同名的键,子类的值会覆盖父类的值 603 | protected static $errorTemplates = [ 604 | 'CustomStartWith' => '“{{param}}”必须以"{{prefix}}"开头"', 605 | ]; 606 | 607 | /** 608 | * 自定义验证器"CustomStartWith"的实现方法,验证输入参数 $value 是否以指定前缀开头 609 | * 610 | * @param $value string 待验证的值 611 | * @param $prefix string 前缀。如果验证器为"CustomStartWith:head",则本参数的值为"head" 612 | * @param $reason string|null 验证失败的错误提示字符串. 如果为null, 需要自动生成 613 | * 如果验证中包含了伪验证器">>>:some reason",本参数值为"some reason", 614 | * 如果没有提供伪验证器">>>",本参数值为null 615 | * @param $alias string 参数别名, 用于错误提示。 616 | * 如果验证中包含了伪验证器"Alias:姓名",则本参数值为"姓名" 617 | * 如果没有提供"Alias",比如"name" => "StrLenGeLe:2,30",本参数值为"name" 618 | * @return mixed 返回参数 $value 的原值 619 | * @throws ValidationException 验证失败抛出异常 620 | */ 621 | public static function validateCustomStartWith($value, $prefix, $reason, $alias) 622 | { 623 | if (strpos(strval($value), $prefix) === 0) 624 | return $value; 625 | 626 | if ($reason !== null) 627 | throw new ValidationException($reason); 628 | 629 | $error = self::getErrorTemplate('CustomStartWith'); // 获取错误提示信息模版 630 | $error = str_replace('{{param}}', $alias, $error); 631 | $error = str_replace('{{prefix}}', $prefix, $error); 632 | throw new ValidationException($error); 633 | } 634 | } 635 | ``` 636 | 用法如下: 637 | ```php 638 | 'name' => 'CustomStartWith:head' 639 | ``` 640 | #### 4.17.2 如何实现自定义验证器 641 | 1. 自定义验证器必须以 `Custom` 开头,比如"CustomAbc"、"CustomXyz" 642 | 2. 定义一个类,继承 `\WebGeeker\Validation\Validation` 643 | 3. 在该类中提供"自定义验证器的实现方法",有几个验证器,就提供几个实现方法。 644 | 4. 类中的错误提示信息模版`$errorTemplates`是可选的,因为你可以在实现方法中直接生成错误提示信息的文本(但是这样就不支持国际化功能了)。 645 | 646 | #### 4.17.3 自定义验证器的实现方法的具体要求 647 | 1. 方法必须以"validate"开头,后面加上验证器名称。比如"validateCustomStartWith"。 648 | 2. 如果自定义验证器没有参数,其实现方法的格式为: 649 | ``` 650 | public static function validateCustomAbc($value, $reason, $alias) {} 651 | ``` 652 | 方法有 3 个参数: 653 | * `$value` 待验证的值 654 | * `$reason` 验证失败的错误提示字符串。它的值就是伪验证器 `>>>` 提供的字符串 655 | * `$alias` 参数别名,用于错误提示。它的值等于伪验证器 `Alias` 提供的字符串 656 | 3. 如果自定义验证器有 `n` 个参数,那么它的验证方法应该有 3 + `n` 个参数, 657 | 多出来的 n 个参数应该放在参数 `$value` 的后面。 658 | 带1个参数的验证器示例:"CustomStartWith:head" 659 | 带多个参数的验证器示例:"CustomStrIn:started,success,failed",多个参数用逗号分隔 660 | 4. 如果验证通过,方法的返回值应该是参数 `$value` 的原始值 661 | 5. 如果验证失败,应该抛出异常 662 | 663 | 下面再提供一个示例类`CustomDemoValidation`,实现了三个自定义验证器`CustomInt`、`CustomIntEq`、`CustomIntGeLe`: 664 | ```php 665 | /** 666 | * 自定义验证器示例类Demo 667 | * 668 | * 本类实现了以下自定义验证器: 669 | * CustomInt 用于验证参数是否是整数 670 | * CustomIntEq 用于验证参数是否是整数,并且与指定数值相等 671 | * CustomIntGeLe 用于验证参数是否是整数,并且其值在[min, max]之间 672 | */ 673 | class CustomDemoValidation extends CustomCaseValidation 674 | { 675 | /** 676 | * @var array 验证失败时的错误提示信息的模版。 677 | * 子类的模版在运行时会与父类的 $errorTemplates 合并, 678 | * 如果出现同名的键, 子类的值会覆盖父类的值 679 | */ 680 | protected static $errorTemplates = [ 681 | 'CustomInt' => '“{{param}}”必须是Custom整数', 682 | 'CustomIntEq' => '“{{param}}”必须等于 {{value}}', 683 | 'CustomIntGeLe' => '“{{param}}”必须大于等于 {{min}} 小于等于 {{max}}', 684 | ]; 685 | 686 | /** 687 | * 自定义验证器"CustomInt"的实现方法 688 | * 689 | * @param $value string 待验证的值 690 | * @param $reason string|null 验证失败的错误提示字符串. 如果为null, 需要自动生成 691 | * @param $alias string 参数别名, 用于错误提示。 692 | * @return mixed 返回参数 $value 的原值 693 | * @throws ValidationException 验证失败抛出异常 694 | */ 695 | public static function validateCustomInt($value, $reason, $alias) 696 | { 697 | $type = gettype($value); 698 | if ($type === 'string') { 699 | if (is_numeric($value) && strpos($value, '.') === false) 700 | return $value; 701 | } elseif ($type === 'integer') { 702 | return $value; 703 | } 704 | 705 | if ($reason !== null) 706 | throw new ValidationException($reason); 707 | 708 | $error = self::getErrorTemplate('CustomInt'); 709 | $error = str_replace('{{param}}', $alias, $error); 710 | throw new ValidationException($error); 711 | } 712 | 713 | /** 714 | * 自定义验证器"CustomIntEq"的实现方法。 715 | * 检测 $value 与 $equalVal 是否相等。 716 | * 717 | * @param $value string 待验证的值 718 | * @param $equalVal string 要比较的值 719 | * @param $reason string|null 验证失败的错误提示字符串. 如果为null, 需要自动生成 720 | * @param $alias string 参数别名, 用于错误提示。 721 | * @return mixed 返回参数 $value 的原值 722 | * @throws ValidationException 验证失败抛出异常 723 | */ 724 | public static function validateCustomIntEq($value, $equalVal, $reason, $alias) 725 | { 726 | $type = gettype($value); 727 | if ($type === 'string') { 728 | if (is_numeric($value) && strpos($value, '.') === false) { 729 | $val = intval($value); 730 | if ($val == $equalVal) 731 | return $value; 732 | $isTypeError = false; 733 | } else 734 | $isTypeError = true; 735 | } elseif ($type === 'integer') { 736 | if ($value == $equalVal) 737 | return $value; 738 | $isTypeError = false; 739 | } else 740 | $isTypeError = true; 741 | 742 | if ($reason !== null) 743 | throw new ValidationException($reason); 744 | 745 | if ($isTypeError) { 746 | $error = self::getErrorTemplate('CustomInt'); 747 | $error = str_replace('{{param}}', $alias, $error); 748 | } else { 749 | $error = self::getErrorTemplate('CustomIntEq'); 750 | $error = str_replace('{{param}}', $alias, $error); 751 | $error = str_replace('{{value}}', $equalVal, $error); 752 | } 753 | throw new ValidationException($error); 754 | } 755 | 756 | /** 757 | * 自定义验证器"CustomIntGeLe"的实现方法。 758 | * 检测 $value 取值是否在[$min, $max]之间。 759 | * 760 | * @param $value string 待验证的值 761 | * @param $min string 最小值 762 | * @param $max string 最大值 763 | * @param $reason string|null 验证失败的错误提示字符串. 如果为null, 需要自动生成 764 | * @param $alias string 参数别名, 用于错误提示。 765 | * @return mixed 返回参数 $value 的原值 766 | * @throws ValidationException 验证失败抛出异常 767 | */ 768 | public static function validateCustomIntGeLe($value, $min, $max, $reason, $alias) 769 | { 770 | $type = gettype($value); 771 | if ($type === 'string') { 772 | if (is_numeric($value) && strpos($value, '.') === false) { 773 | $val = intval($value); 774 | if ($val >= $min && $val <= $max) 775 | return $value; 776 | $isTypeError = false; 777 | } else 778 | $isTypeError = true; 779 | } elseif ($type === 'integer') { 780 | if ($value >= $min && $value <= $max) 781 | return $value; 782 | $isTypeError = false; 783 | } else 784 | $isTypeError = true; 785 | 786 | if ($reason !== null) 787 | throw new ValidationException($reason); 788 | 789 | if ($isTypeError) { 790 | $error = self::getErrorTemplate('CustomInt'); 791 | $error = str_replace('{{param}}', $alias, $error); 792 | } else { 793 | $error = self::getErrorTemplate('CustomIntGeLe'); 794 | $error = str_replace('{{param}}', $alias, $error); 795 | $error = str_replace('{{min}}', $min, $error); 796 | $error = str_replace('{{max}}', $max, $error); 797 | } 798 | throw new ValidationException($error); 799 | } 800 | } 801 | ``` 802 | **注意**:上面的 `CustomDemoValidation` 类继承的是另一个自定义验证器类 `CustomCaseValidation`。 803 | 804 | 你可以通过连续继承的方式把多个自定义验证器类的功能合并,详情可参考 4.17.6 小节 805 | 806 | #### 4.17.4 自定义验证器的参数解析方法 807 | 808 | 如果是带参数的验证器,比如`"CustomFloatGtLt:1.0,10"`,验证器的参数`"1.0,10"`会用默认的参数解析方法处理: 809 | ```php 810 | explode(',', $paramString) 811 | ``` 812 | 默认的参数解析方法的有如下缺点: 813 | * 解析出来的参数只会是字符串 814 | * 不会检测验证器的参数的合法性 815 | * 验证器参数的内容中无法使用逗号,因为逗号是做为参数分隔符来处理的。 816 | * 无法支持可变个数的验证器参数 817 | 818 | 解决以上问题的方法就是提供**自定义验证器的参数解析方法**(以"CustomFloatGtLt"为例): 819 | ```php 820 | protected static function parseParamsOfCustomFloatGtLt($paramString) {} 821 | ``` 822 | * 方法名以"parseParamsOf"开头,后接自定义验证器名称(比如上面的自定义验证器"CustomFloatGtLt") 823 | * 方法的参数是一个字符串 `$paramString` ,它的值会是自定义验证器的参数。比如对于验证器`"CustomFloatGtLt:1.0,10"`,`$paramString`就等于`"1.0,10"` 824 | * 方法的返回值应该是一个数组(不可为null,也不可为空数组)。比如`"1.0,10"`应该被解析成`[1.0, 10.0]` 825 | * 如果参数解析失败,需要抛出异常,给出失败的原因。 826 | 827 | 下面给出第三个示例类 `CustomExampleValidation`。 828 | 注意其中的参数解析方法 `parseParamsOfCustomFloatGtLt()`。 829 | 另一个要注意的是`$langCode2ErrorTemplates`,它是“错误提示信息模版” `$errorTemplates` 的翻译对照表(参考4.15 国际化) 830 | ```php 831 | /** 832 | * 自定义验证器示例类Example 833 | * 834 | * 本类实现了以下自定义验证器: 835 | * CustomFloatGtLt 用于验证参数是否是浮点数,并且其值在(min, max)之间 836 | */ 837 | class CustomExampleValidation extends Validation 838 | { 839 | /** 840 | * @var array 验证失败时的错误提示信息的模版。 841 | * 子类的模版在运行时会与父类的 $errorTemplates 合并, 842 | * 如果出现同名的键, 子类的值会覆盖父类的值 843 | */ 844 | protected static $errorTemplates = [ 845 | 'CustomFloatGtLt' => '“{{param}}”必须大于 {{min}} 小于 {{max}}', 846 | ]; 847 | 848 | /** 849 | * @var \string[][] “错误提示信息模版” $errorTemplates 的翻译对照表 850 | * 子类的翻译对照表在运行时会与父类的 $langCode2ErrorTemplates (递归)合并, 851 | * 如果出现同名的键, 子类的值会覆盖父类的值 852 | */ 853 | protected static $langCode2ErrorTemplates = [ 854 | "zh-tw" => [ 855 | 'CustomFloatGtLt' => '“{{param}}”必須大於 {{min}} 小於 {{max}}', 856 | ], 857 | "en-us" => [ 858 | 'CustomFloatGtLt' => '{{param}} must be greater than {{min}} and less than {{max}}', 859 | ], 860 | ]; 861 | 862 | /** 863 | * 自定义验证器"CustomFloatGtLt"的参数解析方法 864 | * 865 | * 假设验证器为"CustomFloatGtLt:1.0,10" 866 | * 则 $paramString = "1.0,10" 867 | * 本函数返回值为: [1.0, 10.0] 868 | * 869 | * @param $paramString string 验证器的参数 870 | * @return array 返回解析后的参数的数组。数组的长度就是验证器的 871 | * 参数个数,必须与对应的 validateCustomFloatGtLt() 方法的参数个数匹配。 872 | * @throws ValidationException 参数解析失败抛出异常 873 | */ 874 | protected static function parseParamsOfCustomFloatGtLt($paramString) 875 | { 876 | $vals = explode(',', $paramString); 877 | if (count($vals) !== 2) 878 | throw new ValidationException("自定义验证器 CustomFloatGtLt 格式错误. 正确格式示例: CustomFloatGtLt:1.0,2.0"); 879 | $p1 = $vals[0]; 880 | $p2 = $vals[1]; 881 | if (is_numeric($p1) === false || is_numeric($p2) === false) 882 | throw new ValidationException("自定义验证器 CustomFloatGtLt 参数类型错误. 正确的示例: CustomFloatGtLt:1.0,2.0"); 883 | return [$p1, $p2]; 884 | } 885 | 886 | /** 887 | * 自定义验证器"CustomFloatGtLt"的验证方法的实现 888 | * 889 | * @param $value mixed 待验证的值 890 | * @param $min float 最小值。对应上面的 parseParamsOfCustomFloatGtLt() 方法 891 | * 返回的数组的第0个元素($p1) 892 | * @param $max float 最大值。对应上面的 parseParamsOfCustomFloatGtLt() 方法 893 | * 返回的数组的第1个元素($p2) 894 | * @param $reason string|null 验证失败的错误提示字符串. 如果为null, 需要自动生成 895 | * @param $alias string 参数别名, 用于错误提示。 896 | * @return mixed 返回参数 $value 的原值 897 | * @throws ValidationException 验证失败抛出异常 898 | */ 899 | public static function validateCustomFloatGtLt($value, $min, $max, $reason, $alias) 900 | { 901 | $type = gettype($value); 902 | if ($type === 'string') { 903 | if (is_numeric($value)) { 904 | $val = floatval($value); 905 | if ($val > $min && $val < $max) 906 | return $value; 907 | $isTypeError = false; 908 | } else 909 | $isTypeError = true; 910 | } elseif ($type === 'double' || $type === 'integer') { 911 | if ($value > $min && $value < $max) 912 | return $value; 913 | $isTypeError = false; 914 | } else 915 | $isTypeError = true; 916 | 917 | if ($reason !== null) 918 | throw new ValidationException($reason); 919 | 920 | if ($isTypeError) { 921 | $error = '“{{param}}”必须是浮点数'; 922 | $error = str_replace('{{param}}', $alias, $error); 923 | } else { 924 | $error = self::getErrorTemplate('CustomFloatGtLt'); 925 | $error = str_replace('{{param}}', $alias, $error); 926 | $error = str_replace('{{min}}', $min, $error); 927 | $error = str_replace('{{max}}', $max, $error); 928 | } 929 | throw new ValidationException($error); 930 | } 931 | } 932 | ``` 933 | 934 | #### 4.17.5 参数个数可变的自定义验证器 935 | 936 | 所谓参数个数可变,就是自定义验证器可以带任意数量的参数(但不能不带参数)。 937 | 938 | 比如"CustomStrIn",既可以带两个参数"CustomStrIn:started,finished,也可以带3个参数如"CustomStrIn:started,success,failed" 939 | 940 | 具体实现方法参考下面的第四的示例类 `CustomSampleValidation`。 941 | 注意其中的参数解析方法 `parseParamsOfCustomStrIn()`,它返回的是一个数组的数组,这是实现可变参数的关键。 942 | 另一个要注意的是`$langCodeToTranslations`(实际上它不应该出现在这个例子中),它是文本翻译对照表,用来翻译伪验证器 `Alias` 或 `>>>` 提供的文本(参考4.15 国际化)。 943 | ```php 944 | /** 945 | * 自定义验证器示例类Sample 946 | * 947 | * 本类实现了以下自定义验证器: 948 | * CustomStrIn 用于验证参数是否是字符串,并且其取值是指定的几个字符串之一 949 | */ 950 | class CustomSampleValidation extends Validation 951 | { 952 | /** 953 | * @var array 验证失败时的错误提示信息的模版。 954 | * 子类的模版在运行时会与父类的 $errorTemplates 合并, 955 | * 如果出现同名的键, 子类的值会覆盖父类的值 956 | */ 957 | protected static $errorTemplates = [ 958 | 'CustomStrIn' => '“{{param}}”只能取这些值: {{valueList}}', 959 | ]; 960 | 961 | /** 962 | * @var \string[][] 文本翻译对照表 963 | * 子类的文本翻译对照表在运行时会与父类的 $langCodeToTranslations (递归)合并, 964 | * 如果出现同名的键, 子类的值会覆盖父类的值 965 | */ 966 | protected static $langCodeToTranslations = [ 967 | "zh-tw" => [ 968 | "状态" => "狀態", 969 | ], 970 | "en-us" => [ 971 | "状态" => "status", 972 | ], 973 | ]; 974 | 975 | /** 976 | * 自定义验证器"CustomStrIn"的参数解析方法 977 | * 978 | * 假设验证器为"CustomStrIn:pending,started,success,failed" 979 | * 则 $paramString = "pending,started,success,failed" 980 | * 则本函数返回值为(数组的数组): [ 981 | * ['pending', 'started', 'success', 'failed'] 982 | * ] 983 | * 返回"数组的数组"可以实现验证器的参数个数可变。 984 | * 985 | * @param $paramString string 验证器的参数 986 | * @return array 返回解析后的参数的数组。数组的长度就是验证器的 987 | * 参数个数,必须与对应的 validateCustomStrIn() 方法的参数个数匹配。 988 | */ 989 | protected static function parseParamsOfCustomStrIn($paramString) 990 | { 991 | $list = explode(',', $paramString); 992 | return [$list]; // 注意这里返回的是数组的数组 993 | } 994 | 995 | /** 996 | * 自定义验证器"CustomStrIn"的验证方法的实现 997 | * 998 | * @param $value mixed 待验证的值 999 | * @param $valueList string[] 可取值的列表。 1000 | * 对应上面的 parseParamsOfCustomStrIn() 方法返回的数组的第0个元素($list) 1001 | * @param $reason string|null 验证失败的错误提示字符串. 如果为null, 需要自动生成 1002 | * @param $alias string 参数别名, 用于错误提示。 1003 | * @return mixed 返回参数 $value 的原值 1004 | * @throws ValidationException 验证失败抛出异常 1005 | */ 1006 | public static function validateCustomStrIn($value, $valueList, $reason, $alias) 1007 | { 1008 | if (is_array($valueList) === false || count($valueList) === 0) 1009 | throw new ValidationException("“${alias}”参数的验证模版(StrIn:)格式错误, 必须提供可取值的列表"); 1010 | 1011 | if (is_string($value)) { 1012 | if (in_array($value, $valueList, true)) 1013 | return $value; 1014 | $isTypeError = false; 1015 | } else 1016 | $isTypeError = true; 1017 | 1018 | if ($reason !== null) 1019 | throw new ValidationException($reason); 1020 | 1021 | if ($isTypeError) { 1022 | $error = '“{{param}}”必须是Custom字符串'; 1023 | $error = str_replace('{{param}}', $alias, $error); 1024 | } else { 1025 | $error = self::getErrorTemplate('CustomStrIn'); 1026 | $error = str_replace('{{param}}', $alias, $error); 1027 | $error = str_replace('{{valueList}}', '"'.implode('", "', $valueList).'"', $error); 1028 | } 1029 | 1030 | throw new ValidationException($error); 1031 | } 1032 | } 1033 | ``` 1034 | 1035 | #### 4.17.6 合并多个自定义验证器类 1036 | 1037 | 可以通过连续继承的方式把多个自定义验证器类的功能合并,比如可以按下面的关系来继承: 1038 | ``` 1039 | MyValidation > CustomDemoValidation > CustomCaseValidation > Validation 1040 | ``` 1041 | 这样当你调用`MyValidation::validate()`时,就可以使用上面四个类中定义的所有的验证器或自定义验证器。 1042 | 1043 | **不建议继承层次太多**。 1044 | 1045 | 在运行时,继承链上的所有类的错误提示信息模版 `$errorTemplates` 会被合并成一个总的模版。如果出现重复的键,总是子类的值覆盖父类的值。 1046 | 1047 | 如果某些自定义验证器不是全局通用的,而是某个模块专用的。可以写一个这个模块专用的验证器类,如`ModuleXValidation`, `ModuleYValidation`,按如下方式继承: 1048 | ``` 1049 | ModuleXValidation > MyValidation > CustomDemoValidation > CustomCaseValidation > Validation 1050 | ModuleYValidation > MyValidation > CustomDemoValidation > CustomCaseValidation > Validation 1051 | ``` 1052 | * 在模块X中,你可以调用`ModuleXValidation::validate()`来验证参数; 1053 | * 在模块Y中,你可以调用`ModuleYValidation::validate()`来验证参数; 1054 | * 而在其它模块中,还是调用`MyValidation::validate()`来验证参数。 1055 | 1056 | 这里再强调一下,**不要滥用自定义验证器功能**。跟业务相关强相关的一些验证,如果用的地方很少,可以考虑直接用PHP代码来验证,或者封装成PHP函数直接调用,不一定非要纳入到 webgeeker-validation 验证体系中。 1057 | 1058 | #### 4.17.7 自定义验证器的国际化 1059 | 1060 | 自定义验证器的国际化的方法与内置验证器是一样,可参考 4.15 小节。 1061 | 1062 | 上一小节提到了自定义验证器类的连续继承,继承链上的所有翻译表 `$langCode2ErrorTemplates` 或 `$langCodeToTranslations` 在运行时,都会被(递归)合并成一张表。 1063 | 1064 | 所以我们有多种放置翻译表的方案: 1065 | 1. 提供一个类比如 `MyValidation`,专门用于国际化,所有的翻译表都放在这个类中,正如 4.15 小节所介绍的那样; 1066 | 2. 把翻译表分散放到继承链上的多个类中。 1067 | 3. 同时使用上面两种方案; 1068 | 1069 | ## A 附录 - 验证器列表 1070 | 1071 | ### A.1 整型 1072 | 1073 | 整型验证器全部以"Int"开头。 1074 | 1075 | | 整型验证器 | 示例 | 说明 | 1076 | | :------| :------ | :------ | 1077 | | Int | Int | “{{param}}”必须是整数 | 1078 | | IntEq | IntEq:100 | “{{param}}”必须等于 {{value}} | 1079 | | IntNe | IntNe:100 | “{{param}}”不能等于 {{value}} | 1080 | | IntGt | IntGt:100 | “{{param}}”必须大于 {{min}} | 1081 | | IntGe | IntGe:100 | “{{param}}”必须大于等于 {{min}} | 1082 | | IntLt | IntLt:100 | “{{param}}”必须小于 {{max}} | 1083 | | IntLe | IntLe:100 | “{{param}}”必须小于等于 {{max}} | 1084 | | IntGtLt | IntGtLt:1,100 | “{{param}}”必须大于 {{min}} 小于 {{max}} | 1085 | | IntGeLe | IntGeLe:1,100 | “{{param}}”必须大于等于 {{min}} 小于等于 {{max}} | 1086 | | IntGtLe | IntGtLe:1,100 | “{{param}}”必须大于 {{min}} 小于等于 {{max}} | 1087 | | IntGeLt | IntGeLt:1,100 | “{{param}}”必须大于等于 {{min}} 小于 {{max}} | 1088 | | IntIn | IntIn:2,3,5,7,11 | “{{param}}”只能取这些值: {{valueList}} | 1089 | | IntNotIn | IntNotIn:2,3,5,7,11 | “{{param}}”不能取这些值: {{valueList}} | 1090 | 1091 | ### A.2 浮点型 1092 | 1093 | 内部一律使用double来处理 1094 | 1095 | | 浮点型验证器 | 示例 | 说明 | 1096 | | :------| :------ | :------ | 1097 | | Float | Float | “{{param}}”必须是浮点数 | 1098 | | FloatGt | FloatGt:1.0 | “{{param}}”必须大于 {{min}} | 1099 | | FloatGe | FloatGe:1.0 | “{{param}}”必须大于等于 {{min}} | 1100 | | FloatLt | FloatLt:1.0 | “{{param}}”必须小于 {{max}} | 1101 | | FloatLe | FloatLe:1.0 | “{{param}}”必须小于等于 {{max}} | 1102 | | FloatGtLt | FloatGtLt:0,1.0 | “{{param}}”必须大于 {{min}} 小于 {{max}} | 1103 | | FloatGeLe | FloatGeLe:0,1.0 | “{{param}}”必须大于等于 {{min}} 小于等于 {{max}} | 1104 | | FloatGtLe | FloatGtLe:0,1.0 | “{{param}}”必须大于 {{min}} 小于等于 {{max}} | 1105 | | FloatGeLt | FloatGeLt:0,1.0 | “{{param}}”必须大于等于 {{min}} 小于 {{max}} | 1106 | 1107 | ### A.3 bool型 1108 | 1109 | | bool型验证器 | 示例 | 说明 | 1110 | | :------| :------ | :------ | 1111 | | Bool | Bool | 合法的取值为: `true`, `false`, `"true"`, `"false"`(忽略大小写) | 1112 | | BoolTrue | BoolTrue | 合法的取值为: `true`, `"true"`(忽略大小写) | 1113 | | BoolFalse | BoolFalse | 合法的取值为: `false`,`"false"`(忽略大小写) | 1114 | | BoolSmart | BoolSmart | 合法的取值为: `true`, `false`, `"true"`, `"false"`, `1`, `0`, `"1"`, `"0"`, `"yes"`, `"no"`, `"y"`, `"n"`(忽略大小写) | 1115 | | BoolSmartTrue | BoolSmartTrue | 合法的取值为: `true`, `"true"`, `1`, `"1"`, `"yes"`, `"y"`(忽略大小写) | 1116 | | BoolSmartFalse | BoolSmartFalse | 合法的取值为: `false`, `"false"`, `0`, `"0"`, `"no"`, `"n"`(忽略大小写) | 1117 | 1118 | ### A.4 字符串型 1119 | 1120 | | 字符串型验证器 | 示例 | 说明 | 1121 | | :------| :------ | :------ | 1122 | | Str | Str | “{{param}}”必须是字符串 | 1123 | | StrEq | StrEq:abc | “{{param}}”必须等于"{{value}}" | 1124 | | StrEqI | StrEqI:abc | “{{param}}”必须等于"{{value}}"(忽略大小写) | 1125 | | StrNe | StrNe:abc | “{{param}}”不能等于"{{value}}" | 1126 | | StrNeI | StrNeI:abc | “{{param}}”不能等于"{{value}}"(忽略大小写) | 1127 | | StrIn | StrIn:abc,def,g | “{{param}}”只能取这些值: {{valueList}} | 1128 | | StrInI | StrInI:abc,def,g | “{{param}}”只能取这些值: {{valueList}}(忽略大小写) | 1129 | | StrNotIn | StrNotIn:abc,def,g | “{{param}}”不能取这些值: {{valueList}} | 1130 | | StrNotInI | StrNotInI:abc,def,g | “{{param}}”不能取这些值: {{valueList}}(忽略大小写) | 1131 | | StrLen | StrLen:8 | “{{param}}”长度必须等于 {{length}} | 1132 | | StrLenGe | StrLenGe:8 | “{{param}}”长度必须大于等于 {{min}} | 1133 | | StrLenLe | StrLenLe:8 | “{{param}}”长度必须小于等于 {{max}} | 1134 | | StrLenGeLe | StrLenGeLe:6,8 | “{{param}}”长度必须在 {{min}} - {{max}} 之间 | 1135 | | ByteLen | ByteLen:8 | “{{param}}”长度(字节)必须等于 {{length}} | 1136 | | ByteLenGe | ByteLenGe:8 | “{{param}}”长度(字节)必须大于等于 {{min}} | 1137 | | ByteLenLe | ByteLenLe:8 | “{{param}}”长度(字节)必须小于等于 {{max}} | 1138 | | ByteLenGeLe | ByteLenGeLe:6,8 | “{{param}}”长度(字节)必须在 {{min}} - {{max}} 之间 | 1139 | | Letters | Letters | “{{param}}”只能包含字母 | 1140 | | Alphabet | Alphabet | 同Letters | 1141 | | Numbers | Numbers | “{{param}}”只能是纯数字 | 1142 | | Digits | Digits | 同Numbers | 1143 | | LettersNumbers | LettersNumbers | “{{param}}”只能包含字母和数字 | 1144 | | Numeric | Numeric | “{{param}}”必须是数值。一般用于大数处理(超过double表示范围的数,一般会用字符串来表示)(尚未实现大数处理), 如果是正常范围内的数, 可以使用'Int'或'Float'来检测 | 1145 | | VarName | VarName | “{{param}}”只能包含字母、数字和下划线,并且以字母或下划线开头 | 1146 | | Email | Email | “{{param}}”必须是合法的email | 1147 | | Url | Url | “{{param}}”必须是合法的Url地址 | 1148 | | Ip | Ip | “{{param}}”必须是合法的IP地址 | 1149 | | Mac | Mac | “{{param}}”必须是合法的MAC地址 | 1150 | | Regexp | Regexp:/^abc$/ | Perl正则表达式匹配 | 1151 | 1152 | ### A.5 数组型 1153 | 1154 | | 数组型验证器 | 示例 | 说明 | 1155 | | :------| :------ | :------ | 1156 | | Arr | Arr | “{{param}}”必须是数组 | 1157 | | ArrLen | ArrLen:5 | “{{param}}”数组长度必须等于 {{length}} | 1158 | | ArrLenGe | ArrLenGe:1 | “{{param}}”数组长度必须大于等于 {{min}} | 1159 | | ArrLenLe | ArrLenLe:9 | “{{param}}”数组长度必须小于等于 {{max}} | 1160 | | ArrLenGeLe | ArrLenGeLe:1,9 | “{{param}}”长数组度必须在 {{min}} ~ {{max}} 之间 | 1161 | 1162 | ### A.6 对象型 1163 | 1164 | | 对象型验证器 | 示例 | 说明 | 1165 | | :------| :------ | :------ | 1166 | | Obj | Obj | “{{param}}”必须是对象 | 1167 | 1168 | ### A.7 文件型 1169 | 1170 | | 文件型验证器 | 示例 | 说明 | 1171 | | :------| :------ | :------ | 1172 | | File | File | “{{param}}”必须是文件 | 1173 | | FileMaxSize | FileMaxSize:10mb | “{{param}}”必须是文件, 且文件大小不超过{{size}} | 1174 | | FileMinSize | FileMinSize:100kb | “{{param}}”必须是文件, 且文件大小不小于{{size}} | 1175 | | FileImage | FileImage | “{{param}}”必须是图片 | 1176 | | FileVideo | FileVideo | “{{param}}”必须是视频文件 | 1177 | | FileAudio | FileAudio | “{{param}}”必须是音频文件 | 1178 | | FileMimes | FileMimes:mpeg,jpeg,png | “{{param}}”必须是这些MIME类型的文件:{{mimes}} | 1179 | 1180 | ### A.8 日期和时间型 1181 | 1182 | | 日期和时间型验证器 | 示例 | 说明 | 1183 | | :------| :------ | :------ | 1184 | | Date | Date | “{{param}}”必须符合日期格式YYYY-MM-DD | 1185 | | DateFrom | DateFrom:2017-04-13 | “{{param}}”不得早于 {{from}} | 1186 | | DateTo | DateTo:2017-04-13 | “{{param}}”不得晚于 {{to}} | 1187 | | DateFromTo | DateFromTo:2017-04-13,2017-04-13 | “{{param}}”必须在 {{from}} ~ {{to}} 之间 | 1188 | | DateTime | DateTime | “{{param}}”必须符合日期时间格式YYYY-MM-DD HH:mm:ss | 1189 | | DateTimeFrom | DateTimeFrom:2017-04-13 12:00:00 | “{{param}}”不得早于 {{from}} | 1190 | | DateTimeTo | DateTimeTo:2017-04-13 12:00:00 | “{{param}}”必须早于 {{to}} | 1191 | | DateTimeFromTo | DateTimeFromTo:2017-04-13 12:00:00,2017-04-13 12:00:00 | “{{param}}”必须在 {{from}} ~ {{to}} 之间 | 1192 | 1193 | ### A.9 条件判断型 1194 | 1195 | 在一条验证规则中,条件验证器必须在其它验证器前面,多个条件验证器可以串联。 1196 | 1197 | 注意,条件判断中的“条件”一般是检测**另外一个参数**的值,而当前参数的值是由串联在条件判断验证器后面的其它验证器来验证。 1198 | 1199 | | 条件判断型验证器 | 示例 | 说明 | 1200 | | :------| :------ | :------ | 1201 | | If| If:selected | 如果参数"selected"值等于 1, true, '1', 'true', 'yes'或 'y'(字符串忽略大小写) | 1202 | | IfNot| IfNot:selected | 如果参数"selected"值等于 0, false, '0', 'false', 'no'或'n'(字符串忽略大小写) | 1203 | | IfTrue| IfTrue:selected | 如果参数"selected"值等于 true 或 'true'(忽略大小写) | 1204 | | IfFalse| IfFalse:selected | 如果参数"selected"值等于 false 或 'false'(忽略大小写) | 1205 | | IfExist| IfExist:var | 如果参数"var"存在 | 1206 | | IfNotExist| IfNotExist:var | 如果参数"var"不存在 | 1207 | | IfIntEq| IfIntEq:var,1 | if (var === 1) | 1208 | | IfIntNe| IfIntNe:var,2 | if (var !== 2). 特别要注意的是如果条件参数var的数据类型不匹配, 那么If条件是成立的; 而其它几个IfIntXx当条件参数var的数据类型不匹配时, If条件不成立 | 1209 | | IfIntGt| IfIntGt:var,0 | if (var > 0) | 1210 | | IfIntLt| IfIntLt:var,1 | if (var < 0) | 1211 | | IfIntGe| IfIntGe:var,6 | if (var >= 6) | 1212 | | IfIntLe| IfIntLe:var,8 | if (var <= 8) | 1213 | | IfIntIn| IfIntIn:var,2,3,5,7 | if (in_array(var, \[2,3,5,7])) | 1214 | | IfIntNotIn| IfIntNotIn:var,2,3,5,7 | if (!in_array(var, \[2,3,5,7])) | 1215 | | IfStrEq| IfStrEq:var,waiting | if (var === 'waiting') | 1216 | | IfStrNe| IfStrNe:var,editing | if (var !== 'editing'). 特别要注意的是如果条件参数var的数据类型不匹配, 那么If条件是成立的; 而其它几个IfStrXx当条件参数var的数据类型不匹配时, If条件不成立 | 1217 | | IfStrGt| IfStrGt:var,a | if (var > 'a') | 1218 | | IfStrLt| IfStrLt:var,z | if (var < 'z') | 1219 | | IfStrGe| IfStrGe:var,A | if (var >= 'A') | 1220 | | IfStrLe| IfStrLe:var,Z | if (var <= 'Z') | 1221 | | IfStrIn| IfStrIn:var,normal,warning,error | if (in_array(var, \['normal', 'warning', 'error'], true)) | 1222 | | IfStrNotIn| IfStrNotIn:var,warning,error | if (!in_array(var, \['warning', 'error'], true)) | 1223 | 1224 | ### A.10 其它验证器 1225 | 1226 | | 其它验证器 | 示例 | 说明 | 1227 | | :------| :------ | :------ | 1228 | | Required | Required | 待验证的参数是必需的。如果验证器串联,除了条件型验证器外,必须为第一个验证器 | 1229 | | Alias | Alias:参数名称 | 自定义错误提示文本中的参数名称(必须是最后一个验证器) | 1230 | | \>>> | \>>>:这是自定义错误提示文本 | 自定义错误提示文本(与Alias验证器二选一,必须是最后一个验证器) | 1231 | | 自定义PHP函数 | function() {} | 暂不提供该机制,因为如果遇到本工具不支持的复杂参数验证,你可以直接写PHP代码来验证,不需要再经由本工具来验证(否则就是脱裤子放屁,多此一举) | 1232 | -------------------------------------------------------------------------------- /src/Validation/ValidationException.php: -------------------------------------------------------------------------------- 1 | >>"提供的字符串 26 | * $alias 参数别名,用于错误提示。它的值等于伪验证器"Alias"提供的字符串 27 | * 3. 如果自定义验证器有n个参数,那么它的验证方法应该有 3 + n 个参数, 28 | * 多出来的 n 个参数应该跟在参数 $value 的后面。 29 | * 带1个参数的验证器示例:"CustomStartWith:head" 30 | * 带多个参数的验证器示例:"CustomStrIn:started,success,failed",多个参数用逗号分隔 31 | * 4. 如果验证通过,应该返回$value参数的原始值 32 | * 5. 如果验证失败,应该抛出异常 33 | */ 34 | 35 | /** 36 | * 自定义验证器示例类 37 | * 38 | * 本类实现了以下自定义验证器: 39 | * CustomStartWith 用于验证参数是否以指定前缀开头 40 | * 41 | */ 42 | class CustomCaseValidation extends Validation 43 | { 44 | /** 45 | * @var array 验证失败时的错误提示信息的模版。 46 | * 子类的模版在运行时会与父类的 $errorTemplates 合并, 47 | * 如果出现同名的键, 子类的值会覆盖父类的值 48 | */ 49 | protected static $errorTemplates = [ 50 | 'CustomStartWith' => '“{{param}}”必须以"{{prefix}}"开头"', 51 | ]; 52 | 53 | /** 54 | * 自定义验证器"CustomStartWith"的实现方法,验证输入参数 $value 是否以指定前缀开头 55 | * 56 | * @param $value string 待验证的值 57 | * @param $prefix string 前缀。如果验证器为"CustomStartWith:head",则本参数的值为"head" 58 | * @param $reason string|null 验证失败的错误提示字符串. 如果为null, 需要自动生成 59 | * 如果验证中包含了伪验证器">>>:some reason",本参数值为"some reason", 60 | * 如果没有提供伪验证器">>>",本参数值为null 61 | * @param $alias string 参数别名, 用于错误提示。 62 | * 如果验证中包含了伪验证器"Alias:姓名",则本参数值为"姓名" 63 | * 如果没有提供"Alias",比如"name" => "StrLenGeLe:2,30",本参数值为"name" 64 | * @return mixed 返回参数 $value 的原值 65 | * @throws ValidationException 验证失败抛出异常 66 | */ 67 | public static function validateCustomStartWith($value, $prefix, $reason, $alias) 68 | { 69 | if (strpos(strval($value), $prefix) === 0) 70 | return $value; 71 | 72 | if ($reason !== null) 73 | throw new ValidationException($reason); 74 | 75 | $error = self::getErrorTemplate('CustomStartWith'); // 获取错误提示信息模版 76 | $error = str_replace('{{param}}', $alias, $error); 77 | $error = str_replace('{{prefix}}', $prefix, $error); 78 | throw new ValidationException($error); 79 | } 80 | } -------------------------------------------------------------------------------- /tests/Validation/CustomDemoValidation.php: -------------------------------------------------------------------------------- 1 | '“{{param}}”必须是Custom整数', 25 | 'CustomIntEq' => '“{{param}}”必须等于 {{value}}', 26 | 'CustomIntGeLe' => '“{{param}}”必须大于等于 {{min}} 小于等于 {{max}}', 27 | ]; 28 | 29 | /** 30 | * 自定义验证器"CustomInt"的实现方法 31 | * 32 | * @param $value string 待验证的值 33 | * @param $reason string|null 验证失败的错误提示字符串. 如果为null, 需要自动生成 34 | * @param $alias string 参数别名, 用于错误提示。 35 | * @return mixed 返回参数 $value 的原值 36 | * @throws ValidationException 验证失败抛出异常 37 | */ 38 | public static function validateCustomInt($value, $reason, $alias) 39 | { 40 | $type = gettype($value); 41 | if ($type === 'string') { 42 | if (is_numeric($value) && strpos($value, '.') === false) 43 | return $value; 44 | } elseif ($type === 'integer') { 45 | return $value; 46 | } 47 | 48 | if ($reason !== null) 49 | throw new ValidationException($reason); 50 | 51 | $error = self::getErrorTemplate('CustomInt'); 52 | $error = str_replace('{{param}}', $alias, $error); 53 | throw new ValidationException($error); 54 | } 55 | 56 | /** 57 | * 自定义验证器"CustomIntEq"的实现方法。 58 | * 检测 $value 与 $equalVal 是否相等。 59 | * 60 | * @param $value string 待验证的值 61 | * @param $equalVal string 要比较的值 62 | * @param $reason string|null 验证失败的错误提示字符串. 如果为null, 需要自动生成 63 | * @param $alias string 参数别名, 用于错误提示。 64 | * @return mixed 返回参数 $value 的原值 65 | * @throws ValidationException 验证失败抛出异常 66 | */ 67 | public static function validateCustomIntEq($value, $equalVal, $reason, $alias) 68 | { 69 | $type = gettype($value); 70 | if ($type === 'string') { 71 | if (is_numeric($value) && strpos($value, '.') === false) { 72 | $val = intval($value); 73 | if ($val == $equalVal) 74 | return $value; 75 | $isTypeError = false; 76 | } else 77 | $isTypeError = true; 78 | } elseif ($type === 'integer') { 79 | if ($value == $equalVal) 80 | return $value; 81 | $isTypeError = false; 82 | } else 83 | $isTypeError = true; 84 | 85 | if ($reason !== null) 86 | throw new ValidationException($reason); 87 | 88 | if ($isTypeError) { 89 | $error = self::getErrorTemplate('CustomInt'); 90 | $error = str_replace('{{param}}', $alias, $error); 91 | } else { 92 | $error = self::getErrorTemplate('CustomIntEq'); 93 | $error = str_replace('{{param}}', $alias, $error); 94 | $error = str_replace('{{value}}', $equalVal, $error); 95 | } 96 | throw new ValidationException($error); 97 | } 98 | 99 | /** 100 | * 自定义验证器"CustomIntGeLe"的实现方法。 101 | * 检测 $value 取值是否在[$min, $max]之间。 102 | * 103 | * @param $value string 待验证的值 104 | * @param $min string 最小值 105 | * @param $max string 最大值 106 | * @param $reason string|null 验证失败的错误提示字符串. 如果为null, 需要自动生成 107 | * @param $alias string 参数别名, 用于错误提示。 108 | * @return mixed 返回参数 $value 的原值 109 | * @throws ValidationException 验证失败抛出异常 110 | */ 111 | public static function validateCustomIntGeLe($value, $min, $max, $reason, $alias) 112 | { 113 | $type = gettype($value); 114 | if ($type === 'string') { 115 | if (is_numeric($value) && strpos($value, '.') === false) { 116 | $val = intval($value); 117 | if ($val >= $min && $val <= $max) 118 | return $value; 119 | $isTypeError = false; 120 | } else 121 | $isTypeError = true; 122 | } elseif ($type === 'integer') { 123 | if ($value >= $min && $value <= $max) 124 | return $value; 125 | $isTypeError = false; 126 | } else 127 | $isTypeError = true; 128 | 129 | if ($reason !== null) 130 | throw new ValidationException($reason); 131 | 132 | if ($isTypeError) { 133 | $error = self::getErrorTemplate('CustomInt'); 134 | $error = str_replace('{{param}}', $alias, $error); 135 | } else { 136 | $error = self::getErrorTemplate('CustomIntGeLe'); 137 | $error = str_replace('{{param}}', $alias, $error); 138 | $error = str_replace('{{min}}', $min, $error); 139 | $error = str_replace('{{max}}', $max, $error); 140 | } 141 | throw new ValidationException($error); 142 | } 143 | 144 | } -------------------------------------------------------------------------------- /tests/Validation/CustomExampleValidation.php: -------------------------------------------------------------------------------- 1 | '“{{param}}”必须大于 {{min}} 小于 {{max}}', 37 | ]; 38 | 39 | /** 40 | * @var \string[][] “错误提示信息模版” $errorTemplates 的翻译对照表 41 | * 子类的翻译对照表在运行时会与父类的 $langCode2ErrorTemplates (递归)合并, 42 | * 如果出现同名的键, 子类的值会覆盖父类的值 43 | */ 44 | protected static $langCode2ErrorTemplates = [ 45 | "zh-tw" => [ 46 | 'CustomFloatGtLt' => '“{{param}}”必須大於 {{min}} 小於 {{max}}', 47 | ], 48 | "en-us" => [ 49 | 'CustomFloatGtLt' => '{{param}} must be greater than {{min}} and less than {{max}}', 50 | ], 51 | ]; 52 | 53 | /** 54 | * 自定义验证器"CustomFloatGtLt"的参数解析方法 55 | * 56 | * 假设验证器为"CustomFloatGtLt:1.0,10" 57 | * 则 $paramString = "1.0,10" 58 | * 本函数返回值为: [1.0, 10.0] 59 | * 60 | * @param $paramString string 验证器的参数 61 | * @return array 返回解析后的参数的数组。数组的长度就是验证器的 62 | * 参数个数,必须与对应的 validateCustomFloatGtLt() 方法的参数个数匹配。 63 | * @throws ValidationException 参数解析失败抛出异常 64 | */ 65 | protected static function parseParamsOfCustomFloatGtLt($paramString) 66 | { 67 | $vals = explode(',', $paramString); 68 | if (count($vals) !== 2) 69 | throw new ValidationException("自定义验证器 CustomFloatGtLt 格式错误. 正确格式示例: CustomFloatGtLt:1.0,2.0"); 70 | $p1 = $vals[0]; 71 | $p2 = $vals[1]; 72 | if (is_numeric($p1) === false || is_numeric($p2) === false) 73 | throw new ValidationException("自定义验证器 CustomFloatGtLt 参数类型错误. 正确的示例: CustomFloatGtLt:1.0,2.0"); 74 | return [$p1, $p2]; 75 | } 76 | 77 | /** 78 | * 自定义验证器"CustomFloatGtLt"的验证方法的实现 79 | * 80 | * @param $value mixed 待验证的值 81 | * @param $min float 最小值。对应上面的 parseParamsOfCustomFloatGtLt() 方法 82 | * 返回的数组的第0个元素($p1) 83 | * @param $max float 最大值。对应上面的 parseParamsOfCustomFloatGtLt() 方法 84 | * 返回的数组的第1个元素($p2) 85 | * @param $reason string|null 验证失败的错误提示字符串. 如果为null, 需要自动生成 86 | * @param $alias string 参数别名, 用于错误提示。 87 | * @return mixed 返回参数 $value 的原值 88 | * @throws ValidationException 验证失败抛出异常 89 | */ 90 | public static function validateCustomFloatGtLt($value, $min, $max, $reason, $alias) 91 | { 92 | $type = gettype($value); 93 | if ($type === 'string') { 94 | if (is_numeric($value)) { 95 | $val = floatval($value); 96 | if ($val > $min && $val < $max) 97 | return $value; 98 | $isTypeError = false; 99 | } else 100 | $isTypeError = true; 101 | } elseif ($type === 'double' || $type === 'integer') { 102 | if ($value > $min && $value < $max) 103 | return $value; 104 | $isTypeError = false; 105 | } else 106 | $isTypeError = true; 107 | 108 | if ($reason !== null) 109 | throw new ValidationException($reason); 110 | 111 | if ($isTypeError) { 112 | $error = '“{{param}}”必须是浮点数'; 113 | $error = str_replace('{{param}}', $alias, $error); 114 | } else { 115 | $error = self::getErrorTemplate('CustomFloatGtLt'); 116 | $error = str_replace('{{param}}', $alias, $error); 117 | $error = str_replace('{{min}}', $min, $error); 118 | $error = str_replace('{{max}}', $max, $error); 119 | } 120 | throw new ValidationException($error); 121 | } 122 | 123 | } -------------------------------------------------------------------------------- /tests/Validation/CustomSampleValidation.php: -------------------------------------------------------------------------------- 1 | '“{{param}}”只能取这些值: {{valueList}}', 23 | ]; 24 | 25 | /** 26 | * @var \string[][] 文本翻译对照表 27 | * 子类的文本翻译对照表在运行时会与父类的 $langCodeToTranslations (递归)合并, 28 | * 如果出现同名的键, 子类的值会覆盖父类的值 29 | */ 30 | protected static $langCodeToTranslations = [ 31 | "zh-tw" => [ 32 | "状态" => "狀態", 33 | ], 34 | "en-us" => [ 35 | "状态" => "status", 36 | ], 37 | ]; 38 | 39 | /** 40 | * 自定义验证器"CustomStrIn"的参数解析方法 41 | * 42 | * 假设验证器为"CustomStrIn:pending,started,success,failed" 43 | * 则 $paramString = "pending,started,success,failed" 44 | * 则本函数返回值为(数组的数组): [ 45 | * ['pending', 'started', 'success', 'failed'] 46 | * ] 47 | * 返回"数组的数组"可以实现验证器的参数个数可变。 48 | * 49 | * @param $paramString string 验证器的参数 50 | * @return array 返回解析后的参数的数组。数组的长度就是验证器的 51 | * 参数个数,必须与对应的 validateCustomStrIn() 方法的参数个数匹配。 52 | */ 53 | protected static function parseParamsOfCustomStrIn($paramString) 54 | { 55 | $list = explode(',', $paramString); 56 | return [$list]; // 注意这里返回的是数组的数组 57 | } 58 | 59 | /** 60 | * 自定义验证器"CustomStrIn"的验证方法的实现 61 | * 62 | * @param $value mixed 待验证的值 63 | * @param $valueList string[] 可取值的列表。 64 | * 对应上面的 parseParamsOfCustomStrIn() 方法返回的数组的第0个元素($list) 65 | * @param $reason string|null 验证失败的错误提示字符串. 如果为null, 需要自动生成 66 | * @param $alias string 参数别名, 用于错误提示。 67 | * @return mixed 返回参数 $value 的原值 68 | * @throws ValidationException 验证失败抛出异常 69 | */ 70 | public static function validateCustomStrIn($value, $valueList, $reason, $alias) 71 | { 72 | if (is_array($valueList) === false || count($valueList) === 0) 73 | throw new ValidationException("“${alias}”参数的验证模版(StrIn:)格式错误, 必须提供可取值的列表"); 74 | 75 | if (is_string($value)) { 76 | if (in_array($value, $valueList, true)) 77 | return $value; 78 | $isTypeError = false; 79 | } else 80 | $isTypeError = true; 81 | 82 | if ($reason !== null) 83 | throw new ValidationException($reason); 84 | 85 | if ($isTypeError) { 86 | $error = '“{{param}}”必须是Custom字符串'; 87 | $error = str_replace('{{param}}', $alias, $error); 88 | } else { 89 | $error = self::getErrorTemplate('CustomStrIn'); 90 | $error = str_replace('{{param}}', $alias, $error); 91 | $error = str_replace('{{valueList}}', '"'.implode('", "', $valueList).'"', $error); 92 | } 93 | 94 | throw new ValidationException($error); 95 | } 96 | 97 | } -------------------------------------------------------------------------------- /tests/Validation/CustomValidation.php: -------------------------------------------------------------------------------- 1 | '“{{param}}”必须是Custom整数', 32 | 'CustomIntEq' => '“{{param}}”必须等于 {{value}}', 33 | 'CustomIntGeLe' => '“{{param}}”必须大于等于 {{min}} 小于等于 {{max}}', 34 | 'CustomFloat' => '“{{param}}”必须是浮点数', 35 | 'CustomFloatGtLt' => '“{{param}}”必须大于 {{min}} 小于 {{max}}', 36 | 'CustomStr' => '“{{param}}”必须是Custom字符串', 37 | 'CustomStrEq' => '“{{param}}”必须等于"{{value}}"', 38 | ]; 39 | 40 | public static function validateCustomInt($value, $reason, $alias) 41 | { 42 | $type = gettype($value); 43 | if ($type === 'string') { 44 | if (is_numeric($value) && strpos($value, '.') === false) 45 | return $value; 46 | } elseif ($type === 'integer') { 47 | return $value; 48 | } 49 | 50 | if ($reason !== null) 51 | throw new ValidationException($reason); 52 | 53 | $error = self::getErrorTemplate('CustomInt'); 54 | $error = str_replace('{{param}}', $alias, $error); 55 | throw new ValidationException($error); 56 | } 57 | 58 | public static function validateCustomIntNoErrorTemplate($value, $reason, $alias) 59 | { 60 | $type = gettype($value); 61 | if ($type === 'string') { 62 | if (is_numeric($value) && strpos($value, '.') === false) 63 | return $value; 64 | } elseif ($type === 'integer') { 65 | return $value; 66 | } 67 | 68 | if ($reason !== null) 69 | throw new ValidationException($reason); 70 | 71 | $error = self::getErrorTemplate('CustomIntNoErrorTemplate'); 72 | $error = str_replace('{{param}}', $alias, $error); 73 | throw new ValidationException($error); 74 | } 75 | 76 | public static function validateCustomIntEq($value, $equalVal, $reason, $alias) 77 | { 78 | $type = gettype($value); 79 | if ($type === 'string') { 80 | if (is_numeric($value) && strpos($value, '.') === false) { 81 | $val = intval($value); 82 | if ($val == $equalVal) 83 | return $value; 84 | $isTypeError = false; 85 | } else 86 | $isTypeError = true; 87 | } elseif ($type === 'integer') { 88 | if ($value == $equalVal) 89 | return $value; 90 | $isTypeError = false; 91 | } else 92 | $isTypeError = true; 93 | 94 | if ($reason !== null) 95 | throw new ValidationException($reason); 96 | 97 | if ($isTypeError) { 98 | $error = self::getErrorTemplate('CustomInt'); 99 | $error = str_replace('{{param}}', $alias, $error); 100 | } else { 101 | $error = self::getErrorTemplate('CustomIntEq'); 102 | $error = str_replace('{{param}}', $alias, $error); 103 | $error = str_replace('{{value}}', $equalVal, $error); 104 | } 105 | throw new ValidationException($error); 106 | } 107 | 108 | public static function validateCustomIntGeLe($value, $min, $max, $reason, $alias) 109 | { 110 | $type = gettype($value); 111 | if ($type === 'string') { 112 | if (is_numeric($value) && strpos($value, '.') === false) { 113 | $val = intval($value); 114 | if ($val >= $min && $val <= $max) 115 | return $value; 116 | $isTypeError = false; 117 | } else 118 | $isTypeError = true; 119 | } elseif ($type === 'integer') { 120 | if ($value >= $min && $value <= $max) 121 | return $value; 122 | $isTypeError = false; 123 | } else 124 | $isTypeError = true; 125 | 126 | if ($reason !== null) 127 | throw new ValidationException($reason); 128 | 129 | if ($isTypeError) { 130 | $error = self::getErrorTemplate('CustomInt'); 131 | $error = str_replace('{{param}}', $alias, $error); 132 | } else { 133 | $error = self::getErrorTemplate('CustomIntGeLe'); 134 | $error = str_replace('{{param}}', $alias, $error); 135 | $error = str_replace('{{min}}', $min, $error); 136 | $error = str_replace('{{max}}', $max, $error); 137 | } 138 | throw new ValidationException($error); 139 | } 140 | 141 | public static function validateCustomFloat($value, $reason, $alias) 142 | { 143 | $type = gettype($value); 144 | if ($type === 'string') { 145 | if (is_numeric($value)) 146 | return $value; 147 | } elseif ($type === 'double' || $type === 'integer') 148 | return $value; 149 | 150 | if ($reason !== null) 151 | throw new ValidationException($reason); 152 | 153 | $error = self::getErrorTemplate('CustomFloat'); 154 | $error = str_replace('{{param}}', $alias, $error); 155 | throw new ValidationException($error); 156 | } 157 | 158 | /** 159 | // 验证器 CustomFloatGtLt 的参数解析器(可以不实现) 160 | * @param $validatorParamString string 验证器的参数字符串。比如验证器"IntGtLt:1,3",本参数的值会是"1,3" 161 | * @return array 返回解析后的参数的数组。数组的长度就是参数个数,必须与对应的 validateCustomFloatGtLt() 方法的参数个数匹配。 162 | * @throws ValidationException 参数解析失败抛出异常 163 | */ 164 | protected static function parseParamsOfCustomFloatGtLt($validatorParamString) 165 | { 166 | $vals = explode(',', $validatorParamString); 167 | if (count($vals) < 2) // 这里写法是错的,是为了测试故意写错的 168 | throw new ValidationException("自定义验证器 CustomFloatGtLt 格式错误. 正确格式示例: CustomFloatGtLt:1.0,2.0"); 169 | $p1 = $vals[0]; 170 | $p2 = $vals[1]; 171 | if (is_numeric($p1) === false || is_numeric($p2) === false) 172 | throw new ValidationException("自定义验证器 CustomFloatGtLt 参数类型错误. 正确的示例: CustomFloatGtLt:1.0,2.0"); 173 | $vals[0] = doubleval($p1); 174 | $vals[1] = doubleval($p2); 175 | return $vals; 176 | } 177 | 178 | public static function validateCustomFloatGtLt($value, $min, $max, $reason, $alias) 179 | { 180 | $type = gettype($value); 181 | if ($type === 'string') { 182 | if (is_numeric($value)) { 183 | $val = floatval($value); 184 | if ($val > $min && $val < $max) 185 | return $value; 186 | $isTypeError = false; 187 | } else 188 | $isTypeError = true; 189 | } elseif ($type === 'double' || $type === 'integer') { 190 | if ($value > $min && $value < $max) 191 | return $value; 192 | $isTypeError = false; 193 | } else 194 | $isTypeError = true; 195 | 196 | if ($reason !== null) 197 | throw new ValidationException($reason); 198 | 199 | if ($isTypeError) { 200 | $error = self::getErrorTemplate('CustomFloat'); 201 | $error = str_replace('{{param}}', $alias, $error); 202 | } else { 203 | $error = self::getErrorTemplate('CustomFloatGtLt'); 204 | $error = str_replace('{{param}}', $alias, $error); 205 | $error = str_replace('{{min}}', $min, $error); 206 | $error = str_replace('{{max}}', $max, $error); 207 | } 208 | throw new ValidationException($error); 209 | } 210 | 211 | public static function validateCustomStr($value, $reason, $alias) 212 | { 213 | if (is_string($value)) { 214 | return $value; 215 | } 216 | 217 | if ($reason !== null) 218 | throw new ValidationException($reason); 219 | 220 | $error = self::getErrorTemplate('CustomStr'); 221 | $error = str_replace('{{param}}', $alias, $error); 222 | throw new ValidationException($error); 223 | } 224 | 225 | public static function validateCustomStrEq($value, $equalsValue, $reason, $alias) 226 | { 227 | if (is_string($value)) { 228 | if ($value === $equalsValue) 229 | return $value; 230 | $isTypeError = false; 231 | } else 232 | $isTypeError = true; 233 | 234 | if ($reason !== null) 235 | throw new ValidationException($reason); 236 | 237 | if ($isTypeError) { 238 | $error = self::getErrorTemplate('CustomStr'); 239 | $error = str_replace('{{param}}', $alias, $error); 240 | } else { 241 | $error = self::getErrorTemplate('CustomStrEq'); 242 | $error = str_replace('{{param}}', $alias, $error); 243 | $error = str_replace('{{value}}', $equalsValue, $error); 244 | } 245 | throw new ValidationException($error); 246 | } 247 | 248 | } -------------------------------------------------------------------------------- /tests/Validation/MyValidation.php: -------------------------------------------------------------------------------- 1 | [ 34 | "“{{param}}”必须是整数" => "“{{param}}”必須是整數", 35 | "“{{param}}”必须是字符串" => "“{{param}}”必須是字符串", 36 | ], 37 | "en-us" => [ 38 | "“{{param}}”必须是整数" => "{{param}} must be a integer", 39 | "“{{param}}”必须是字符串" => "{{param}} must be a string", 40 | ], 41 | ]; 42 | 43 | // 文本翻译对照表 44 | protected static $langCodeToTranslations = [ 45 | "zh-tw" => [ 46 | "变量" => "變量", 47 | "变量必须是整数" => "變量必須是整數", 48 | "自定义变量" => '自定義變量', 49 | "自定义变量必须是Custom整数" => "自定義變量必須是Custom整數", 50 | ], 51 | "en-us" => [ 52 | "变量" => "variable", 53 | "变量必须是整数" => "variable must be an integer", 54 | "自定义变量" => 'custom variable', 55 | "自定义变量必须是Custom整数" => "custom variable must be a custom integer", 56 | ], 57 | ]; 58 | 59 | } -------------------------------------------------------------------------------- /tests/Validation/MyValidation2.php: -------------------------------------------------------------------------------- 1 | [ 31 | 'Int' => '“{{param}}”必須是整數', // 🌝 32 | 'CustomInt' => '“{{param}}”必須是Custom整數', 33 | 'IntGt' => '“{{param}}”必須大於 {{min}}', 34 | 'Str' => '“{{param}}”必須是字符串', 35 | ], 36 | "en-us" => [ 37 | 'Int' => '{{param}} must be an integer', 38 | 'CustomInt' => '{{param}} must be a custom integer', 39 | 'IntGt' => '{{param}} must be greater than {{min}}', 40 | 'Str' => '{{param}} must be a string', 41 | ], 42 | ]; 43 | 44 | /** 45 | * 旧的“错误提示信息模版”翻译对照表(不建议使用) 46 | * @deprecated 47 | */ 48 | protected static $langCodeToErrorTemplates = [ 49 | "zh-tw" => [ 50 | "“{{param}}”必须是整数" => "“{{param}}”必須是整數啊", 51 | "“{{param}}”必须是字符串" => "“{{param}}”必須是字符串啊", 52 | ], 53 | "en-us" => [ 54 | "“{{param}}”必须是整数" => "{{param}} must be a integer", 55 | "“{{param}}”必须是字符串" => "{{param}} must be string", 56 | ], 57 | ]; 58 | 59 | } -------------------------------------------------------------------------------- /tests/Validation/MyValidation3.php: -------------------------------------------------------------------------------- 1 | [ 10 | 'CustomInt' => '“{{param}}”必須是Custom整數', 11 | 'CustomStrIn' => '“{{param}}”只能取這些值: {{valueList}}', 12 | ], 13 | "en-us" => [ 14 | 'CustomInt' => '{{param}} must be an custom integer', 15 | 'CustomStrIn' => '{{param}} can only take these values: {{valueList}}', 16 | ], 17 | ]; 18 | 19 | // 文本翻译对照表 20 | protected static $langCodeToTranslations = [ 21 | "zh-tw" => [ 22 | "自定义变量必须是整数" => "自定義變量必須是整數", 23 | ], 24 | "en-us" => [ 25 | "自定义变量必须是整数" => "custom variable must be an integer", 26 | ], 27 | ]; 28 | 29 | } --------------------------------------------------------------------------------