From 17b1ef81b4fd9fa9b5eda3428b54d812c71662ff Mon Sep 17 00:00:00 2001 From: mtak3 Date: Fri, 14 Feb 2020 07:47:52 +0000 Subject: [PATCH 1/3] update for cake-4.x --- .editorconfig | 16 + .gitignore | 2 + composer.json | 8 +- composer.lock | 2035 ------------------- phpunit.xml.dist | 11 - src/Plugin.php | 2 + src/Validation/Validator.php | 52 +- tests/TestCase/Validation/ValidatorTest.php | 26 +- tests/bootstrap.php | 2 +- 9 files changed, 52 insertions(+), 2102 deletions(-) create mode 100644 .editorconfig delete mode 100644 composer.lock diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..bd0ddd7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +; This file is for unifying the coding style for different editors and IDEs. +; More information at http://editorconfig.org + +root = false + +[*] +indent_style = space +indent_size = 4 +charset = "utf-8" +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.yml] +indent_style = space +indent_size = 2 diff --git a/.gitignore b/.gitignore index 57872d0..0794651 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /vendor/ +composer.lock +.phpunit.result.cache diff --git a/composer.json b/composer.json index ec0102e..ccb9bd4 100644 --- a/composer.json +++ b/composer.json @@ -3,12 +3,12 @@ "description": "CakePHP3 Validator that set the validation message", "type": "cakephp3-plugin", "require": { - "php": ">=5.6.0", - "cakephp/cakephp": ">=3.6.0,<4.0.0" + "php": ">=7.2", + "cakephp/cakephp": "^4.0" }, "require-dev": { - "cakephp/cakephp-codesniffer": "^3.0", - "phpunit/phpunit": "^4.8|^5.7|^6.0" + "cakephp/cakephp-codesniffer": "~4.0.0", + "phpunit/phpunit": "^8.5" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 28289aa..0000000 --- a/composer.lock +++ /dev/null @@ -1,2035 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "70c081c2977a9108ccc88cfbc5fc2fa0", - "packages": [ - { - "name": "aura/intl", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/auraphp/Aura.Intl.git", - "reference": "7fce228980b19bf4dee2d7bbd6202a69b0dde926" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/auraphp/Aura.Intl/zipball/7fce228980b19bf4dee2d7bbd6202a69b0dde926", - "reference": "7fce228980b19bf4dee2d7bbd6202a69b0dde926", - "shasum": "" - }, - "require": { - "php": "^5.6|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Aura\\Intl\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Aura.Intl Contributors", - "homepage": "https://github.com/auraphp/Aura.Intl/contributors" - } - ], - "description": "The Aura Intl package provides internationalization tools, specifically message translation.", - "homepage": "https://github.com/auraphp/Aura.Intl", - "keywords": [ - "g11n", - "globalization", - "i18n", - "internationalization", - "intl", - "l10n", - "localization" - ], - "time": "2017-01-20T05:00:11+00:00" - }, - { - "name": "cakephp/cakephp", - "version": "3.8.5", - "source": { - "type": "git", - "url": "https://github.com/cakephp/cakephp.git", - "reference": "ea64434740f0d2a53438f95a3de414de63a11101" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/cakephp/cakephp/zipball/ea64434740f0d2a53438f95a3de414de63a11101", - "reference": "ea64434740f0d2a53438f95a3de414de63a11101", - "shasum": "" - }, - "require": { - "aura/intl": "^3.0.0", - "cakephp/chronos": "^1.0.1", - "ext-intl": "*", - "ext-mbstring": "*", - "php": ">=5.6.0", - "psr/log": "^1.0.0", - "psr/simple-cache": "^1.0.0", - "zendframework/zend-diactoros": "^1.4.0" - }, - "conflict": { - "phpunit/phpunit": "<5.7" - }, - "replace": { - "cakephp/cache": "self.version", - "cakephp/collection": "self.version", - "cakephp/core": "self.version", - "cakephp/database": "self.version", - "cakephp/datasource": "self.version", - "cakephp/event": "self.version", - "cakephp/filesystem": "self.version", - "cakephp/form": "self.version", - "cakephp/i18n": "self.version", - "cakephp/log": "self.version", - "cakephp/orm": "self.version", - "cakephp/utility": "self.version", - "cakephp/validation": "self.version" - }, - "require-dev": { - "cakephp/cakephp-codesniffer": "^3.0", - "cakephp/chronos": "^1.2.1", - "phpunit/phpunit": "^5.7.14|^6.0" - }, - "suggest": { - "ext-curl": "To enable more efficient network calls in Http\\Client.", - "ext-openssl": "To use Security::encrypt() or have secure CSRF token generation.", - "lib-ICU": "The intl PHP library, to use Text::transliterate() or Text::slug()" - }, - "type": "library", - "autoload": { - "psr-4": { - "Cake\\": "src/" - }, - "files": [ - "src/Core/functions.php", - "src/Collection/functions.php", - "src/I18n/functions.php", - "src/Utility/bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "CakePHP Community", - "homepage": "https://github.com/cakephp/cakephp/graphs/contributors" - } - ], - "description": "The CakePHP framework", - "homepage": "https://cakephp.org", - "keywords": [ - "conventions over configuration", - "dry", - "form", - "framework", - "mvc", - "orm", - "psr-7", - "rapid-development", - "validation" - ], - "time": "2019-10-07T00:51:50+00:00" - }, - { - "name": "cakephp/chronos", - "version": "1.2.8", - "source": { - "type": "git", - "url": "https://github.com/cakephp/chronos.git", - "reference": "0292f06e8cc23fc82f0574889da2d8bf27b613c1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/cakephp/chronos/zipball/0292f06e8cc23fc82f0574889da2d8bf27b613c1", - "reference": "0292f06e8cc23fc82f0574889da2d8bf27b613c1", - "shasum": "" - }, - "require": { - "php": "^5.5.9|^7" - }, - "require-dev": { - "athletic/athletic": "~0.1", - "cakephp/cakephp-codesniffer": "^3.0", - "phpbench/phpbench": "@dev", - "phpstan/phpstan": "^0.6.4", - "phpunit/phpunit": "<6.0 || ^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Cake\\Chronos\\": "src/" - }, - "files": [ - "src/carbon_compat.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Brian Nesbitt", - "email": "brian@nesbot.com", - "homepage": "http://nesbot.com" - }, - { - "name": "The CakePHP Team", - "homepage": "http://cakephp.org" - } - ], - "description": "A simple API extension for DateTime.", - "homepage": "http://cakephp.org", - "keywords": [ - "date", - "datetime", - "time" - ], - "time": "2019-06-17T15:19:18+00:00" - }, - { - "name": "psr/http-message", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", - "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ], - "time": "2016-08-06T14:39:51+00:00" - }, - { - "name": "psr/log", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", - "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "time": "2018-11-20T15:27:04+00:00" - }, - { - "name": "psr/simple-cache", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/simple-cache.git", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\SimpleCache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interfaces for simple caching", - "keywords": [ - "cache", - "caching", - "psr", - "psr-16", - "simple-cache" - ], - "time": "2017-10-23T01:57:42+00:00" - }, - { - "name": "zendframework/zend-diactoros", - "version": "1.8.7", - "source": { - "type": "git", - "url": "https://github.com/zendframework/zend-diactoros.git", - "reference": "a85e67b86e9b8520d07e6415fcbcb8391b44a75b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/a85e67b86e9b8520d07e6415fcbcb8391b44a75b", - "reference": "a85e67b86e9b8520d07e6415fcbcb8391b44a75b", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0", - "psr/http-message": "^1.0" - }, - "provide": { - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "ext-dom": "*", - "ext-libxml": "*", - "php-http/psr7-integration-tests": "dev-master", - "phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7", - "zendframework/zend-coding-standard": "~1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-release-1.8": "1.8.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions/create_uploaded_file.php", - "src/functions/marshal_headers_from_sapi.php", - "src/functions/marshal_method_from_sapi.php", - "src/functions/marshal_protocol_version_from_sapi.php", - "src/functions/marshal_uri_from_sapi.php", - "src/functions/normalize_server.php", - "src/functions/normalize_uploaded_files.php", - "src/functions/parse_cookie_header.php" - ], - "psr-4": { - "Zend\\Diactoros\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-2-Clause" - ], - "description": "PSR HTTP Message implementations", - "homepage": "https://github.com/zendframework/zend-diactoros", - "keywords": [ - "http", - "psr", - "psr-7" - ], - "time": "2019-08-06T17:53:53+00:00" - } - ], - "packages-dev": [ - { - "name": "cakephp/cakephp-codesniffer", - "version": "3.1.2", - "source": { - "type": "git", - "url": "https://github.com/cakephp/cakephp-codesniffer.git", - "reference": "45a1dcc2e83598362b8c323df3e67510676457fe" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/cakephp/cakephp-codesniffer/zipball/45a1dcc2e83598362b8c323df3e67510676457fe", - "reference": "45a1dcc2e83598362b8c323df3e67510676457fe", - "shasum": "" - }, - "require": { - "php": ">=5.6", - "squizlabs/php_codesniffer": "^3.0.0" - }, - "require-dev": { - "phpunit/phpunit": "<6.0" - }, - "type": "phpcodesniffer-standard", - "autoload": { - "psr-4": { - "CakePHP\\": "CakePHP" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "CakePHP Community", - "homepage": "https://github.com/cakephp/cakephp-codesniffer/graphs/contributors" - } - ], - "description": "CakePHP CodeSniffer Standards", - "homepage": "http://cakephp.org", - "keywords": [ - "codesniffer", - "framework" - ], - "time": "2019-08-30T01:55:00+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "a2c590166b2133a4633738648b6b064edae0814a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a", - "reference": "a2c590166b2133a4633738648b6b064edae0814a", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "doctrine/coding-standard": "^6.0", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.13", - "phpstan/phpstan-phpunit": "^0.11", - "phpstan/phpstan-shim": "^0.11", - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "time": "2019-03-17T17:37:11+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.9.3", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/007c053ae6f31bba39dfa19a7726f56e9763bbea", - "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "replace": { - "myclabs/deep-copy": "self.version" - }, - "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, - "files": [ - "src/DeepCopy/deep_copy.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "time": "2019-08-09T12:45:53+00:00" - }, - { - "name": "phar-io/manifest", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "phar-io/version": "^1.0.1", - "php": "^5.6 || ^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2017-03-05T18:14:27+00:00" - }, - { - "name": "phar-io/version", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "time": "2017-03-05T17:38:23+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a", - "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "~6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "time": "2018-08-07T13:53:10+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "4.3.2", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/b83ff7cfcfee7827e1e78b637a5904fe6a96698e", - "reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e", - "shasum": "" - }, - "require": { - "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0.0 || ^2.0.0", - "phpdocumentor/type-resolver": "~0.4 || ^1.0.0", - "webmozart/assert": "^1.0" - }, - "require-dev": { - "doctrine/instantiator": "^1.0.5", - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^6.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2019-09-12T14:27:41+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", - "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", - "shasum": "" - }, - "require": { - "php": "^7.1", - "phpdocumentor/reflection-common": "^2.0" - }, - "require-dev": { - "ext-tokenizer": "^7.1", - "mockery/mockery": "~1", - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "time": "2019-08-22T18:11:29+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "1.9.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/f6811d96d97bdf400077a0cc100ae56aa32b9203", - "reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", - "sebastian/comparator": "^1.1|^2.0|^3.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0" - }, - "require-dev": { - "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.8.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "time": "2019-10-03T11:07:50+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "5.3.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "c89677919c5dd6d3b3852f230a663118762218ac" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c89677919c5dd6d3b3852f230a663118762218ac", - "reference": "c89677919c5dd6d3b3852f230a663118762218ac", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-xmlwriter": "*", - "php": "^7.0", - "phpunit/php-file-iterator": "^1.4.2", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^2.0.1", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.0", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "suggest": { - "ext-xdebug": "^2.5.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.3.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "time": "2018-04-06T15:36:58+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "1.4.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "time": "2017-11-27T13:52:08+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "time": "2015-06-21T13:50:34+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "1.0.9", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "time": "2017-02-26T11:10:40+00:00" - }, - { - "name": "phpunit/php-token-stream", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "791198a2c6254db10131eecfe8c06670700904db" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", - "reference": "791198a2c6254db10131eecfe8c06670700904db", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.2.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "time": "2017-11-27T05:48:46+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "6.5.14", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "bac23fe7ff13dbdb461481f706f0e9fe746334b7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bac23fe7ff13dbdb461481f706f0e9fe746334b7", - "reference": "bac23fe7ff13dbdb461481f706f0e9fe746334b7", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "myclabs/deep-copy": "^1.6.1", - "phar-io/manifest": "^1.0.1", - "phar-io/version": "^1.0", - "php": "^7.0", - "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^5.3", - "phpunit/php-file-iterator": "^1.4.3", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^1.0.9", - "phpunit/phpunit-mock-objects": "^5.0.9", - "sebastian/comparator": "^2.1", - "sebastian/diff": "^2.0", - "sebastian/environment": "^3.1", - "sebastian/exporter": "^3.1", - "sebastian/global-state": "^2.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^1.0", - "sebastian/version": "^2.0.1" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2", - "phpunit/dbunit": "<3.0" - }, - "require-dev": { - "ext-pdo": "*" - }, - "suggest": { - "ext-xdebug": "*", - "phpunit/php-invoker": "^1.1" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.5.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "time": "2019-02-01T05:22:47+00:00" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "5.0.10", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/cd1cf05c553ecfec36b170070573e540b67d3f1f", - "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.5", - "php": "^7.0", - "phpunit/php-text-template": "^1.2.1", - "sebastian/exporter": "^3.1" - }, - "conflict": { - "phpunit/phpunit": "<6.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.5.11" - }, - "suggest": { - "ext-soap": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "abandoned": true, - "time": "2018-08-09T05:50:03+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04T06:30:41+00:00" - }, - { - "name": "sebastian/comparator", - "version": "2.1.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", - "shasum": "" - }, - "require": { - "php": "^7.0", - "sebastian/diff": "^2.0 || ^3.0", - "sebastian/exporter": "^3.1" - }, - "require-dev": { - "phpunit/phpunit": "^6.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "time": "2018-02-01T13:46:46+00:00" - }, - { - "name": "sebastian/diff", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff" - ], - "time": "2017-08-03T08:09:46+00:00" - }, - { - "name": "sebastian/environment", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "time": "2017-07-01T08:51:00+00:00" - }, - { - "name": "sebastian/exporter", - "version": "3.1.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e", - "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e", - "shasum": "" - }, - "require": { - "php": "^7.0", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "time": "2019-09-14T09:02:43+00:00" - }, - { - "name": "sebastian/global-state", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "time": "2017-04-27T15:39:26+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "shasum": "" - }, - "require": { - "php": "^7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-08-03T12:35:26+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "773f97c67f28de00d397be301821b06708fca0be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", - "reference": "773f97c67f28de00d397be301821b06708fca0be", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2017-03-29T09:07:27+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-03T06:23:57+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "shasum": "" - }, - "require": { - "php": ">=5.6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2015-07-28T20:34:47+00:00" - }, - { - "name": "sebastian/version", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03T07:35:21+00:00" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "3.5.1", - "source": { - "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "82cd0f854ceca17731d6d019c7098e3755c45060" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/82cd0f854ceca17731d6d019c7098e3755c45060", - "reference": "82cd0f854ceca17731d6d019c7098e3755c45060", - "shasum": "" - }, - "require": { - "ext-simplexml": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" - }, - "bin": [ - "bin/phpcs", - "bin/phpcbf" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "lead" - } - ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", - "keywords": [ - "phpcs", - "standards" - ], - "time": "2019-10-16T21:14:26+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.12.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "550ebaac289296ce228a706d0867afc34687e3f4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4", - "reference": "550ebaac289296ce228a706d0867afc34687e3f4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.12-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "time": "2019-08-06T08:03:45+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.1.3", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", - "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2019-06-13T22:48:21+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "88e6d84706d09a236046d686bbea96f07b3a34f4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/88e6d84706d09a236046d686bbea96f07b3a34f4", - "reference": "88e6d84706d09a236046d686bbea96f07b3a34f4", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0", - "symfony/polyfill-ctype": "^1.8" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.36 || ^7.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "time": "2019-08-24T08:43:50+00:00" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": ">=5.6.0" - }, - "platform-dev": [] -} diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 36ec542..3c32348 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -3,7 +3,6 @@ colors="true" processIsolation="false" stopOnFailure="false" - syntaxCheck="false" bootstrap="./tests/bootstrap.php" > @@ -19,14 +18,4 @@ - - - ./vendor/ - ./vendor/ - - ./tests/ - ./tests/ - - - diff --git a/src/Plugin.php b/src/Plugin.php index 43f6042..ed9fa06 100755 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -1,4 +1,6 @@ _presenceMessages[$field])) { return $this->_presenceMessages[$field]; } - + $message = $this->getMessage('required', [$field]); if ($message) { return $message; @@ -38,18 +36,14 @@ public function getRequiredMessage($field) } /** - * Gets the notEmpty message for a field - * override metho - * - * @param string $field Field name - * @return string|null + * {@inheritDoc} */ - public function getNotEmptyMessage($field) + public function getNotEmptyMessage(string $field): ?string { if (isset($this->_allowEmptyMessages[$field])) { return $this->_allowEmptyMessages[$field]; } - + $message = $this->getMessage('notEmpty', [$field]); if ($message) { return $message; @@ -59,30 +53,9 @@ public function getNotEmptyMessage($field) } /** - * Adds a new rule to a field's rule set. If second argument is an array - * then rules list for the field will be replaced with second argument and - * third argument will be ignored. - * - * ### Example: - * - * ``` - * $validator - * ->add('title', 'required', ['rule' => 'notBlank']) - * ->add('user_id', 'valid', ['rule' => 'numeric', 'message' => 'Invalid User']) - * - * $validator->add('password', [ - * 'size' => ['rule' => ['lengthBetween', 8, 20]], - * 'hasSpecialCharacter' => ['rule' => 'validateSpecialchar', 'message' => 'not valid'] - * ]); - * ``` - * override method - * - * @param string $field The name of the field from which the rule will be added - * @param array|string $name The alias for a single rule or multiple rules array - * @param array|\Cake\Validation\ValidationRule $rule the rule to add - * @return $this + * {@inheritDoc} */ - public function add($field, $name, $rule = []) + public function add(string $field, $name, $rule = []) { if (!is_array($name)) { $rules = [$name => $rule]; @@ -122,9 +95,10 @@ public function add($field, $name, $rule = []) * @param array $args args * @return string|null */ - public function getMessage($validationName, $args) + public function getMessage(string $validationName, array $args = []) { - if (!$message = Configure::read($this->_configureKeyPrefix . '.' . $validationName)) { + $message = Configure::read($this->_configureKeyPrefix . '.' . $validationName); + if (!$message) { return null; } $args[0] = __d($this->_i18nDomain, $args[0]); diff --git a/tests/TestCase/Validation/ValidatorTest.php b/tests/TestCase/Validation/ValidatorTest.php index ce94830..15c3efd 100755 --- a/tests/TestCase/Validation/ValidatorTest.php +++ b/tests/TestCase/Validation/ValidatorTest.php @@ -1,4 +1,6 @@ Validator ->requirePresence('column') ->maxLength('column', 2) - ->notEmpty('column'); + ->notEmptyString('column'); - $errors = $this->Validator->errors([]); + $errors = $this->Validator->validate([]); $this->assertEquals('This field is required', $errors['column']['_required']); - $errors = $this->Validator->errors(['column' => '']); + $errors = $this->Validator->validate(['column' => '']); $this->assertEquals('This field cannot be left empty', $errors['column']['_empty']); - $errors = $this->Validator->errors(['column' => 'abc']); + $errors = $this->Validator->validate(['column' => 'abc']); $this->assertEquals('The provided value is invalid', $errors['column']['maxLength']); } @@ -61,9 +63,9 @@ public function testValidationName() { $this->Validator ->scalar('column') // call Validation::isScalar() - ->allowEmpty('column'); + ->allowEmptyString('column'); - $errors = $this->Validator->errors(['column' => ['aaa']]); + $errors = $this->Validator->validate(['column' => ['aaa']]); // not ValidationName $this->assertEquals('The provided value is invalid', $errors['column']['scalar']); } @@ -84,15 +86,15 @@ public function testOverrideMessage() $this->Validator ->requirePresence('column') ->maxLength('column', 2) - ->notEmpty('column'); + ->notEmptyString('column'); - $errors = $this->Validator->errors([]); + $errors = $this->Validator->validate([]); $this->assertEquals('required!!', $errors['column']['_required']); - $errors = $this->Validator->errors(['column' => '']); + $errors = $this->Validator->validate(['column' => '']); $this->assertEquals('notEmpty!!', $errors['column']['_empty']); - $errors = $this->Validator->errors(['column' => 'abc']); + $errors = $this->Validator->validate(['column' => 'abc']); $this->assertEquals('maxLength!! less than 2', $errors['column']['maxLength']); } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index de326ed..5cb1ce2 100755 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,5 +1,5 @@ Date: Mon, 16 Mar 2020 06:14:27 +0000 Subject: [PATCH 2/3] each message for allow/not Empty XXXX method --- :q! | 1759 +++++++++++++++++++ src/Validation/Validator.php | 96 + tests/TestCase/Validation/ValidatorTest.php | 87 + 3 files changed, 1942 insertions(+) create mode 100644 :q! diff --git a/:q! b/:q! new file mode 100644 index 0000000..ab88843 --- /dev/null +++ b/:q! @@ -0,0 +1,1759 @@ +'; + + /** + * Greater than or equal to comparison operator. + */ + public const COMPARE_GREATER_OR_EQUAL = '>='; + + /** + * Less than comparison operator. + */ + public const COMPARE_LESS = '<'; + + /** + * Less than or equal to comparison operator. + */ + public const COMPARE_LESS_OR_EQUAL = '<='; + + /** + * Datetime ISO8601 format + */ + public const DATETIME_ISO8601 = 'iso8601'; + + /** + * Some complex patterns needed in multiple places + * + * @var array + */ + protected static $_pattern = [ + 'hostname' => '(?:[_\p{L}0-9][-_\p{L}0-9]*\.)*(?:[\p{L}0-9][-\p{L}0-9]{0,62})\.(?:(?:[a-z]{2}\.)?[a-z]{2,})', + 'latitude' => '[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?)', + 'longitude' => '[-+]?(180(\.0+)?|((1[0-7]\d)|([1-9]?\d))(\.\d+)?)', + ]; + + /** + * Holds an array of errors messages set in this class. + * These are used for debugging purposes + * + * @var array + */ + public static $errors = []; + + /** + * Checks that a string contains something other than whitespace + * + * Returns true if string contains something other than whitespace + * + * @param mixed $check Value to check + * @return bool Success + */ + public static function notBlank($check): bool + { + if (empty($check) && !is_bool($check) && !is_numeric($check)) { + return false; + } + + return static::_check($check, '/[^\s]+/m'); + } + + /** + * Checks that a string contains only integer or letters. + * + * This method's definition of letters and integers includes unicode characters. + * Use `asciiAlphaNumeric()` if you want to exclude unicode. + * + * @param mixed $check Value to check + * @return bool Success + */ + public static function alphaNumeric($check): bool + { + if ((empty($check) && $check !== '0') || !is_scalar($check)) { + return false; + } + + return self::_check($check, '/^[\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]+$/Du'); + } + + /** + * Checks that a doesn't contain any alpha numeric characters + * + * This method's definition of letters and integers includes unicode characters. + * Use `notAsciiAlphaNumeric()` if you want to exclude ascii only. + * + * @param mixed $check Value to check + * @return bool Success + */ + public static function notAlphaNumeric($check): bool + { + return !static::alphaNumeric($check); + } + + /** + * Checks that a string contains only ascii integer or letters. + * + * @param mixed $check Value to check + * @return bool Success + */ + public static function asciiAlphaNumeric($check): bool + { + if ((empty($check) && $check !== '0') || !is_scalar($check)) { + return false; + } + + return self::_check($check, '/^[[:alnum:]]+$/'); + } + + /** + * Checks that a doesn't contain any non-ascii alpha numeric characters + * + * @param mixed $check Value to check + * @return bool Success + */ + public static function notAsciiAlphaNumeric($check): bool + { + return !static::asciiAlphaNumeric($check); + } + + /** + * Checks that a string length is within specified range. + * Spaces are included in the character count. + * Returns true if string matches value min, max, or between min and max, + * + * @param mixed $check Value to check for length + * @param int $min Minimum value in range (inclusive) + * @param int $max Maximum value in range (inclusive) + * @return bool Success + */ + public static function lengthBetween($check, int $min, int $max): bool + { + if (!is_scalar($check)) { + return false; + } + $length = mb_strlen((string)$check); + + return $length >= $min && $length <= $max; + } + + /** + * Validation of credit card numbers. + * Returns true if $check is in the proper credit card format. + * + * @param mixed $check credit card number to validate + * @param string|string[] $type 'all' may be passed as a string, defaults to fast which checks format of + * most major credit cards if an array is used only the values of the array are checked. + * Example: ['amex', 'bankcard', 'maestro'] + * @param bool $deep set to true this will check the Luhn algorithm of the credit card. + * @param string|null $regex A custom regex, this will be used instead of the defined regex values. + * @return bool Success + * @see \Cake\Validation\Validation::luhn() + */ + public static function creditCard($check, $type = 'fast', bool $deep = false, ?string $regex = null): bool + { + if (!(is_string($check) || is_int($check))) { + return false; + } + + $check = str_replace(['-', ' '], '', (string)$check); + if (mb_strlen($check) < 13) { + return false; + } + + if ($regex !== null && static::_check($check, $regex)) { + return !$deep || static::luhn($check); + } + $cards = [ + 'all' => [ + 'amex' => '/^3[47]\\d{13}$/', + 'bankcard' => '/^56(10\\d\\d|022[1-5])\\d{10}$/', + 'diners' => '/^(?:3(0[0-5]|[68]\\d)\\d{11})|(?:5[1-5]\\d{14})$/', + 'disc' => '/^(?:6011|650\\d)\\d{12}$/', + 'electron' => '/^(?:417500|4917\\d{2}|4913\\d{2})\\d{10}$/', + 'enroute' => '/^2(?:014|149)\\d{11}$/', + 'jcb' => '/^(3\\d{4}|2131|1800)\\d{11}$/', + 'maestro' => '/^(?:5020|6\\d{3})\\d{12}$/', + 'mc' => '/^(5[1-5]\\d{14})|(2(?:22[1-9]|2[3-9][0-9]|[3-6][0-9]{2}|7[0-1][0-9]|720)\\d{12})$/', + 'solo' => '/^(6334[5-9][0-9]|6767[0-9]{2})\\d{10}(\\d{2,3})?$/', + // phpcs:ignore Generic.Files.LineLength + 'switch' => '/^(?:49(03(0[2-9]|3[5-9])|11(0[1-2]|7[4-9]|8[1-2])|36[0-9]{2})\\d{10}(\\d{2,3})?)|(?:564182\\d{10}(\\d{2,3})?)|(6(3(33[0-4][0-9])|759[0-9]{2})\\d{10}(\\d{2,3})?)$/', + 'visa' => '/^4\\d{12}(\\d{3})?$/', + 'voyager' => '/^8699[0-9]{11}$/', + ], + // phpcs:ignore Generic.Files.LineLength + 'fast' => '/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6011[0-9]{12}|3(?:0[0-5]|[68][0-9])[0-9]{11}|3[47][0-9]{13})$/', + ]; + + if (is_array($type)) { + foreach ($type as $value) { + $regex = $cards['all'][strtolower($value)]; + + if (static::_check($check, $regex)) { + return static::luhn($check); + } + } + } elseif ($type === 'all') { + foreach ($cards['all'] as $value) { + $regex = $value; + + if (static::_check($check, $regex)) { + return static::luhn($check); + } + } + } else { + $regex = $cards['fast']; + + if (static::_check($check, $regex)) { + return static::luhn($check); + } + } + + return false; + } + + /** + * Used to check the count of a given value of type array or Countable. + * + * @param mixed $check The value to check the count on. + * @param string $operator Can be either a word or operand + * is greater >, is less <, greater or equal >= + * less or equal <=, is less <, equal to ==, not equal != + * @param int $expectedCount The expected count value. + * @return bool Success + */ + public static function numElements($check, string $operator, int $expectedCount): bool + { + if (!is_array($check) && !$check instanceof Countable) { + return false; + } + + return self::comparison(count($check), $operator, $expectedCount); + } + + /** + * Used to compare 2 numeric values. + * + * @param string|int $check1 The left value to compare. + * @param string $operator Can be either a word or operand + * is greater >, is less <, greater or equal >= + * less or equal <=, is less <, equal to ==, not equal != + * @param string|int $check2 The right value to compare. + * @return bool Success + */ + public static function comparison($check1, string $operator, $check2): bool + { + if ((float)$check1 != $check1) { + return false; + } + + $operator = str_replace([' ', "\t", "\n", "\r", "\0", "\x0B"], '', strtolower($operator)); + switch ($operator) { + case static::COMPARE_GREATER: + if ($check1 > $check2) { + return true; + } + break; + case static::COMPARE_LESS: + if ($check1 < $check2) { + return true; + } + break; + case static::COMPARE_GREATER_OR_EQUAL: + if ($check1 >= $check2) { + return true; + } + break; + case static::COMPARE_LESS_OR_EQUAL: + if ($check1 <= $check2) { + return true; + } + break; + case static::COMPARE_EQUAL: + if ($check1 == $check2) { + return true; + } + break; + case static::COMPARE_NOT_EQUAL: + if ($check1 != $check2) { + return true; + } + break; + case static::COMPARE_SAME: + if ($check1 === $check2) { + return true; + } + break; + case static::COMPARE_NOT_SAME: + if ($check1 !== $check2) { + return true; + } + break; + default: + static::$errors[] = 'You must define the $operator parameter for Validation::comparison()'; + } + + return false; + } + + /** + * Compare one field to another. + * + * If both fields have exactly the same value this method will return true. + * + * @param mixed $check The value to find in $field. + * @param string $field The field to check $check against. This field must be present in $context. + * @param array $context The validation context. + * @return bool + */ + public static function compareWith($check, string $field, array $context): bool + { + return self::compareFields($check, $field, static::COMPARE_SAME, $context); + } + + /** + * Compare one field to another. + * + * Return true if the comparison matches the expected result. + * + * @param mixed $check The value to find in $field. + * @param string $field The field to check $check against. This field must be present in $context. + * @param string $operator Comparison operator. + * @param array $context The validation context. + * @return bool + * @since 3.6.0 + */ + public static function compareFields($check, string $field, string $operator, array $context): bool + { + if (!isset($context['data']) || !array_key_exists($field, $context['data'])) { + return false; + } + + return static::comparison($check, $operator, $context['data'][$field]); + } + + /** + * Checks if a string contains one or more non-alphanumeric characters. + * + * Returns true if string contains at least the specified number of non-alphanumeric characters + * + * @param mixed $check Value to check + * @param int $count Number of non-alphanumerics to check for + * @return bool Success + * @deprecated 4.0 Use notAlphaNumeric() instead. Will be removed in 5.0 + */ + public static function containsNonAlphaNumeric($check, int $count = 1): bool + { + if (!is_string($check)) { + return false; + } + + $matches = preg_match_all('/[^a-zA-Z0-9]/', $check); + + return $matches >= $count; + } + + /** + * Used when a custom regular expression is needed. + * + * @param string $check The value to check. + * @param string|null $regex If $check is passed as a string, $regex must also be set to valid regular expression + * @return bool Success + */ + public static function custom(string $check, ?string $regex = null): bool + { + if ($regex === null) { + static::$errors[] = 'You must define a regular expression for Validation::custom()'; + + return false; + } + + return static::_check($check, $regex); + } + + /** + * Date validation, determines if the string passed is a valid date. + * keys that expect full month, day and year will validate leap years. + * + * Years are valid from 0001 to 2999. + * + * ### Formats: + * + * - `dmy` 27-12-2006 or 27-12-06 separators can be a space, period, dash, forward slash + * - `mdy` 12-27-2006 or 12-27-06 separators can be a space, period, dash, forward slash + * - `ymd` 2006-12-27 or 06-12-27 separators can be a space, period, dash, forward slash + * - `dMy` 27 December 2006 or 27 Dec 2006 + * - `Mdy` December 27, 2006 or Dec 27, 2006 comma is optional + * - `My` December 2006 or Dec 2006 + * - `my` 12/2006 or 12/06 separators can be a space, period, dash, forward slash + * - `ym` 2006/12 or 06/12 separators can be a space, period, dash, forward slash + * - `y` 2006 just the year without any separators + * + * @param mixed $check a valid date string/object + * @param string|array $format Use a string or an array of the keys above. + * Arrays should be passed as ['dmy', 'mdy', etc] + * @param string|null $regex If a custom regular expression is used this is the only validation that will occur. + * @return bool Success + */ + public static function date($check, $format = 'ymd', ?string $regex = null): bool + { + if ($check instanceof DateTimeInterface) { + return true; + } + if (is_object($check)) { + return false; + } + if (is_array($check)) { + $check = static::_getDateString($check); + $format = 'ymd'; + } + + if ($regex !== null) { + return static::_check($check, $regex); + } + $month = '(0[123456789]|10|11|12)'; + $separator = '([- /.])'; + // Don't allow 0000, but 0001-2999 are ok. + $fourDigitYear = '(?:(?!0000)[012]\d{3})'; + $twoDigitYear = '(?:\d{2})'; + $year = '(?:' . $fourDigitYear . '|' . $twoDigitYear . ')'; + + // phpcs:disable Generic.Files.LineLength + // 2 or 4 digit leap year sub-pattern + $leapYear = '(?:(?:(?:(?!0000)[012]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))'; + // 4 digit leap year sub-pattern + $fourDigitLeapYear = '(?:(?:(?:(?!0000)[012]\\d)(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))'; + + $regex['dmy'] = '%^(?:(?:31(\\/|-|\\.|\\x20)(?:0?[13578]|1[02]))\\1|(?:(?:29|30)' . + $separator . '(?:0?[1,3-9]|1[0-2])\\2))' . $year . '$|^(?:29' . + $separator . '0?2\\3' . $leapYear . ')$|^(?:0?[1-9]|1\\d|2[0-8])' . + $separator . '(?:(?:0?[1-9])|(?:1[0-2]))\\4' . $year . '$%'; + + $regex['mdy'] = '%^(?:(?:(?:0?[13578]|1[02])(\\/|-|\\.|\\x20)31)\\1|(?:(?:0?[13-9]|1[0-2])' . + $separator . '(?:29|30)\\2))' . $year . '$|^(?:0?2' . $separator . '29\\3' . $leapYear . ')$|^(?:(?:0?[1-9])|(?:1[0-2]))' . + $separator . '(?:0?[1-9]|1\\d|2[0-8])\\4' . $year . '$%'; + + $regex['ymd'] = '%^(?:(?:' . $leapYear . + $separator . '(?:0?2\\1(?:29)))|(?:' . $year . + $separator . '(?:(?:(?:0?[13578]|1[02])\\2(?:31))|(?:(?:0?[1,3-9]|1[0-2])\\2(29|30))|(?:(?:0?[1-9])|(?:1[0-2]))\\2(?:0?[1-9]|1\\d|2[0-8]))))$%'; + + $regex['dMy'] = '/^((31(?!\\ (Feb(ruary)?|Apr(il)?|June?|(Sep(?=\\b|t)t?|Nov)(ember)?)))|((30|29)(?!\\ Feb(ruary)?))|(29(?=\\ Feb(ruary)?\\ ' . $fourDigitLeapYear . '))|(0?[1-9])|1\\d|2[0-8])\\ (Jan(uary)?|Feb(ruary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sep(?=\\b|t)t?|Nov|Dec)(ember)?)\\ ' . $fourDigitYear . '$/'; + + $regex['Mdy'] = '/^(?:(((Jan(uary)?|Ma(r(ch)?|y)|Jul(y)?|Aug(ust)?|Oct(ober)?|Dec(ember)?)\\ 31)|((Jan(uary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sep)(tember)?|(Nov|Dec)(ember)?)\\ (0?[1-9]|([12]\\d)|30))|(Feb(ruary)?\\ (0?[1-9]|1\\d|2[0-8]|(29(?=,?\\ ' . $fourDigitLeapYear . ')))))\\,?\\ ' . $fourDigitYear . ')$/'; + + $regex['My'] = '%^(Jan(uary)?|Feb(ruary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sep(?=\\b|t)t?|Nov|Dec)(ember)?)' . + $separator . $fourDigitYear . '$%'; + // phpcs:enable Generic.Files.LineLength + + $regex['my'] = '%^(' . $month . $separator . $year . ')$%'; + $regex['ym'] = '%^(' . $year . $separator . $month . ')$%'; + $regex['y'] = '%^(' . $fourDigitYear . ')$%'; + + $format = is_array($format) ? array_values($format) : [$format]; + foreach ($format as $key) { + if (static::_check($check, $regex[$key]) === true) { + return true; + } + } + + return false; + } + + /** + * Validates a datetime value + * + * All values matching the "date" core validation rule, and the "time" one will be valid + * + * @param mixed $check Value to check + * @param string|array $dateFormat Format of the date part. See Validation::date() for more information. + * Or `Validation::DATETIME_ISO8601` to valid an ISO8601 datetime value. + * @param string|null $regex Regex for the date part. If a custom regular expression is used + * this is the only validation that will occur. + * @return bool True if the value is valid, false otherwise + * @see \Cake\Validation\Validation::date() + * @see \Cake\Validation\Validation::time() + */ + public static function datetime($check, $dateFormat = 'ymd', ?string $regex = null): bool + { + if ($check instanceof DateTimeInterface) { + return true; + } + if (is_object($check)) { + return false; + } + if ($dateFormat === static::DATETIME_ISO8601 && !static::iso8601($check)) { + return false; + } + + $valid = false; + if (is_array($check)) { + $check = static::_getDateString($check); + $dateFormat = 'ymd'; + } + $parts = preg_split('/[\sT]+/', $check); + if (!empty($parts) && count($parts) > 1) { + $date = rtrim(array_shift($parts), ','); + $time = implode(' ', $parts); + if ($dateFormat === static::DATETIME_ISO8601) { + $dateFormat = 'ymd'; + $time = preg_split("/[TZ\-\+\.]/", $time); + $time = array_shift($time); + } + $valid = static::date($date, $dateFormat, $regex) && static::time($time); + } + + return $valid; + } + + /** + * Validates an iso8601 datetime format + * ISO8601 recognize datetime like 2019 as a valid date. To validate and check date integrity, use @see \Cake\Validation\Validation::datetime() + * + * @param mixed $check Value to check + * @return bool True if the value is valid, false otherwise + * @see Regex credits: https://www.myintervals.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/ + */ + public static function iso8601($check): bool + { + if ($check instanceof DateTimeInterface) { + return true; + } + if (is_object($check)) { + return false; + } + + // phpcs:ignore Generic.Files.LineLength + $regex = '/^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/'; + + return static::_check($check, $regex); + } + + /** + * Time validation, determines if the string passed is a valid time. + * Validates time as 24hr (HH:MM[:SS][.FFFFFF]) or am/pm ([H]H:MM[a|p]m) + * + * Seconds and fractional seconds (microseconds) are allowed but optional + * in 24hr format. + * + * @param mixed $check a valid time string/object + * @return bool Success + */ + public static function time($check): bool + { + if ($check instanceof DateTimeInterface) { + return true; + } + if (is_array($check)) { + $check = static::_getDateString($check); + } + + if (!is_scalar($check)) { + return false; + } + + $meridianClockRegex = '^((0?[1-9]|1[012])(:[0-5]\d){0,2} ?([AP]M|[ap]m))$'; + $standardClockRegex = '^([01]\d|2[0-3])((:[0-5]\d){0,2}|(:[0-5]\d){2}\.\d{0,6})$'; + + return static::_check($check, '%' . $meridianClockRegex . '|' . $standardClockRegex . '%'); + } + + /** + * Date and/or time string validation. + * Uses `I18n::Time` to parse the date. This means parsing is locale dependent. + * + * @param mixed $check a date string or object (will always pass) + * @param string $type Parser type, one out of 'date', 'time', and 'datetime' + * @param string|int|null $format any format accepted by IntlDateFormatter + * @return bool Success + * @throws \InvalidArgumentException when unsupported $type given + * @see \Cake\I18n\Time::parseDate(), \Cake\I18n\Time::parseTime(), \Cake\I18n\Time::parseDateTime() + */ + public static function localizedTime($check, string $type = 'datetime', $format = null): bool + { + if ($check instanceof DateTimeInterface) { + return true; + } + if (is_object($check)) { + return false; + } + static $methods = [ + 'date' => 'parseDate', + 'time' => 'parseTime', + 'datetime' => 'parseDateTime', + ]; + if (empty($methods[$type])) { + throw new InvalidArgumentException('Unsupported parser type given.'); + } + $method = $methods[$type]; + + return Time::$method($check, $format) !== null; + } + + /** + * Validates if passed value is boolean-like. + * + * The list of what is considered to be boolean values, may be set via $booleanValues. + * + * @param bool|int|string $check Value to check. + * @param array $booleanValues List of valid boolean values, defaults to `[true, false, 0, 1, '0', '1']`. + * @return bool Success. + */ + public static function boolean($check, array $booleanValues = []): bool + { + if (!$booleanValues) { + $booleanValues = [true, false, 0, 1, '0', '1']; + } + + return in_array($check, $booleanValues, true); + } + + /** + * Validates if given value is truthy. + * + * The list of what is considered to be truthy values, may be set via $truthyValues. + * + * @param bool|int|string $check Value to check. + * @param array $truthyValues List of valid truthy values, defaults to `[true, 1, '1']`. + * @return bool Success. + */ + public static function truthy($check, array $truthyValues = []): bool + { + if (!$truthyValues) { + $truthyValues = [true, 1, '1']; + } + + return in_array($check, $truthyValues, true); + } + + /** + * Validates if given value is falsey. + * + * The list of what is considered to be falsey values, may be set via $falseyValues. + * + * @param bool|int|string $check Value to check. + * @param array $falseyValues List of valid falsey values, defaults to `[false, 0, '0']`. + * @return bool Success. + */ + public static function falsey($check, array $falseyValues = []): bool + { + if (!$falseyValues) { + $falseyValues = [false, 0, '0']; + } + + return in_array($check, $falseyValues, true); + } + + /** + * Checks that a value is a valid decimal. Both the sign and exponent are optional. + * + * Valid Places: + * + * - null => Any number of decimal places, including none. The '.' is not required. + * - true => Any number of decimal places greater than 0, or a float|double. The '.' is required. + * - 1..N => Exactly that many number of decimal places. The '.' is required. + * + * @param mixed $check The value the test for decimal. + * @param int|true|null $places Decimal places. + * @param string|null $regex If a custom regular expression is used, this is the only validation that will occur. + * @return bool Success + */ + public static function decimal($check, $places = null, ?string $regex = null): bool + { + if (!is_scalar($check)) { + return false; + } + + if ($regex === null) { + $lnum = '[0-9]+'; + $dnum = "[0-9]*[\.]{$lnum}"; + $sign = '[+-]?'; + $exp = "(?:[eE]{$sign}{$lnum})?"; + + if ($places === null) { + $regex = "/^{$sign}(?:{$lnum}|{$dnum}){$exp}$/"; + } elseif ($places === true) { + if (is_float($check) && floor($check) === $check) { + $check = sprintf('%.1f', $check); + } + $regex = "/^{$sign}{$dnum}{$exp}$/"; + } elseif (is_numeric($places)) { + $places = '[0-9]{' . $places . '}'; + $dnum = "(?:[0-9]*[\.]{$places}|{$lnum}[\.]{$places})"; + $regex = "/^{$sign}{$dnum}{$exp}$/"; + } else { + return false; + } + } + + // account for localized floats. + $locale = ini_get('intl.default_locale') ?: static::DEFAULT_LOCALE; + $formatter = new NumberFormatter($locale, NumberFormatter::DECIMAL); + $decimalPoint = $formatter->getSymbol(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL); + $groupingSep = $formatter->getSymbol(NumberFormatter::GROUPING_SEPARATOR_SYMBOL); + + // There are two types of non-breaking spaces - we inject a space to account for human input + if ($groupingSep == "\xc2\xa0" || $groupingSep == "\xe2\x80\xaf") { + $check = str_replace([' ', $groupingSep, $decimalPoint], ['', '', '.'], (string)$check); + } else { + $check = str_replace([$groupingSep, $decimalPoint], ['', '.'], (string)$check); + } + + return static::_check($check, $regex); + } + + /** + * Validates for an email address. + * + * Only uses getmxrr() checking for deep validation, or + * any PHP version on a non-windows distribution + * + * @param mixed $check Value to check + * @param bool $deep Perform a deeper validation (if true), by also checking availability of host + * @param string|null $regex Regex to use (if none it will use built in regex) + * @return bool Success + */ + public static function email($check, ?bool $deep = false, ?string $regex = null): bool + { + if (!is_string($check)) { + return false; + } + + if ($regex === null) { + // phpcs:ignore Generic.Files.LineLength + $regex = '/^[\p{L}0-9!#$%&\'*+\/=?^_`{|}~-]+(?:\.[\p{L}0-9!#$%&\'*+\/=?^_`{|}~-]+)*@' . self::$_pattern['hostname'] . '$/ui'; + } + $return = static::_check($check, $regex); + if ($deep === false || $deep === null) { + return $return; + } + + if ($return === true && preg_match('/@(' . static::$_pattern['hostname'] . ')$/i', $check, $regs)) { + if (function_exists('getmxrr') && getmxrr($regs[1], $mxhosts)) { + return true; + } + if (function_exists('checkdnsrr') && checkdnsrr($regs[1], 'MX')) { + return true; + } + + return is_array(gethostbynamel($regs[1] . '.')); + } + + return false; + } + + /** + * Checks that value is exactly $comparedTo. + * + * @param mixed $check Value to check + * @param mixed $comparedTo Value to compare + * @return bool Success + */ + public static function equalTo($check, $comparedTo): bool + { + return $check === $comparedTo; + } + + /** + * Checks that value has a valid file extension. + * + * @param string|array|\Psr\Http\Message\UploadedFileInterface $check Value to check + * @param string[] $extensions file extensions to allow. By default extensions are 'gif', 'jpeg', 'png', 'jpg' + * @return bool Success + */ + public static function extension($check, array $extensions = ['gif', 'jpeg', 'png', 'jpg']): bool + { + if ($check instanceof UploadedFileInterface) { + $check = $check->getClientFilename(); + } elseif (is_array($check) && isset($check['name'])) { + $check = $check['name']; + } elseif (is_array($check)) { + return static::extension(array_shift($check), $extensions); + } + + if (empty($check)) { + return false; + } + + $extension = strtolower(pathinfo($check, PATHINFO_EXTENSION)); + foreach ($extensions as $value) { + if ($extension === strtolower($value)) { + return true; + } + } + + return false; + } + + /** + * Validation of an IP address. + * + * @param mixed $check The string to test. + * @param string $type The IP Protocol version to validate against + * @return bool Success + */ + public static function ip($check, string $type = 'both'): bool + { + if (!is_string($check)) { + return false; + } + + $type = strtolower($type); + $flags = 0; + if ($type === 'ipv4') { + $flags = FILTER_FLAG_IPV4; + } + if ($type === 'ipv6') { + $flags = FILTER_FLAG_IPV6; + } + + return (bool)filter_var($check, FILTER_VALIDATE_IP, ['flags' => $flags]); + } + + /** + * Checks whether the length of a string (in characters) is greater or equal to a minimal length. + * + * @param mixed $check The string to test + * @param int $min The minimal string length + * @return bool Success + */ + public static function minLength($check, int $min): bool + { + if (!is_scalar($check)) { + return false; + } + + return mb_strlen((string)$check) >= $min; + } + + /** + * Checks whether the length of a string (in characters) is smaller or equal to a maximal length. + * + * @param mixed $check The string to test + * @param int $max The maximal string length + * @return bool Success + */ + public static function maxLength($check, int $max): bool + { + if (!is_scalar($check)) { + return false; + } + + return mb_strlen((string)$check) <= $max; + } + + /** + * Checks whether the length of a string (in bytes) is greater or equal to a minimal length. + * + * @param mixed $check The string to test + * @param int $min The minimal string length (in bytes) + * @return bool Success + */ + public static function minLengthBytes($check, int $min): bool + { + if (!is_scalar($check)) { + return false; + } + + return strlen((string)$check) >= $min; + } + + /** + * Checks whether the length of a string (in bytes) is smaller or equal to a maximal length. + * + * @param mixed $check The string to test + * @param int $max The maximal string length + * @return bool Success + */ + public static function maxLengthBytes($check, int $max): bool + { + if (!is_scalar($check)) { + return false; + } + + return strlen((string)$check) <= $max; + } + + /** + * Checks that a value is a monetary amount. + * + * @param mixed $check Value to check + * @param string $symbolPosition Where symbol is located (left/right) + * @return bool Success + */ + public static function money($check, string $symbolPosition = 'left'): bool + { + $money = '(?!0,?\d)(?:\d{1,3}(?:([, .])\d{3})?(?:\1\d{3})*|(?:\d+))((?!\1)[,.]\d{1,2})?'; + if ($symbolPosition === 'right') { + $regex = '/^' . $money . '(? provide a list of choices that selections must be made from + * - max => maximum number of non-zero choices that can be made + * - min => minimum number of non-zero choices that can be made + * + * @param mixed $check Value to check + * @param array $options Options for the check. + * @param bool $caseInsensitive Set to true for case insensitive comparison. + * @return bool Success + */ + public static function multiple($check, array $options = [], bool $caseInsensitive = false): bool + { + $defaults = ['in' => null, 'max' => null, 'min' => null]; + $options += $defaults; + + $check = array_filter((array)$check, function ($value) { + return $value || is_numeric($value); + }); + if (empty($check)) { + return false; + } + if ($options['max'] && count($check) > $options['max']) { + return false; + } + if ($options['min'] && count($check) < $options['min']) { + return false; + } + if ($options['in'] && is_array($options['in'])) { + if ($caseInsensitive) { + $options['in'] = array_map('mb_strtolower', $options['in']); + } + foreach ($check as $val) { + $strict = !is_numeric($val); + if ($caseInsensitive) { + $val = mb_strtolower($val); + } + if (!in_array((string)$val, $options['in'], $strict)) { + return false; + } + } + } + + return true; + } + + /** + * Checks if a value is numeric. + * + * @param mixed $check Value to check + * @return bool Success + */ + public static function numeric($check): bool + { + return is_numeric($check); + } + + /** + * Checks if a value is a natural number. + * + * @param mixed $check Value to check + * @param bool $allowZero Set true to allow zero, defaults to false + * @return bool Success + * @see https://en.wikipedia.org/wiki/Natural_number + */ + public static function naturalNumber($check, bool $allowZero = false): bool + { + $regex = $allowZero ? '/^(?:0|[1-9][0-9]*)$/' : '/^[1-9][0-9]*$/'; + + return static::_check($check, $regex); + } + + /** + * Validates that a number is in specified range. + * + * If $lower and $upper are set, the range is inclusive. + * If they are not set, will return true if $check is a + * legal finite on this platform. + * + * @param mixed $check Value to check + * @param float|null $lower Lower limit + * @param float|null $upper Upper limit + * @return bool Success + */ + public static function range($check, ?float $lower = null, ?float $upper = null): bool + { + if (!is_numeric($check)) { + return false; + } + if ((float)$check != $check) { + return false; + } + if (isset($lower, $upper)) { + return $check >= $lower && $check <= $upper; + } + + return is_finite((float)$check); + } + + /** + * Checks that a value is a valid URL according to https://www.w3.org/Addressing/URL/url-spec.txt + * + * The regex checks for the following component parts: + * + * - a valid, optional, scheme + * - a valid ip address OR + * a valid domain name as defined by section 2.3.1 of https://www.ietf.org/rfc/rfc1035.txt + * with an optional port number + * - an optional valid path + * - an optional query string (get parameters) + * - an optional fragment (anchor tag) as defined in RFC 3986 + * + * @param mixed $check Value to check + * @param bool $strict Require URL to be prefixed by a valid scheme (one of http(s)/ftp(s)/file/news/gopher) + * @return bool Success + * @link https://tools.ietf.org/html/rfc3986 + */ + public static function url($check, bool $strict = false): bool + { + if (!is_string($check)) { + return false; + } + + static::_populateIp(); + + $emoji = '\x{1F190}-\x{1F9EF}'; + $alpha = '0-9\p{L}\p{N}' . $emoji; + $hex = '(%[0-9a-f]{2})'; + $subDelimiters = preg_quote('/!"$&\'()*+,-.@_:;=~[]', '/'); + $path = '([' . $subDelimiters . $alpha . ']|' . $hex . ')'; + $fragmentAndQuery = '([\?' . $subDelimiters . $alpha . ']|' . $hex . ')'; + // phpcs:disable Generic.Files.LineLength + $regex = '/^(?:(?:https?|ftps?|sftp|file|news|gopher):\/\/)' . ($strict ? '' : '?') . + '(?:' . static::$_pattern['IPv4'] . '|\[' . static::$_pattern['IPv6'] . '\]|' . static::$_pattern['hostname'] . ')(?::[1-9][0-9]{0,4})?' . + '(?:\/' . $path . '*)?' . + '(?:\?' . $fragmentAndQuery . '*)?' . + '(?:#' . $fragmentAndQuery . '*)?$/iu'; + // phpcs:enable Generic.Files.LineLength + + return static::_check($check, $regex); + } + + /** + * Checks if a value is in a given list. Comparison is case sensitive by default. + * + * @param mixed $check Value to check. + * @param string[] $list List to check against. + * @param bool $caseInsensitive Set to true for case insensitive comparison. + * @return bool Success. + */ + public static function inList($check, array $list, bool $caseInsensitive = false): bool + { + if ($caseInsensitive) { + $list = array_map('mb_strtolower', $list); + $check = mb_strtolower($check); + } else { + $list = array_map('strval', $list); + } + + return in_array((string)$check, $list, true); + } + + /** + * Checks that a value is a valid UUID - https://tools.ietf.org/html/rfc4122 + * + * @param mixed $check Value to check + * @return bool Success + */ + public static function uuid($check): bool + { + $regex = '/^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[0-5][a-fA-F0-9]{3}-[089aAbB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$/'; + + return self::_check($check, $regex); + } + + /** + * Runs a regular expression match. + * + * @param mixed $check Value to check against the $regex expression + * @param string $regex Regular expression + * @return bool Success of match + */ + protected static function _check($check, string $regex): bool + { + return is_scalar($check) && preg_match($regex, (string)$check); + } + + /** + * Luhn algorithm + * + * @param mixed $check Value to check. + * @return bool Success + * @see https://en.wikipedia.org/wiki/Luhn_algorithm + */ + public static function luhn($check): bool + { + if (!is_scalar($check) || (int)$check === 0) { + return false; + } + $sum = 0; + $check = (string)$check; + $length = strlen($check); + + for ($position = 1 - ($length % 2); $position < $length; $position += 2) { + $sum += (int)$check[$position]; + } + + for ($position = $length % 2; $position < $length; $position += 2) { + $number = (int)$check[$position] * 2; + $sum += $number < 10 ? $number : $number - 9; + } + + return $sum % 10 === 0; + } + + /** + * Checks the mime type of a file. + * + * Will check the mimetype of files/UploadedFileInterface instances + * by checking the using finfo on the file, not relying on the content-type + * sent by the client. + * + * @param string|array|\Psr\Http\Message\UploadedFileInterface $check Value to check. + * @param array|string $mimeTypes Array of mime types or regex pattern to check. + * @return bool Success + * @throws \RuntimeException when mime type can not be determined. + * @throws \LogicException when ext/fileinfo is missing + */ + public static function mimeType($check, $mimeTypes = []): bool + { + $file = static::getFilename($check); + if ($file === false) { + return false; + } + + if (!function_exists('finfo_open')) { + throw new LogicException('ext/fileinfo is required for validating file mime types'); + } + + if (!is_file($file)) { + throw new RuntimeException('Cannot validate mimetype for a missing file'); + } + + $finfo = finfo_open(FILEINFO_MIME); + $finfo = finfo_file($finfo, $file); + + if (!$finfo) { + throw new RuntimeException('Can not determine the mimetype.'); + } + + [$mime] = explode(';', $finfo); + + if (is_string($mimeTypes)) { + return self::_check($mime, $mimeTypes); + } + + foreach ($mimeTypes as $key => $val) { + $mimeTypes[$key] = strtolower($val); + } + + return in_array(strtolower($mime), $mimeTypes, true); + } + + /** + * Helper for reading the file out of the various file implementations + * we accept. + * + * @param string|array|\Psr\Http\Message\UploadedFileInterface $check The data to read a filename out of. + * @return string|false Either the filename or false on failure. + */ + protected static function getFilename($check) + { + if ($check instanceof UploadedFileInterface) { + try { + // Uploaded files throw exceptions on upload errors. + return $check->getStream()->getMetadata('uri'); + } catch (RuntimeException $e) { + return false; + } + } + if (is_array($check) && isset($check['tmp_name'])) { + return $check['tmp_name']; + } + + if (is_string($check)) { + return $check; + } + + return false; + } + + /** + * Checks the filesize + * + * Will check the filesize of files/UploadedFileInterface instances + * by checking the filesize() on disk and not relying on the length + * reported by the client. + * + * @param string|array|\Psr\Http\Message\UploadedFileInterface $check Value to check. + * @param string $operator See `Validation::comparison()`. + * @param int|string $size Size in bytes or human readable string like '5MB'. + * @return bool Success + */ + public static function fileSize($check, string $operator, $size): bool + { + $file = static::getFilename($check); + if ($file === false) { + return false; + } + + if (is_string($size)) { + $size = Text::parseFileSize($size); + } + $filesize = filesize($file); + + return static::comparison($filesize, $operator, $size); + } + + /** + * Checking for upload errors + * + * @param string|array|\Psr\Http\Message\UploadedFileInterface $check Value to check. + * @param bool $allowNoFile Set to true to allow UPLOAD_ERR_NO_FILE as a pass. + * @return bool + * @see https://secure.php.net/manual/en/features.file-upload.errors.php + */ + public static function uploadError($check, bool $allowNoFile = false): bool + { + if ($check instanceof UploadedFileInterface) { + $code = $check->getError(); + } elseif (is_array($check) && isset($check['error'])) { + $code = $check['error']; + } else { + $code = $check; + } + if ($allowNoFile) { + return in_array((int)$code, [UPLOAD_ERR_OK, UPLOAD_ERR_NO_FILE], true); + } + + return (int)$code === UPLOAD_ERR_OK; + } + + /** + * Validate an uploaded file. + * + * Helps join `uploadError`, `fileSize` and `mimeType` into + * one higher level validation method. + * + * ### Options + * + * - `types` - An array of valid mime types. If empty all types + * will be accepted. The `type` will not be looked at, instead + * the file type will be checked with ext/finfo. + * - `minSize` - The minimum file size in bytes. Defaults to not checking. + * - `maxSize` - The maximum file size in bytes. Defaults to not checking. + * - `optional` - Whether or not this file is optional. Defaults to false. + * If true a missing file will pass the validator regardless of other constraints. + * + * @param mixed $file The uploaded file data from PHP. + * @param array $options An array of options for the validation. + * @return bool + */ + public static function uploadedFile($file, array $options = []): bool + { + $options += [ + 'minSize' => null, + 'maxSize' => null, + 'types' => null, + 'optional' => false, + ]; + if (!is_array($file) && !($file instanceof UploadedFileInterface)) { + return false; + } + $error = $isUploaded = false; + if ($file instanceof UploadedFileInterface) { + $error = $file->getError(); + $isUploaded = true; + } + if (is_array($file)) { + $keys = ['error', 'name', 'size', 'tmp_name', 'type']; + ksort($file); + if (array_keys($file) !== $keys) { + return false; + } + $error = (int)$file['error']; + $isUploaded = is_uploaded_file($file['tmp_name']); + } + + if (!static::uploadError($file, $options['optional'])) { + return false; + } + if ($options['optional'] && $error === UPLOAD_ERR_NO_FILE) { + return true; + } + if ( + isset($options['minSize']) + && !static::fileSize($file, static::COMPARE_GREATER_OR_EQUAL, $options['minSize']) + ) { + return false; + } + if ( + isset($options['maxSize']) + && !static::fileSize($file, static::COMPARE_LESS_OR_EQUAL, $options['maxSize']) + ) { + return false; + } + if (isset($options['types']) && !static::mimeType($file, $options['types'])) { + return false; + } + + return $isUploaded; + } + + /** + * Validates the size of an uploaded image. + * + * @param mixed $file The uploaded file data from PHP. + * @param array $options Options to validate width and height. + * @return bool + * @throws \InvalidArgumentException + */ + public static function imageSize($file, array $options): bool + { + if (!isset($options['height']) && !isset($options['width'])) { + throw new InvalidArgumentException( + 'Invalid image size validation parameters! Missing `width` and / or `height`.' + ); + } + + $file = static::getFilename($file); + if ($file === false) { + return false; + } + + [$width, $height] = getimagesize($file); + $validHeight = null; + $validWidth = null; + + if (isset($options['height'])) { + $validHeight = self::comparison($height, $options['height'][0], $options['height'][1]); + } + if (isset($options['width'])) { + $validWidth = self::comparison($width, $options['width'][0], $options['width'][1]); + } + if ($validHeight !== null && $validWidth !== null) { + return $validHeight && $validWidth; + } + if ($validHeight !== null) { + return $validHeight; + } + if ($validWidth !== null) { + return $validWidth; + } + + throw new InvalidArgumentException('The 2nd argument is missing the `width` and / or `height` options.'); + } + + /** + * Validates the image width. + * + * @param array $file The uploaded file data from PHP. + * @param string $operator Comparison operator. + * @param int $width Min or max width. + * @return bool + */ + public static function imageWidth(array $file, string $operator, int $width): bool + { + return self::imageSize($file, [ + 'width' => [ + $operator, + $width, + ], + ]); + } + + /** + * Validates the image width. + * + * @param array $file The uploaded file data from PHP. + * @param string $operator Comparison operator. + * @param int $height Min or max width. + * @return bool + */ + public static function imageHeight(array $file, string $operator, int $height): bool + { + return self::imageSize($file, [ + 'height' => [ + $operator, + $height, + ], + ]); + } + + /** + * Validates a geographic coordinate. + * + * Supported formats: + * + * - `, ` Example: `-25.274398, 133.775136` + * + * ### Options + * + * - `type` - A string of the coordinate format, right now only `latLong`. + * - `format` - By default `both`, can be `long` and `lat` as well to validate + * only a part of the coordinate. + * + * @param mixed $value Geographic location as string + * @param array $options Options for the validation logic. + * @return bool + */ + public static function geoCoordinate($value, array $options = []): bool + { + if (!is_scalar($value)) { + return false; + } + + $options += [ + 'format' => 'both', + 'type' => 'latLong', + ]; + if ($options['type'] !== 'latLong') { + throw new RuntimeException(sprintf( + 'Unsupported coordinate type "%s". Use "latLong" instead.', + $options['type'] + )); + } + $pattern = '/^' . self::$_pattern['latitude'] . ',\s*' . self::$_pattern['longitude'] . '$/'; + if ($options['format'] === 'long') { + $pattern = '/^' . self::$_pattern['longitude'] . '$/'; + } + if ($options['format'] === 'lat') { + $pattern = '/^' . self::$_pattern['latitude'] . '$/'; + } + + return (bool)preg_match($pattern, (string)$value); + } + + /** + * Convenience method for latitude validation. + * + * @param mixed $value Latitude as string + * @param array $options Options for the validation logic. + * @return bool + * @link https://en.wikipedia.org/wiki/Latitude + * @see \Cake\Validation\Validation::geoCoordinate() + */ + public static function latitude($value, array $options = []): bool + { + $options['format'] = 'lat'; + + return self::geoCoordinate($value, $options); + } + + /** + * Convenience method for longitude validation. + * + * @param mixed $value Latitude as string + * @param array $options Options for the validation logic. + * @return bool + * @link https://en.wikipedia.org/wiki/Longitude + * @see \Cake\Validation\Validation::geoCoordinate() + */ + public static function longitude($value, array $options = []): bool + { + $options['format'] = 'long'; + + return self::geoCoordinate($value, $options); + } + + /** + * Check that the input value is within the ascii byte range. + * + * This method will reject all non-string values. + * + * @param mixed $value The value to check + * @return bool + */ + public static function ascii($value): bool + { + if (!is_string($value)) { + return false; + } + + return strlen($value) <= mb_strlen($value, 'utf-8'); + } + + /** + * Check that the input value is a utf8 string. + * + * This method will reject all non-string values. + * + * # Options + * + * - `extended` - Disallow bytes higher within the basic multilingual plane. + * MySQL's older utf8 encoding type does not allow characters above + * the basic multilingual plane. Defaults to false. + * + * @param mixed $value The value to check + * @param array $options An array of options. See above for the supported options. + * @return bool + */ + public static function utf8($value, array $options = []): bool + { + if (!is_string($value)) { + return false; + } + $options += ['extended' => false]; + if ($options['extended']) { + return true; + } + + return preg_match('/[\x{10000}-\x{10FFFF}]/u', $value) === 0; + } + + /** + * Check that the input value is an integer + * + * This method will accept strings that contain only integer data + * as well. + * + * @param mixed $value The value to check + * @return bool + */ + public static function isInteger($value): bool + { + if (is_int($value)) { + return true; + } + + if (!is_string($value) || !is_numeric($value)) { + return false; + } + + return (bool)preg_match('/^-?[0-9]+$/', $value); + } + + /** + * Check that the input value is an array. + * + * @param mixed $value The value to check + * @return bool + */ + public static function isArray($value): bool + { + return is_array($value); + } + + /** + * Check that the input value is a scalar. + * + * This method will accept integers, floats, strings and booleans, but + * not accept arrays, objects, resources and nulls. + * + * @param mixed $value The value to check + * @return bool + */ + public static function isScalar($value): bool + { + return is_scalar($value); + } + + /** + * Check that the input value is a 6 digits hex color. + * + * @param mixed $check The value to check + * @return bool Success + */ + public static function hexColor($check): bool + { + return static::_check($check, '/^#[0-9a-f]{6}$/iD'); + } + + /** + * Check that the input value has a valid International Bank Account Number IBAN syntax + * Requirements are uppercase, no whitespaces, max length 34, country code and checksum exist at right spots, + * body matches against checksum via Mod97-10 algorithm + * + * @param mixed $check The value to check + * + * @return bool Success + */ + public static function iban($check): bool + { + if ( + !is_string($check) || + !preg_match('/^[A-Z]{2}[0-9]{2}[A-Z0-9]{1,30}$/', $check) + ) { + return false; + } + + $country = substr($check, 0, 2); + $checkInt = intval(substr($check, 2, 2)); + $account = substr($check, 4); + $search = range('A', 'Z'); + $replace = []; + foreach (range(10, 35) as $tmp) { + $replace[] = strval($tmp); + } + $numStr = str_replace($search, $replace, $account . $country . '00'); + $checksum = intval(substr($numStr, 0, 1)); + $numStrLength = strlen($numStr); + for ($pos = 1; $pos < $numStrLength; $pos++) { + $checksum *= 10; + $checksum += intval(substr($numStr, $pos, 1)); + $checksum %= 97; + } + + return $checkInt === 98 - $checksum; + } + + /** + * Converts an array representing a date or datetime into a ISO string. + * The arrays are typically sent for validation from a form generated by + * the CakePHP FormHelper. + * + * @param array $value The array representing a date or datetime. + * @return string + */ + protected static function _getDateString(array $value): string + { + $formatted = ''; + if ( + isset($value['year'], $value['month'], $value['day']) && + ( + is_numeric($value['year']) && + is_numeric($value['month']) && + is_numeric($value['day']) + ) + ) { + $formatted .= sprintf('%d-%02d-%02d ', $value['year'], $value['month'], $value['day']); + } + + if (isset($value['hour'])) { + if (isset($value['meridian']) && (int)$value['hour'] === 12) { + $value['hour'] = 0; + } + if (isset($value['meridian'])) { + $value['hour'] = strtolower($value['meridian']) === 'am' ? $value['hour'] : $value['hour'] + 12; + } + $value += ['minute' => 0, 'second' => 0, 'microsecond' => 0]; + if ( + is_numeric($value['hour']) && + is_numeric($value['minute']) && + is_numeric($value['second']) && + is_numeric($value['microsecond']) + ) { + $formatted .= sprintf( + '%02d:%02d:%02d.%06d', + $value['hour'], + $value['minute'], + $value['second'], + $value['microsecond'] + ); + } + } + + return trim($formatted); + } + + /** + * Lazily populate the IP address patterns used for validations + * + * @return void + */ + protected static function _populateIp(): void + { + // phpcs:disable Generic.Files.LineLength + if (!isset(static::$_pattern['IPv6'])) { + $pattern = '((([0-9A-Fa-f]{1,4}:){7}(([0-9A-Fa-f]{1,4})|:))|(([0-9A-Fa-f]{1,4}:){6}'; + $pattern .= '(:|((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})'; + $pattern .= '|(:[0-9A-Fa-f]{1,4})))|(([0-9A-Fa-f]{1,4}:){5}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})'; + $pattern .= '(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:)'; + $pattern .= '{4}(:[0-9A-Fa-f]{1,4}){0,1}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2}))'; + $pattern .= '{3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){3}(:[0-9A-Fa-f]{1,4}){0,2}'; + $pattern .= '((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|'; + $pattern .= '((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){2}(:[0-9A-Fa-f]{1,4}){0,3}'; + $pattern .= '((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2}))'; + $pattern .= '{3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:)(:[0-9A-Fa-f]{1,4})'; + $pattern .= '{0,4}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)'; + $pattern .= '|((:[0-9A-Fa-f]{1,4}){1,2})))|(:(:[0-9A-Fa-f]{1,4}){0,5}((:((25[0-5]|2[0-4]'; + $pattern .= '\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4})'; + $pattern .= '{1,2})))|(((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})))(%.+)?'; + + static::$_pattern['IPv6'] = $pattern; + } + if (!isset(static::$_pattern['IPv4'])) { + $pattern = '(?:(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])'; + static::$_pattern['IPv4'] = $pattern; + } + // phpcs:enable Generic.Files.LineLength + } + + /** + * Reset internal variables for another validation run. + * + * @return void + */ + protected static function _reset(): void + { + static::$errors = []; + } +} diff --git a/src/Validation/Validator.php b/src/Validation/Validator.php index 98be09a..803672f 100755 --- a/src/Validation/Validator.php +++ b/src/Validation/Validator.php @@ -112,4 +112,100 @@ public function getMessage(string $validationName, array $args = []) return __d($this->_i18nDomain, $message, ...$args); } + + /** + * {@inheritDoc} + */ + public function allowEmptyString(string $field, ?string $message = null, $when = true) + { + return parent::allowEmptyString($field, $message ?? $this->getMessage(__FUNCTION__, [$field]), $when); + } + + /** + * {@inheritDoc} + */ + public function notEmptyString(string $field, ?string $message = null, $when = false) + { + return parent::notEmptyString($field, $message ?? $this->getMessage(__FUNCTION__, [$field]), $when); + } + + /** + * {@inheritDoc} + */ + public function allowEmptyArray(string $field, ?string $message = null, $when = true) + { + return parent::allowEmptyArray($field, $message ?? $this->getMessage(__FUNCTION__, [$field]), $when); + } + + /** + * {@inheritDoc} + */ + public function notEmptyArray(string $field, ?string $message = null, $when = false) + { + return parent::notEmptyArray($field, $message ?? $this->getMessage(__FUNCTION__, [$field]), $when); + } + + /** + * {@inheritDoc} + */ + public function allowEmptyFile(string $field, ?string $message = null, $when = true) + { + return parent::allowEmptyFile($field, $message ?? $this->getMessage(__FUNCTION__, [$field]), $when); + } + + /** + * {@inheritDoc} + */ + public function notEmptyFile(string $field, ?string $message = null, $when = false) + { + return parent::notEmptyFile($field, $message ?? $this->getMessage(__FUNCTION__, [$field]), $when); + } + + /** + * {@inheritDoc} + */ + public function allowEmptyDate(string $field, ?string $message = null, $when = true) + { + return parent::allowEmptyDate($field, $message ?? $this->getMessage(__FUNCTION__, [$field]), $when); + } + + /** + * {@inheritDoc} + */ + public function notEmptyDate(string $field, ?string $message = null, $when = false) + { + return parent::notEmptyDate($field, $message ?? $this->getMessage(__FUNCTION__, [$field]), $when); + } + + /** + * {@inheritDoc} + */ + public function allowEmptyDatetime(string $field, ?string $message = null, $when = true) + { + return parent::allowEmptyDatetime($field, $message ?? $this->getMessage(__FUNCTION__, [$field]), $when); + } + + /** + * {@inheritDoc} + */ + public function notEmptyDatetime(string $field, ?string $message = null, $when = false) + { + return parent::notEmptyDatetime($field, $message ?? $this->getMessage(__FUNCTION__, [$field]), $when); + } + + /** + * {@inheritDoc} + */ + public function allowEmptyTime(string $field, ?string $message = null, $when = true) + { + return parent::allowEmptyTime($field, $message ?? $this->getMessage(__FUNCTION__, [$field]), $when); + } + + /** + * {@inheritDoc} + */ + public function notEmptyTime(string $field, ?string $message = null, $when = false) + { + return parent::notEmptyTime($field, $message ?? $this->getMessage(__FUNCTION__, [$field]), $when); + } } diff --git a/tests/TestCase/Validation/ValidatorTest.php b/tests/TestCase/Validation/ValidatorTest.php index 15c3efd..fe32e0a 100755 --- a/tests/TestCase/Validation/ValidatorTest.php +++ b/tests/TestCase/Validation/ValidatorTest.php @@ -97,4 +97,91 @@ public function testOverrideMessage() $errors = $this->Validator->validate(['column' => 'abc']); $this->assertEquals('maxLength!! less than 2', $errors['column']['maxLength']); } + + /** + * test EmptyXXXX method + * + * @return void + */ + public function testEmptyXXXX() + { + Configure::write('messagesValidator.messages', [ + 'notEmptyString' => 'notEmptyString!!', + 'notEmptyArray' => 'notEmptyArray!!', + 'notEmptyFile' => 'notEmptyFile!!', + 'notEmptyDate' => 'notEmptyDate!!', + 'notEmptyDatetime' => 'notEmptyDatetime!!', + 'notEmptyTime' => 'notEmptyTime!!', + 'allowEmptyString' => 'allowEmptyString!!', + 'allowEmptyArray' => 'allowEmptyArray!!', + 'allowEmptyFile' => 'allowEmptyFile!!', + 'allowEmptyDate' => 'allowEmptyDate!!', + 'allowEmptyDatetime' => 'allowEmptyDatetime!!', + 'allowEmptyTime' => 'allowEmptyTime!!', + ]); + + // notEmpty + $this->Validator + ->notEmptyString('string') + ->notEmptyArray('array') + ->notEmptyFile('file') + ->notEmptyDate('date') + ->notEmptyDatetime('datetime') + ->notEmptyTime('time'); + + $errors = $this->Validator->validate([ + 'string' => '', + 'array' => [], + 'file' => [ + 'name' => 'name', + 'type' => 'type', + 'tmp_name' => 'tmp_name', + 'error' => UPLOAD_ERR_NO_FILE, + ], + 'date' => '', + 'datetime' => '', + 'time' => '', + ]); + $this->assertEquals('notEmptyString!!', $errors['string']['_empty']); + $this->assertEquals('notEmptyArray!!', $errors['array']['_empty']); + $this->assertEquals('notEmptyFile!!', $errors['file']['_empty']); + $this->assertEquals('notEmptyDate!!', $errors['date']['_empty']); + $this->assertEquals('notEmptyDatetime!!', $errors['datetime']['_empty']); + $this->assertEquals('notEmptyTime!!', $errors['time']['_empty']); + + // allowEmptyXXX + $this->Validator + ->notEmptyString('string') + ->notEmptyArray('array') + ->notEmptyFile('file') + ->notEmptyDate('date') + ->notEmptyDatetime('datetime') + ->notEmptyTime('time') + ->allowEmptyString('string', null, 'update') + ->allowEmptyArray('array', null, 'update') + ->allowEmptyFile('file', null, 'update') + ->allowEmptyDate('date', null, 'update') + ->allowEmptyDatetime('datetime', null, 'update') + ->allowEmptyTime('time', null, 'update'); + + $errors = $this->Validator->validate([ + 'string' => '', + 'array' => [], + 'file' => [ + 'name' => 'name', + 'type' => 'type', + 'tmp_name' => 'tmp_name', + 'error' => UPLOAD_ERR_NO_FILE, + ], + 'date' => '', + 'datetime' => '', + 'time' => '', + ]); + $this->assertEquals('allowEmptyString!!', $errors['string']['_empty']); + $this->assertEquals('allowEmptyArray!!', $errors['array']['_empty']); + $this->assertEquals('allowEmptyFile!!', $errors['file']['_empty']); + $this->assertEquals('allowEmptyDate!!', $errors['date']['_empty']); + $this->assertEquals('allowEmptyDatetime!!', $errors['datetime']['_empty']); + $this->assertEquals('allowEmptyTime!!', $errors['time']['_empty']); + } } From 66e9cddeb7e9f9684b1e479f6cd5138a0a1aa83c Mon Sep 17 00:00:00 2001 From: mtak3 Date: Thu, 26 Mar 2020 11:40:07 +0900 Subject: [PATCH 3/3] Delete :q! --- :q! | 1759 ----------------------------------------------------------- 1 file changed, 1759 deletions(-) delete mode 100644 :q! diff --git a/:q! b/:q! deleted file mode 100644 index ab88843..0000000 --- a/:q! +++ /dev/null @@ -1,1759 +0,0 @@ -'; - - /** - * Greater than or equal to comparison operator. - */ - public const COMPARE_GREATER_OR_EQUAL = '>='; - - /** - * Less than comparison operator. - */ - public const COMPARE_LESS = '<'; - - /** - * Less than or equal to comparison operator. - */ - public const COMPARE_LESS_OR_EQUAL = '<='; - - /** - * Datetime ISO8601 format - */ - public const DATETIME_ISO8601 = 'iso8601'; - - /** - * Some complex patterns needed in multiple places - * - * @var array - */ - protected static $_pattern = [ - 'hostname' => '(?:[_\p{L}0-9][-_\p{L}0-9]*\.)*(?:[\p{L}0-9][-\p{L}0-9]{0,62})\.(?:(?:[a-z]{2}\.)?[a-z]{2,})', - 'latitude' => '[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?)', - 'longitude' => '[-+]?(180(\.0+)?|((1[0-7]\d)|([1-9]?\d))(\.\d+)?)', - ]; - - /** - * Holds an array of errors messages set in this class. - * These are used for debugging purposes - * - * @var array - */ - public static $errors = []; - - /** - * Checks that a string contains something other than whitespace - * - * Returns true if string contains something other than whitespace - * - * @param mixed $check Value to check - * @return bool Success - */ - public static function notBlank($check): bool - { - if (empty($check) && !is_bool($check) && !is_numeric($check)) { - return false; - } - - return static::_check($check, '/[^\s]+/m'); - } - - /** - * Checks that a string contains only integer or letters. - * - * This method's definition of letters and integers includes unicode characters. - * Use `asciiAlphaNumeric()` if you want to exclude unicode. - * - * @param mixed $check Value to check - * @return bool Success - */ - public static function alphaNumeric($check): bool - { - if ((empty($check) && $check !== '0') || !is_scalar($check)) { - return false; - } - - return self::_check($check, '/^[\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]+$/Du'); - } - - /** - * Checks that a doesn't contain any alpha numeric characters - * - * This method's definition of letters and integers includes unicode characters. - * Use `notAsciiAlphaNumeric()` if you want to exclude ascii only. - * - * @param mixed $check Value to check - * @return bool Success - */ - public static function notAlphaNumeric($check): bool - { - return !static::alphaNumeric($check); - } - - /** - * Checks that a string contains only ascii integer or letters. - * - * @param mixed $check Value to check - * @return bool Success - */ - public static function asciiAlphaNumeric($check): bool - { - if ((empty($check) && $check !== '0') || !is_scalar($check)) { - return false; - } - - return self::_check($check, '/^[[:alnum:]]+$/'); - } - - /** - * Checks that a doesn't contain any non-ascii alpha numeric characters - * - * @param mixed $check Value to check - * @return bool Success - */ - public static function notAsciiAlphaNumeric($check): bool - { - return !static::asciiAlphaNumeric($check); - } - - /** - * Checks that a string length is within specified range. - * Spaces are included in the character count. - * Returns true if string matches value min, max, or between min and max, - * - * @param mixed $check Value to check for length - * @param int $min Minimum value in range (inclusive) - * @param int $max Maximum value in range (inclusive) - * @return bool Success - */ - public static function lengthBetween($check, int $min, int $max): bool - { - if (!is_scalar($check)) { - return false; - } - $length = mb_strlen((string)$check); - - return $length >= $min && $length <= $max; - } - - /** - * Validation of credit card numbers. - * Returns true if $check is in the proper credit card format. - * - * @param mixed $check credit card number to validate - * @param string|string[] $type 'all' may be passed as a string, defaults to fast which checks format of - * most major credit cards if an array is used only the values of the array are checked. - * Example: ['amex', 'bankcard', 'maestro'] - * @param bool $deep set to true this will check the Luhn algorithm of the credit card. - * @param string|null $regex A custom regex, this will be used instead of the defined regex values. - * @return bool Success - * @see \Cake\Validation\Validation::luhn() - */ - public static function creditCard($check, $type = 'fast', bool $deep = false, ?string $regex = null): bool - { - if (!(is_string($check) || is_int($check))) { - return false; - } - - $check = str_replace(['-', ' '], '', (string)$check); - if (mb_strlen($check) < 13) { - return false; - } - - if ($regex !== null && static::_check($check, $regex)) { - return !$deep || static::luhn($check); - } - $cards = [ - 'all' => [ - 'amex' => '/^3[47]\\d{13}$/', - 'bankcard' => '/^56(10\\d\\d|022[1-5])\\d{10}$/', - 'diners' => '/^(?:3(0[0-5]|[68]\\d)\\d{11})|(?:5[1-5]\\d{14})$/', - 'disc' => '/^(?:6011|650\\d)\\d{12}$/', - 'electron' => '/^(?:417500|4917\\d{2}|4913\\d{2})\\d{10}$/', - 'enroute' => '/^2(?:014|149)\\d{11}$/', - 'jcb' => '/^(3\\d{4}|2131|1800)\\d{11}$/', - 'maestro' => '/^(?:5020|6\\d{3})\\d{12}$/', - 'mc' => '/^(5[1-5]\\d{14})|(2(?:22[1-9]|2[3-9][0-9]|[3-6][0-9]{2}|7[0-1][0-9]|720)\\d{12})$/', - 'solo' => '/^(6334[5-9][0-9]|6767[0-9]{2})\\d{10}(\\d{2,3})?$/', - // phpcs:ignore Generic.Files.LineLength - 'switch' => '/^(?:49(03(0[2-9]|3[5-9])|11(0[1-2]|7[4-9]|8[1-2])|36[0-9]{2})\\d{10}(\\d{2,3})?)|(?:564182\\d{10}(\\d{2,3})?)|(6(3(33[0-4][0-9])|759[0-9]{2})\\d{10}(\\d{2,3})?)$/', - 'visa' => '/^4\\d{12}(\\d{3})?$/', - 'voyager' => '/^8699[0-9]{11}$/', - ], - // phpcs:ignore Generic.Files.LineLength - 'fast' => '/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6011[0-9]{12}|3(?:0[0-5]|[68][0-9])[0-9]{11}|3[47][0-9]{13})$/', - ]; - - if (is_array($type)) { - foreach ($type as $value) { - $regex = $cards['all'][strtolower($value)]; - - if (static::_check($check, $regex)) { - return static::luhn($check); - } - } - } elseif ($type === 'all') { - foreach ($cards['all'] as $value) { - $regex = $value; - - if (static::_check($check, $regex)) { - return static::luhn($check); - } - } - } else { - $regex = $cards['fast']; - - if (static::_check($check, $regex)) { - return static::luhn($check); - } - } - - return false; - } - - /** - * Used to check the count of a given value of type array or Countable. - * - * @param mixed $check The value to check the count on. - * @param string $operator Can be either a word or operand - * is greater >, is less <, greater or equal >= - * less or equal <=, is less <, equal to ==, not equal != - * @param int $expectedCount The expected count value. - * @return bool Success - */ - public static function numElements($check, string $operator, int $expectedCount): bool - { - if (!is_array($check) && !$check instanceof Countable) { - return false; - } - - return self::comparison(count($check), $operator, $expectedCount); - } - - /** - * Used to compare 2 numeric values. - * - * @param string|int $check1 The left value to compare. - * @param string $operator Can be either a word or operand - * is greater >, is less <, greater or equal >= - * less or equal <=, is less <, equal to ==, not equal != - * @param string|int $check2 The right value to compare. - * @return bool Success - */ - public static function comparison($check1, string $operator, $check2): bool - { - if ((float)$check1 != $check1) { - return false; - } - - $operator = str_replace([' ', "\t", "\n", "\r", "\0", "\x0B"], '', strtolower($operator)); - switch ($operator) { - case static::COMPARE_GREATER: - if ($check1 > $check2) { - return true; - } - break; - case static::COMPARE_LESS: - if ($check1 < $check2) { - return true; - } - break; - case static::COMPARE_GREATER_OR_EQUAL: - if ($check1 >= $check2) { - return true; - } - break; - case static::COMPARE_LESS_OR_EQUAL: - if ($check1 <= $check2) { - return true; - } - break; - case static::COMPARE_EQUAL: - if ($check1 == $check2) { - return true; - } - break; - case static::COMPARE_NOT_EQUAL: - if ($check1 != $check2) { - return true; - } - break; - case static::COMPARE_SAME: - if ($check1 === $check2) { - return true; - } - break; - case static::COMPARE_NOT_SAME: - if ($check1 !== $check2) { - return true; - } - break; - default: - static::$errors[] = 'You must define the $operator parameter for Validation::comparison()'; - } - - return false; - } - - /** - * Compare one field to another. - * - * If both fields have exactly the same value this method will return true. - * - * @param mixed $check The value to find in $field. - * @param string $field The field to check $check against. This field must be present in $context. - * @param array $context The validation context. - * @return bool - */ - public static function compareWith($check, string $field, array $context): bool - { - return self::compareFields($check, $field, static::COMPARE_SAME, $context); - } - - /** - * Compare one field to another. - * - * Return true if the comparison matches the expected result. - * - * @param mixed $check The value to find in $field. - * @param string $field The field to check $check against. This field must be present in $context. - * @param string $operator Comparison operator. - * @param array $context The validation context. - * @return bool - * @since 3.6.0 - */ - public static function compareFields($check, string $field, string $operator, array $context): bool - { - if (!isset($context['data']) || !array_key_exists($field, $context['data'])) { - return false; - } - - return static::comparison($check, $operator, $context['data'][$field]); - } - - /** - * Checks if a string contains one or more non-alphanumeric characters. - * - * Returns true if string contains at least the specified number of non-alphanumeric characters - * - * @param mixed $check Value to check - * @param int $count Number of non-alphanumerics to check for - * @return bool Success - * @deprecated 4.0 Use notAlphaNumeric() instead. Will be removed in 5.0 - */ - public static function containsNonAlphaNumeric($check, int $count = 1): bool - { - if (!is_string($check)) { - return false; - } - - $matches = preg_match_all('/[^a-zA-Z0-9]/', $check); - - return $matches >= $count; - } - - /** - * Used when a custom regular expression is needed. - * - * @param string $check The value to check. - * @param string|null $regex If $check is passed as a string, $regex must also be set to valid regular expression - * @return bool Success - */ - public static function custom(string $check, ?string $regex = null): bool - { - if ($regex === null) { - static::$errors[] = 'You must define a regular expression for Validation::custom()'; - - return false; - } - - return static::_check($check, $regex); - } - - /** - * Date validation, determines if the string passed is a valid date. - * keys that expect full month, day and year will validate leap years. - * - * Years are valid from 0001 to 2999. - * - * ### Formats: - * - * - `dmy` 27-12-2006 or 27-12-06 separators can be a space, period, dash, forward slash - * - `mdy` 12-27-2006 or 12-27-06 separators can be a space, period, dash, forward slash - * - `ymd` 2006-12-27 or 06-12-27 separators can be a space, period, dash, forward slash - * - `dMy` 27 December 2006 or 27 Dec 2006 - * - `Mdy` December 27, 2006 or Dec 27, 2006 comma is optional - * - `My` December 2006 or Dec 2006 - * - `my` 12/2006 or 12/06 separators can be a space, period, dash, forward slash - * - `ym` 2006/12 or 06/12 separators can be a space, period, dash, forward slash - * - `y` 2006 just the year without any separators - * - * @param mixed $check a valid date string/object - * @param string|array $format Use a string or an array of the keys above. - * Arrays should be passed as ['dmy', 'mdy', etc] - * @param string|null $regex If a custom regular expression is used this is the only validation that will occur. - * @return bool Success - */ - public static function date($check, $format = 'ymd', ?string $regex = null): bool - { - if ($check instanceof DateTimeInterface) { - return true; - } - if (is_object($check)) { - return false; - } - if (is_array($check)) { - $check = static::_getDateString($check); - $format = 'ymd'; - } - - if ($regex !== null) { - return static::_check($check, $regex); - } - $month = '(0[123456789]|10|11|12)'; - $separator = '([- /.])'; - // Don't allow 0000, but 0001-2999 are ok. - $fourDigitYear = '(?:(?!0000)[012]\d{3})'; - $twoDigitYear = '(?:\d{2})'; - $year = '(?:' . $fourDigitYear . '|' . $twoDigitYear . ')'; - - // phpcs:disable Generic.Files.LineLength - // 2 or 4 digit leap year sub-pattern - $leapYear = '(?:(?:(?:(?!0000)[012]\\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))'; - // 4 digit leap year sub-pattern - $fourDigitLeapYear = '(?:(?:(?:(?!0000)[012]\\d)(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))'; - - $regex['dmy'] = '%^(?:(?:31(\\/|-|\\.|\\x20)(?:0?[13578]|1[02]))\\1|(?:(?:29|30)' . - $separator . '(?:0?[1,3-9]|1[0-2])\\2))' . $year . '$|^(?:29' . - $separator . '0?2\\3' . $leapYear . ')$|^(?:0?[1-9]|1\\d|2[0-8])' . - $separator . '(?:(?:0?[1-9])|(?:1[0-2]))\\4' . $year . '$%'; - - $regex['mdy'] = '%^(?:(?:(?:0?[13578]|1[02])(\\/|-|\\.|\\x20)31)\\1|(?:(?:0?[13-9]|1[0-2])' . - $separator . '(?:29|30)\\2))' . $year . '$|^(?:0?2' . $separator . '29\\3' . $leapYear . ')$|^(?:(?:0?[1-9])|(?:1[0-2]))' . - $separator . '(?:0?[1-9]|1\\d|2[0-8])\\4' . $year . '$%'; - - $regex['ymd'] = '%^(?:(?:' . $leapYear . - $separator . '(?:0?2\\1(?:29)))|(?:' . $year . - $separator . '(?:(?:(?:0?[13578]|1[02])\\2(?:31))|(?:(?:0?[1,3-9]|1[0-2])\\2(29|30))|(?:(?:0?[1-9])|(?:1[0-2]))\\2(?:0?[1-9]|1\\d|2[0-8]))))$%'; - - $regex['dMy'] = '/^((31(?!\\ (Feb(ruary)?|Apr(il)?|June?|(Sep(?=\\b|t)t?|Nov)(ember)?)))|((30|29)(?!\\ Feb(ruary)?))|(29(?=\\ Feb(ruary)?\\ ' . $fourDigitLeapYear . '))|(0?[1-9])|1\\d|2[0-8])\\ (Jan(uary)?|Feb(ruary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sep(?=\\b|t)t?|Nov|Dec)(ember)?)\\ ' . $fourDigitYear . '$/'; - - $regex['Mdy'] = '/^(?:(((Jan(uary)?|Ma(r(ch)?|y)|Jul(y)?|Aug(ust)?|Oct(ober)?|Dec(ember)?)\\ 31)|((Jan(uary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sep)(tember)?|(Nov|Dec)(ember)?)\\ (0?[1-9]|([12]\\d)|30))|(Feb(ruary)?\\ (0?[1-9]|1\\d|2[0-8]|(29(?=,?\\ ' . $fourDigitLeapYear . ')))))\\,?\\ ' . $fourDigitYear . ')$/'; - - $regex['My'] = '%^(Jan(uary)?|Feb(ruary)?|Ma(r(ch)?|y)|Apr(il)?|Ju((ly?)|(ne?))|Aug(ust)?|Oct(ober)?|(Sep(?=\\b|t)t?|Nov|Dec)(ember)?)' . - $separator . $fourDigitYear . '$%'; - // phpcs:enable Generic.Files.LineLength - - $regex['my'] = '%^(' . $month . $separator . $year . ')$%'; - $regex['ym'] = '%^(' . $year . $separator . $month . ')$%'; - $regex['y'] = '%^(' . $fourDigitYear . ')$%'; - - $format = is_array($format) ? array_values($format) : [$format]; - foreach ($format as $key) { - if (static::_check($check, $regex[$key]) === true) { - return true; - } - } - - return false; - } - - /** - * Validates a datetime value - * - * All values matching the "date" core validation rule, and the "time" one will be valid - * - * @param mixed $check Value to check - * @param string|array $dateFormat Format of the date part. See Validation::date() for more information. - * Or `Validation::DATETIME_ISO8601` to valid an ISO8601 datetime value. - * @param string|null $regex Regex for the date part. If a custom regular expression is used - * this is the only validation that will occur. - * @return bool True if the value is valid, false otherwise - * @see \Cake\Validation\Validation::date() - * @see \Cake\Validation\Validation::time() - */ - public static function datetime($check, $dateFormat = 'ymd', ?string $regex = null): bool - { - if ($check instanceof DateTimeInterface) { - return true; - } - if (is_object($check)) { - return false; - } - if ($dateFormat === static::DATETIME_ISO8601 && !static::iso8601($check)) { - return false; - } - - $valid = false; - if (is_array($check)) { - $check = static::_getDateString($check); - $dateFormat = 'ymd'; - } - $parts = preg_split('/[\sT]+/', $check); - if (!empty($parts) && count($parts) > 1) { - $date = rtrim(array_shift($parts), ','); - $time = implode(' ', $parts); - if ($dateFormat === static::DATETIME_ISO8601) { - $dateFormat = 'ymd'; - $time = preg_split("/[TZ\-\+\.]/", $time); - $time = array_shift($time); - } - $valid = static::date($date, $dateFormat, $regex) && static::time($time); - } - - return $valid; - } - - /** - * Validates an iso8601 datetime format - * ISO8601 recognize datetime like 2019 as a valid date. To validate and check date integrity, use @see \Cake\Validation\Validation::datetime() - * - * @param mixed $check Value to check - * @return bool True if the value is valid, false otherwise - * @see Regex credits: https://www.myintervals.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/ - */ - public static function iso8601($check): bool - { - if ($check instanceof DateTimeInterface) { - return true; - } - if (is_object($check)) { - return false; - } - - // phpcs:ignore Generic.Files.LineLength - $regex = '/^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/'; - - return static::_check($check, $regex); - } - - /** - * Time validation, determines if the string passed is a valid time. - * Validates time as 24hr (HH:MM[:SS][.FFFFFF]) or am/pm ([H]H:MM[a|p]m) - * - * Seconds and fractional seconds (microseconds) are allowed but optional - * in 24hr format. - * - * @param mixed $check a valid time string/object - * @return bool Success - */ - public static function time($check): bool - { - if ($check instanceof DateTimeInterface) { - return true; - } - if (is_array($check)) { - $check = static::_getDateString($check); - } - - if (!is_scalar($check)) { - return false; - } - - $meridianClockRegex = '^((0?[1-9]|1[012])(:[0-5]\d){0,2} ?([AP]M|[ap]m))$'; - $standardClockRegex = '^([01]\d|2[0-3])((:[0-5]\d){0,2}|(:[0-5]\d){2}\.\d{0,6})$'; - - return static::_check($check, '%' . $meridianClockRegex . '|' . $standardClockRegex . '%'); - } - - /** - * Date and/or time string validation. - * Uses `I18n::Time` to parse the date. This means parsing is locale dependent. - * - * @param mixed $check a date string or object (will always pass) - * @param string $type Parser type, one out of 'date', 'time', and 'datetime' - * @param string|int|null $format any format accepted by IntlDateFormatter - * @return bool Success - * @throws \InvalidArgumentException when unsupported $type given - * @see \Cake\I18n\Time::parseDate(), \Cake\I18n\Time::parseTime(), \Cake\I18n\Time::parseDateTime() - */ - public static function localizedTime($check, string $type = 'datetime', $format = null): bool - { - if ($check instanceof DateTimeInterface) { - return true; - } - if (is_object($check)) { - return false; - } - static $methods = [ - 'date' => 'parseDate', - 'time' => 'parseTime', - 'datetime' => 'parseDateTime', - ]; - if (empty($methods[$type])) { - throw new InvalidArgumentException('Unsupported parser type given.'); - } - $method = $methods[$type]; - - return Time::$method($check, $format) !== null; - } - - /** - * Validates if passed value is boolean-like. - * - * The list of what is considered to be boolean values, may be set via $booleanValues. - * - * @param bool|int|string $check Value to check. - * @param array $booleanValues List of valid boolean values, defaults to `[true, false, 0, 1, '0', '1']`. - * @return bool Success. - */ - public static function boolean($check, array $booleanValues = []): bool - { - if (!$booleanValues) { - $booleanValues = [true, false, 0, 1, '0', '1']; - } - - return in_array($check, $booleanValues, true); - } - - /** - * Validates if given value is truthy. - * - * The list of what is considered to be truthy values, may be set via $truthyValues. - * - * @param bool|int|string $check Value to check. - * @param array $truthyValues List of valid truthy values, defaults to `[true, 1, '1']`. - * @return bool Success. - */ - public static function truthy($check, array $truthyValues = []): bool - { - if (!$truthyValues) { - $truthyValues = [true, 1, '1']; - } - - return in_array($check, $truthyValues, true); - } - - /** - * Validates if given value is falsey. - * - * The list of what is considered to be falsey values, may be set via $falseyValues. - * - * @param bool|int|string $check Value to check. - * @param array $falseyValues List of valid falsey values, defaults to `[false, 0, '0']`. - * @return bool Success. - */ - public static function falsey($check, array $falseyValues = []): bool - { - if (!$falseyValues) { - $falseyValues = [false, 0, '0']; - } - - return in_array($check, $falseyValues, true); - } - - /** - * Checks that a value is a valid decimal. Both the sign and exponent are optional. - * - * Valid Places: - * - * - null => Any number of decimal places, including none. The '.' is not required. - * - true => Any number of decimal places greater than 0, or a float|double. The '.' is required. - * - 1..N => Exactly that many number of decimal places. The '.' is required. - * - * @param mixed $check The value the test for decimal. - * @param int|true|null $places Decimal places. - * @param string|null $regex If a custom regular expression is used, this is the only validation that will occur. - * @return bool Success - */ - public static function decimal($check, $places = null, ?string $regex = null): bool - { - if (!is_scalar($check)) { - return false; - } - - if ($regex === null) { - $lnum = '[0-9]+'; - $dnum = "[0-9]*[\.]{$lnum}"; - $sign = '[+-]?'; - $exp = "(?:[eE]{$sign}{$lnum})?"; - - if ($places === null) { - $regex = "/^{$sign}(?:{$lnum}|{$dnum}){$exp}$/"; - } elseif ($places === true) { - if (is_float($check) && floor($check) === $check) { - $check = sprintf('%.1f', $check); - } - $regex = "/^{$sign}{$dnum}{$exp}$/"; - } elseif (is_numeric($places)) { - $places = '[0-9]{' . $places . '}'; - $dnum = "(?:[0-9]*[\.]{$places}|{$lnum}[\.]{$places})"; - $regex = "/^{$sign}{$dnum}{$exp}$/"; - } else { - return false; - } - } - - // account for localized floats. - $locale = ini_get('intl.default_locale') ?: static::DEFAULT_LOCALE; - $formatter = new NumberFormatter($locale, NumberFormatter::DECIMAL); - $decimalPoint = $formatter->getSymbol(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL); - $groupingSep = $formatter->getSymbol(NumberFormatter::GROUPING_SEPARATOR_SYMBOL); - - // There are two types of non-breaking spaces - we inject a space to account for human input - if ($groupingSep == "\xc2\xa0" || $groupingSep == "\xe2\x80\xaf") { - $check = str_replace([' ', $groupingSep, $decimalPoint], ['', '', '.'], (string)$check); - } else { - $check = str_replace([$groupingSep, $decimalPoint], ['', '.'], (string)$check); - } - - return static::_check($check, $regex); - } - - /** - * Validates for an email address. - * - * Only uses getmxrr() checking for deep validation, or - * any PHP version on a non-windows distribution - * - * @param mixed $check Value to check - * @param bool $deep Perform a deeper validation (if true), by also checking availability of host - * @param string|null $regex Regex to use (if none it will use built in regex) - * @return bool Success - */ - public static function email($check, ?bool $deep = false, ?string $regex = null): bool - { - if (!is_string($check)) { - return false; - } - - if ($regex === null) { - // phpcs:ignore Generic.Files.LineLength - $regex = '/^[\p{L}0-9!#$%&\'*+\/=?^_`{|}~-]+(?:\.[\p{L}0-9!#$%&\'*+\/=?^_`{|}~-]+)*@' . self::$_pattern['hostname'] . '$/ui'; - } - $return = static::_check($check, $regex); - if ($deep === false || $deep === null) { - return $return; - } - - if ($return === true && preg_match('/@(' . static::$_pattern['hostname'] . ')$/i', $check, $regs)) { - if (function_exists('getmxrr') && getmxrr($regs[1], $mxhosts)) { - return true; - } - if (function_exists('checkdnsrr') && checkdnsrr($regs[1], 'MX')) { - return true; - } - - return is_array(gethostbynamel($regs[1] . '.')); - } - - return false; - } - - /** - * Checks that value is exactly $comparedTo. - * - * @param mixed $check Value to check - * @param mixed $comparedTo Value to compare - * @return bool Success - */ - public static function equalTo($check, $comparedTo): bool - { - return $check === $comparedTo; - } - - /** - * Checks that value has a valid file extension. - * - * @param string|array|\Psr\Http\Message\UploadedFileInterface $check Value to check - * @param string[] $extensions file extensions to allow. By default extensions are 'gif', 'jpeg', 'png', 'jpg' - * @return bool Success - */ - public static function extension($check, array $extensions = ['gif', 'jpeg', 'png', 'jpg']): bool - { - if ($check instanceof UploadedFileInterface) { - $check = $check->getClientFilename(); - } elseif (is_array($check) && isset($check['name'])) { - $check = $check['name']; - } elseif (is_array($check)) { - return static::extension(array_shift($check), $extensions); - } - - if (empty($check)) { - return false; - } - - $extension = strtolower(pathinfo($check, PATHINFO_EXTENSION)); - foreach ($extensions as $value) { - if ($extension === strtolower($value)) { - return true; - } - } - - return false; - } - - /** - * Validation of an IP address. - * - * @param mixed $check The string to test. - * @param string $type The IP Protocol version to validate against - * @return bool Success - */ - public static function ip($check, string $type = 'both'): bool - { - if (!is_string($check)) { - return false; - } - - $type = strtolower($type); - $flags = 0; - if ($type === 'ipv4') { - $flags = FILTER_FLAG_IPV4; - } - if ($type === 'ipv6') { - $flags = FILTER_FLAG_IPV6; - } - - return (bool)filter_var($check, FILTER_VALIDATE_IP, ['flags' => $flags]); - } - - /** - * Checks whether the length of a string (in characters) is greater or equal to a minimal length. - * - * @param mixed $check The string to test - * @param int $min The minimal string length - * @return bool Success - */ - public static function minLength($check, int $min): bool - { - if (!is_scalar($check)) { - return false; - } - - return mb_strlen((string)$check) >= $min; - } - - /** - * Checks whether the length of a string (in characters) is smaller or equal to a maximal length. - * - * @param mixed $check The string to test - * @param int $max The maximal string length - * @return bool Success - */ - public static function maxLength($check, int $max): bool - { - if (!is_scalar($check)) { - return false; - } - - return mb_strlen((string)$check) <= $max; - } - - /** - * Checks whether the length of a string (in bytes) is greater or equal to a minimal length. - * - * @param mixed $check The string to test - * @param int $min The minimal string length (in bytes) - * @return bool Success - */ - public static function minLengthBytes($check, int $min): bool - { - if (!is_scalar($check)) { - return false; - } - - return strlen((string)$check) >= $min; - } - - /** - * Checks whether the length of a string (in bytes) is smaller or equal to a maximal length. - * - * @param mixed $check The string to test - * @param int $max The maximal string length - * @return bool Success - */ - public static function maxLengthBytes($check, int $max): bool - { - if (!is_scalar($check)) { - return false; - } - - return strlen((string)$check) <= $max; - } - - /** - * Checks that a value is a monetary amount. - * - * @param mixed $check Value to check - * @param string $symbolPosition Where symbol is located (left/right) - * @return bool Success - */ - public static function money($check, string $symbolPosition = 'left'): bool - { - $money = '(?!0,?\d)(?:\d{1,3}(?:([, .])\d{3})?(?:\1\d{3})*|(?:\d+))((?!\1)[,.]\d{1,2})?'; - if ($symbolPosition === 'right') { - $regex = '/^' . $money . '(? provide a list of choices that selections must be made from - * - max => maximum number of non-zero choices that can be made - * - min => minimum number of non-zero choices that can be made - * - * @param mixed $check Value to check - * @param array $options Options for the check. - * @param bool $caseInsensitive Set to true for case insensitive comparison. - * @return bool Success - */ - public static function multiple($check, array $options = [], bool $caseInsensitive = false): bool - { - $defaults = ['in' => null, 'max' => null, 'min' => null]; - $options += $defaults; - - $check = array_filter((array)$check, function ($value) { - return $value || is_numeric($value); - }); - if (empty($check)) { - return false; - } - if ($options['max'] && count($check) > $options['max']) { - return false; - } - if ($options['min'] && count($check) < $options['min']) { - return false; - } - if ($options['in'] && is_array($options['in'])) { - if ($caseInsensitive) { - $options['in'] = array_map('mb_strtolower', $options['in']); - } - foreach ($check as $val) { - $strict = !is_numeric($val); - if ($caseInsensitive) { - $val = mb_strtolower($val); - } - if (!in_array((string)$val, $options['in'], $strict)) { - return false; - } - } - } - - return true; - } - - /** - * Checks if a value is numeric. - * - * @param mixed $check Value to check - * @return bool Success - */ - public static function numeric($check): bool - { - return is_numeric($check); - } - - /** - * Checks if a value is a natural number. - * - * @param mixed $check Value to check - * @param bool $allowZero Set true to allow zero, defaults to false - * @return bool Success - * @see https://en.wikipedia.org/wiki/Natural_number - */ - public static function naturalNumber($check, bool $allowZero = false): bool - { - $regex = $allowZero ? '/^(?:0|[1-9][0-9]*)$/' : '/^[1-9][0-9]*$/'; - - return static::_check($check, $regex); - } - - /** - * Validates that a number is in specified range. - * - * If $lower and $upper are set, the range is inclusive. - * If they are not set, will return true if $check is a - * legal finite on this platform. - * - * @param mixed $check Value to check - * @param float|null $lower Lower limit - * @param float|null $upper Upper limit - * @return bool Success - */ - public static function range($check, ?float $lower = null, ?float $upper = null): bool - { - if (!is_numeric($check)) { - return false; - } - if ((float)$check != $check) { - return false; - } - if (isset($lower, $upper)) { - return $check >= $lower && $check <= $upper; - } - - return is_finite((float)$check); - } - - /** - * Checks that a value is a valid URL according to https://www.w3.org/Addressing/URL/url-spec.txt - * - * The regex checks for the following component parts: - * - * - a valid, optional, scheme - * - a valid ip address OR - * a valid domain name as defined by section 2.3.1 of https://www.ietf.org/rfc/rfc1035.txt - * with an optional port number - * - an optional valid path - * - an optional query string (get parameters) - * - an optional fragment (anchor tag) as defined in RFC 3986 - * - * @param mixed $check Value to check - * @param bool $strict Require URL to be prefixed by a valid scheme (one of http(s)/ftp(s)/file/news/gopher) - * @return bool Success - * @link https://tools.ietf.org/html/rfc3986 - */ - public static function url($check, bool $strict = false): bool - { - if (!is_string($check)) { - return false; - } - - static::_populateIp(); - - $emoji = '\x{1F190}-\x{1F9EF}'; - $alpha = '0-9\p{L}\p{N}' . $emoji; - $hex = '(%[0-9a-f]{2})'; - $subDelimiters = preg_quote('/!"$&\'()*+,-.@_:;=~[]', '/'); - $path = '([' . $subDelimiters . $alpha . ']|' . $hex . ')'; - $fragmentAndQuery = '([\?' . $subDelimiters . $alpha . ']|' . $hex . ')'; - // phpcs:disable Generic.Files.LineLength - $regex = '/^(?:(?:https?|ftps?|sftp|file|news|gopher):\/\/)' . ($strict ? '' : '?') . - '(?:' . static::$_pattern['IPv4'] . '|\[' . static::$_pattern['IPv6'] . '\]|' . static::$_pattern['hostname'] . ')(?::[1-9][0-9]{0,4})?' . - '(?:\/' . $path . '*)?' . - '(?:\?' . $fragmentAndQuery . '*)?' . - '(?:#' . $fragmentAndQuery . '*)?$/iu'; - // phpcs:enable Generic.Files.LineLength - - return static::_check($check, $regex); - } - - /** - * Checks if a value is in a given list. Comparison is case sensitive by default. - * - * @param mixed $check Value to check. - * @param string[] $list List to check against. - * @param bool $caseInsensitive Set to true for case insensitive comparison. - * @return bool Success. - */ - public static function inList($check, array $list, bool $caseInsensitive = false): bool - { - if ($caseInsensitive) { - $list = array_map('mb_strtolower', $list); - $check = mb_strtolower($check); - } else { - $list = array_map('strval', $list); - } - - return in_array((string)$check, $list, true); - } - - /** - * Checks that a value is a valid UUID - https://tools.ietf.org/html/rfc4122 - * - * @param mixed $check Value to check - * @return bool Success - */ - public static function uuid($check): bool - { - $regex = '/^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[0-5][a-fA-F0-9]{3}-[089aAbB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$/'; - - return self::_check($check, $regex); - } - - /** - * Runs a regular expression match. - * - * @param mixed $check Value to check against the $regex expression - * @param string $regex Regular expression - * @return bool Success of match - */ - protected static function _check($check, string $regex): bool - { - return is_scalar($check) && preg_match($regex, (string)$check); - } - - /** - * Luhn algorithm - * - * @param mixed $check Value to check. - * @return bool Success - * @see https://en.wikipedia.org/wiki/Luhn_algorithm - */ - public static function luhn($check): bool - { - if (!is_scalar($check) || (int)$check === 0) { - return false; - } - $sum = 0; - $check = (string)$check; - $length = strlen($check); - - for ($position = 1 - ($length % 2); $position < $length; $position += 2) { - $sum += (int)$check[$position]; - } - - for ($position = $length % 2; $position < $length; $position += 2) { - $number = (int)$check[$position] * 2; - $sum += $number < 10 ? $number : $number - 9; - } - - return $sum % 10 === 0; - } - - /** - * Checks the mime type of a file. - * - * Will check the mimetype of files/UploadedFileInterface instances - * by checking the using finfo on the file, not relying on the content-type - * sent by the client. - * - * @param string|array|\Psr\Http\Message\UploadedFileInterface $check Value to check. - * @param array|string $mimeTypes Array of mime types or regex pattern to check. - * @return bool Success - * @throws \RuntimeException when mime type can not be determined. - * @throws \LogicException when ext/fileinfo is missing - */ - public static function mimeType($check, $mimeTypes = []): bool - { - $file = static::getFilename($check); - if ($file === false) { - return false; - } - - if (!function_exists('finfo_open')) { - throw new LogicException('ext/fileinfo is required for validating file mime types'); - } - - if (!is_file($file)) { - throw new RuntimeException('Cannot validate mimetype for a missing file'); - } - - $finfo = finfo_open(FILEINFO_MIME); - $finfo = finfo_file($finfo, $file); - - if (!$finfo) { - throw new RuntimeException('Can not determine the mimetype.'); - } - - [$mime] = explode(';', $finfo); - - if (is_string($mimeTypes)) { - return self::_check($mime, $mimeTypes); - } - - foreach ($mimeTypes as $key => $val) { - $mimeTypes[$key] = strtolower($val); - } - - return in_array(strtolower($mime), $mimeTypes, true); - } - - /** - * Helper for reading the file out of the various file implementations - * we accept. - * - * @param string|array|\Psr\Http\Message\UploadedFileInterface $check The data to read a filename out of. - * @return string|false Either the filename or false on failure. - */ - protected static function getFilename($check) - { - if ($check instanceof UploadedFileInterface) { - try { - // Uploaded files throw exceptions on upload errors. - return $check->getStream()->getMetadata('uri'); - } catch (RuntimeException $e) { - return false; - } - } - if (is_array($check) && isset($check['tmp_name'])) { - return $check['tmp_name']; - } - - if (is_string($check)) { - return $check; - } - - return false; - } - - /** - * Checks the filesize - * - * Will check the filesize of files/UploadedFileInterface instances - * by checking the filesize() on disk and not relying on the length - * reported by the client. - * - * @param string|array|\Psr\Http\Message\UploadedFileInterface $check Value to check. - * @param string $operator See `Validation::comparison()`. - * @param int|string $size Size in bytes or human readable string like '5MB'. - * @return bool Success - */ - public static function fileSize($check, string $operator, $size): bool - { - $file = static::getFilename($check); - if ($file === false) { - return false; - } - - if (is_string($size)) { - $size = Text::parseFileSize($size); - } - $filesize = filesize($file); - - return static::comparison($filesize, $operator, $size); - } - - /** - * Checking for upload errors - * - * @param string|array|\Psr\Http\Message\UploadedFileInterface $check Value to check. - * @param bool $allowNoFile Set to true to allow UPLOAD_ERR_NO_FILE as a pass. - * @return bool - * @see https://secure.php.net/manual/en/features.file-upload.errors.php - */ - public static function uploadError($check, bool $allowNoFile = false): bool - { - if ($check instanceof UploadedFileInterface) { - $code = $check->getError(); - } elseif (is_array($check) && isset($check['error'])) { - $code = $check['error']; - } else { - $code = $check; - } - if ($allowNoFile) { - return in_array((int)$code, [UPLOAD_ERR_OK, UPLOAD_ERR_NO_FILE], true); - } - - return (int)$code === UPLOAD_ERR_OK; - } - - /** - * Validate an uploaded file. - * - * Helps join `uploadError`, `fileSize` and `mimeType` into - * one higher level validation method. - * - * ### Options - * - * - `types` - An array of valid mime types. If empty all types - * will be accepted. The `type` will not be looked at, instead - * the file type will be checked with ext/finfo. - * - `minSize` - The minimum file size in bytes. Defaults to not checking. - * - `maxSize` - The maximum file size in bytes. Defaults to not checking. - * - `optional` - Whether or not this file is optional. Defaults to false. - * If true a missing file will pass the validator regardless of other constraints. - * - * @param mixed $file The uploaded file data from PHP. - * @param array $options An array of options for the validation. - * @return bool - */ - public static function uploadedFile($file, array $options = []): bool - { - $options += [ - 'minSize' => null, - 'maxSize' => null, - 'types' => null, - 'optional' => false, - ]; - if (!is_array($file) && !($file instanceof UploadedFileInterface)) { - return false; - } - $error = $isUploaded = false; - if ($file instanceof UploadedFileInterface) { - $error = $file->getError(); - $isUploaded = true; - } - if (is_array($file)) { - $keys = ['error', 'name', 'size', 'tmp_name', 'type']; - ksort($file); - if (array_keys($file) !== $keys) { - return false; - } - $error = (int)$file['error']; - $isUploaded = is_uploaded_file($file['tmp_name']); - } - - if (!static::uploadError($file, $options['optional'])) { - return false; - } - if ($options['optional'] && $error === UPLOAD_ERR_NO_FILE) { - return true; - } - if ( - isset($options['minSize']) - && !static::fileSize($file, static::COMPARE_GREATER_OR_EQUAL, $options['minSize']) - ) { - return false; - } - if ( - isset($options['maxSize']) - && !static::fileSize($file, static::COMPARE_LESS_OR_EQUAL, $options['maxSize']) - ) { - return false; - } - if (isset($options['types']) && !static::mimeType($file, $options['types'])) { - return false; - } - - return $isUploaded; - } - - /** - * Validates the size of an uploaded image. - * - * @param mixed $file The uploaded file data from PHP. - * @param array $options Options to validate width and height. - * @return bool - * @throws \InvalidArgumentException - */ - public static function imageSize($file, array $options): bool - { - if (!isset($options['height']) && !isset($options['width'])) { - throw new InvalidArgumentException( - 'Invalid image size validation parameters! Missing `width` and / or `height`.' - ); - } - - $file = static::getFilename($file); - if ($file === false) { - return false; - } - - [$width, $height] = getimagesize($file); - $validHeight = null; - $validWidth = null; - - if (isset($options['height'])) { - $validHeight = self::comparison($height, $options['height'][0], $options['height'][1]); - } - if (isset($options['width'])) { - $validWidth = self::comparison($width, $options['width'][0], $options['width'][1]); - } - if ($validHeight !== null && $validWidth !== null) { - return $validHeight && $validWidth; - } - if ($validHeight !== null) { - return $validHeight; - } - if ($validWidth !== null) { - return $validWidth; - } - - throw new InvalidArgumentException('The 2nd argument is missing the `width` and / or `height` options.'); - } - - /** - * Validates the image width. - * - * @param array $file The uploaded file data from PHP. - * @param string $operator Comparison operator. - * @param int $width Min or max width. - * @return bool - */ - public static function imageWidth(array $file, string $operator, int $width): bool - { - return self::imageSize($file, [ - 'width' => [ - $operator, - $width, - ], - ]); - } - - /** - * Validates the image width. - * - * @param array $file The uploaded file data from PHP. - * @param string $operator Comparison operator. - * @param int $height Min or max width. - * @return bool - */ - public static function imageHeight(array $file, string $operator, int $height): bool - { - return self::imageSize($file, [ - 'height' => [ - $operator, - $height, - ], - ]); - } - - /** - * Validates a geographic coordinate. - * - * Supported formats: - * - * - `, ` Example: `-25.274398, 133.775136` - * - * ### Options - * - * - `type` - A string of the coordinate format, right now only `latLong`. - * - `format` - By default `both`, can be `long` and `lat` as well to validate - * only a part of the coordinate. - * - * @param mixed $value Geographic location as string - * @param array $options Options for the validation logic. - * @return bool - */ - public static function geoCoordinate($value, array $options = []): bool - { - if (!is_scalar($value)) { - return false; - } - - $options += [ - 'format' => 'both', - 'type' => 'latLong', - ]; - if ($options['type'] !== 'latLong') { - throw new RuntimeException(sprintf( - 'Unsupported coordinate type "%s". Use "latLong" instead.', - $options['type'] - )); - } - $pattern = '/^' . self::$_pattern['latitude'] . ',\s*' . self::$_pattern['longitude'] . '$/'; - if ($options['format'] === 'long') { - $pattern = '/^' . self::$_pattern['longitude'] . '$/'; - } - if ($options['format'] === 'lat') { - $pattern = '/^' . self::$_pattern['latitude'] . '$/'; - } - - return (bool)preg_match($pattern, (string)$value); - } - - /** - * Convenience method for latitude validation. - * - * @param mixed $value Latitude as string - * @param array $options Options for the validation logic. - * @return bool - * @link https://en.wikipedia.org/wiki/Latitude - * @see \Cake\Validation\Validation::geoCoordinate() - */ - public static function latitude($value, array $options = []): bool - { - $options['format'] = 'lat'; - - return self::geoCoordinate($value, $options); - } - - /** - * Convenience method for longitude validation. - * - * @param mixed $value Latitude as string - * @param array $options Options for the validation logic. - * @return bool - * @link https://en.wikipedia.org/wiki/Longitude - * @see \Cake\Validation\Validation::geoCoordinate() - */ - public static function longitude($value, array $options = []): bool - { - $options['format'] = 'long'; - - return self::geoCoordinate($value, $options); - } - - /** - * Check that the input value is within the ascii byte range. - * - * This method will reject all non-string values. - * - * @param mixed $value The value to check - * @return bool - */ - public static function ascii($value): bool - { - if (!is_string($value)) { - return false; - } - - return strlen($value) <= mb_strlen($value, 'utf-8'); - } - - /** - * Check that the input value is a utf8 string. - * - * This method will reject all non-string values. - * - * # Options - * - * - `extended` - Disallow bytes higher within the basic multilingual plane. - * MySQL's older utf8 encoding type does not allow characters above - * the basic multilingual plane. Defaults to false. - * - * @param mixed $value The value to check - * @param array $options An array of options. See above for the supported options. - * @return bool - */ - public static function utf8($value, array $options = []): bool - { - if (!is_string($value)) { - return false; - } - $options += ['extended' => false]; - if ($options['extended']) { - return true; - } - - return preg_match('/[\x{10000}-\x{10FFFF}]/u', $value) === 0; - } - - /** - * Check that the input value is an integer - * - * This method will accept strings that contain only integer data - * as well. - * - * @param mixed $value The value to check - * @return bool - */ - public static function isInteger($value): bool - { - if (is_int($value)) { - return true; - } - - if (!is_string($value) || !is_numeric($value)) { - return false; - } - - return (bool)preg_match('/^-?[0-9]+$/', $value); - } - - /** - * Check that the input value is an array. - * - * @param mixed $value The value to check - * @return bool - */ - public static function isArray($value): bool - { - return is_array($value); - } - - /** - * Check that the input value is a scalar. - * - * This method will accept integers, floats, strings and booleans, but - * not accept arrays, objects, resources and nulls. - * - * @param mixed $value The value to check - * @return bool - */ - public static function isScalar($value): bool - { - return is_scalar($value); - } - - /** - * Check that the input value is a 6 digits hex color. - * - * @param mixed $check The value to check - * @return bool Success - */ - public static function hexColor($check): bool - { - return static::_check($check, '/^#[0-9a-f]{6}$/iD'); - } - - /** - * Check that the input value has a valid International Bank Account Number IBAN syntax - * Requirements are uppercase, no whitespaces, max length 34, country code and checksum exist at right spots, - * body matches against checksum via Mod97-10 algorithm - * - * @param mixed $check The value to check - * - * @return bool Success - */ - public static function iban($check): bool - { - if ( - !is_string($check) || - !preg_match('/^[A-Z]{2}[0-9]{2}[A-Z0-9]{1,30}$/', $check) - ) { - return false; - } - - $country = substr($check, 0, 2); - $checkInt = intval(substr($check, 2, 2)); - $account = substr($check, 4); - $search = range('A', 'Z'); - $replace = []; - foreach (range(10, 35) as $tmp) { - $replace[] = strval($tmp); - } - $numStr = str_replace($search, $replace, $account . $country . '00'); - $checksum = intval(substr($numStr, 0, 1)); - $numStrLength = strlen($numStr); - for ($pos = 1; $pos < $numStrLength; $pos++) { - $checksum *= 10; - $checksum += intval(substr($numStr, $pos, 1)); - $checksum %= 97; - } - - return $checkInt === 98 - $checksum; - } - - /** - * Converts an array representing a date or datetime into a ISO string. - * The arrays are typically sent for validation from a form generated by - * the CakePHP FormHelper. - * - * @param array $value The array representing a date or datetime. - * @return string - */ - protected static function _getDateString(array $value): string - { - $formatted = ''; - if ( - isset($value['year'], $value['month'], $value['day']) && - ( - is_numeric($value['year']) && - is_numeric($value['month']) && - is_numeric($value['day']) - ) - ) { - $formatted .= sprintf('%d-%02d-%02d ', $value['year'], $value['month'], $value['day']); - } - - if (isset($value['hour'])) { - if (isset($value['meridian']) && (int)$value['hour'] === 12) { - $value['hour'] = 0; - } - if (isset($value['meridian'])) { - $value['hour'] = strtolower($value['meridian']) === 'am' ? $value['hour'] : $value['hour'] + 12; - } - $value += ['minute' => 0, 'second' => 0, 'microsecond' => 0]; - if ( - is_numeric($value['hour']) && - is_numeric($value['minute']) && - is_numeric($value['second']) && - is_numeric($value['microsecond']) - ) { - $formatted .= sprintf( - '%02d:%02d:%02d.%06d', - $value['hour'], - $value['minute'], - $value['second'], - $value['microsecond'] - ); - } - } - - return trim($formatted); - } - - /** - * Lazily populate the IP address patterns used for validations - * - * @return void - */ - protected static function _populateIp(): void - { - // phpcs:disable Generic.Files.LineLength - if (!isset(static::$_pattern['IPv6'])) { - $pattern = '((([0-9A-Fa-f]{1,4}:){7}(([0-9A-Fa-f]{1,4})|:))|(([0-9A-Fa-f]{1,4}:){6}'; - $pattern .= '(:|((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})'; - $pattern .= '|(:[0-9A-Fa-f]{1,4})))|(([0-9A-Fa-f]{1,4}:){5}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})'; - $pattern .= '(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:)'; - $pattern .= '{4}(:[0-9A-Fa-f]{1,4}){0,1}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2}))'; - $pattern .= '{3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){3}(:[0-9A-Fa-f]{1,4}){0,2}'; - $pattern .= '((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|'; - $pattern .= '((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){2}(:[0-9A-Fa-f]{1,4}){0,3}'; - $pattern .= '((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2}))'; - $pattern .= '{3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:)(:[0-9A-Fa-f]{1,4})'; - $pattern .= '{0,4}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)'; - $pattern .= '|((:[0-9A-Fa-f]{1,4}){1,2})))|(:(:[0-9A-Fa-f]{1,4}){0,5}((:((25[0-5]|2[0-4]'; - $pattern .= '\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4})'; - $pattern .= '{1,2})))|(((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})))(%.+)?'; - - static::$_pattern['IPv6'] = $pattern; - } - if (!isset(static::$_pattern['IPv4'])) { - $pattern = '(?:(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|(?:(?:1[0-9])?|[1-9]?)[0-9])'; - static::$_pattern['IPv4'] = $pattern; - } - // phpcs:enable Generic.Files.LineLength - } - - /** - * Reset internal variables for another validation run. - * - * @return void - */ - protected static function _reset(): void - { - static::$errors = []; - } -}