diff --git a/.gitignore b/.gitignore index 96b0f3f..a48379e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ composer.phar +/composer.lock /vendor/ /phpunit.xml !.gitkeep \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d26397..7045654 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,37 +1,66 @@ # Changelog +## 1.2.0 + +### Fixed + +* Add missing, but referenced error codes in `ErrorLoggerInterface` + +### Changed + +* Updated README +* `composer.lock` is removed from VCS + +### Added + +* Simple validators: `Callback`, `ClassName`, `Email`, `Size`, `Type` and `WpFilter` +* `SecondaryValidatorInterface` and secondary validators: `Negate`, `Bulk` and `Pool` +* `DataValidator::with_validators()` method +* Unit tests for all new classes + +----- + + ## 1.1.1 -#### Fixed +### Fixed + * Avoid possible function redeclaration in case of multiple autoloaders, thanks @schlessera, see #6 -#### Added +### Added + * _Nothing_ -#### Changed +### Changed + * _Nothing_ ---------------- +----- + ## 1.1.0 -#### Fixed +### Fixed + * CS fixes -#### Changed +### Changed + * General refactoring * Decoupled messages from validators -* Deprecated get_error_messages() validators method -* Deprecated ArrayValue in favor of newly added DataValidator -* Error codes constants moved to ErrorLoggerInterface from single validators classes (old constants still available for BC, but usage is deprecated) - -#### Added -* Multi validator -* DataValidator validator -* ErrorLoggerInterface, ErrorLogger and WordPressErrorLogger +* Deprecated `get_error_messages()` validators method +* Deprecated `ArrayValue` in favor of newly added `DataValidator` +* Error codes constants moved to `ErrorLoggerInterface` from single validators classes (old constants still available for BC, but usage is deprecated) + +### Added + +* `Multi` validator +* `DataValidator` +* `ErrorLoggerInterface`, ErrorLogger and WordPressErrorLogger * Support for messages translation ---------------- +----- ## 1.0.0 + * Initial Release. diff --git a/README.md b/README.md index bc02cf3..7e611d1 100644 --- a/README.md +++ b/README.md @@ -7,20 +7,26 @@ This package provides a collection of validators for WordPress. * [Installation](#installation) * [What it is and how it works](#what-it-is-and-how-it-works) * [Simple Validators](#simple-validators) + * [Secondary Validators](#secondary-validators) + * `Negate` example + * `Bulk` example + * `Pool` example * [Compound validators](#compound-validators) + * `Multi` example + * `MultiOr` example * [Error codes and input data](#error-codes-and-input-data) * [Validators factory](#validators-factory) * [Error messages](#error-messages) * [Error templates](#error-templates) * Code-specific templates * Error-specific templates -* [`DataValidator`](#) +* [`DataValidator`](#datavalidator) * [Add validators to all items](#) * [Add validator to specific items](#) * [Customize error message templates](#) * [Item keys in error messages](#) * [Item key labels for error messages](#) -* [Custom validators](#) +* [Custom validators](#custom-validators) * [Custom validator example](#custom-validator-example) * [Upgrading from version 1.0](#upgrading-from-version-10) * [Other notes](#other-notes) @@ -45,8 +51,17 @@ The package provides validator objects that can be used to verify that some give Most important method for each validator is `is_valid()` that receives some data and returns `true` or `false`, depending on the provided data meets validator requirements. -We can distinguish between "simple" and "compound" validators. Where the latter are validators that are made combining -simple validators together. +We can distinguish among three types of validators: + +- "simple" +- "secondary" +- "compound" + +**Simple** validators are used to verify single values, according to some specifications. + +**Secondary** validators are created taking a simple validator and modifying its behavior. Can be seen as "decorators" for validators. + + **Compound** validators are made by combining togheter more validators. ### Simple validators @@ -54,14 +69,20 @@ This is a summary of simple validators provided as of now with the package: Name | Can be used for | Options | Description --------- | --------- | --------- | --------- -`Between` | Any data | `min`, `max`,`inclusive` | Verifies that given value is between a maximum and a minimum defined in options. +`Between` | Any data | `min`, `max`,`inclusive` | Verifies given value is between a maximum and a minimum defined in options. +`Callback` | Any data | `callback` | Run give callback passing value to validate. If callback returns a true-ish value, value is considered valid. +`ClassName` | Strings | `autoload` | Check that value is a valid class name string. Trigger autoload by default, but ir can be prevented by option. `Date` | String, array, integers and`DateTimeInterface` objects | `format` | Verifies that given data is a valid date according to format defined in options. -`GreaterThan` | Any data | `min`,`inclusive` | Verifies that given value is `>` (or `>=`) option value. -`InArray` | Any data | `haystack`,`strict` | Verifies that given value is present in an haystack defined in options. -`LessThan` | Any data | `max`,`inclusive` | Verifies that given value is `<` (or `<=`) option value. -`NotEmpty` | Any data | --- | Verifies that given value is not empty. (Unlike PHP `empty()` function `0` and `'0'` are not considered empty) -`RegEx` | Strings | `pattern` | Verifies that given string matches a regular expression pattern defined in options. +`Email` | Strings | `check_dns` | Check that value is a valid email. Optionally also checks DNS. +`GreaterThan` | Any data | `min`,`inclusive` | Verifies given value is `>` (or `>=`) option value. +`InArray` | Any data | `haystack`,`strict` | Verifies given value is present in an haystack defined in options. +`LessThan` | Any data | `max`,`inclusive` | Verifies given value is `<` (or `<=`) option value. +`NotEmpty` | Any data | --- | Verifies given value is not empty. (Unlike PHP `empty()` function `0` and `'0'` are not considered empty) +`RegEx` | Strings | `pattern` | Verifies given string matches a regular expression pattern defined in options. +`Size` | Any data | `size` | Verifies given data has size defined by option. For strings it means length, arrays and countable objects are counted, numbers cast to integer. +`Type` | Any data | `type` | Verifies that given data is of a specific type. Works with built-in types like "integer", "string" and with class and interface names. Also has 2 special types "numeric" and "traversable" `Url` | Strings | `allowed_protocols`, `check_dns` | Verifies that given string is a valid URL. Optionally also checks DNS. +`WpFilter` | Any data | `filter` | Calls `apply_filters` with the filter set, passing the value as argument. If callbacks hooked to filter returns a true-ish value, value is considered valid. All validators are defined in `Inpsyde\Validator` namespace, so it is possible to use them like this: @@ -80,38 +101,98 @@ if ( $between->is_valid($value) ) { Other validators can be used in a pretty identical fashion. +### Secondary validators + +At the moment, are available following secondary validators: + +Name | Can be used for | Description +--------- | --------- | --------- | +`Bulk` | Traversable data | Takes one validator and applies it to all items of a traversable value. Validate if validator validates *all* the items. +`Negate` | Any data | Takes one validator and negate its result. If given validator validates, the `Negate` validator will fail, and the other way around. +`Pool` | Traversable data | Similar to `Bulk`, it applies a validator to all items of a traversable value. But it validates if the validator validates any of the items. + +All secondary validators have a `with_validator()` static method, that can be used as named constructor to obtain an instance. + +#### `Negate` example + +Here an example on how to use `Negate` to check that given value is _not_ included in a given haystack of values: + +```php +$not_in_array = Negate::with_validator( new InArray( [ 'haystack' => [ 'foo', 'bar' ] ] ) ); + +$not_in_array->is_valid( 'hello' ); // true +$not_in_array->is_valid( 'foo' ); // false +``` + +#### `Bulk` example + +Here an example on how to use `Bulk` to check that given array contains only strings: + +```php +$array_of_strings = Bulk::with_validator( new Type( [ 'type' => 'string' ] ) ); + +$array_of_strings->is_valid( [ 'foo', 'bar' ] ); // true +$array_of_strings->is_valid( [ 'foo', true ); // false +``` + +#### `Pool` example + +Here an example on how to use `Pool` to check that given array contains at least a `WP_Post` object: + +```php +$has_post = Pool::with_validator( new Type( [ 'type' => 'WP_Post' ] ) ); + +$has_post->is_valid( [ 'foo', new \WP_Post([ 'id' => 1 ]) ] ); // true +$has_post->is_valid( [ 'foo', true ); // false +``` + +`Pool` traverse the given value and returns true when first item of the value validates the inner validator. + + + ### Compound validators -At the moment, there are two compound validators, they are: +At the moment, following compound validators ar available: Name | Can be used for | Options | Description --------- | --------- | --------- | --------- | -`Multi` | Any data | `stop_on_failure` | Combine more validators together to check the same value. Will be valid if all child validators are valid. +`Multi` | Any data | `stop_on_failure` | Combine more validators together to check the same value. Will be valid if all child validators are valid. I.e. it combines validators with `AND` login operand. +`MultiOr` | Any data | --- | Combine more validators together to check the same value. Will be valid if any of the child validators is valid. I.e. it combines validators with `OR` login operand. `DataValidator` | arrays or instances of `Traversable` | --- | Validate a collection of data, each child validator is assigned to a different part of the data, assigned by key **`DataValidator`** is the more powerful validator of the package, because it is the only validator implementing `ErrorLoggerAwareValidatorInterface` interface that make possible to obtain error messages for validated data ia a very simple way. For this reason usage of this validator is treated separately below. -**`Multi`** is simpler: it just takes a list of validators and use all of them to validate a single value. +#### `Multi` example -For example: +Here an example on how to use `Multi` validator, to check that given value is an array _and_ has two items _and_ bot of +them are strings: ```php use Inpsyde\Validator; -$custom_between = new Validator\Multi( +$two_items_string_array = new Validator\Multi( ['stop_on_failure' => TRUE ], [ - new Validator\GreaterThan(['min' => 10, 'inclusive' => true]), - new Validator\LessThan(['max' => 20, 'inclusive' => false]), + new Validator\Type( [ 'type' => 'array' ] ), + new Validator\Size( [ 'type' => 2 ] ), + Validator\Bulk::with_validator( new Validator\Type( [ 'type' => 'string' ] ) ), ] ); + +$two_items_string_array->is_valid( [' foo', 'bar' ] ); // true +$two_items_string_array->is_valid( [ 'foo', 1 ] ); // false +$two_items_string_array->is_valid( [ 'foo', 'bar', 'baz' ] ); // false + ``` The first constructor argument is an array of options, just like for all the "simple" validators. The second argument is an array of validators. +Please note how we used a secondary validator (`Bulk`) as a _child_ validator for `Multi`: this is totally fine, because +simple, secondary and compound validators all implements same interface. + By default all validators are executed for the given value when `is_valid()` is called, but setting the option `stop_on_failure` to `TRUE`, the validator stops to perform validation when the first failing validator is reached. @@ -122,9 +203,10 @@ that accepts a variadic number of validator objects: ```php use Inpsyde\Validator; -$custom_between = Validator\Multi::with_validators( - new Validator\GreaterThan(['min' => 10, 'inclusive' => true]), - new Validator\LessThan(['max' => 20, 'inclusive' => false]), +$two_items_string_array = Validator\Multi::with_validators( + new Validator\Type( [ 'type' => 'array' ] ), + new Validator\Size( [ 'type' => 2 ] ), + Validator\Bulk::with_validator( new Validator\Type( [ 'type' => 'string' ] ) ), ); ``` @@ -134,7 +216,29 @@ When constructed like this, the `stop_on_failure` options is set to its default, ```php use Inpsyde\Validator; -$custom_between = Validator\Multi::with_validators(...$validators)->stop_on_failure(); +$two_items_string_array = Validator\Multi::with_validators(...$validators)->stop_on_failure(); +``` + +#### `MultiOr` example + +`MultiOr` is very similar to `Multi`, but the latter combines validator with an `AND` operand, the former with `OR` operand. + +In other words, using `Multi` _all_ the inner validators have to validate to make `Multi` validate, on the contrary `MultiOr` +validates if _at least one of_ inner validators validates. + +Here an example on how to use `MultiOr` to validate a value to be in the range from 5 to 10 _or_ in the range 50 to 100: + +```php +use Inpsyde\Validator; + +$custom_range = Validator\MultiOr::with_validators( + new Validator\Between( [ 'min' => 5, 'max' => 10 ] ), + new Validator\Between( [ 'min' => 50, 'max' => 100 ] ), +); + +$custom_range->is_valid( 7 ) // true +$custom_range->is_valid( 30 ) // false +$custom_range->is_valid( 60 ) // true ``` ### Error codes and input data diff --git a/composer.json b/composer.json index f5d172b..cfb15ea 100644 --- a/composer.json +++ b/composer.json @@ -24,8 +24,9 @@ "php": ">=5.5.0" }, "require-dev": { - "phpunit/phpunit": "~4.8|~5.1", - "mockery/mockery": "0.9.*" + "phpunit/phpunit": "~4.8.0|~5.4.0", + "mockery/mockery": "~0.9.0", + "brain/monkey": "~1.4.0" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock deleted file mode 100644 index d86a24b..0000000 --- a/composer.lock +++ /dev/null @@ -1,1193 +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#composer-lock-the-lock-file", - "This file is @generated automatically" - ], - "hash": "5ec98e46c53f8ddcaef7834b875b5e7b", - "content-hash": "7fc167cc1dca38763bdaf2cb11fa11b9", - "packages": [], - "packages-dev": [ - { - "name": "doctrine/instantiator", - "version": "1.0.5", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", - "shasum": "" - }, - "require": { - "php": ">=5.3,<8.0-DEV" - }, - "require-dev": { - "athletic/athletic": "~0.1.8", - "ext-pdo": "*", - "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.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://github.com/doctrine/instantiator", - "keywords": [ - "constructor", - "instantiate" - ], - "time": "2015-06-14 21:17:01" - }, - { - "name": "mockery/mockery", - "version": "0.9.3", - "source": { - "type": "git", - "url": "https://github.com/padraic/mockery.git", - "reference": "686f85fa5b3b079cc0157d7cd3e9adb97f0b41e1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/padraic/mockery/zipball/686f85fa5b3b079cc0157d7cd3e9adb97f0b41e1", - "reference": "686f85fa5b3b079cc0157d7cd3e9adb97f0b41e1", - "shasum": "" - }, - "require": { - "lib-pcre": ">=7.0", - "php": ">=5.3.2" - }, - "require-dev": { - "hamcrest/hamcrest-php": "~1.1", - "phpunit/phpunit": "~4.0", - "satooshi/php-coveralls": "~0.7@dev" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.9.x-dev" - } - }, - "autoload": { - "psr-0": { - "Mockery": "library/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Pádraic Brady", - "email": "padraic.brady@gmail.com", - "homepage": "http://blog.astrumfutura.com" - }, - { - "name": "Dave Marshall", - "email": "dave.marshall@atstsolutions.co.uk", - "homepage": "http://davedevelopment.co.uk" - } - ], - "description": "Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succint API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending.", - "homepage": "http://github.com/padraic/mockery", - "keywords": [ - "BDD", - "TDD", - "library", - "mock", - "mock objects", - "mockery", - "stub", - "test", - "test double", - "testing" - ], - "time": "2014-12-22 10:06:19" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "1.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", - "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", - "shasum": "" - }, - "require": { - "php": ">=5.5" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.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": "2015-12-27 11:43:31" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "9270140b940ff02e58ec577c237274e92cd40cdd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9270140b940ff02e58ec577c237274e92cd40cdd", - "reference": "9270140b940ff02e58ec577c237274e92cd40cdd", - "shasum": "" - }, - "require": { - "php": ">=5.5", - "phpdocumentor/reflection-common": "^1.0@dev", - "phpdocumentor/type-resolver": "^0.2.0", - "webmozart/assert": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^4.4" - }, - "type": "library", - "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": "2016-06-10 09:48:41" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "0.2", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b39c7a5b194f9ed7bd0dd345c751007a41862443", - "reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443", - "shasum": "" - }, - "require": { - "php": ">=5.5", - "phpdocumentor/reflection-common": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.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" - } - ], - "time": "2016-06-10 07:14:17" - }, - { - "name": "phpspec/prophecy", - "version": "v1.6.1", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "58a8137754bc24b25740d4281399a4a3596058e0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/58a8137754bc24b25740d4281399a4a3596058e0", - "reference": "58a8137754bc24b25740d4281399a4a3596058e0", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", - "sebastian/comparator": "^1.1", - "sebastian/recursion-context": "^1.0" - }, - "require-dev": { - "phpspec/phpspec": "^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.6.x-dev" - } - }, - "autoload": { - "psr-0": { - "Prophecy\\": "src/" - } - }, - "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": "2016-06-07 08:13:47" - }, - { - "name": "phpunit/php-code-coverage", - "version": "2.2.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", - "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "phpunit/php-file-iterator": "~1.3", - "phpunit/php-text-template": "~1.2", - "phpunit/php-token-stream": "~1.3", - "sebastian/environment": "^1.3.2", - "sebastian/version": "~1.0" - }, - "require-dev": { - "ext-xdebug": ">=2.1.4", - "phpunit/phpunit": "~4" - }, - "suggest": { - "ext-dom": "*", - "ext-xdebug": ">=2.2.1", - "ext-xmlwriter": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.2.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": "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": "2015-10-06 15:47:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "1.4.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0", - "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0", - "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": "2015-06-21 13:08:43" - }, - { - "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-21 13:50:34" - }, - { - "name": "phpunit/php-timer", - "version": "1.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260", - "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4|~5" - }, - "type": "library", - "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": "2016-05-12 18:03:57" - }, - { - "name": "phpunit/php-token-stream", - "version": "1.4.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", - "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-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": "2015-09-15 10:49:45" - }, - { - "name": "phpunit/phpunit", - "version": "4.8.26", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "fc1d8cd5b5de11625979125c5639347896ac2c74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fc1d8cd5b5de11625979125c5639347896ac2c74", - "reference": "fc1d8cd5b5de11625979125c5639347896ac2c74", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "php": ">=5.3.3", - "phpspec/prophecy": "^1.3.1", - "phpunit/php-code-coverage": "~2.1", - "phpunit/php-file-iterator": "~1.4", - "phpunit/php-text-template": "~1.2", - "phpunit/php-timer": "^1.0.6", - "phpunit/phpunit-mock-objects": "~2.3", - "sebastian/comparator": "~1.1", - "sebastian/diff": "~1.2", - "sebastian/environment": "~1.3", - "sebastian/exporter": "~1.2", - "sebastian/global-state": "~1.0", - "sebastian/version": "~1.0", - "symfony/yaml": "~2.1|~3.0" - }, - "suggest": { - "phpunit/php-invoker": "~1.1" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.8.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": "2016-05-17 03:09:28" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "2.3.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", - "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.2", - "php": ">=5.3.3", - "phpunit/php-text-template": "~1.2", - "sebastian/exporter": "~1.2" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "suggest": { - "ext-soap": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3.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": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "time": "2015-10-02 06:51:40" - }, - { - "name": "sebastian/comparator", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "937efb279bd37a375bcadf584dec0726f84dbf22" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22", - "reference": "937efb279bd37a375bcadf584dec0726f84dbf22", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "sebastian/diff": "~1.2", - "sebastian/exporter": "~1.2" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.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": "http://www.github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "time": "2015-07-26 15:48:44" - }, - { - "name": "sebastian/diff", - "version": "1.4.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", - "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-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": "2015-12-08 07:14:41" - }, - { - "name": "sebastian/environment", - "version": "1.3.7", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "4e8f0da10ac5802913afc151413bc8c53b6c2716" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/4e8f0da10ac5802913afc151413bc8c53b6c2716", - "reference": "4e8f0da10ac5802913afc151413bc8c53b6c2716", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3.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": "2016-05-17 03:18:57" - }, - { - "name": "sebastian/exporter", - "version": "1.2.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4", - "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "sebastian/recursion-context": "~1.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "~4.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3.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" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "time": "2016-06-17 09:04:28" - }, - { - "name": "sebastian/global-state", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.2" - }, - "suggest": { - "ext-uopz": "*" - }, - "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": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "time": "2015-10-12 03:26:01" - }, - { - "name": "sebastian/recursion-context", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "913401df809e99e4f47b27cdd781f4a258d58791" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/913401df809e99e4f47b27cdd781f4a258d58791", - "reference": "913401df809e99e4f47b27cdd781f4a258d58791", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, - "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": "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": "2015-11-11 19:50:13" - }, - { - "name": "sebastian/version", - "version": "1.0.6", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", - "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", - "shasum": "" - }, - "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": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "time": "2015-06-21 13:59:46" - }, - { - "name": "symfony/yaml", - "version": "v2.8.8", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "dba4bb5846798cd12f32e2d8f3f35d77045773c8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/dba4bb5846798cd12f32e2d8f3f35d77045773c8", - "reference": "dba4bb5846798cd12f32e2d8f3f35d77045773c8", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Yaml Component", - "homepage": "https://symfony.com", - "time": "2016-06-29 05:29:29" - }, - { - "name": "webmozart/assert", - "version": "1.0.2", - "source": { - "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "30eed06dd6bc88410a4ff7f77b6d22f3ce13dbde" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/30eed06dd6bc88410a4ff7f77b6d22f3ce13dbde", - "reference": "30eed06dd6bc88410a4ff7f77b6d22f3ce13dbde", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-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": "2015-08-24 13:29:44" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": ">=5.5.0" - }, - "platform-dev": [], - "platform-overrides": { - "php": "5.5.0" - } -} diff --git a/inc/i18n.php b/inc/i18n.php index 3bba28b..9678703 100644 --- a/inc/i18n.php +++ b/inc/i18n.php @@ -22,21 +22,10 @@ */ function load_translations( $path = '' ) { - // If called outside WP context, let's cleanup WP globals we added and exit. - if (! function_exists('apply_filters') ) { - - if (isset($GLOBALS['wp_filter']['after_setup_theme'][99][__NAMESPACE__ . '\\' . 'load_translations'])) { - unset($GLOBALS['wp_filter']['after_setup_theme'][99][__NAMESPACE__ . '\\' . 'load_translations']); - if (empty($GLOBALS['wp_filter']['after_setup_theme'][99])) { - unset($GLOBALS['wp_filter']['after_setup_theme'][99]); - } - if (empty($GLOBALS['wp_filter']['after_setup_theme'])) { - unset($GLOBALS['wp_filter']['after_setup_theme']); - } - if (empty($GLOBALS['wp_filter'])) { - unset($GLOBALS['wp_filter']); - } - } + // If called outside WP context, let's cleanup WP globals and exit. + if ( ! function_exists( 'apply_filters' ) ) { + + cleanup_globals(); return; } @@ -73,8 +62,50 @@ function load_translations( $path = '' ) { return $done[ 0 ]; } -// This file might be loaded from Composer autoload before `add_action` is available. -// In that case, we "manually" add the function that loads the translation in global `$wp_filter`. +/** + * If the package is used outside of WP context, we probably want to cleanup global vars + * we used to setup WP hook to load translations. + */ +function cleanup_globals() { + + // If in WP context, don't mess up with global `$wp_filter`. + // If global `$wp_filter` already empty, there's nothing to cleanup. + if ( + function_exists( 'add_action' ) + || ! is_array( $GLOBALS[ 'wp_filter' ] ) + || empty( $GLOBALS[ 'wp_filter' ] ) + ) { + return; + } + + $callable = __NAMESPACE__ . '\\load_translations'; + + if ( isset( $GLOBALS[ 'wp_filter' ][ 'after_setup_theme' ][ 99 ][ $callable ] ) ) { + unset( $GLOBALS[ 'wp_filter' ][ 'after_setup_theme' ][ 99 ][ $callable ] ); + } + + if ( + array_key_exists( 'after_setup_theme', $GLOBALS[ 'wp_filter' ] ) + && array_key_exists( 99, $GLOBALS[ 'wp_filter' ][ 'after_setup_theme' ] ) + && empty( $GLOBALS[ 'wp_filter' ][ 'after_setup_theme' ][ 99 ] ) + ) { + unset( $GLOBALS[ 'wp_filter' ][ 'after_setup_theme' ][ 99 ] ); + } + + if ( + array_key_exists( 'after_setup_theme', $GLOBALS[ 'wp_filter' ] ) + && empty( $GLOBALS[ 'wp_filter' ][ 'after_setup_theme' ] ) + ) { + unset( $GLOBALS[ 'wp_filter' ][ 'after_setup_theme' ] ); + } + + if ( empty( $GLOBALS[ 'wp_filter' ] ) ) { + unset( $GLOBALS[ 'wp_filter' ] ); + } +} + +// This file is loaded by Composer autoload, and that may happen before `add_action` is available. +// In that case, we "manually" add in global `$wp_filter` the function that loads translations. // We use `after_setup_theme` with late priority so that from a plugin or theme would be possible to remove the hook // (and load no translation) or change the translation path via 'inpsyde-validator.translation_path' filter. // If an user want to load translation before 'after_setup_theme' is fired, it is possible to call @@ -87,14 +118,14 @@ function load_translations( $path = '' ) { isset( $wp_filter[ 'after_setup_theme' ] ) or $wp_filter[ 'after_setup_theme' ] = [ ]; isset( $wp_filter[ 'after_setup_theme' ][ 99 ] ) or $wp_filter[ 'after_setup_theme' ][ 99 ] = [ ]; - $wp_filter[ 'after_setup_theme' ][ 99 ][ __NAMESPACE__ . '\\' . 'load_translations' ] = [ + $wp_filter[ 'after_setup_theme' ][ 99 ][ __NAMESPACE__ . '\\load_translations' ] = [ __NAMESPACE__ . '\\' . 'load_translations', 1 ]; } else { - add_action( 'after_setup_theme', __NAMESPACE__ . '\\' . 'load_translations', 99 ); + add_action( 'after_setup_theme', __NAMESPACE__ . '\\load_translations', 99 ); } diff --git a/src/Bulk.php b/src/Bulk.php new file mode 100644 index 0000000..8aba425 --- /dev/null +++ b/src/Bulk.php @@ -0,0 +1,113 @@ + + * @package inpsyde-validator + * @license http://opensource.org/licenses/MIT MIT + */ +class Bulk implements SecondaryValidatorInterface { + + use ValidatorDataGetterTrait; + use GetErrorMessagesTrait; + + /** + * @var array + */ + protected $options = [ ]; + + /** + * @inheritdoc + * @return Bulk + */ + public static function with_validator( ExtendedValidatorInterface $validator ) { + + return new static( [ 'validator' => $validator ] ); + } + + /** + * @param array $options + */ + public function __construct( array $options = [ ] ) { + + if ( empty( $options[ 'validator' ] ) ) { + throw new \InvalidArgumentException( + sprintf( '%s "validator" option must be a validators instance or identifier.', __CLASS__ ) + ); + } + + $validator_id = $options[ 'validator' ]; + $options = empty( $options[ 'options' ] ) || ! is_array( $options[ 'options' ] ) + ? [ ] + : $options[ 'options' ]; + + $factory = new ValidatorFactory(); + $validator = $factory->create( $validator_id, $options ); + + if ( ! $validator instanceof ExtendedValidatorInterface ) { + throw new \InvalidArgumentException( + sprintf( '%s "validator" option must be a validators instance or identifier.', __CLASS__ ) + ); + } + + $this->options[ 'validator' ] = $validator; + $this->input_data = $this->options; + $this->input_data[ 'value' ] = NULL; + } + + /** + * @inheritdoc + */ + public function is_valid( $value ) { + + if ( ! is_array( $value ) && ! $value instanceof \Traversable ) { + $this->error_code = Error\ErrorLoggerInterface::INVALID_TYPE_NON_TRAVERSABLE; + $this->update_error_messages(); + + return FALSE; + } + + if ( empty( $value ) ) { + $this->error_code = Error\ErrorLoggerInterface::IS_EMPTY; + $this->update_error_messages(); + + return FALSE; + } + + /** @var ExtendedValidatorInterface $validator */ + $validator = $this->options[ 'validator' ]; + + $valid = NULL; + + foreach ( $value as $item ) { + + $valid = $validator->is_valid( $item ); + $this->input_data = $validator->get_input_data(); + + if ( ! $valid ) { + $this->error_code = $validator->get_error_code(); + $this->update_error_messages(); + + return FALSE; + } + } + + is_null( $valid ) and $this->error_code = Error\ErrorLoggerInterface::IS_EMPTY; + + return (bool) $valid; + } + +} \ No newline at end of file diff --git a/src/Callback.php b/src/Callback.php new file mode 100644 index 0000000..f1ee751 --- /dev/null +++ b/src/Callback.php @@ -0,0 +1,80 @@ + + * @package inpsyde-validator + * @license http://opensource.org/licenses/MIT MIT + */ +class Callback implements ExtendedValidatorInterface { + + use ValidatorDataGetterTrait; + use GetErrorMessagesTrait; + + /** + * @var array + */ + protected $options = [ ]; + + /** + * @param callable $callback + * + * @return Callback + */ + public static function with_callback( callable $callback ) { + + return new static( [ 'callback' => $callback ] ); + } + + /** + * @param array $options + */ + public function __construct( array $options = [ ] ) { + + if ( empty( $options[ 'callback' ] ) || ! is_callable( $options[ 'callback' ] ) ) { + throw new \InvalidArgumentException( sprintf( '%s "callback" option must be callable.', __CLASS__ ) ); + } + + $this->options[ 'callback' ] = $options[ 'callback' ]; + $this->options[ 'error_code' ] = empty( $options[ 'error_code' ] ) || ! is_string( $options[ 'error_code' ] ) + ? ErrorLoggerInterface::CUSTOM_ERROR + : $options[ 'error_code' ]; + + $this->input_data = $this->options; + $this->input_data[ 'value' ] = NULL; + } + + /** + * @inheritdoc + */ + public function is_valid( $value ) { + + $this->input_data = [ 'value' => $value ]; + + /** @var callable $callback */ + $callback = $this->options[ 'callback' ]; + + $valid = filter_var( $callback( $value ), FILTER_VALIDATE_BOOLEAN ); + + if ( ! $valid ) { + $this->error_code = $this->options[ 'error_code' ]; + $this->update_error_messages(); + } + + return $valid; + } + +} \ No newline at end of file diff --git a/src/ClassName.php b/src/ClassName.php new file mode 100644 index 0000000..af5eb9e --- /dev/null +++ b/src/ClassName.php @@ -0,0 +1,67 @@ + + * @package inpsyde-validator + * @license http://opensource.org/licenses/MIT MIT + */ +class ClassName implements ExtendedValidatorInterface { + + use ValidatorDataGetterTrait; + use GetErrorMessagesTrait; + + /** + * @var array + */ + protected $options = [ ]; + + /** + * @param array $options + */ + public function __construct( array $options = [ ] ) { + + $this->options[ 'autoload' ] = array_key_exists( 'autoload', $options ) + ? filter_var( $options[ 'autoload' ], FILTER_VALIDATE_BOOLEAN ) + : TRUE; + + $this->input_data = $this->options; + $this->input_data[ 'value' ] = NULL; + } + + /** + * @inheritdoc + */ + public function is_valid( $value ) { + + $this->input_data[ 'value' ] = $value; + + if ( ! is_string( $value ) ) { + $this->error_code = Error\ErrorLoggerInterface::INVALID_TYPE_NON_STRING; + $this->update_error_messages(); + + return FALSE; + } + + if ( class_exists( $value, $this->options[ 'autoload' ] ) ) { + return TRUE; + } + + $this->error_code = Error\ErrorLoggerInterface::NOT_CLASS_NAME; + $this->update_error_messages(); + + return FALSE; + } + +} \ No newline at end of file diff --git a/src/DataValidator.php b/src/DataValidator.php index b8027a9..1004898 100644 --- a/src/DataValidator.php +++ b/src/DataValidator.php @@ -49,6 +49,23 @@ final class DataValidator implements MapValidatorInterface, ErrorLoggerAwareVali */ private $key_labels = [ ]; + /** + * @inheritdoc + */ + public static function with_validators() { + + $instance = new static(); + $args = func_get_args(); + array_walk( + $args, function ( $validator ) use ( $instance ) { + + $instance->add_validator_to_stack( $validator, self::GENERIC_VALIDATOR_KEY ); + } + ); + + return $instance; + } + /** * @param Error\ErrorLoggerInterface $error_logger */ diff --git a/src/Email.php b/src/Email.php new file mode 100644 index 0000000..d834d7f --- /dev/null +++ b/src/Email.php @@ -0,0 +1,85 @@ + + * @package inpsyde-validator + * @license http://opensource.org/licenses/MIT MIT + */ +class Email implements ExtendedValidatorInterface { + + use ValidatorDataGetterTrait; + use GetErrorMessagesTrait; + + /** + * @param array $options + */ + public function __construct( array $options = [ ] ) { + + $this->options[ 'check_dns' ] = isset( $options[ 'check_dns' ] ) + ? filter_var( $options[ 'check_dns' ], FILTER_VALIDATE_BOOLEAN ) + : FALSE; + + $this->input_data = $this->options; + $this->input_data[ 'value' ] = NULL; + } + + /** + * {@inheritdoc} + */ + public function is_valid( $value ) { + + $this->input_data[ 'value' ] = $value; + + ( is_object( $value ) && method_exists( $value, '__toString' ) ) and $value = (string) $value; + + if ( ! is_string( $value ) ) { + $this->error_code = Error\ErrorLoggerInterface::INVALID_TYPE_NON_STRING; + $this->update_error_messages(); + + return FALSE; + + } + + if ( ! $value ) { + $this->error_code = Error\ErrorLoggerInterface::IS_EMPTY; + $this->update_error_messages(); + + return FALSE; + } + + if ( ! filter_var( $value, FILTER_VALIDATE_EMAIL ) ) { + $this->error_code = Error\ErrorLoggerInterface::NOT_EMAIL; + $this->update_error_messages(); + + return FALSE; + } + + if ( ! $this->options[ 'check_dns' ] ) { + return TRUE; + } + + $parts = explode( '@', $value, 2 ); + + if ( ! checkdnsrr( end( $parts ), 'ANY' ) ) { + $this->error_code = Error\ErrorLoggerInterface::INVALID_DNS; + $this->update_error_messages(); + + return FALSE; + } + + return TRUE; + } + +} \ No newline at end of file diff --git a/src/Error/ErrorLogger.php b/src/Error/ErrorLogger.php index ee9252e..8fcbc9c 100644 --- a/src/Error/ErrorLogger.php +++ b/src/Error/ErrorLogger.php @@ -47,27 +47,35 @@ class ErrorLogger implements ErrorLoggerInterface { public function __construct( array $messages = [ ] ) { $default = [ - self::INVALID_TYPE_NON_STRING => 'Invalid type given for %value%. String expected.', + self::CUSTOM_ERROR => 'Some errors occurred for %value%.', + self::INVALID_DATE => 'The input %value% does not appear to be a valid date.', + self::INVALID_DATE_FORMAT => 'The input %value% does not fit the date format %format%.', + self::INVALID_DNS => 'The host for the given input %value% could not be resolved.', + self::INVALID_TYPE_NON_ARRAY => 'Invalid type given for %value%. Array expected.', + self::INVALID_TYPE_NON_COUNTABLE => 'Invalid type given for %value%. Countable data expected.', + self::INVALID_TYPE_NON_DATE => 'Invalid type given for %value%. String, integer, array or DateTime expected.', self::INVALID_TYPE_NON_NUMERIC => 'Invalid type given for %value%. Integer or float expected.', self::INVALID_TYPE_NON_SCALAR => 'Invalid type given for %value%. String, integer or float expected.', + self::INVALID_TYPE_NON_STRING => 'Invalid type given for %value%. String expected.', self::INVALID_TYPE_NON_TRAVERSABLE => 'Invalid type given for %value%. Array or object implementing Traversable expected.', - self::INVALID_TYPE_NON_DATE => 'Invalid type given for %value%. String, integer, array or DateTime expected.', + self::INVALID_TYPE_GIVEN => 'Invalid type given for %value%. %type% expected.', + self::INVALID_SIZE => 'Size for input %value% is not %size%.', + self::IS_EMPTY => 'This value should not be empty.', + self::MULTIPLE_ERRORS => 'Some errors occurred for %value%.', self::NOT_BETWEEN => 'The input %value% is not between %min% and %max%, inclusively.', self::NOT_BETWEEN_STRICT => 'The input %value% is not strictly between %min% and %max%.', - self::INVALID_DATE => 'The input %value% does not appear to be a valid date.', - self::INVALID_DATE_FORMAT => 'The input %value% does not fit the date format %format%.', + self::NOT_CLASS_NAME => 'The input %value% is not a valid class name.', + self::NOT_EMAIL => 'The input %value% is not a valid email address.', self::NOT_GREATER => 'The input %value% is not greater than %min%.', self::NOT_GREATER_INCLUSIVE => 'The input %value% is not greater or equal than %min%.', self::NOT_IN_ARRAY => 'The input %value% is not in the haystack: %haystack%.', self::NOT_LESS => 'The input %value% is not less than %max%.', self::NOT_LESS_INCLUSIVE => 'The input %value% is not less or equal than %max%.', - self::IS_EMPTY => 'This value should not be empty.', + self::NOT_NEGATE_VALIDATOR => '%validator_name%::is_valid() returned true, but was expected to return false.', self::NOT_MATCH => 'The input does not match against pattern %pattern%.', - self::REGEX_INTERNAL_ERROR => 'There was an internal error while using the pattern %pattern%.', self::NOT_URL => 'The input %value% is not a valid URL.', - self::INVALID_DNS => 'The host for the given input %value% could not be resolved.', - self::MULTIPLE_ERRORS => 'Some errors occurred for %value%.', - self::CUSTOM_ERROR => 'Some errors occurred for %value%.', + self::REGEX_INTERNAL_ERROR => 'There was an internal error while using the pattern %pattern%.', + self::WP_FILTER_ERROR => 'The filter %filter% returned a false value for %value%.', ]; $this->messages = array_merge( $default, array_filter( $messages, 'is_string' ) ); diff --git a/src/Error/ErrorLoggerInterface.php b/src/Error/ErrorLoggerInterface.php index f5f26a8..a104728 100644 --- a/src/Error/ErrorLoggerInterface.php +++ b/src/Error/ErrorLoggerInterface.php @@ -10,8 +10,6 @@ namespace Inpsyde\Validator\Error; -use Inpsyde\Validator\ExtendedValidatorInterface; - /** * @author Giuseppe Mazzapica * @license http://opensource.org/licenses/MIT MIT @@ -24,23 +22,30 @@ interface ErrorLoggerInterface extends \Countable, \IteratorAggregate { const INVALID_DATE_FORMAT = 'invalid_date_format'; const INVALID_DNS = 'invalid_dns'; const INVALID_TYPE_NON_ARRAY = 'invalid_type_non_array'; + const INVALID_TYPE_NON_COUNTABLE = 'invalid_type_non_countable'; const INVALID_TYPE_NON_DATE = 'invalid_type_non_date'; const INVALID_TYPE_NON_NUMERIC = 'invalid_type_non_numeric'; const INVALID_TYPE_NON_SCALAR = 'invalid_type_non_scalar'; const INVALID_TYPE_NON_STRING = 'invalid_type_non_string'; const INVALID_TYPE_NON_TRAVERSABLE = 'invalid_type_non_traversable'; + const INVALID_TYPE_GIVEN = 'invalid_type_given'; + const INVALID_SIZE = 'invalid_size'; const IS_EMPTY = 'is_empty'; const NOT_BETWEEN = 'not_between'; const NOT_BETWEEN_STRICT = 'not_between_strict'; + const NOT_CLASS_NAME = 'not_class_name'; + const NOT_EMAIL = 'not_email'; const NOT_GREATER = 'not_greater_than'; const NOT_GREATER_INCLUSIVE = 'not_greater_than_inclusive'; const NOT_IN_ARRAY = 'not_in_array'; const NOT_LESS = 'not_less_than'; const NOT_LESS_INCLUSIVE = 'not_less_than_inclusive'; + const NOT_NEGATE_VALIDATOR = 'not_negate'; const NOT_MATCH = 'not_match'; const NOT_URL = 'not_url'; const MULTIPLE_ERRORS = 'multiple_errors'; const REGEX_INTERNAL_ERROR = 'regex_internal'; + const WP_FILTER_ERROR = 'wp_filter_error'; /** * Logs an error. diff --git a/src/Error/WordPressErrorLogger.php b/src/Error/WordPressErrorLogger.php index e4c0f3e..8ea4b00 100644 --- a/src/Error/WordPressErrorLogger.php +++ b/src/Error/WordPressErrorLogger.php @@ -32,9 +32,57 @@ class WordPressErrorLogger implements ErrorLoggerInterface { public function __construct( array $messages = [ ] ) { $default = [ - self::INVALID_TYPE_NON_STRING => sprintf( + self::CUSTOM_ERROR => sprintf( __( - 'Invalid type given for %s. String expected.', + 'Some errors occurred for %s.', + 'inpsyde-validator' + ), + '%value%' + ), + self::INVALID_DATE => sprintf( + __( + 'The input %s does not appear to be a valid date.', + 'inpsyde-validator' + ), + '%value%' + ), + self::INVALID_DATE_FORMAT => sprintf( + __( + 'The input %1$s does not fit the date format %2$s .', + 'inpsyde-validator' + ), + '%value%', + '%format%' + ), + self::INVALID_DNS => sprintf( + __( + 'The host for the given input %s could not be resolved.', + 'inpsyde-validator' + ), + '%value%' + ), + self::INVALID_SIZE => sprintf( + 'Size for input %1$s is not %2$s.', + '%value%', + '%size%' + ), + self::INVALID_TYPE_NON_ARRAY => sprintf( + __( + 'Invalid type given for %s. Array expected.', + 'inpsyde-validator' + ), + '%value%' + ), + self::INVALID_TYPE_NON_COUNTABLE => sprintf( + __( + 'Invalid type given for %s. Countable data expected.', + 'inpsyde-validator' + ), + '%value%' + ), + self::INVALID_TYPE_NON_DATE => sprintf( + __( + 'Invalid type given for %s. String, integer, array or DateTime expected.', 'inpsyde-validator' ), '%value%' @@ -53,6 +101,13 @@ public function __construct( array $messages = [ ] ) { ), '%value%' ), + self::INVALID_TYPE_NON_STRING => sprintf( + __( + 'Invalid type given for %s. String expected.', + 'inpsyde-validator' + ), + '%value%' + ), self::INVALID_TYPE_NON_TRAVERSABLE => sprintf( __( 'Invalid type given for %s. Array or object implementing Traversable expected.', @@ -60,9 +115,21 @@ public function __construct( array $messages = [ ] ) { ), '%value%' ), - self::INVALID_TYPE_NON_DATE => sprintf( + self::INVALID_TYPE_GIVEN => sprintf( __( - 'Invalid type given for %s. String, integer, array or DateTime expected.', + 'Invalid type given for %1$s. %2$s expected.', + 'inpsyde-validator' + ), + '%value%', + '%type%' + ), + self::IS_EMPTY => __( + 'This value should not be empty.', + 'inpsyde-validator' + ), + self::MULTIPLE_ERRORS => sprintf( + __( + 'ome errors occurred for %s.', 'inpsyde-validator' ), '%value%' @@ -85,20 +152,19 @@ public function __construct( array $messages = [ ] ) { '%min%', '%max%' ), - self::INVALID_DATE => sprintf( + self::NOT_CLASS_NAME => sprintf( __( - 'The input %s does not appear to be a valid date.', + 'The input %s is not a valid class name.', 'inpsyde-validator' ), '%value%' ), - self::INVALID_DATE_FORMAT => sprintf( + self::NOT_EMAIL => sprintf( __( - 'The input %1$s does not fit the date format %2$s .', + 'The input %s is not a valid email address.', 'inpsyde-validator' ), - '%value%', - '%format%' + '%value%' ), self::NOT_GREATER => sprintf( __( @@ -140,9 +206,11 @@ public function __construct( array $messages = [ ] ) { '%value%', '%max%' ), - self::IS_EMPTY => __( - 'This value should not be empty.', - 'inpsyde-validator' + self::NOT_NEGATE_VALIDATOR => sprintf( + __( + '%s returned true, but was expected to return false.', + '%validator_name%::is_valid()' + ) ), self::NOT_MATCH => sprintf( __( @@ -152,14 +220,6 @@ public function __construct( array $messages = [ ] ) { '%value%', '%pattern%' ), - self::REGEX_INTERNAL_ERROR => sprintf( - __( - 'There was an internal error while using the pattern %2$s for string %1$s.', - 'inpsyde-validator' - ), - '%value%', - '%pattern%' - ), self::NOT_URL => sprintf( __( 'The input %s is not a valid URL.', @@ -167,25 +227,20 @@ public function __construct( array $messages = [ ] ) { ), '%value%' ), - self::INVALID_DNS => sprintf( - __( - 'The host for the given input %s could not be resolved.', - 'inpsyde-validator' - ), - '%value%' - ), - self::MULTIPLE_ERRORS => sprintf( + self::REGEX_INTERNAL_ERROR => sprintf( __( - 'ome errors occurred for %s.', + 'There was an internal error while using the pattern %2$s for string %1$s.', 'inpsyde-validator' ), - '%value%' + '%value%', + '%pattern%' ), - self::CUSTOM_ERROR => sprintf( + self::WP_FILTER_ERROR => sprintf( __( - 'Some errors occurred for %s.', + 'The filter %2$s returned a false value for %1$s.', 'inpsyde-validator' ), + '%filter%', '%value%' ), ]; diff --git a/src/Multi.php b/src/Multi.php index 51afef7..7b133d8 100644 --- a/src/Multi.php +++ b/src/Multi.php @@ -21,16 +21,8 @@ class Multi implements ExtendedValidatorInterface, MultiValidatorInterface { use ValidatorDataGetterTrait; use GetErrorMessagesTrait; - - /** - * @var ExtendedValidatorInterface[] - */ - private $validators = [ ]; - - /** - * @var array - */ - private $error_data = [ ]; + use MultiValidatorDataGetterTrait; + use MultiValidatorValidatorsTrait; /** * @var array @@ -38,11 +30,13 @@ class Multi implements ExtendedValidatorInterface, MultiValidatorInterface { private $options = [ ]; /** - * Named constructor that can be used obtain an instance by passing a variadic number of validators. + * Named constructor + * + * @return Multi */ public static function with_validators() { - return new static( [ ], func_get_args() ); + return new static( [ 'validators' => func_get_args() ] ); } /** @@ -53,12 +47,17 @@ public static function with_validators() { */ public function __construct( array $options = [ ], array $validators = [ ] ) { - $this->options = isset( $options[ 'stop_on_failure' ] ) + $this->options = array_key_exists( 'stop_on_failure', $options ) ? filter_var( $options[ 'stop_on_failure' ], FILTER_VALIDATE_BOOLEAN ) : FALSE; $factory = new ValidatorFactory(); + array_key_exists( 'validators', $options ) and $validators = array_merge( + (array) $options[ 'validators' ], + $validators + ); + foreach ( $validators as $validator ) { $options = [ ]; @@ -68,21 +67,10 @@ public function __construct( array $options = [ ], array $validators = [ ] ) { $validator = $validator[ 'validator' ]; } - $instance = $factory->create( $validator, $options ); - $this->validators[] = $instance; + $this->add_validator( $factory->create( $validator, $options ) ); } } - /** - * @inheritdoc - */ - public function add_validator( ExtendedValidatorInterface $validator ) { - - $this->validators[] = $validator; - - return $this; - } - /** * @inheritdoc */ @@ -111,39 +99,12 @@ public function is_valid( $value ) { } } - $valid or $this->update_error_messages(); - - return $valid; - } - - /** - * @inheritdoc - */ - public function get_error_codes() { - - return array_keys( $this->error_data ); - } - - /** - * @inheritdoc - */ - public function get_error_data( $error_code = NULL ) { - - if ( is_null( $error_code ) ) { - return $this->error_data; - } elseif ( ! isset( $this->error_data[ $error_code ] ) ) { - return [ ]; + if ( $valid ) { + $this->input_data = [ 'value' => NULL ]; + $this->error_code = ''; } - return $this->error_data[ $error_code ]; - } - - /** - * @inheritdoc - */ - public function count() { - - return count( $this->validators ); + return $valid; } /** diff --git a/src/MultiOr.php b/src/MultiOr.php new file mode 100644 index 0000000..051dba4 --- /dev/null +++ b/src/MultiOr.php @@ -0,0 +1,105 @@ + + * @package inpsyde-validator + * @license http://opensource.org/licenses/MIT MIT + */ +class MultiOr implements ExtendedValidatorInterface, MultiValidatorInterface { + + use ValidatorDataGetterTrait; + use GetErrorMessagesTrait; + use MultiValidatorDataGetterTrait; + use MultiValidatorValidatorsTrait; + + /** + * Named constructor + * + * @return MultiOr + */ + public static function with_validators() { + + return new static( [ 'validators' => func_get_args() ] ); + } + + /** + * MultiOr constructor. + * + * @param array $options + * @param ExtendedValidatorInterface[] $validators + */ + public function __construct( array $options = [ ], array $validators = [ ] ) { + + $factory = new ValidatorFactory(); + + array_key_exists( 'validators', $options ) and $validators = array_merge( + (array) $options[ 'validators' ], + $validators + ); + + foreach ( $validators as $validator ) { + + $options = [ ]; + + if ( is_array( $validator ) && isset( $validator[ 'validator' ] ) ) { + empty( $validator[ 'options' ] ) or $options = (array) $validator[ 'options' ]; + $validator = $validator[ 'validator' ]; + } + + $this->add_validator( $factory->create( $validator, $options ) ); + } + } + + /** + * @inheritdoc + */ + public function is_valid( $value ) { + + $this->input_data = [ 'value' => $value ]; + + $error_data = [ ]; + $error_code = ''; + + if ( ! $this->validators ) { + return TRUE; + } + + foreach ( $this->validators as $validator ) { + + if ( $validator->is_valid( $value ) ) { + $this->input_data = [ 'value' => $value ]; + $this->error_code = ''; + $this->error_data = [ ]; + + return TRUE; + } + + $data = $validator->get_input_data(); + $data[ 'value' ] = $value; + $this->input_data = $data; + $error_code = $validator->get_error_code(); + isset( $error_data[ $error_code ] ) or $error_data[ $error_code ] = [ ]; + $error_data[ $error_code ][] = $data; + } + + $this->error_data = $error_data; + $this->error_code = $error_code; + + $this->update_error_messages(); + + return FALSE; + } + +} \ No newline at end of file diff --git a/src/MultiValidatorDataGetterTrait.php b/src/MultiValidatorDataGetterTrait.php new file mode 100644 index 0000000..4bfadf2 --- /dev/null +++ b/src/MultiValidatorDataGetterTrait.php @@ -0,0 +1,46 @@ + + * @license http://opensource.org/licenses/MIT MIT + * @package inpsyde-validator + */ +trait MultiValidatorDataGetterTrait { + + /** + * @var array + */ + private $error_data = [ ]; + + /** + * @inheritdoc + */ + public function get_error_codes() { + + return $this->error_data ? array_keys( $this->error_data ) : [ ]; + } + + /** + * @inheritdoc + */ + public function get_error_data( $error_code = NULL ) { + + if ( is_null( $error_code ) ) { + return $this->error_data; + } elseif ( ! array_key_exists( $error_code, $this->error_data ) ) { + return [ ]; + } + + return $this->error_data[ $error_code ]; + } +} \ No newline at end of file diff --git a/src/MultiValidatorValidatorsTrait.php b/src/MultiValidatorValidatorsTrait.php new file mode 100644 index 0000000..d4e7e2a --- /dev/null +++ b/src/MultiValidatorValidatorsTrait.php @@ -0,0 +1,45 @@ + + * @license http://opensource.org/licenses/MIT MIT + * @package inpsyde-validator + */ +trait MultiValidatorValidatorsTrait { + + /** + * @var ExtendedValidatorInterface[] + */ + private $validators = [ ]; + + /** + * @param ExtendedValidatorInterface $validator + * + * @return MultiValidatorInterface + * @see MultiValidatorInterface::add_validator() + */ + public function add_validator( ExtendedValidatorInterface $validator ) { + + $this->validators[] = $validator; + + return $this; + } + + /** + * @see \Countable::count() + */ + public function count() { + + return count( $this->validators ); + } +} \ No newline at end of file diff --git a/src/Negate.php b/src/Negate.php new file mode 100644 index 0000000..655a47a --- /dev/null +++ b/src/Negate.php @@ -0,0 +1,92 @@ + + * @package inpsyde-validator + * @license http://opensource.org/licenses/MIT MIT + */ +class Negate implements SecondaryValidatorInterface { + + use ValidatorDataGetterTrait; + use GetErrorMessagesTrait; + + /** + * @var array + */ + protected $options = [ ]; + + /** + * @inheritdoc + * @return Negate + */ + public static function with_validator( ExtendedValidatorInterface $validator ) { + + return new static( [ 'validator' => $validator ] ); + } + + /** + * @param array $options + */ + public function __construct( array $options = [ ] ) { + + if ( empty( $options[ 'validator' ] ) ) { + throw new \InvalidArgumentException( + sprintf( '%s "validator" option must be a validators instance or identifier.', __CLASS__ ) + ); + } + + $validator_id = $options[ 'validator' ]; + $options = empty( $options[ 'options' ] ) || ! is_array( $options[ 'options' ] ) + ? [ ] + : $options[ 'options' ]; + + $factory = new ValidatorFactory(); + $validator = $factory->create( $validator_id, $options ); + + if ( ! $validator instanceof ExtendedValidatorInterface ) { + throw new \InvalidArgumentException( + sprintf( '%s "validator" option must be a validators instance or identifier.', __CLASS__ ) + ); + } + + $this->options[ 'validator' ] = $validator; + $this->input_data = $this->options; + $class_parts = explode( '\\', get_class( $validator ) ); + $this->input_data[ 'validator_name' ] = end( $class_parts ); + $this->input_data[ 'value' ] = NULL; + } + + /** + * @inheritdoc + */ + public function is_valid( $value ) { + + $this->input_data[ 'value' ] = $value; + + /** @var ExtendedValidatorInterface $validator */ + $validator = $this->options[ 'validator' ]; + $valid = $validator->is_valid( $value ); + + if ( $valid ) { + $this->error_code = ErrorLoggerInterface::NOT_NEGATE_VALIDATOR; + $this->update_error_messages(); + } + + return ! $valid; + } + +} \ No newline at end of file diff --git a/src/Pool.php b/src/Pool.php new file mode 100644 index 0000000..1b3b3ad --- /dev/null +++ b/src/Pool.php @@ -0,0 +1,109 @@ + + * @package inpsyde-validator + * @license http://opensource.org/licenses/MIT MIT + */ +class Pool implements SecondaryValidatorInterface { + + use ValidatorDataGetterTrait; + use GetErrorMessagesTrait; + + /** + * @var array + */ + protected $options = [ ]; + + /** + * @inheritdoc + * @return Pool + */ + public static function with_validator( ExtendedValidatorInterface $validator ) { + + return new static( [ 'validator' => $validator ] ); + } + + /** + * @param array $options + */ + public function __construct( array $options = [ ] ) { + + if ( empty( $options[ 'validator' ] ) ) { + throw new \InvalidArgumentException( + sprintf( '%s "validator" option must be a validators instance or identifier.', __CLASS__ ) + ); + } + + $validator_id = $options[ 'validator' ]; + $options = empty( $options[ 'options' ] ) || ! is_array( $options[ 'options' ] ) + ? [ ] + : $options[ 'options' ]; + + $factory = new ValidatorFactory(); + $validator = $factory->create( $validator_id, $options ); + + if ( ! $validator instanceof ExtendedValidatorInterface ) { + throw new \InvalidArgumentException( + sprintf( '%s "validator" option must be a validators instance or identifier.', __CLASS__ ) + ); + } + + $this->options[ 'validator' ] = $validator; + $this->input_data = $this->options; + $this->input_data[ 'value' ] = NULL; + } + + /** + * @inheritdoc + */ + public function is_valid( $value ) { + + if ( ! is_array( $value ) && ! $value instanceof \Traversable ) { + $this->error_code = Error\ErrorLoggerInterface::INVALID_TYPE_NON_TRAVERSABLE; + $this->update_error_messages(); + + return FALSE; + } + + /** @var ExtendedValidatorInterface $validator */ + $validator = $this->options[ 'validator' ]; + + $valid = NULL; + + foreach ( $value as $item ) { + + $valid = $validator->is_valid( $item ); + $this->input_data = $validator->get_input_data(); + + if ( $valid ) { + return TRUE; + } + } + + if ( is_null( $valid ) ) { + $this->error_code = Error\ErrorLoggerInterface::IS_EMPTY; + $this->update_error_messages(); + + return FALSE; + } + + $this->error_code = $validator->get_error_code(); + $this->update_error_messages(); + + return FALSE; + } + +} \ No newline at end of file diff --git a/src/RegEx.php b/src/RegEx.php index c1103c6..05324ee 100644 --- a/src/RegEx.php +++ b/src/RegEx.php @@ -79,6 +79,7 @@ public function is_valid( $value ) { if ( ! is_string( $value ) && ! is_int( $value ) && ! is_float( $value ) ) { $this->error_code = Error\ErrorLoggerInterface::INVALID_TYPE_NON_SCALAR; + $this->update_error_messages(); return FALSE; } @@ -87,6 +88,7 @@ public function is_valid( $value ) { if ( $valid === FALSE ) { $this->error_code = Error\ErrorLoggerInterface::REGEX_INTERNAL_ERROR; + $this->update_error_messages(); return FALSE; } diff --git a/src/SecondaryValidatorInterface.php b/src/SecondaryValidatorInterface.php new file mode 100644 index 0000000..eb69924 --- /dev/null +++ b/src/SecondaryValidatorInterface.php @@ -0,0 +1,29 @@ + + * @package inpsyde-validator + * @license http://opensource.org/licenses/MIT MIT + */ +interface SecondaryValidatorInterface extends ExtendedValidatorInterface { + + /** + * @param ExtendedValidatorInterface $validator + * + * @return SecondaryValidatorInterface + */ + public static function with_validator( ExtendedValidatorInterface $validator ); + +} \ No newline at end of file diff --git a/src/Size.php b/src/Size.php new file mode 100644 index 0000000..6d7ce5a --- /dev/null +++ b/src/Size.php @@ -0,0 +1,107 @@ + + * @package inpsyde-validator + * @license http://opensource.org/licenses/MIT MIT + */ +class Size implements ExtendedValidatorInterface { + + use ValidatorDataGetterTrait; + use GetErrorMessagesTrait; + + /** + * @var array + */ + protected $options = [ ]; + + /** + * @param array $options + */ + public function __construct( array $options = [ ] ) { + + $size = array_key_exists( 'size', $options ) ? $options[ 'size' ] : 0; + is_numeric( $size ) and $size = (int) $size; + + if ( ! is_int( $size ) || $size < 0 ) { + throw new \InvalidArgumentException( + sprintf( 'Size option for %s must be a positive integer or 0.', __CLASS__ ) + ); + } + + $this->options[ 'size' ] = $size; + $this->input_data = $this->options; + $this->input_data[ 'value' ] = NULL; + + } + + /** + * @inheritdoc + */ + public function is_valid( $value ) { + + $this->input_data = [ 'value' => $value ]; + + if ( + is_resource( $value ) + || ( is_object( $value ) && ! ( $value instanceof \Countable || $value instanceof \stdClass ) ) + ) { + $this->error_code = Error\ErrorLoggerInterface::INVALID_TYPE_NON_COUNTABLE; + $this->update_error_messages(); + + return FALSE; + } + + if ( $value instanceof \stdClass ) { + $value_copy = clone $value; + $value = get_object_vars( $value_copy ); + } + + if ( $this->calc_size( $value ) === $this->options[ 'size' ] ) { + return TRUE; + } + + $this->error_code = Error\ErrorLoggerInterface::INVALID_SIZE; + $this->update_error_messages(); + + return FALSE; + } + + /** + * @param mixed $value + * + * @return int + */ + private function calc_size( $value ) { + + $type = gettype( $value ); + + switch ( $type ) { + case 'string' : + return function_exists( 'mb_strlen' ) ? mb_strlen( $value ) : strlen( $value ); + case 'integer' : + case 'double' : + case 'boolean' : + return (int) $value; + case 'array' : + case 'object' : + return count( $value ); + } + + return - 1; + + } + +} \ No newline at end of file diff --git a/src/Type.php b/src/Type.php new file mode 100644 index 0000000..7cf430c --- /dev/null +++ b/src/Type.php @@ -0,0 +1,93 @@ + + * @package inpsyde-validator + * @license http://opensource.org/licenses/MIT MIT + */ +class Type implements ExtendedValidatorInterface { + + use ValidatorDataGetterTrait; + use GetErrorMessagesTrait; + + private static $types = [ + 'integer' => 'integer', + 'int' => 'integer', + 'double' => 'double', + 'float' => 'double', + 'string' => 'string', + 'int' => 'integer', + 'boolean' => 'boolean', + 'bool' => 'boolean', + 'resource' => 'resource', + 'object' => 'object', + 'null' => 'null', + 'traversable' => 'traversable', + 'numeric' => 'numeric', + 'number' => 'numeric', + ]; + + /** + * @var array + */ + protected $options = [ ]; + + /** + * @param array $options + */ + public function __construct( array $options = [ ] ) { + + if ( empty( $options[ 'type' ] ) || ! is_string( $options[ 'type' ] ) ) { + throw new \InvalidArgumentException( sprintf( '%s "type" option must be in a string.', __CLASS__ ) ); + } + + $type = $options[ 'type' ]; + $lower = strtolower( $type ); + + $this->options[ 'type' ] = array_key_exists( $lower, self::$types ) ? self::$types[ $lower ] : $type; + $this->input_data = $this->options; + $this->input_data[ 'value' ] = NULL; + } + + /** + * @inheritdoc + */ + public function is_valid( $value ) { + + $this->input_data = [ 'value' => $value ]; + + if ( strtolower( gettype( $value ) ) === $this->options[ 'type' ] ) { + return TRUE; + } + + if ( is_object( $value ) && is_a( $value, $this->options[ 'type' ] ) ) { + return TRUE; + } + + if ( $this->options[ 'type' ] === 'numeric' && is_numeric( $value ) ) { + return TRUE; + } + + if ( $this->options[ 'type' ] === 'traversable' && ( is_array( $value ) || $value instanceof \Traversable ) ) { + return TRUE; + } + + $this->error_code = Error\ErrorLoggerInterface::INVALID_TYPE_GIVEN; + $this->update_error_messages(); + + return FALSE; + } + +} \ No newline at end of file diff --git a/src/Url.php b/src/Url.php index 5405f0d..03a40a5 100644 --- a/src/Url.php +++ b/src/Url.php @@ -111,6 +111,7 @@ public function is_valid( $value ) { if ( ! is_string( $value ) ) { $this->error_code = Error\ErrorLoggerInterface::INVALID_TYPE_NON_STRING; + $this->update_error_messages(); return FALSE; @@ -118,6 +119,7 @@ public function is_valid( $value ) { if ( $value === '' ) { $this->error_code = Error\ErrorLoggerInterface::IS_EMPTY; + $this->update_error_messages(); return FALSE; } @@ -125,6 +127,7 @@ public function is_valid( $value ) { $pattern = sprintf( self::PATTERN, implode( '|', $this->options[ 'allowed_protocols' ] ) ); if ( ! preg_match( $pattern, $value ) ) { $this->error_code = Error\ErrorLoggerInterface::NOT_URL; + $this->update_error_messages(); return FALSE; } diff --git a/src/WpFilter.php b/src/WpFilter.php new file mode 100644 index 0000000..95763c7 --- /dev/null +++ b/src/WpFilter.php @@ -0,0 +1,68 @@ + + * @package inpsyde-validator + * @license http://opensource.org/licenses/MIT MIT + */ +class WpFilter implements ExtendedValidatorInterface { + + use ValidatorDataGetterTrait; + use GetErrorMessagesTrait; + + /** + * @var array + */ + protected $options = [ ]; + + /** + * @param array $options + */ + public function __construct( array $options = [ ] ) { + + if ( empty( $options[ 'filter' ] ) || ! is_string( $options[ 'filter' ] ) ) { + throw new \InvalidArgumentException( sprintf( '%s "filter" option must be in a string.', __CLASS__ ) ); + } + + if ( ! function_exists( 'apply_filters' ) ) { + throw new \InvalidArgumentException( sprintf( '%s can only be used in WordPress context.', __CLASS__ ) ); + } + + $this->options[ 'filter' ] = $options[ 'filter' ]; + $this->input_data = $this->options; + $this->input_data[ 'value' ] = NULL; + } + + /** + * @inheritdoc + */ + public function is_valid( $value ) { + + $this->input_data = [ 'value' => $value ]; + + $valid = apply_filters( $this->options[ 'filter' ], $value ); + $valid = filter_var( $valid, FILTER_VALIDATE_BOOLEAN ); + + if ( ! $valid ) { + $this->error_code = ErrorLoggerInterface::WP_FILTER_ERROR; + $this->update_error_messages(); + } + + return $valid; + } + +} \ No newline at end of file diff --git a/tests/phpunit/Unit/BulkTest.php b/tests/phpunit/Unit/BulkTest.php new file mode 100644 index 0000000..7677725 --- /dev/null +++ b/tests/phpunit/Unit/BulkTest.php @@ -0,0 +1,145 @@ + + * @package inpsyde-validator + * @license http://opensource.org/licenses/MIT MIT + */ +class BulkTest extends \PHPUnit_Framework_TestCase { + + /** + * @expectedException \InvalidArgumentException + */ + public function test_constructor_needs_validator() { + + new Bulk(); + } + + public function test_with_validator() { + + $validator = Mockery::mock( ExtendedValidatorInterface::class ) + ->shouldReceive( 'is_valid' ) + ->with( 'foo' ) + ->once() + ->andReturn( TRUE ) + ->getMock() + ->shouldReceive( 'get_input_data' ) + ->andReturn( [ 'value' => 'foo' ] ) + ->getMock() + ->shouldReceive( 'get_error_code' ) + ->andReturn( '' ); + + $bulk = Bulk::with_validator( $validator->getMock() ); + + $this->assertTrue( $bulk->is_valid( [ 'foo' ] ) ); + } + + public function test_constructor_can_use_factory() { + + $validator = Mockery::mock( ExtendedValidatorInterface::class ); + $validator_class = get_class( $validator ); + + $bulk = new Bulk( [ 'validator' => $validator_class ] ); + $input = $bulk->get_input_data(); + $validator_stored = $input[ 'validator' ]; + + $this->assertInternalType( 'object', $validator_stored ); + $this->assertInstanceOf( $validator_class, $validator_stored ); + } + + public function test_is_valid_accepts_traversable_only() { + + $bulk = new Bulk( [ 'validator' => Mockery::mock( ExtendedValidatorInterface::class ) ] ); + $valid = $bulk->is_valid( '' ); + + $this->assertFalse( $valid ); + $this->assertSame( ErrorLoggerInterface::INVALID_TYPE_NON_TRAVERSABLE, $bulk->get_error_code() ); + } + + /** + * @param mixed $value + * @param ExtendedValidatorInterface $validator + * @param bool $expected_valid + * @param string $error + * + * @dataProvider is_valid_cases_provider + */ + public function test_is_valid( $value, $validator, $expected_valid, $error ) { + + $bulk = new Bulk( [ 'validator' => $validator ] ); + $result = $bulk->is_valid( $value ); + $expected_valid + ? $this->assertTrue( $result, serialize( $value ) ) + : $this->assertFalse( $result, serialize( $value ) ); + + $this->assertSame( $error, $bulk->get_error_code() ); + } + + public function is_valid_cases_provider() { + + return [ + [ [ 2, 4, 6 ], new GreaterThan( [ 'min' => 1 ] ), TRUE, '' ], + [ [ 2, 4, 6 ], new GreaterThan( [ 'min' => 3 ] ), FALSE, ErrorLoggerInterface::NOT_GREATER ], + [ [ 1, 2, 'x' ], new NotEmpty(), TRUE, '' ], + [ [ '', NULL, FALSE ], new NotEmpty(), FALSE, ErrorLoggerInterface::IS_EMPTY ], + [ [ 'http://example.com', 'example.com' ], new Url(), FALSE, ErrorLoggerInterface::NOT_URL ], + [ [ 'http://example.com', 'https://example.it' ], new Url(), TRUE, '' ], + ]; + } + + public function test_get_input_data_returns_offending_data() { + + $bulk = new Bulk( [ 'validator' => new GreaterThan( [ 'min' => 5 ] ) ] ); + $valid = $bulk->is_valid( [ 6, 7, 8, 3, 10 ] ); + $data = $bulk->get_input_data(); + + $this->assertFalse( $valid ); + $this->assertInternalType( 'array', $data ); + $this->assertArrayHasKey( 'value', $data ); + $this->assertSame( $data[ 'value' ], 3 ); + } + + public function test_consecutive_calls() { + + $bulk = new Bulk( [ 'validator' => new GreaterThan( [ 'min' => 5 ] ) ] ); + + $this->assertTrue( $bulk->is_valid( [ 6, 7, 8, 10 ] ) ); + $code = $bulk->get_error_code(); + $data = $bulk->get_input_data(); + + $this->assertSame( '', $code ); + $this->assertInternalType( 'array', $data ); + $this->assertArrayHasKey( 'value', $data ); + $this->assertSame( 10, $data[ 'value' ] ); + + $this->assertFalse( $bulk->is_valid( [ 6, 7, 8, 2, 10 ] ) ); + $code = $bulk->get_error_code(); + $data = $bulk->get_input_data(); + + $this->assertSame( ErrorLoggerInterface::NOT_GREATER, $code ); + $this->assertInternalType( 'array', $data ); + $this->assertArrayHasKey( 'value', $data ); + $this->assertSame( 2, $data[ 'value' ] ); + } + +} \ No newline at end of file diff --git a/tests/phpunit/Unit/CallbackTest.php b/tests/phpunit/Unit/CallbackTest.php new file mode 100644 index 0000000..30cc001 --- /dev/null +++ b/tests/phpunit/Unit/CallbackTest.php @@ -0,0 +1,64 @@ + + * @package inpsyde-validator + * @license http://opensource.org/licenses/MIT MIT + */ +class CallbackTest extends \PHPUnit_Framework_TestCase { + + /** + * @expectedException \InvalidArgumentException + */ + public function test_constructor_needs_callback() { + + new Callback( [ ] ); + } + + /** + * @param mixed $value + * @param callable $validator + * @param bool $expected_valid + * + * @dataProvider is_valid_cases_provider + */ + public function test_is_valid( $value, callable $validator, $expected_valid ) { + + $bulk = new Callback( [ 'callback' => $validator ] ); + + $result = $bulk->is_valid( $value ); + $expected_valid ? $this->assertTrue( $result ) : $this->assertFalse( $result ); + } + + public function is_valid_cases_provider() { + + $inpsyde = function ( $value ) { + + return $value === 'Inpsyde'; + }; + + return [ + [ 1, 'is_string', FALSE ], + [ 'x', 'is_string', TRUE ], + [ 1, 'is_int', TRUE ], + [ 'x', 'is_int', FALSE ], + [ ' Inpsyde ', $inpsyde, FALSE ], + [ 'Inpsyde', $inpsyde, TRUE ], + ]; + } + +} \ No newline at end of file diff --git a/tests/phpunit/Unit/ClassNameTest.php b/tests/phpunit/Unit/ClassNameTest.php new file mode 100644 index 0000000..4532fab --- /dev/null +++ b/tests/phpunit/Unit/ClassNameTest.php @@ -0,0 +1,60 @@ + + * @package inpsyde-validator + * @license http://opensource.org/licenses/MIT MIT + */ +class ClassNameTest extends \PHPUnit_Framework_TestCase { + + /** + * @param mixed $value + * @param bool $expected_valid + * @param $message + * + * @dataProvider is_valid_cases_provider + */ + public function test_is_valid( $value, $expected_valid, $message ) { + + $bulk = new ClassName(); + + $result = $bulk->is_valid( $value ); + $expected_valid ? $this->assertTrue( $result, $message ) : $this->assertFalse( $result, $message ); + } + + public function is_valid_cases_provider() { + + return [ + [ \ArrayAccess::class, FALSE, '"ArrayAccess" is an interface name' ], + [ \ArrayObject::class, TRUE, '"ArrayObject" is a class name' ], + [ new \ArrayObject(), FALSE, 'ArrayObject object is not a class name' ], + [ '_FOO_', FALSE, '_FOO_ is not a class name' ], + [ __CLASS__, TRUE, __CLASS__ . ' is a class name' ], + ]; + } + + public function test_specific_error_for_non_strings() { + + $bulk = new ClassName(); + + $this->assertFalse( $bulk->is_valid( TRUE ) ); + $this->assertSame( ErrorLoggerInterface::INVALID_TYPE_NON_STRING, $bulk->get_error_code() ); + + } + +} \ No newline at end of file diff --git a/tests/phpunit/Unit/EmailTest.php b/tests/phpunit/Unit/EmailTest.php new file mode 100644 index 0000000..ff8f563 --- /dev/null +++ b/tests/phpunit/Unit/EmailTest.php @@ -0,0 +1,109 @@ + + * @package inpsyde-validator + * @license http://opensource.org/licenses/MIT MIT + */ +class EmailTest extends \PHPUnit_Framework_TestCase { + + /** + * Ensures that the validator follows expected behavior + * + * @dataProvider provide_is_valid_cases + * + * @param string $input + * @param bool $expect + */ + public function test_is_valid( $input, $expect ) { + + $validator = new Email(); + + $expect + ? $this->assertTrue( $validator->is_valid( $input ), "Error for {$input}" ) + : $this->assertFalse( $validator->is_valid( $input ), "Error for {$input}" ); + } + + /** + * @return array + */ + public function provide_is_valid_cases() { + + return [ + // $input, $expect + [ 'http://www.example.com', FALSE ], + [ 'www.example.com', FALSE ], + [ 'test@www.example.com', TRUE ], + [ 'test@www@example.com', FALSE ], + [ 'test@', FALSE ], + [ 'test@example', FALSE ], + [ 'test@example.', FALSE ], + [ 'test@.example', FALSE ], + [ '@example.com', FALSE ], + [ '!#$%&`*+/=?^`{|}~@example.com', TRUE ], + [ 'test\@test@iana.org@example.com', FALSE ], + [ 'test@255.255.255.255', FALSE ] + ]; + } + + /** + * Tests that error code is returned according to validation results and options. + */ + public function test_get_error_code() { + + $validator = new Email( [ 'check_dns' => TRUE ] ); + + $validator->is_valid( new \stdClass() ); + $code_non_string = $validator->get_error_code(); + + $validator->is_valid( '' ); + $code_empty = $validator->get_error_code(); + + $validator->is_valid( 'foo' ); + $code_non_email = $validator->get_error_code(); + + $validator->is_valid( 'foo@dsasfsdfsdfdsf.mehmehe' ); + $code_no_dns = $validator->get_error_code(); + + $this->assertSame( ErrorLoggerInterface::INVALID_TYPE_NON_STRING, $code_non_string ); + $this->assertSame( ErrorLoggerInterface::IS_EMPTY, $code_empty ); + $this->assertSame( ErrorLoggerInterface::NOT_EMAIL, $code_non_email ); + $this->assertSame( ErrorLoggerInterface::INVALID_DNS, $code_no_dns ); + } + + /** + * Tests that input data is returned according to validation results and options. + */ + public function test_get_input_data() { + + $validator = new Email(); + + $validator->is_valid( 'info@example.com' ); + $input = $validator->get_input_data(); + + $this->assertInternalType( 'array', $input ); + $this->assertArrayHasKey( 'value', $input ); + $this->assertSame( 'info@example.com', $input[ 'value' ] ); + + $validator->is_valid( 'meh' ); + + $input = $validator->get_input_data(); + $this->assertSame( 'meh', $input[ 'value' ] ); + } + +} \ No newline at end of file diff --git a/tests/phpunit/Unit/MultiOrTest.php b/tests/phpunit/Unit/MultiOrTest.php new file mode 100644 index 0000000..d5793e1 --- /dev/null +++ b/tests/phpunit/Unit/MultiOrTest.php @@ -0,0 +1,125 @@ + + * @package inpsyde-validator + * @license http://opensource.org/licenses/MIT MIT + */ +class MultiOrTest extends \PHPUnit_Framework_TestCase { + + public function test_constructor_can_use_factory() { + + $validator = Mockery::mock( ExtendedValidatorInterface::class ); + + new MultiOr( [ ], [ get_class( $validator ) ] ); + + } + + /** + * @param mixed $value + * @param array $validators + * @param bool $expected_valid + * @param array $errors + * + * @dataProvider is_valid_cases_provider + */ + public function test_is_valid( $value, array $validators, $expected_valid, array $errors = [ ] ) { + + $multi = new MultiOr( [ 'validators' => $validators ] ); + $result = $multi->is_valid( $value ); + $message = sprintf( "Failing for %s: got %s.", $value, $expected_valid ? 'false' : 'true' ); + $expected_valid ? $this->assertTrue( $result, $message ) : $this->assertFalse( $result, $message ); + $this->assertSame( $errors, $multi->get_error_codes() ); + } + + public function is_valid_cases_provider() { + + $true = new Callback( + [ + 'callback' => function () { + + return TRUE; + } + ] + ); + + $false = new Callback( + [ + 'callback' => function () { + + return FALSE; + } + ] + ); + + $not_empty = new NotEmpty(); + + $greater = new GreaterThan( [ 'min' => 2 ] ); + + return [ + [ + 3, + [ $true, $false, $not_empty, $greater ], + TRUE, + [ ] + ], + [ + 1, + [ $true, $false, $not_empty, $greater ], + TRUE, + [ ] + ], + [ + 12, + [ $not_empty, $false, $false ], + TRUE, + [ ] + ], + [ + 1, + [ $false, $greater ], + FALSE, + [ ErrorLoggerInterface::CUSTOM_ERROR, ErrorLoggerInterface::NOT_GREATER ] + ], + [ + '', + [ $not_empty, $false ], + FALSE, + [ ErrorLoggerInterface::IS_EMPTY, ErrorLoggerInterface::CUSTOM_ERROR ] + ], + [ + '', + [ $not_empty ], + FALSE, + [ ErrorLoggerInterface::IS_EMPTY ] + ], + [ + '', + [ ], + TRUE, + [ ] + ], + ]; + } + +} \ No newline at end of file diff --git a/tests/phpunit/Unit/NegateTest.php b/tests/phpunit/Unit/NegateTest.php new file mode 100644 index 0000000..04afbef --- /dev/null +++ b/tests/phpunit/Unit/NegateTest.php @@ -0,0 +1,106 @@ + + * @package inpsyde-validator + * @license http://opensource.org/licenses/MIT MIT + */ +class NegateTest extends \PHPUnit_Framework_TestCase { + + /** + * @expectedException \InvalidArgumentException + */ + public function test_constructor_needs_validator() { + + new Negate(); + } + + public function test_with_validator() { + + $validator = Mockery::mock( ExtendedValidatorInterface::class ) + ->shouldReceive( 'is_valid' ) + ->with( 'foo' ) + ->once() + ->andReturn( FALSE ) + ->getMock() + ->shouldReceive( 'get_input_data' ) + ->andReturn( [ 'value' => 'foo' ] ) + ->getMock() + ->shouldReceive( 'get_error_code' ) + ->andReturn( '' ); + + $negate = Negate::with_validator( $validator->getMock() ); + + $this->assertTrue( $negate->is_valid( 'foo' ) ); + } + + public function test_constructor_can_use_factory() { + + $validator = Mockery::mock( ExtendedValidatorInterface::class ); + $validator_class = get_class( $validator ); + + $negate = new Negate( [ 'validator' => $validator_class ] ); + $input = $negate->get_input_data(); + $validator_stored = $input[ 'validator' ]; + + $this->assertInternalType( 'object', $validator_stored ); + $this->assertInstanceOf( $validator_class, $validator_stored ); + } + + /** + * @param mixed $value + * @param ExtendedValidatorInterface $validator + * @param bool $expected_valid + * + * @dataProvider is_valid_cases_provider + */ + public function test_is_valid( $value, $validator, $expected_valid ) { + + $negate = new Negate( [ 'validator' => $validator ] ); + $result = $negate->is_valid( $value ); + + $expected_valid + ? $this->assertTrue( $result, serialize( $value ) ) + : $this->assertFalse( $result, serialize( $value ) ); + } + + public function is_valid_cases_provider() { + + $true = Callback::with_callback( + function () { + + return TRUE; + } + ); + + $false = Callback::with_callback( + function () { + + return FALSE; + } + ); + + return [ + [ TRUE, $true, FALSE ], + [ FALSE, $false, TRUE ], + ]; + } + +} \ No newline at end of file diff --git a/tests/phpunit/Unit/PoolTest.php b/tests/phpunit/Unit/PoolTest.php new file mode 100644 index 0000000..4e98b91 --- /dev/null +++ b/tests/phpunit/Unit/PoolTest.php @@ -0,0 +1,166 @@ + + * @package inpsyde-validator + * @license http://opensource.org/licenses/MIT MIT + */ +class PoolTest extends \PHPUnit_Framework_TestCase { + + /** + * @expectedException \InvalidArgumentException + */ + public function test_constructor_needs_validator() { + + new Pool(); + } + + public function test_with_validator() { + + $validator = Mockery::mock( ExtendedValidatorInterface::class ) + ->shouldReceive( 'is_valid' ) + ->with( 'foo' ) + ->once() + ->andReturn( TRUE ) + ->getMock() + ->shouldReceive( 'get_input_data' ) + ->andReturn( [ 'value' => 'foo' ] ) + ->getMock() + ->shouldReceive( 'get_error_code' ) + ->andReturn( '' ); + + $pool = Pool::with_validator( $validator->getMock() ); + + $this->assertTrue( $pool->is_valid( [ 'foo' ] ) ); + } + + public function test_constructor_can_use_factory() { + + $validator = Mockery::mock( ExtendedValidatorInterface::class ); + $validator_class = get_class( $validator ); + + $pool = new Pool( [ 'validator' => $validator_class ] ); + $input = $pool->get_input_data(); + $validator_stored = $input[ 'validator' ]; + + $this->assertInternalType( 'object', $validator_stored ); + $this->assertInstanceOf( $validator_class, $validator_stored ); + } + + public function test_is_valid_accepts_traversable_only() { + + $pool = new Pool( [ 'validator' => Mockery::mock( ExtendedValidatorInterface::class ) ] ); + $valid = $pool->is_valid( '' ); + + $this->assertFalse( $valid ); + $this->assertSame( ErrorLoggerInterface::INVALID_TYPE_NON_TRAVERSABLE, $pool->get_error_code() ); + } + + /** + * @param mixed $value + * @param ExtendedValidatorInterface $validator + * @param bool $expected_valid + * @param string $error + * + * @dataProvider is_valid_cases_provider + */ + public function test_is_valid( $value, $validator, $expected_valid, $error ) { + + $pool = new Pool( [ 'validator' => $validator ] ); + $result = $pool->is_valid( $value ); + $expected_valid ? $this->assertTrue( $result ) : $this->assertFalse( $result ); + $this->assertSame( $error, $pool->get_error_code() ); + } + + public function is_valid_cases_provider() { + + return [ + [ + [ 2, 4, 6 ], + new GreaterThan( [ 'min' => 1 ] ), + TRUE, + '' + ], + [ + [ 2, 4, 6 ], + new GreaterThan( [ 'min' => 3 ] ), + TRUE, + '' + ], + [ + [ 1, 2, 'x' ], + new NotEmpty(), + TRUE, + '' + ], + [ + [ '', NULL, FALSE ], + new NotEmpty(), + FALSE, + ErrorLoggerInterface::IS_EMPTY + ], + [ + [ 'http://example.com', 'example.com' ], + new Url(), + TRUE, + '' + ], + [ + [ 'http://example.com', 'https://example.it' ], + new Url(), + TRUE, + '' + ], + [ + [ ], + new GreaterThan( [ 'min' => 1 ] ), + FALSE, + ErrorLoggerInterface::IS_EMPTY + ], + ]; + } + + public function test_consecutive_calls() { + + $pool = new Pool( [ 'validator' => new GreaterThan( [ 'min' => 5 ] ) ] ); + + $this->assertTrue( $pool->is_valid( [ 6, 7, 8, 10 ] ) ); + $code = $pool->get_error_code(); + $data = $pool->get_input_data(); + + $this->assertSame( '', $code ); + $this->assertInternalType( 'array', $data ); + $this->assertArrayHasKey( 'value', $data ); + $this->assertSame( 6, $data[ 'value' ] ); + + $this->assertFalse( $pool->is_valid( [ 1, 2, 3, 4 ] ) ); + $code = $pool->get_error_code(); + $data = $pool->get_input_data(); + + $this->assertSame( ErrorLoggerInterface::NOT_GREATER, $code ); + $this->assertInternalType( 'array', $data ); + $this->assertArrayHasKey( 'value', $data ); + $this->assertSame( 4, $data[ 'value' ] ); + } + +} \ No newline at end of file diff --git a/tests/phpunit/Unit/SizeTest.php b/tests/phpunit/Unit/SizeTest.php new file mode 100644 index 0000000..75be7fd --- /dev/null +++ b/tests/phpunit/Unit/SizeTest.php @@ -0,0 +1,95 @@ + + * @package inpsyde-validator + * @license http://opensource.org/licenses/MIT MIT + */ +class SizeTest extends \PHPUnit_Framework_TestCase { + + /** + * Ensures that the validator follows expected behavior + * + * @dataProvider provide_is_valid_cases + * + * @param string $input + * @param int $size + * @param bool $expect + */ + public function test_is_valid( $input, $size, $expect ) { + + $validator = new Size( [ 'size' => $size ] ); + + $input_str = is_scalar( $input ) ? (string) $input : serialize( $input ); + + $expect + ? $this->assertTrue( $validator->is_valid( $input ), "{$input_str} size is not {$size}." ) + : $this->assertFalse( $validator->is_valid( $input ), "{$input_str} size is {$size}." ); + } + + /** + * @return array + */ + public function provide_is_valid_cases() { + + return [ + // $input, $size, $expect + [ 'foo bar', 6, FALSE ], + [ 'foo bar', 7, TRUE ], + [ '12345', 5, TRUE ], + [ '', 0, TRUE ], + [ ' ', 0, FALSE ], + [ ' ', 1, TRUE ], + [ 1, 1, TRUE ], + [ 12345, 12345, TRUE ], + [ 1.9, 1, TRUE ], + [ 9.9, 10, FALSE ], + [ 0755, (int) 0755, TRUE ], + [ [ NULL ], 1, TRUE ], + [ [ ], 0, TRUE ], + [ [ '', '2' ], 2, TRUE ], + [ new \ArrayObject( [ '', '2' ] ), 2, TRUE ], + [ new \ArrayObject(), 0, TRUE ], + [ new \ArrayObject(), 1, FALSE ], + [ (object) [ 'foo' => 'bar' ], 1, TRUE ], + ]; + } + + /** + * Tests that error code is returned according to validation results and options. + */ + public function test_get_error_code() { + + $validator = new Size( [ 'size' => 1 ] ); + + $validator->is_valid( Mockery::mock() ); + $code_non_countable = $validator->get_error_code(); + + $validator->is_valid( STDIN ); + $code_non_countable_res = $validator->get_error_code(); + + $validator->is_valid( 2 ); + $code_non_size = $validator->get_error_code(); + + $this->assertSame( ErrorLoggerInterface::INVALID_TYPE_NON_COUNTABLE, $code_non_countable ); + $this->assertSame( ErrorLoggerInterface::INVALID_TYPE_NON_COUNTABLE, $code_non_countable_res ); + $this->assertSame( ErrorLoggerInterface::INVALID_SIZE, $code_non_size ); + } + +} \ No newline at end of file diff --git a/tests/phpunit/Unit/TypeTest.php b/tests/phpunit/Unit/TypeTest.php new file mode 100644 index 0000000..03e25ef --- /dev/null +++ b/tests/phpunit/Unit/TypeTest.php @@ -0,0 +1,119 @@ + + * @package inpsyde-validator + * @license http://opensource.org/licenses/MIT MIT + */ +class TypeTest extends \PHPUnit_Framework_TestCase { + + /** + * @expectedException \InvalidArgumentException + */ + public function test_type_is_required() { + + new Type(); + + } + + /** + * @expectedException \InvalidArgumentException + */ + public function test_type_need_to_be_string() { + + new Type( [ 'type' => TRUE ] ); + + } + + /** + * Ensures that the validator follows expected behavior + * + * @dataProvider provide_is_valid_cases + * + * @param string $input + * @param string $type + * @param bool $expect + */ + public function test_is_valid( $input, $type, $expect ) { + + $validator = new Type( [ 'type' => $type ] ); + + $input_str = is_scalar( $input ) ? (string) $input : serialize( $input ); + + $expect + ? $this->assertTrue( $validator->is_valid( $input ), "{$input_str} type is not {$type}." ) + : $this->assertFalse( $validator->is_valid( $input ), "{$input_str} type is {$type}." ); + } + + /** + * @return array + */ + public function provide_is_valid_cases() { + + return [ + // $input, $type, $expect + [ 'foo bar', 'string', TRUE ], + [ 1, 'string', FALSE ], + [ '', 'string', TRUE ], + [ 'CAPS', 'string', TRUE ], + [ 2, 'integer', TRUE ], + [ 3, 'int', TRUE ], + [ 0, 'int', TRUE ], + [ 0.0, 'int', FALSE ], + [ 3.0, 'int', FALSE ], + [ 4.0, 'integer', FALSE ], + [ 4.5, 'float', TRUE ], + [ 5.5, 'double', TRUE ], + [ 6.5, 'DOUBLE', TRUE ], + [ 6.5, 'float', TRUE ], + [ TRUE, 'bool', TRUE ], + [ TRUE, 'boolean', TRUE ], + [ FALSE, 'bool', TRUE ], + [ FALSE, 'boolean', TRUE ], + [ 1, 'bool', FALSE ], + [ 0, 'boolean', FALSE ], + [ NULL, 'NULL', TRUE ], + [ NULL, 'null', TRUE ], + [ '', 'null', FALSE ], + [ 3, 'numeric', TRUE ], + [ 0, 'numeric', TRUE ], + [ 0.0, 'numeric', TRUE ], + [ 0.0, 'Numeric', TRUE ], + [ '2.0', 'numeric', TRUE ], + [ '200', 'numeric', TRUE ], + [ 'one', 'numeric', FALSE ], + [ 3, 'number', TRUE ], + [ 0, 'number', TRUE ], + [ 0.0, 'number', TRUE ], + [ 0.0, 'number', TRUE ], + [ '2.0', 'number', TRUE ], + [ '200', 'number', TRUE ], + [ 'one', 'number', FALSE ], + [ new \ArrayObject(), 'traversable', TRUE ], + [ [ ], 'traversable', TRUE ], + [ [ NULL ], 'traversable', TRUE ], + [ (object) [ ], 'object', TRUE ], + [ [ ], 'object', FALSE ], + [ new \ArrayObject(), 'object', TRUE ], + [ new \ArrayObject(), \ArrayObject::class, TRUE ], + [ new \ArrayObject(), \ArrayAccess::class, TRUE ], + [ (object) [ ], \stdClass::class, TRUE ], + [ [ ], \ArrayAccess::class, FALSE ], + ]; + } + +} \ No newline at end of file diff --git a/tests/phpunit/Unit/WpFilterTest.php b/tests/phpunit/Unit/WpFilterTest.php new file mode 100644 index 0000000..79f5117 --- /dev/null +++ b/tests/phpunit/Unit/WpFilterTest.php @@ -0,0 +1,89 @@ + + * @package inpsyde-validator + * @license http://opensource.org/licenses/MIT MIT + */ +class WpFilterTest extends \PHPUnit_Framework_TestCase { + + public function setUp() { + + parent::setUp(); + Monkey::setUpWP(); + } + + public function tearDown() { + + Monkey::tearDownWP(); + parent::tearDown(); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function test_filter_is_required() { + + new WpFilter(); + + } + + /** + * @expectedException \InvalidArgumentException + */ + public function test_filter_need_to_be_string() { + + new WpFilter( [ 'type' => TRUE ] ); + + } + + public function test_is_valid_no_filters() { + + $validator = new WpFilter( [ 'filter' => 'test-filter' ] ); + + $this->assertFalse( $validator->is_valid( FALSE ) ); + $this->assertTrue( $validator->is_valid( TRUE ) ); + } + + public function test_is_valid_cast_to_bool_return_value() { + + Filters::expectApplied( 'test-filter-return-bar' ) + ->once() + ->with( 'foo' ) + ->andReturn( 'bar', 'true' ); + + Filters::expectApplied( 'test-filter-return-true' ) + ->once() + ->with( 'foo' ) + ->andReturn( 'true' ); + + Filters::expectApplied( 'test-filter-return-null' ) + ->once() + ->with( 'foo' ) + ->andReturnNull(); + + $validator1 = new WpFilter( [ 'filter' => 'test-filter-return-bar' ] ); + $validator2 = new WpFilter( [ 'filter' => 'test-filter-return-true' ] ); + $validator3 = new WpFilter( [ 'filter' => 'test-filter-return-null' ] ); + + $this->assertFalse( $validator1->is_valid( 'foo' ) ); + $this->assertTrue( $validator2->is_valid( 'foo' ) ); + $this->assertFalse( $validator3->is_valid( 'foo' ) ); + } +} \ No newline at end of file