bus-factor / ares
Leightweight standalone validation library
Requires
- php: >=7.3
- ext-json: *
- bus-factor/ddd: ^5.0
Requires (Dev)
- phpunit/phpunit: >=5.7
This package is auto-updated.
Last update: 2024-11-07 14:40:34 UTC
README
__ _ _ __ ___ ___
/ _` | '__/ _ \/ __|
| (_| | | | __/\__ \
\__,_|_| \___||___/
Ares is a lightweight standalone validation library.
Table of Contents
- Installation
- Basic Usage
- Validation Errors
- Validation Options
- Validation Rules
- Custom Types
- Custom Validation Error Messages
- Custom Validation Rules
- Sanitization
Installation
Install the library via composer:
composer require bus-factor/ares
Basic Usage
<?php use Ares\Ares; // atomic types $ares = new Ares(['type' => 'string']); $valid = $ares->validate('John Doe'); $errors = $ares->getValidationErrors(); // complex/nested types $ares = new Ares([ 'type' => 'map', 'schema' => [ 'firstName' => ['type' => 'string', 'required' => true], 'lastName' => ['type' => 'string', 'required' => true], ], ]); $valid = $ares->validate(['firstName' => 'John', 'lastName' => 'Doe']); $errors = $ares->getValidationErrors();
Validation Errors
The validate()
method returns true
if the provided data is valid, otherwise false
.
The getValidationErrors()
method returns an Ares\Validation\Error\ErrorCollection
object that holds a list of Ares\Validation\Error\Error
instances that are collected during the last data validation.
The list of validation errors gets reset each time validate()
is called.
Each Ares\Validation\Error\Error
object implements the JsonSerializable
interface and contains details about the error.
The Ares\Validation\Error\ErrorCollection
object is iterable but also offers 2 convenience methods:
::toArrayJsonApiStyle()
- returns an array of arrays that reflects the json:api spec::toArrayNested
- returns a nested array of error messages whose nested structure matches the input data's structure
Validation Options
Validation options may be passed on validation:
$schema = []; $options = []; $ares = new Ares($schema, $options);
Default validation options are:
Validator::OPTIONS_DEFAULTS = [ 'allBlankable' => false, 'allNullable' => false, 'allRequired' => true, 'allUnknownAllowed' => false, ]
allBlankable
This option applies to the type string
only.
If set true
, blank values are considered valid.
If set false
, blank values are considered invalid.
$schema = [ 'type' => 'map', 'schema' => [ 'name' => ['type' => 'string'], ], ]; $ares = new Ares($schema); $ares->validate(['name' => ''], ['allBlankable' => true]); // -> true $ares->validate(['name' => ''], ['allBlankable' => false]); // -> false
This option may be overridden per field by using the blankable
rule:
$schema = [ 'type' => 'map', 'schema' => [ 'name' => ['type' => 'string'], 'email' => ['type' => 'string', 'blankable' => true], ], ]; $ares->validate(['name' => 'John Doe', 'email' => ''], ['allBlankable' => false]); // -> true
allNullable
If set true
, null
is considered a valid value.
If set false
, null
is not considered a valid value.
$schema = [ 'type' => 'map', 'schema' => [ 'name' => ['type' => 'string'], ], ]; $ares->validate(['name' => null], ['allNullable' => true]); // -> true $ares->validate(['name' => null], ['allNullable' => false]); // -> false
This option may be overridden per field by using the nullable
rule:
$schema = [ 'type' => 'map', 'schema' => [ 'name' => ['type' => 'string'], 'email' => ['type' => 'string', 'nullable' => true], ], ]; $ares->validate(['name' => 'John Doe', 'email' => null], ['allNullable' => false]); // -> true
allRequired
If set true
(default) fields that are defined in the schema and not present in the input, are considered invalid.
If set false
fields that are defined in the schema and not present in the input, are considered valid.
$schema = [ 'type' => 'map', 'schema' => [ 'name' => ['type' => 'string'], ], ]; $ares = new Ares($schema); $ares->validate([], ['allRequired' => true]); // -> false $ares->validate([], ['allRequired' => false]); // -> true
This option may be overridden per field by using the required
rule:
$schema = [ 'type' => 'map', 'schema' => [ 'name' => ['type' => 'string'], 'email' => ['type' => 'string', 'required' => false], ], ]; $ares = new Ares($schema); $ares->validate(['name' => 'John Doe'], ['allRequired' => true]); // -> true
allUnknownAllowed
This option applies to the type map
only.
If set true
fields that occur in the input data but are not defined in the schema are considered invalid.
If set false
fields that occur in the input data but are not defined in the schema are considered valid.
$schema = [ 'type' => 'map', 'schema' => [ 'name' => ['type' => 'string'], ], ]; $ares = new Ares($schema); $ares->validate(['name' => 'John Doe', 'initials' => 'JD'], ['allUnknownAllowed' => false]); // -> false $ares->validate(['name' => 'John Doe', 'initials' => 'JD'], ['allUnknownAllowed' => true]); // -> true
Validation Rules
allowed
The allowed
validation rule checks if a value is in a given set of allowed values (enumeration).
Examples:
$ares = new Ares(['type' => 'string', 'allowed' => ['small', 'large']]); $ares->validate('medium'); // -> false $ares->validate('small'); // -> true
The allowed
validation rule is the opposite of the forbidden
validation rule.
blankable
The blankable
rule applies to string
typed values only.
If set true
, blank strings are considered valid.
If set false
, blank strings are considered invalid (default).
Examples:
$ares = new Ares(['type' => 'string', 'blankable' => false]); $ares->validate(''); // -> false $ares->validate(' '); // -> false $ares->validate('John Doe'); // -> true $ares = new Ares(['type' => 'string', 'blankable' => true]); $ares->validate(' '); // -> true
The blankable
validation rule may be used in combination with the allBlankable
validation option.
datetime
The datetime
validation rule applies to string
typed values only.
If set true
, any parsable date/time string is considered valid.
If set false
, date/time validation will not take place at all.
If set a specific date/time format string, the given value will be checked against that format too.
See DateTime::createFromFormat() for details about format strings.
Examples:
$ares = new Ares(['type' => 'string', 'datetime' => true]); $ares->validate('foo'); // -> false $ares->validate('2018-03-23'); // -> true $ares = new Ares(['type' => 'string', 'datetime' => 'd.m.Y H:i']); $ares->validate('2018-03-23'); // -> false $ares->validate('23.03.2019 00:20'); // -> true
directory
The directory
validation rule checks if the given string value contains the path to an existing directory.
If set true
, only paths to existing directories are considered valid.
If set false
, all input is considered valid (no validation).
Examples:
$ares = new Ares(['type' => 'string', 'directory' => true]); $ares->validate(''); // -> false $ares->validate(__FILE__); // -> false $ares->validate(__DIR__); // -> true
The email
validation rule checks if a value is a valid email address.
If set true
, only valid email addresses are considered valid.
If set false
, all input is considered valid (no validation).
Examples:
$ares = new Ares(['type' => 'string', 'email' => true]); $ares->validate('John Doe'); // -> false $ares->validate('john.doe@example.com'); // -> true
file
The file
validation rule checks if the given string value contains the path to an existing file.
If set true
, only paths to existing files are considered valid.
If set false
, all input is considered valid (no validation).
Examples:
$ares = new Ares(['type' => 'string', 'file' => true]); $ares->validate(''); // -> false $ares->validate(__DIR__); // -> false $ares->validate(__FILE__); // -> true
forbidden
The forbidden
validation rule checks if a value is in a given set of forbidden values (enumeration).
Examples:
$ares = new Ares(['type' => 'string', 'forbidden' => ['small', 'medium']]); $ares->validate('medium'); // -> false $ares->validate('large'); // -> true
The forbidden
validation rule is the opposite of the allowed
validation rule.
length
The length
validation rule applies to string
and list
typed values.
The length
validation rule checks if a string, or list has a specified exact length.
Examples:
$ares = new Ares(['type' => 'string', 'length' => 3]); $ares->validate('foobar'); // -> false $ares->validate('foo'); // -> true $ares = new Ares([ 'type' => 'list', 'length' => 3, 'schema' => [ 'type' => 'integer' ], ]) $ares->validate([1, 2]); // -> false $ares->validate([1, 2, 3]); // -> true
max
The max
validation rule applies to float
and integer
typed values only.
The max
validation rule checks if a value is equal to or smaller a specified maximum value.
Examples:
$ares = new Ares(['type' => 'integer', 'max' => 5]); $ares->validate(6); // -> false $ares->validate(2); // -> true
Note this validation rule will throw a Ares\Exception\InapplicableValidationRuleException
when used in conjunction with non-supported value types.
maxlength
The maxlength
validation rule applies to string
and list
typed values.
The maxlength
validation rule checks if a string, or list does not exceed a specified maximum length.
Examples:
$ares = new Ares(['type' => 'string', 'maxlength' => 5]); $ares->validate('foobar'); // -> false $ares->validate('foo'); // -> true $ares = new Ares([ 'type' => 'list', 'maxlength' => 3, 'schema' => [ 'type' => 'integer' ], ]) $ares->validate([1, 2, 3, 4]); // -> false $ares->validate([1, 2, 3]); // -> true
min
The min
validation rule applies to float
and integer
typed values only.
The min
validation rule checks if a value is equal to or greater a specified minimum value.
Examples:
$ares = new Ares(['type' => 'integer', 'min' => 5]); $ares->validate(4); // -> false $ares->validate(8); // -> true
Note this validation rule will throw a Ares\Exception\InapplicableValidationRuleException
when used in conjunction with non-supported value types.
minlength
The minlength
validation rule applies to string
and list
typed values.
The minlength
validation rule checks if a string, or list is not shorter than a specified minimum length.
Examples:
$ares = new Ares(['type' => 'string', 'minlength' => 5]); $ares->validate('foo'); // -> false $ares->validate('foobar'); // -> true $ares = new Ares([ 'type' => 'list', 'minlength' => 3, 'schema' => [ 'type' => 'integer' ], ]) $ares->validate([1, 2]); // -> false $ares->validate([1, 2, 3]); // -> true
nullable
If set true
, null
is considered a valid value.
If set false
, null
is considered an invalid value (default).
Examples:
$ares = new Ares(['type' => 'string', 'nullable' => false]); $ares->validate(null); // -> false $ares->validate('John Doe'); // -> true $ares = new Ares(['type' => 'string', 'nullable' => true]); $ares->validate(null); // -> true
The nullable
validation rule may be used in combination with the allNullable
validation option.
regex
The regex
validation rule applies to string
typed values only.
The regex
validation rule checks if a string matches a regular expression.
Examples:
$ares = new Ares([ 'type' => 'map', 'schema' => [ 'key' => [ 'type' => 'string', 'regex' => '/^[A-Z]{3}$/', ], ], ]); $ares->validate(['key' => 'foobar']); // -> false $ares->validate(['key' => 'FOO']); // -> true
required
Use the required
rule to enforce the presence of a value.
If set true
, absent fields are considered invalid.
If set false
, absent fields are considered valid (default).
Examples:
$ares = new Ares([ 'type' => 'map', 'schema' => [ 'name' => ['type' => 'string', 'required' => true], ], ]); $ares->validate([]); // -> false $ares->validate(['name' => 'John Doe']); // -> true
The required
validation rule may be used in combination with the allRequired
validation option.
schema
The schema
rule is mandatory when using type list
, map
, or tuple
.
schema (list)
The validator expects the schema to define a list item's validation rules.
Examples:
$ares = new Ares([ 'type' => 'list', 'schema' => [ 'type' => 'integer', ], ]); $ares->validate(['foo', 'bar']); // -> false $ares->validate([1, 2, 3]); // -> true
schema (map)
The validator expects the schema to define per field validation rules for associative array input.
Examples:
$ares = new Ares([ 'type' => 'map', 'schema' => [ 'email' => ['type' => 'string', 'required' => true], 'password' => ['type' => 'string', 'required' => true], ], ]); $ares->validate(['email' => 'john.doe@example.com']); // -> false $ares->validate(['email' => 'john.doe@example.com', 'password' => 'j4n3:)']); // -> true
schema (tuple)
The validator expects the schema to define validation rules per input array element. During validation input array elements are expected to be continuous indexed starting from 0 (0, 1, 2, ...).
Examples:
$ares = new Ares([ 'type' => 'tuple', 'schema' => [ ['type' => 'string', 'email' => true], ['type' => 'integer'], ], ]); $ares->validate(['john.doe@example.com']); // -> false $ares->validate([1 => 'john.doe@example.com', 2 => 23]); // -> false $ares->validate(['john.doe@example.com', 23]); // -> true
Internally, all schema
elements of a tuple
are required and cannot be declared optional by schema.
type
The type
rule is mandatory and defines the expected/allowed value type. Supported types are:
boolean
float
integer
numeric
(float
orinteger
)string
map
list
tuple
Examples:
$ares = new Ares(['type' => 'float']); $ares->validate(5); // -> false $ares->validate('John Doe'); // -> false
Read the section Custom Types to find out how to define and reuse your own types.
unknownAllowed
The unknownAllowed
validation rule checks if a map
contains fields that are not defined in the schema.
If set true
, fields that are not defined in the schema are considered valid.
If set false
, fields that are not defined in the schema are considered invalid.
Examples:
$ares = new Ares([ 'type' => 'map', 'schema' => [ 'name' => ['type' => 'string'], ], 'unknownAllowed' => false, ]); $ares->validate(['name' => 'John Doe', 'email' => 'john.doe@example.com']); // -> false $ares->validate(['name' => 'John Doe']); // -> true
url
The url
validation rule checks if a value is a valid URL.
Examples:
$ares = new Ares(['type' => 'string', 'url' => true]); $ares->validate('example'); // -> false $ares->validate('https://example.com'); // -> true
uuid
The uuid
validation rule checks if a value is a valid UUID.
Examples:
$ares = new Ares(['type' => 'string', 'uuid' => true]); $ares->validate('example'); // -> false $ares->validate('609de7b6-0ef5-11ea-8d71-362b9e155667'); // -> true
Custom Types
Basically, a custom type is a user defined schema that is stored in and retrieved from a registry. Here's an example how it works:
use Ares\Ares; use Ares\Schema\TypeRegistry; TypeRegistry::register('GermanDateString', [ 'type' => 'string', ['datetime' => 'd.m.Y', 'message' => 'Invalid date format, try something like "24.02.2019"'], ]); TypeRegistry::register('ListOfHobbies', [ 'type' => 'list', 'schema' => [ 'type' => 'string', 'allowed' => ['Reading', 'Biking'], ], ]); TypeRegistry::register('Student', [ 'type' => 'map', 'schema' => [ 'birthDate' => ['type' => 'GermanDateString'], 'hobbies' => ['type' => 'ListOfHobbies', 'minlength' => 1], ], ]); $schema = ['type' => 'Student']; $ares = new Ares($schema); $ares->validate(['birthDate' => '1998-06-14', 'hobbies' => []]); // false $ares->validate(['birthDate' => '14.06.1998', 'hobbies' => ['Reading']]); // true
Previously registered types are unregistered using TypeRegistry::unregister()
.
All priviously registered types are unregistered at once using TypeRegistry::unregisterAll()
.
It is also possible to define recursive types.
Custom Validation Error Messages
Change the Validation Error Message of a single Rule
The following example shows how validation error messages can be customized:
// validation rule without custom message (default) $ares = new Ares([ 'type' => 'integer', ]); // validation rule with custom message $ares = new Ares([ ['type' => 'integer', 'message' => 'Pleaser provide an integer value'] ]);
Just wrap your rule (key-value) into an array and add a 'message'
key.
Localization of Validation Error Messages
All built-in validation rules use the Ares\Error\ErrorMessageRendererInterface
to render the messages.
If not specified, an instance of Ares\Error\ErrorMessageRenderer
is created and passed to the validation process.
If necessary, a custom error message renderer can be passed to the validator:
use Ares\Ares; use Ares\Validation\Error\ErrorMessageRendererInterface; class MyErrorMessageRenderer implements ErrorMessageRendererInterface { // ... } // ... $ares = new Ares($schema); $ares->getValidator()->setErrorMessageRenderer(new MyErrorMessageRenderer()); $valid = $ares->validate($data);
Custom Validation Rules
The following simple example shows how custom validation rules are implemented and integrated:
use Ares\Ares; use Ares\Schema\Type; use Ares\Validation\Context; use Ares\Validation\RuleRegistry; use Ares\Validation\Rule\AbstractRule; class ZipCodeRule extends AbstractRule { public const ID = 'zipcode'; public const ERROR_MESSAGE = 'Invalid ZIP code'; /** * Returns all supported value types. * * @return array */ public function getSupportedTypes(): array { return [ Type::STRING, ]; } /** * Perform the value validation. * * @param mixed $args Validation rule arguments. * @param mixed $data Data being validated. * @param Context $context Validation context. * @return bool */ public function performValidation($args, $data, Context $context): bool { // implement validation ... // add error if the validation fails $context->addError(self::ID, self::ERROR_MESSAGE); // TRUE - skip all following validation rules for the current field // FALSE - run all following validation rules for the current field return false; } } RuleRegistry::register(ZipCodeRule::ID, new ZipCodeRule()); $schema = [ 'type' => 'string', 'zipcode' => true, ]; $ares = new Ares($schema);
Sanitization
This following example shows how to sanitize data:
$schema = [ 'type' => 'map', 'schema' => [ 'name' => ['type' => 'string'], 'age' => ['type' => 'integer'], 'active' => ['type' => 'boolean'], ], ]; $ares = new Ares($schema); $data = [ 'name' => ' John Doe ', 'age' => '23', 'active' => '1', 'hobby' => 'Reading', ]; $sanitizedData = $ares->sanitize($data); // Result: // [ // 'name' => 'John Doe', // 'age' => 23, // 'active' => true, // ]
As shown in the example, by default sanitization makes these adjustments:
- Trim strings
- Convert numeric strings into integer, or string values
- Convert numeric non-empty strings into boolean values
- Removes unknown fields from the input data
Sanitization Options
trimStrings
If set true
(default) sorrounding whitespace will be removed from strings.
If set false
sorrounding whitespace will be preserved.
purgeUnknown
If set true
(default) unknown fields (fields/indices not defined in the schema) will be removed from the input data.
If set false
unknown fields will be preserved.