Spiral provides a validation component that allows you to validate data using the spiral/validator package. This is a simple, lightweight validator that provides an array-based Domain Specific Language (DSL) to construct complex validation chains.
See more
Read more about validation in the Validation section.
To install the component run the following command:
composer require spiral/validator
To enable the component, you just need to add Spiral\Validator\Bootloader\ValidatorBootloader
to the bootloaders
list, which is located in the class of your application.
public function defineBootloaders(): array
{
return [
// ...
\Spiral\Validator\Bootloader\ValidatorBootloader::class,
// ...
];
}
Read more about bootloaders in the Framework — Bootloaders section.
The configuration file for this component should be located at app/config/validator.php
. Here you can register all
required validation checkers, conditions and aliases required for your application.
use Spiral\Validator;
return [
// Checkers are resolved using container and provide the ability to isolate some validation rules
// under common name and class. You can register new checkers at any moment without any
// performance issues.
'checkers' => [
'type' => Validator\Checker\TypeChecker::class,
'number' => Validator\Checker\NumberChecker::class,
'mixed' => Validator\Checker\MixedChecker::class,
'address' => Validator\Checker\AddressChecker::class,
'string' => Validator\Checker\StringChecker::class,
'file' => Validator\Checker\FileChecker::class,
'image' => Validator\Checker\ImageChecker::class,
'datetime' => Validator\Checker\DatetimeChecker::class,
'entity' => Validator\Checker\EntityChecker::class,
'array' => Validator\Checker\ArrayChecker::class,
],
// Enable/disable validation conditions
'conditions' => [
'absent' => Validator\Condition\AbsentCondition::class,
'present' => Validator\Condition\PresentCondition::class,
'anyOf' => Validator\Condition\AnyOfCondition::class,
'noneOf' => Validator\Condition\NoneOfCondition::class,
'withAny' => Validator\Condition\WithAnyCondition::class,
'withoutAny' => Validator\Condition\WithoutAnyCondition::class,
'withAll' => Validator\Condition\WithAllCondition::class,
'withoutAll' => Validator\Condition\WithoutAllCondition::class,
],
// Aliases are only used to simplify developer life.
'aliases' => [
'notEmpty' => 'type::notEmpty',
'notNull' => 'type::notNull',
'required' => 'type::notEmpty',
'datetime' => 'type::datetime',
'timezone' => 'type::timezone',
'bool' => 'type::boolean',
'boolean' => 'type::boolean',
'arrayOf' => 'array::of',
'cardNumber' => 'mixed::cardNumber',
'regexp' => 'string::regexp',
'email' => 'address::email',
'url' => 'address::url',
'file' => 'file::exists',
'uploaded' => 'file::uploaded',
'filesize' => 'file::size',
'image' => 'image::valid',
'array' => 'is_array',
'callable' => 'is_callable',
'double' => 'is_double',
'float' => 'is_float',
'int' => 'is_int',
'integer' => 'is_integer',
'numeric' => 'is_numeric',
'long' => 'is_long',
'null' => 'is_null',
'object' => 'is_object',
'real' => 'is_real',
'resource' => 'is_resource',
'scalar' => 'is_scalar',
'string' => 'is_string',
'match' => 'mixed::match',
]
];
When the validation component is enabled in your application, it will register itself with
the \Spiral\Validator\FilterDefinition
class as validation name and be available for use with Spiral validation
component.
You can use the Spiral\Validator\ValidatorInterface
interface to access the validator and perform validation tasks.
Alternatively, you can use the Spiral\Validation\ValidationProviderInterface
interface to access the validator by its
class name.
use Spiral\Http\Request\InputManager;
use Spiral\Validation\ValidationProviderInterface;
class UserController
{
public function create(InputManager $input, ValidationProviderInterface $provider)
{
$validator = $provider->getValidation(\Spiral\Validator\FilterDefinition::class)
->validate(...);
}
}
The spiral/filters
component is a tool for validating HTTP request data in Spiral. It allows you to create
a "Filter" object, which defines the required data that should be extracted from the request object and mapped into the
filter object's properties.
See more
Read more about filters in the Filters — Filter object section.
One way to implement the request fields mapping is through the use of PHP attributes. This allows you to specify which request field should be mapped to each filter property.
Here is an example of filter object with attributes:
namespace App\Filter;
use Spiral\Filters\Attribute\Input\Post;
use Spiral\Filters\Model\Filter;
use Spiral\Filters\Model\FilterDefinitionInterface;
use Spiral\Filters\Model\HasFilterDefinition;
use Spiral\Validator\FilterDefinition;
use Spiral\Validator\Attribute\Input\File;
final class CreatePostFilter extends Filter implements HasFilterDefinition
{
#[Post]
public string $title;
#[Post]
public string $slug;
#[Post]
public int $sort;
#[File]
public UploadedFile $image;
public function filterDefinition(): FilterDefinitionInterface
{
return new FilterDefinition([
'title' => ['required', ['string::length', 5]]
'slug' => ['required', ['string::length', 5]]
'sort' => ['required', 'integer']
'image' => ['required', 'image']
]);
}
}
By implementing the Spiral\Filters\Model\HasFilterDefinition
interface you can specify the validation rules that
should be applied to the data contained in the filter object. The Validation component will then use these rules to
validate the data when the filter object is used.
If you prefer to configure fields mapping using arrays, you can define fields mapping in a filterDefinition
method.
namespace App\Filter;
use Spiral\Filters\Model\Filter;
use Spiral\Filters\Model\FilterDefinitionInterface;
use Spiral\Filters\Model\HasFilterDefinition;
use Spiral\Validator\FilterDefinition;
final class CreatePostFilter extends Filter implements HasFilterDefinition
{
public function filterDefinition(): FilterDefinitionInterface
{
return new FilterDefinition([
'title' => ['required', ['string::length', 5]]
'slug' => ['required', ['string::length', 5]]
'sort' => ['required', 'integer']
'image' => ['required', 'image']
], [
'title' => 'title',
'slug' => 'slug',
'sort' => 'sort',
'image' => 'symfony-file:image',
]);
}
}
The default Spiral Validator accepts validation rules in form of nested array
. The key is the name
of the property
to be validated, where the value is an array of rules
to be applied to the value sequentially:
$validator = $validation->validate(
['key' => null],
[
'key' => [
'notEmpty', // key must not be empty
'string' // must be string
]
]
);
if (!$validator->isValid()) {
dump($validator->getErrors());
}
The rule, in this case, is the name of the checker method or any available PHP function, which can accept value
as the
first argument.
For example, we can use is_numeric
directly inside your rule:
$validator = $validation->validate(
['key' => null],
[
'key' => [
'notEmpty', // key must not be empty
'is_numeric' // must be numeric
]
]
);
In many cases, you would need to declare additional rule parameters, conditions, or custom error messages. To achieve
that, wrap the rule declaration into an array ([]
).
$validator = $validation->validate(
['key' => null],
[
'key' => [
['notEmpty'], // key must not be empty
['is_numeric'] // must be numeric
]
]
);
Note
You can omit the[]
if the rule does not need any parameters.
You can split your rule name using ::
prefix, where the first part is the checker name and the second is the method
name:
Let's get Spiral\Validator\Checker\FileChecker
checker, for example:
final class FileChecker extends AbstractChecker
{
// ...
public function exists(mixed $file): bool // -> file::exists rule
{
return // check if the given file exists;
}
public function uploaded(mixed $file): bool // -> file::uploaded rule
{
return // check if the given file uploaded;
}
public function size(mixed $file, int $size): bool // -> file::size rule
{
return // check the given file size;
}
}
Register it in app/config/validator.php
config file:
use Spiral\Validator;
return [
'checkers' => [
'file' => Validator\Checker\FileChecker::class,
],
// Register aliases if you need to simplify developer life.
'aliases' => [
'file' => 'file::exists',
'uploaded' => 'file::uploaded',
'filesize' => 'file::size',
]
];
And use validation rules to validate a file:
$validator = $validation->validate(
['file' => null],
[
'file' => [
'file::uploaded', // you can use alias 'uploaded'
['file::size', 1024] // FileChecker::size($file, 1024)
]
]
);
All the values listed in rule array will be passed as rule arguments. For example, to check value using in_array
:
$validator = $validation->validate(
['name' => 'f'],
[
'name' => [
'notEmpty',
['in_array', ['a', 'b', 'c'], true] // in_array($value, ['a', 'b', 'c'], true)
]
]
);
To specify regexp pattern:
$validator = $validation->validate(
['name' => 'b'],
[
'name' => [
'notEmpty',
['regexp', '/^a+$/'] // aaa...
]
]
);
Validator will render default error message for any custom rule, to set custom error message set the rule attribute:
$validator = $validation->validate(
['file' => 'b'],
[
'file' => [
'notEmpty',
['regexp', '/^a+$/', 'error' => 'Invalid pattern, "a+" wanted.'] // aaa...
]
]
);
Note
You can assign custom error messages to any rule.
Custom error messages will be automatically translated.
// app/locale/ru/messages.php
return [
'This value is required.' => 'Значение не должно быть пустым.',
];
$translator->setLocale('ru');
$validator = $validation->validate(
['key' => null],
[
'key' => [
['notEmpty', 'error' => 'This value is required.'] // Will return ['key' => 'Значение не должно быть пустым.']
]
]
);
In some cases the rule must be activated only based on some external condition, use rule attribute if
for this
purpose:
$validator = $validation->validate(
[
'password' => '',
'confirmPassword' => ''
],
[
'password' => [
['notEmpty']
],
'confirmPassword' => [
['notEmpty', 'if' => ['withAll' => ['password']]]
]
]
);
Note
In the example, the required error onconfirmPassword
will show ifpassword
is not empty.
You can use multiple conditions or combine them with complex rules:
$validator = $validation->validate(
[
'password' => 'abc',
'confirmPassword' => 'cde'
],
[
'password' => [
['notEmpty']
],
'confirmPassword' => [
['notEmpty', 'if' => ['withAll' => ['password']]],
['match', 'password', 'error' => 'Passwords do not match.']
]
]
);
There are two composition conditions: anyOf
and noneOf
, they contain nested conditions:
$validator = $validation->validate(
[
'password' => 'abc',
'confirmPassword' => 'cde'
],
[
'password' => [
['notEmpty']
],
'confirmPassword' => [
['notEmpty', 'if' => ['anyOf' => ['withAll' => ['password'], 'withoutAll' => ['otherField']]]],
[
'match',
'password',
'error' => 'Passwords do not match.',
'if' => ['noneOf' => ['some condition', 'another condition']]
]
]
]
);
Following conditions available for the usage:
Name | Options | Description |
---|---|---|
withAny | array | When at least one field is not empty. |
withoutAny | array | When at least one field is empty. |
withAll | array | When all the fields are not empty. |
withoutAll | array | When all the fields are empty. |
present | array | When all the fields are presented in the request. |
absent | array | When all the fields are absent in the request. |
noneOf | array | When none of the nested conditions is met. |
anyOf | array | When any of the nested conditions is met. |
Note
You can create your conditions usingSpiral\Validator\ConditionInterface
.
The following validation rules are available.
Note
You can create your own validation rules usingSpiral\Validator\AbstractChecker
orSpiral\Validator\CheckerInterface
.
The most used rule-set is available thought the set of shortcuts:
Alias | Rule |
---|---|
notEmpty | type::notEmpty |
required | type::notEmpty |
datetime | datetime::valid |
timezone | datetime::timezone |
bool | type::boolean |
boolean | type::boolean |
arrayOf | array::of, |
cardNumber | mixed::cardNumber |
regexp | string::regexp |
address::email | |
url | address::url |
file | file::exists |
uploaded | file::uploaded |
filesize | file::size |
image | image::valid |
array | is_array |
callable | is_callable |
double | is_double |
float | is_float |
int | is_int |
integer | is_integer |
numeric | is_numeric |
long | is_long |
null | is_null |
object | is_object |
real | is_real |
resource | is_resource |
scalar | is_scalar |
string | is_string |
match | mixed::match |
Note
prefixtype::
Rule | Parameters | Description |
---|---|---|
notEmpty | asString:bool - true | The value should not be empty (same as !empty ). |
notNull | --- | The value should not be null. |
boolean | strict:bool - false | The value has to be boolean or integer[0,1]. |
Note
All of the rules of this checker are available without the prefix.
Rule | Parameters | Description |
---|---|---|
notEmpty | asString:bool - true | The value should not be empty. |
Examples:
namespace App\Filter;
use Spiral\Filters\Attribute\Input\Post;
use Spiral\Filters\Model\Filter;
use Spiral\Filters\Model\FilterDefinitionInterface;
use Spiral\Filters\Model\HasFilterDefinition;
use Spiral\Validator\FilterDefinition;
class MyFilter extends Filter implements HasFilterDefinition
{
#[Post]
public string $name;
public function filterDefinition(): FilterDefinitionInterface
{
return new FilterDefinition([
'name' => ['required', 'my::abc']
]);
}
}
Note
prefixmixed::
Rule | Parameters | Description |
---|---|---|
cardNumber | --- | Checks the credit card passed by Luhn algorithm. |
match | field:string, strict:bool - false | Checks if the value matches the value from another field. |
accepted | --- | Checks if the value accepted (contains one of the following values 'yes', true, 1, '1','on' ) |
declined | --- | Checks if the value not accepted (contains one of the following values 'no', false, 0, '0','off' ) |
Note
cardNumber
andmatch
rules of this checker are available without prefix.
Note
prefixaddress::
Rule | Parameters | Description |
---|---|---|
--- | Checks if the email is valid. | |
url | schemas:?array - null, defaultSchema:?string - null | Checks if the URL is valid. |
uri | --- | Checks if the URI is valid. |
Note
url
rules are available withoutaddress
prefix via aliases, foruri
useaddress::uri
.
Note
prefixnumber::
Rule | Parameters | Description |
---|---|---|
range | begin:float, end:float | Checks if the number is in the specified range. |
higher | limit:float | Checks if the value is bigger or equal to the specified one. |
lower | limit:float | Checks if the value is smaller or equal to the specified one. |
Note
prefixstring::
Rule | Parameters | Description |
---|---|---|
regexp | expression:string | Checks the string using regexp. |
shorter | length:int | Checks if the string length is shorter or equal to the specified value. |
longer | length:int | Checks if the string length is longer or equal to the specified value. |
length | length:int | Checks if the string length is equal to the specified value. |
range | left:int, right:int | Checks if the string length fits within the specified range. |
empty | string | Checks if the string is empty. |
notEmpty | string | Checks if the string isn't empty. |
Examples:
namespace App\Filter;
use Spiral\Filters\Attribute\Input\Post;
use Spiral\Filters\Model\Filter;
use Spiral\Filters\Model\FilterDefinitionInterface;
use Spiral\Filters\Model\HasFilterDefinition;
use Spiral\Validator\FilterDefinition;
class MyFilter extends Filter implements HasFilterDefinition
{
#[Post]
public string $name;
public function filterDefinition(): FilterDefinitionInterface
{
return new FilterDefinition([
'name' => ['required', ['string::length', 5]]
]);
}
}
prefix
array::
Rule | Parameters | Description |
---|---|---|
count | length:int | Checks if an array has a size equal to the given value. |
shorter | length:int | Checks if an array has a size smaller than or equal to the given value. |
longer | length:int | Checks if an array has a size bigger than or equal to the given value. |
range | min:int, max:int | Checks if an array has a size between the given min and max. |
expectedValues | array | Checks if an array values are included in the given list of values. |
isList | - | Checks if an array is list. |
isAssoc | - | Checks if an array is associative. |
Examples:
namespace App\Filter;
use Spiral\Filters\Attribute\Input\Post;
use Spiral\Filters\Model\Filter;
use Spiral\Filters\Model\FilterDefinitionInterface;
use Spiral\Filters\Model\HasFilterDefinition;
use Spiral\Validator\FilterDefinition;
class MyRequest extends Filter implements HasFilterDefinition
{
#[Post]
public array $tags;
#[Post]
public array $person = [];
#[Post]
public array $settings = [];
public function filterDefinition(): FilterDefinitionInterface
{
return new FilterDefinition([
'tags' => [
['notEmpty'],
['array::range', 1, 10]
],
'person' => [ // <==== Request: ['ugly', 'old', 'long_hair']
['array::isList'],
['array::shorter', 3],
['array::expectedValues', ['good', 'bad', 'ugly', 'long_hair', 'young', 'old', 'strong']]
],
'settings' => [ // <====== Request ['setting1' => 'value', 'setting2' => 'value']
['array::isAssoc'],
]
]);
}
}
Note
prefixfile::
File checker fully supports the filename provided in a string form or using UploadedFileInterface
(PSR-7).
Rule | Parameters | Description |
---|---|---|
exists | --- | Checks if the file exist. |
uploaded | --- | Checks if the file was uploaded. |
size | size:int | Checks if the file size is smaller than the specified value in KB. |
extension | extensions:array | Checks if the file extension in whitelist. The client name of the uploaded file will be used! |
Note
prefiximage::
The image checker extends the file checker and fully supports its features.
Rule | Parameters | Description |
---|---|---|
type | types:array | Checks if the image is within the list of the allowed image types. |
valid | --- | A shortcut to check if the image has an allowed type (JPEG, PNG, and GIF are allowed). |
smaller | width:int, height:int | Checks if the image is smaller than the specified shape (height check if optional). |
bigger | width:int, height:int | Checks if the image is bigger than the specified shape (height check is optional). |
Note
prefixdatetime::
This checker can apply now
value in the constructor
Rule | Parameters | Description |
---|---|---|
future | orNow:bool - false, useMicroSeconds:bool - false |
The value has to be a date in the future. |
past | orNow:bool - false, useMicroSeconds:bool - false |
The value has to be a date in the past. |
format | format:string | The value should match the specified date format. |
before | field:string, orEquals:bool - false, useMicroSeconds:bool - false |
The value should come before the given threshold. |
after | field:string, orEquals:bool - false, useMicroSeconds:bool - false |
The value should come after the given threshold. |
valid | --- | The value has to be a valid datetime definition including numeric timestamp. |
timezone | --- | The value has to be a valid timezone. |
Note
SettinguseMicroSeconds
into true allows to check datetime with microseconds.
Be careful, twonew \DateTime('now')
objects will 99% have different microseconds values so they will never be equal.
It is possible to create application-specific validation rules via custom checker implementation.
namespace App\Security;
use Cycle\Database\Database;
use Spiral\Validator\AbstractChecker;
class DBChecker extends AbstractChecker
{
public const MESSAGES = [
// Method => Error message
'user' => 'No such user.'
];
public function user(int $id): bool
{
return $this->db->table('users')->select()->where('id', $id)->count() === 1;
}
}
Note
Use prebuild constantMESSAGES
to define a custom error template.
To activate checker, register it in ValidationBootloader
:
namespace App\Bootloader;
use App\Security\DBChecker;
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Validator\Bootloader\ValidatorBootloader;
class CheckerBootloader extends Bootloader
{
public function boot(ValidationBootloader $validation): void
{
// Register custom checker
$validation->addChecker('db', DBChecker::class);
// Register alias for checker
$validation->addAlias('db_user', 'db::user');
}
}
You can use the validation now via db::user
(or alias db_user
) rule.