__ _ _ __ ___ ___
/ _` | '__/ _ \/ __|
| (_| | | | __/\__ \
\__,_|_| \___||___/
Ares is a lightweight standalone validation library.
- Installation
- Basic Usage
- Validation Errors
- Validation Options
- Validation Rules
- Custom Types
- Custom Validation Error Messages
- Custom Validation Rules
- Sanitization
Install the library via composer:
composer require bus-factor/ares
<?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();
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 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,
]
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
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
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
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
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.
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.
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
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
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
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.
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
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.
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
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.
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
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.
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
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.
The schema
rule is mandatory when using type list
, map
, or tuple
.
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
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
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.
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.
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
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
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
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.
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.
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);
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);
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
If set true
(default) sorrounding whitespace will be removed from strings.
If set false
sorrounding whitespace will be preserved.
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.