chippyash/validation
Quality Assurance
, (*1)
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'., (*2)
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
, (*3)
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
, (*4)
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.), (*5)
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., (*6)
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., (*7)
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., (*8)
All Chippyash Validators support invoking on the validation class, in which case
you need to supply the messenger:, (*9)
, (*10)
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
}
, (*11)
Alternatively, You can call the isValid() method, in which case, you do not need to
supply the Messenger:, (*12)
, (*13)
use Chippyash\Validation\Common\Double as Validator;
$validator = new Validator();
$result = $validator->isValid($someValue);
if (!$result) {
$errMsg = implode(' : ', $validator->getMessages());
}
, (*14)
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
, (*15)
use Chippyash\Validation\Common\Enum;
$validator = new Enum(['foo','bar']);
$ret = $validator->isValid('bop'); //returns false
, (*16)
- 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)
, (*17)
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
, (*18)
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., (*19)
For more uses of the Netmask validator, see the test cases., (*20)
- 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., (*21)
- 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, (*22)
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, (*23)
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., (*24)
For example, lets say we have some incoming Json:, (*25)
$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., (*26)
$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., (*27)
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., (*28)
The values supplied in the TypeMap can be one of the following:, (*29)
- 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:, (*30)
__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., (*31)
We can now rewrite our validator to validate the entire input data:, (*32)
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., (*33)
Logical Validators
These validators allow you carry out boolean logic. LAnd, LOr, LNot and LXor do as expected., (*34)
Each require ValidatorPatternInterface constructor parameters. Here is superficial example:, (*35)
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:, (*36)
$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:, (*37)
$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., (*38)
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., (*39)
To use, construct the processor with your first (usually required item) validator,
then simply add additional ones to it., (*40)
$validator = new ValidationProcessor($requiredValidator);
$validator->add($optionalValidator);
Run your validation and gather any error messages if required:, (*41)
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., (*42)
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., (*43)
Test Contract in the docs directory., (*44)
Check out ZF4 Packages for more packages, (*45)
UML
Changing the library
- fork it
- write the test
- amend it
- do a pull request
Found a bug you can't figure out?, (*46)
- fork it
- write the test
- do a pull request
NB. Make sure you rebase to HEAD before your pull request, (*47)
Or - raise an issue ticket., (*48)
Where?
The library is hosted at Github. It is
available at Packagist.org, (*49)
Installation
Install Composer, (*50)
For production
<, (*51)
pre>
"chippyash/validation": ">=3,<4"
, (*52)
Or to use the latest, possibly unstable version:, (*53)
, (*54)
"chippyash/validation": "dev-master"
For development
Clone this repo, and then run Composer in local repo root to pull in dependencies, (*55)
git clone git@github.com:chippyash/Validation.git Validation
cd Validation
composer install
To run the tests:, (*56)
cd Validation
vendor/bin/phpunit -c test/phpunit.xml test/
License
This software library is released under the BSD 3 Clause license, (*57)
This software library is Copyright (c) 2015-2020, Ashley Kitson, UK, (*58)
This software library contains code items that are derived from other works:, (*59)
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., (*60)
If the original copyright owners of the derived code items object to this inclusion, please contact the author., (*61)
Thanks
I didn't do this by myself. I'm deeply indebted to those that trod the path before me., (*62)
The following have done work that this library uses:, (*63)
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., (*64)
Zend I18n: Additional validations are available from the Zend I18n lib., (*65)
History
V1.0.0 Initial Release, (*66)
V1.1.0 Update dependencies, (*67)
V1.1.1 Move code coverage to codeclimate, (*68)
V1.1.2 Add link to packages, (*69)
V1.1.3 Verify PHP 7 compatibility, (*70)
V1.1.4 Remove @internal flag on Lambda validator, (*71)
V1.1.5 Allow wider range of zend dependencies, (*72)
V1.2.0 Add additional common validators, (*73)
V1.2.1 update dependencies, (*74)
V1.2.2 update build scripts, (*75)
V1.2.3 update composer - forced by packagist composer.json format change, (*76)
V2.0.0 BC Break. Withdraw old php version support, (*77)
V2.1.0 Change of license from GPL V3 to BSD 3 Clause, (*78)
V3.0.0 BC Break. Remove PHP support < 7.2. Switch Zend dependency to Laminas, (*79)