chippyash / validation
comprehensive function based validation routines
Installs: 10 452
Dependents: 1
Suggesters: 0
Security: 0
Stars: 1
Watchers: 2
Forks: 2
Open Issues: 0
Requires
- php: >=7.2
- chippyash/monad: >=2,<3
- laminas/laminas-filter: >=2,<3
- laminas/laminas-validator: >=2,<3
Requires (Dev)
- ext-intl: *
- laminas/laminas-i18n: 2.5.1|>=2.6.0
- mikey179/vfsstream: >=1.6,<2
- phpunit/phpunit: ~8.5
- symplify/easy-coding-standard: ^6.1
Suggests
- ext-intl: Required for most features of Laminas\I18n; included in default builds of PHP
- laminas/laminas-i18n: To use UK Post code and telephone number validation
README
Quality Assurance
The above badges represent the current development branch. As a rule, I don't push to GitHub unless tests, coverage and usability are acceptable. This may not be true for short periods of time; on holiday, need code for some other downstream project etc. If you need stable code, use a tagged version. Read 'Further Documentation' and 'Installation'.
Please note that developer support for PHP5.4 & 5.5 was withdrawn at version 2.0.0 of this library.
If you need support for PHP 5.4 or 5.5, please use a version >=1,<2
Support for PHP <7.2 was withdrawn at version 3.0.0 of this library.
If you need support for PHP 5.6 - 7.1, use a version ~2.0
What?
Provides extensive and complex validation of nested structures. Primary use case is validating incoming Json data, where, unlike XML, there is no defined validation pattern in common usage. (XML has XSD.)
Why?
Validating incoming data is important because you can't trust the data provider. Early warning in your program makes it robust. In large and/or corporate systems, your upstream data providers can change without you knowing. Being able to validate that the data conforms to an expected pattern prevents your application from failing unnecessarily.
A robust system, first filters, then validates, then optionally filters again (usually referred to as mapping,) any incoming data. This library provides a Zend Validator compatible extension that allows you to create complex validations of nested data.
How
Validation just wouldn't be the same if you didn't know why something failed. All Chippyash Validators use the Chippyash/Validation/Messenger class to store validation errors, and record reasons why a validation passed. Sometimes, knowing why it succeeded is just as important.
All Chippyash Validators support invoking on the validation class, in which case you need to supply the messenger:
use Chippyash\Validation\Messenger; use Chippyash\Validation\Common\Double as Validator; $messenger = new Messenger(); $validator = new Validator(); $result = $validator($someValue, $messenger); $msg = $messenger->implode(); if (!$result) { echo $msg; } else { //parse the messages and switch dependent on why it succeeded }
Alternatively, You can call the isValid() method, in which case, you do not need to supply the Messenger:
use Chippyash\Validation\Common\Double as Validator; $validator = new Validator(); $result = $validator->isValid($someValue); if (!$result) { $errMsg = implode(' : ', $validator->getMessages()); }
Simple Validators
- Chippyash\Validation\Common\DigitString: does the value contain only numeric characters?
- Chippyash\Validation\Common\Double: Is the supplied string equivalent to a double (float) value;
- Chippyash\Validation\Common\Email: Is the supplied string a simple email address
- Chippyash\Validation\Common\Enum: Is supplied string one of a known set of strings
use Chippyash\Validation\Common\Enum; $validator = new Enum(['foo','bar']); $ret = $validator->isValid('bop'); //returns false
- Chippyash\Validation\Common\IsArray: Is the supplied value an array?
- Chippyash\Validation\Common\ArrayKeyExists: Is value and array and has the required key?
- Chippyash\Validation\Common\ArrayKeyNotExists: Is value and array and does not have the required key?
- Chippyash\Validation\Common\IsTraversable: Is the supplied value traversable?
- Chippyash\Validation\Common\Netmask: Does the supplied IP address belong to the constructed Net Mask (CIDR)
use Chippyash\Validation\Common\Netmask; $validator = new Netmask('0.0.0.0/1'); return $validator->isValid('127.0.0.1); //return true return $validator->isValid('128.0.0.1); //return false
You can construct a Netmask Validator with a single CIDR address mask or an array of them. If you call the Netmask isValid (or invoke it) with a null IP, It will try to get the IP from $_SERVER['REMOTE_ADDR'] or $_SERVER['HTTP_X_FORWARDED_FOR'] thus making it ideal for its' primary use case, that of protecting your web app against requests from unauthorised IP addresses.
For more uses of the Netmask validator, see the test cases.
- Chippyash\Validation\Common\UKPostCode: Simple extension of Zend PostCode to check for UK Post Codes. Should be straightforward to create your own country specific validator;
- Chippyash\Validation\Common\UKTelNum. Again, a simple extension of the Zend TelNum Validator
- Chippyash\Validation\Common\ZFValidator: A Simple class allowing you to extend it to create any validator using the Zend Validators.
Complex Validators
Here is where we start to depart from the Zend validators.
- Chippyash\Validation\Common\ArrayPart. Is the value an array, does the required key exist, and does it validate according to the passed in function parameter?
use Chippyash\Validation\Common\ArrayPart; use Chippyash\Validation\Common\Enum; $validator = new ArrayPart('idx', new Enum(['foo','bar'])); $ret = $validator->isValid(['idx' => 'bop']); //false $ret = $validator->isValid(['foo' => 'bop']); //false $ret = $validator->isValid(['idx' => 'bar']); //true
- Chippyash\Validation\Common\Lambda. The Lambda validator expects a function on construction that will accept a value and return true or false:
use Chippyash\Validation\Common\Lambda; $validator = new Lambda(function($value) { return $value === 'foo'; }); $ret = $validator->isValid('bar'); //false
You can pass in an optional second StringType parameter with the failure message
use Chippyash\Validation\Common\Lambda; use Chippyash\Type\String\StringType; $validator = new Lambda(function($value) { return $value === 'foo'; }, new StringType('Oops, not a Foo'); if (!$validator->isValid('bar')) { //false $errMsg = implode(' : ', $validator->getMessages()); }
You can specify a Messenger parameter as the second parameter to your function declaration if you want to handle adding error messages manually
use Chippyash\Validation\Messenger; use Chippyash\Validation\Common\Lambda; $validator = new Lambda(function($value, Messenger $messenger) { if ($value != 'foo') { $messenger->add('error message'); return false; } return true; } );
- Chippyash\Validation\Common\ISO8601DateString: Does the supplied string conform to an ISO8601 datestring pattern. docs tbc. This validator is so complex, that it probably deserves it's own library. So be warned, it may be removed from this one!
Pattern Validators
Pattern validators allow you to validate complex data structures. These data structures will normally be a traversable (array, object with public parameters, object implementing a traversable interface etc.) They are central to the usefulness of this library.
For example, lets say we have some incoming Json:
$json = ' { "a": "2015-12-01", "b": false, "c": [ { "d": "fred", "e": "NN10 6HB" }, { "d": "jim", "e": "EC1V 7DA" }, { "d": "maggie", "e": "LE4 4HB" }, { "d": "sue", "e": "SW17 9JR" } ], "f": [ "a@b.com", "c@d.co.uk" ] } ';
The first thing we'll do is convert this into something PHP can understand, i.e.
$value = json_decode($json); //or use the Zend\Json class for solid support
HasTypeMap
The HasTypeMap validator allows us to validate both the keys and the values of our incoming data and thus forms the heart of any complex validation requirement.
use Chippyash\Validation\Pattern\HasTypeMap; use Chippyash\Validation\Common\ISO8601DateString; use Chippyash\Validation\Common\IsArray; use Chippyash\Validation\Common\Email; $validator = new HasTypeMap([ 'a' => new ISO8601DateString(), 'b' => 'boolean', 'c' => new IsArray(), 'f' => new IsArray() ]); $ret = $validator->isValid($value);
Note, again, the best we can do for the 'c' and 'f' element is determine if it is an array. See the 'Repeater' below for how to solve this problem.
The values supplied in the TypeMap can be one of the following:
- Any returned by PHP gettype(), i.e. "integer" "double" "string", "boolean", "resource", "NULL", "unknown"
- The name of a class, e.g. '\Chippyash\Type\String\StringType'
- A function conforming to the signature 'function($value, Messenger $messenger)' and returning true or false
- An object implementing the ValidationPatternInterface
Repeater
The Repeater pattern allows us to validate a non associative array of values. Its constructor is:
__construct(ValidatorPatternInterface $validator, IntType $min = null, IntType $max = null)
If $min === null, then it will default to 1. If $max === null, then it will default to -1, i.e. no max.
We can now rewrite our validator to validate the entire input data:
use Chippyash\Validation\Pattern\HasTypeMap; use Chippyash\Validation\Pattern\Repeater; use Chippyash\Validation\Common\ISO8601DateString; use Chippyash\Validation\Common\IsArray; use Chippyash\Validation\Common\Email; use Chippyash\Validation\Common\UKPostCode; $validator = new HasTypeMap([ 'a' => new ISO8601DateString(), 'b' => 'boolean', 'c' => new Repeater( new HasTypeMap([ 'd' => 'string', 'e' => new UKPostCode() ]), null, 4 ), 'f' => new Repeater(new Email()) ]); $ret = $validator->isValid($value);
This says that the 'c' element must contain 1-4 items conforming to the given TypeMap. You can see this in action in the examples/has-type-map.php script.
Logical Validators
These validators allow you carry out boolean logic. LAnd, LOr, LNot and LXor do as expected.
Each require ValidatorPatternInterface constructor parameters. Here is superficial example:
use Chippyash\Validation\Logical; use Chippyash\Validation\Common\Lambda; $true = new Lambda(function($value){return true;}); $false = new Lambda(function($value){return false;}); $and = new Logical\LAnd($true, $false); $or = new Logical\LOr($true, $false); $not = new Logical\LNot($true); $xor = new Logical\LXor($true, $false);
And of course, you can combined them:
$validator = new Logical\LNot( new Logical\LAnd($true, Logical\LXor($false, $true))) $ret = $validator->isValid('foo'); //the above is equivelent to $ret = !( true && (false xor true))
The real power of this is that it allows you to create alternate validation:
$nullOrDate = new LOr( new Lambda(function($value) { return is_null($value); }, new Lambda(function($value) { try {new \DateTime($value); return true;} catch (\Exception $e) { return false;} }) );
Validation Processor
All the above assumes you are running a single validation on the data and that all of the items specified by the validator pattern exist in the incoming data. What happens when you have optional items? This is where the ValidationProcessor comes in.
ValidationProcessor allows you to run a number of validation passes over the data. Typically, you'd run a validation for all required data items first, and then run one or more subsequent validations checking for optional items.
To use, construct the processor with your first (usually required item) validator, then simply add additional ones to it.
$validator = new ValidationProcessor($requiredValidator); $validator->add($optionalValidator);
Run your validation and gather any error messages if required:
if (!$validator->validate($value)) { var_dump($validator->getMessenger()->implode()); }
The processor will run each validation in turn and return the combined result. See examples/validation-processor.php for more illustration.
Further documentation
Please note that what you are seeing of this documentation displayed on Github is always the latest dev-master. The features it describes may not be in a released version yet. Please check the documentation of the version you Compose in, or download.
Test Contract in the docs directory.
Check out ZF4 Packages for more packages
UML
Changing the library
- fork it
- write the test
- amend it
- do a pull request
Found a bug you can't figure out?
- fork it
- write the test
- do a pull request
NB. Make sure you rebase to HEAD before your pull request
Or - raise an issue ticket.
Where?
The library is hosted at Github. It is available at Packagist.org
Installation
Install Composer
For production
"chippyash/validation": ">=3,<4"
Or to use the latest, possibly unstable version:
"chippyash/validation": "dev-master"
For development
Clone this repo, and then run Composer in local repo root to pull in dependencies
git clone git@github.com:chippyash/Validation.git Validation cd Validation composer install
To run the tests:
cd Validation vendor/bin/phpunit -c test/phpunit.xml test/
License
This software library is released under the BSD 3 Clause license
This software library is Copyright (c) 2015-2020, Ashley Kitson, UK
This software library contains code items that are derived from other works:
None of the contained code items breaks the overriding license, or vice versa, as far as I can tell. So as long as you stick to GPL V3+ then you are safe. If at all unsure, please seek appropriate advice.
If the original copyright owners of the derived code items object to this inclusion, please contact the author.
Thanks
I didn't do this by myself. I'm deeply indebted to those that trod the path before me.
The following have done work that this library uses:
Zend Validator: This library requires the Zend Validator Library. Zend Validator provides a comprehensive set of use case specific validators. Whilst this library provides some specific examples of how to use them, it builds on it. Nevertheless the Zend Validator library is a robust tool, and this dev wouldn't do without it.
Zend I18n: Additional validations are available from the Zend I18n lib.
History
V1.0.0 Initial Release
V1.1.0 Update dependencies
V1.1.1 Move code coverage to codeclimate
V1.1.2 Add link to packages
V1.1.3 Verify PHP 7 compatibility
V1.1.4 Remove @internal flag on Lambda validator
V1.1.5 Allow wider range of zend dependencies
V1.2.0 Add additional common validators
V1.2.1 update dependencies
V1.2.2 update build scripts
V1.2.3 update composer - forced by packagist composer.json format change
V2.0.0 BC Break. Withdraw old php version support
V2.1.0 Change of license from GPL V3 to BSD 3 Clause
V3.0.0 BC Break. Remove PHP support < 7.2. Switch Zend dependency to Laminas