Skip to content

Constraints

Each Constraint have the following methods:

  • ->__invoke()
  • ->and()
  • ->guard()
  • ->or()
  • ->xor()
  • ->map()
  • ->asPredicate()
  • ->failWith()

->__invoke()

This method is the one to apply the validation on an input and will return a Validation monad that will contain either the validated data or the error messages.

Let's take a simple example to check if the input of a method is a string:

use Innmind\Validation\Is;

function(mixed $input): string {
    $validate = Is::string();

    return $validate($input)->match(
        static fn(string $value) => $value,
        static fn() => throw new \RuntimeException('Input is not a string');
    );
}
Info

Note that we use the style $validate($input) and not $validate->__invoke($input). This style allows to treat the constraints as if it were native functions.

->and()

This method allows to apply extra validation on an input.

Let's take the example of making sure a string is shorter than 255 characters:

use Innmind\Validation\{
    Is,
    Constraint,
    Failure,
};
use Innmind\Immutable\Validation;

function(mixed $input): string {
    $validate = Is::string()->and(Constraint::of(
        static fn(string $value) => match (true) {
            \strlen($value) < 255 => Validation::success($value),
            default => Validation::fail(Failure::of('String is too long')),
        },
    ));

    return $validate($input)->match(
        static fn(string $value) => $value,
        static fn() => throw new \RuntimeException('Input is not valid');
    );
}

->guard()

This is like ->and() except that the failures of the constraint passed as argument won't be recovered by a call to ->xor().

->or()

This method allows to have an alternate validation in case the first one fails. This is useful for unions types.

Let's take the example where the input needs to be a string or an int:

use Innmind\Validation\Is;

function(mixed $input): string|int {
    $validate = Is::string()->or(Is::int());

    return return $validate($input)->match(
        static fn(string|int $value) => $value,
        static fn() => throw new \RuntimeException('Input is not valid'),
    );
}

->xor()

use Innmind\Validation\Is;

$validate = Is::string()
    ->guard(Is::value('foobar'))
    ->xor(Is::int());
$validate($value)->match(
    static fn(string|int $value) => $value,
    static fn() => throw new \RuntimeException('Input is not valid'),
);

Unlike ->or(), if $value is any string other than foobar this will raise an exception. This is because the failure due to Is::value('foobar') is guarded.

This is the only combination preventing a failure from being recovered. Replacing ->guard() by ->and() or ->xor() by ->or() will work the same way as ->and()->or().

->map()

This method allows to transform the validated value to anything you want.

Let's take the example where a need a string but want to output a Str for ease of use afterwards:

use Innmind\Validation\Is;
use Innmind\Immutable\Str;

function(mixed $input): Str {
    $validate = Is::string()->map(
        static fn(string $value) => Str::of($value),
    );

    return return $validate($input)->match(
        static fn(Str $value) => $value,
        static fn() => throw new \RuntimeException('Input is not valid');
    );
}

->asPredicate()

A Predicate acts as a function that returns a bool.

It's intended to be used with Sequence::keep() or Set::keep().

Here's an example to keep all strings inside a Sequence:

use Innmind\Validation\Is;
use Innmind\Immutable\Sequence;

Sequence::of(1, 'a', null, 'b', new \stdClass, 'c')
    ->keep(Is::string()->asPredicate())
    ->toList(); // returns ['a', 'b', 'c']
Note

There's no need to apply transformations on your constraints when used as predicates as the outputed value is not used.

->failWith()

This method allows to change the failure message.

use Innmind\Validation\Is;

$password = Is::string()->failWith('The password is required');

$password($someInput);

Handling failures

In the examples above we've thrown exceptions in case of errors but you have access to all the failures messages and where they happened.

use Innmind\Validation\{
    Is,
    Failure,
};
use Innmind\Immutable\Sequence;

$validate = Is::shape('id', Is::int())
    ->with('username', Is::string());

$validate($input)->match(
    static fn(array $valid) => $valid,
    static fn(Sequence $failures) => \var_dump(
        $failures
            ->map(static fn(Failure $failure) => [
                $failure->path()->toString(),
                $failure->message(),
            ])
            ->toList(),
    ),
);

In case $input is invalid it will print:

[['$', 'Value is not of type array']]
[
    ['$', 'The key id is missing'],
    ['$', 'The key username is missing'],
]
[
    ['id', 'Value is not of type int'],
    ['username', 'Value is not of type string'],
]