Think of a regular expression [ab]+ which matches a string abab. Now imaging the same for arrays.
The purpose of this library is to validate an existing (nested) array against a template and report a mismatch. It has the object-oriented extendable architecture to write and add custom validators.
Note to current users: this version is not backwards compatible with the previous 0.5.6.
composer require lezhnev74/pasvl
Refer to files in Example folder.
// Define the pattern of the data, define keys and values separately
$pattern = [
'*' => [
'type' => 'book',
'title' => ':string :contains("book")',
'chapters' => [
':string :len(2) {1,3}' => [
'title' => ':string',
':exact("interesting") ?' => ':bool',
],
],
],
];
// Provide the data to match against the above pattern.
$data = [
[
'type' => 'book',
'title' => 'Geography book',
'chapters' => [
'eu' => ['title' => 'Europe', 'interesting' => true],
'as' => ['title' => 'America', 'interesting' => false],
],
],
[
'type' => 'book',
'title' => 'Foreign languages book',
'chapters' => [
'de' => ['title' => 'Deutsch'],
],
],
];
$builder = \PASVL\Validation\ValidatorBuilder::forArray($pattern);
$validator = $builder->build();
try {
$validator->validate($data);
} catch (ArrayFailedValidation $e) {
// If data cannot be matched against the pattern, then exception is thrown.
// It is not always easy to detect why the data failed matching, the exception MAY sometimes give you extra hints.
echo "failed: " . $e->getMessage() . "\n";
}$pattern = ":string :regexp('#^[ab]+$#')";
$builder = \PASVL\Validation\ValidatorBuilder::forString($pattern);
$validator = $builder->build();
$validator->validate("abab"); // the string is valid
$validator->validate("abc"); // throws RuleFailed exception with the message: "string does not match regular expression ^[ab]+$"This package supports a special dialect for validation specification. It looks like this:
-
Rule Name Specify zero or one Rule Name to apply to the data. Optinal postfix
?allows data to benull. Refer to the set of built-in rules insrc/Validation/Rules/Library. For custom rules read below underCustom Rules. For example,:string?describes strings andnull. -
Sub-Rule Name Specify zero or more Sub-Rule Names to apply to the data AFTER the Rule is applied. Sub Rules are extra methods of the main Rule. For example,
:number :floatdescribes floats. -
Quantifier Specify quantity expectations for data keys. If none is set then default is assumed -
!. Available quantifiers:!- one key required (default)?- optional key*- any count of keys{2}- strict keys count{2,4}- range of keys count
For example:
$pattern = [":string *" => ":number"]; // the above pattern matches data: $data = ["june"=>10, "aug" => "11"];
- as exact value
$pattern = ["name" => ":any"]; // here the key is the exact value $pattern = ["name?" => ":any"]; // here the key is the exact value, can be absent as well $pattern = [":exact('name')" => ":any"]; // this is the same
- as nullable rule
$pattern = ["name" => ":string?"]; // the value must be a string or null
- as rule with subrules
$pattern = ["name" => ":string :regexp('#\d*#')"]; // the value must be a string which contains only digits
- as rule with quantifiers
$pattern = [":string {2}" => ":any"]; // data must have exactly two string keys
This package supports combinations of rules, expressed in a natural language. Examples:
:string or :number:string and :number(:string and :number) or :array
There are two combination operators: and, or.
and operator has precedence.
Both are left-associative.
By default, the system uses only the built-in rules. However you can extend them with your own implementations. To add new custom rules, follow these steps:
- implement your new rule as a class and extend it from
\PASVL\Validation\Rules\Rule - implement a new rule locator by extending a class
\PASVL\Validation\Rules\RuleLocator - configure your validator like this:
$builder = ValidatorBuilder::forArray($pattern)->withLocator(new MyLocator()); // set your new locator $validator = $builder->build();
This package comes with a few built-in rules and their corresponding sub-rules (see in folder src/Validation/Rules/Library):
:string- the value must be string:regexp(<string>)- provide a regular expression(the same as forpreg_match()):url:email:uuid:contains(<string>):starts(<string>):ends(<string>):in(<string>,<string>,...):len(<int>):max(<int>):min(<int>):between(<int>,<int>)
:number:max(<int>):min(<int>):between(<int>, <int>):int- the number must be an integer:float- the number must be a float:positive:negative:in(<a>,<b>,<c>)- the number must be within values (type coercion possible):inStrict(<a>,<b>,<c>)- the number must be within values (type coercion disabled)
:exact(<value>):bool(<?value>)- the value must be boolean, if optional argument is given the value must be exactly it:object:instance(<fqcn>):propertyExists(<string>):methodExists(<string>)
:array:count(<int>):keys(<string>,<string>,...):min(<int>)- min count:max(<int>)- max count:between(<int>, <int>)- count must be within
:any- a placeholder, any value will match
- PHP casts "1" to 1 for array keys:
$data = ["12" => ""]; $pattern_invalid = [":string" => ""]; $pattern_valid = [":number :int" => ""];
- Technically speaking PASVL is a non-deterministic backtracking parser, and thus it can't always show you what exact key did not match the pattern. That is because, say, a key can match different patterns and there is no way of knowing which one was meant to be correct. In such cases it returns a message like "no matches found at X level".
- Greg Corrigan. Greg spotted a problem with nullable values reported as invalid.
- Henry Combrinck. Henry tested the library extensively on real data and found tricky bugs and edge cases. Awesome contribution to make the package valuable to the community.
- @Averor. Found a bug in parentheses parsing.
- Julien Gidel. Improved
regexpsub-rule.
This project is licensed under the terms of the MIT license.