All Projects → yiisoft → validator

yiisoft / validator

Licence: BSD-3-Clause license
Yii validator library

Programming Languages

PHP
23972 projects - #3 most used programming language

Projects that are alternatives of or similar to validator

valite
🔥 Concurrently execute your validators in a simple, practical and light validator engine.
Stars: ✭ 20 (-57.45%)
Mutual labels:  validator
codeowners-validator
The GitHub CODEOWNERS file validator
Stars: ✭ 142 (+202.13%)
Mutual labels:  validator
log
PSR-3 compatible logger
Stars: ✭ 32 (-31.91%)
Mutual labels:  yii3
national-code
Simple implementation of Iranian national code validation
Stars: ✭ 31 (-34.04%)
Mutual labels:  validator
ZUV
ZUgferd validator using Verapdf
Stars: ✭ 22 (-53.19%)
Mutual labels:  validator
Natours
An awesome tour booking web app written in NodeJS, Express, MongoDB 🗽
Stars: ✭ 94 (+100%)
Mutual labels:  validator
hey-validator
Data validator
Stars: ✭ 14 (-70.21%)
Mutual labels:  validator
romans
A Simple PHP Roman Numerals Library
Stars: ✭ 40 (-14.89%)
Mutual labels:  validator
volder
volder is powerful Object schema validation lets you describe your data using a simple and readable schema and transform a value to match the requirements
Stars: ✭ 106 (+125.53%)
Mutual labels:  validator
toi
A TypeScript validation library capable of inferring types
Stars: ✭ 25 (-46.81%)
Mutual labels:  validator
utf8-validator
UTF-8 Validator
Stars: ✭ 18 (-61.7%)
Mutual labels:  validator
python-sshpubkeys
OpenSSH public key parser for Python
Stars: ✭ 85 (+80.85%)
Mutual labels:  validator
vayder
Easy and concise validations for Express routes
Stars: ✭ 26 (-44.68%)
Mutual labels:  validator
simple-validator
Simple Validator is an awesome and easy to use validator for php
Stars: ✭ 73 (+55.32%)
Mutual labels:  validator
mailer
Generic mailer
Stars: ✭ 16 (-65.96%)
Mutual labels:  yii3
Hammer
Simple, reliable FHIR validator
Stars: ✭ 27 (-42.55%)
Mutual labels:  validator
formurai
Lightweight and powerfull library for declarative form validation
Stars: ✭ 49 (+4.26%)
Mutual labels:  validator
yii-cycle
Cycle ORM support for Yii
Stars: ✭ 30 (-36.17%)
Mutual labels:  yii3
max-validator
Advanced validation library for Javascript & React | Inspired by laravel validation
Stars: ✭ 29 (-38.3%)
Mutual labels:  validator
swagger-object-validator
Node-Module to validate your model against a swagger spec and receive in-depth error traces
Stars: ✭ 27 (-42.55%)
Mutual labels:  validator

Yii Validator


The package provides data validation capabilities.

Latest Stable Version Total Downloads Build status Scrutinizer Code Quality Code Coverage Mutation testing badge static analysis type-coverage

Features

  • Could be used with any object.
  • Supports PHP 8 attributes.
  • Skip further validation if an error occurred for the same field.
  • Skip validation of empty value.
  • Error message formatting.
  • Conditional validation.
  • Could pass context to validation rule.
  • Common rules bundled.

Requirements

  • PHP 8.0 or higher.

Installation

The package could be installed with composer:

composer require yiisoft/validator --prefer-dist

General usage

Library could be used in two ways: validating a single value and validating a set of data.

Validating a single value

use Yiisoft\Validator\ValidatorInterface;
use Yiisoft\Validator\Rule\Required;
use Yiisoft\Validator\Rule\Number;
use Yiisoft\Validator\Result;

// Usually obtained from container
$validator = $container->get(ValidatorInterface::class);

$rules = [
    new Required(),
    new Number(min: 10),
    static function ($value): Result {
        $result = new Result();
        if ($value !== 42) {
            $result->addError('Value should be 42.');
            // or
            $result->addError('Value should be {value}.', ['value' => 42]);
        }

        return $result;
    }
];

$result = $validator->validate(41, $rules);
if (!$result->isValid()) {
    foreach ($result->getErrorMessages() as $error) {
        // ...
    }
}

Validating a set of data

use Yiisoft\Validator\DataSetInterface;
use Yiisoft\Validator\Validator;
use Yiisoft\Validator\ValidatorInterface;
use Yiisoft\Validator\Rule\Number;
use Yiisoft\Validator\Result;

final class MoneyTransfer implements DataSetInterface
{
    private $amount;
    
    public function __construct($amount) {
        $this->amount = $amount;
    }
    
    public function getAttributeValue(string $key){
        if (!isset($this->$key)) {
            throw new \InvalidArgumentException("There is no \"$key\" in MoneyTransfer.");
        }
        
        return $this->$key;
    }
}

// Usually obtained from container
$validator = $container->get(ValidatorInterface::class);

$moneyTransfer = new MoneyTransfer(142);
$rules = [    
    'amount' => [
        new Number(asInteger: true, max: 100),
        static function ($value): Result {
            $result = new Result();
            if ($value === 13) {
                $result->addError('Value should not be 13.');
            }
            return $result;
        }
    ],
];
$result = $validator->validate($moneyTransfer, $rules);
if ($result->isValid() === false) {
    foreach ($result->getErrors() as $error) {
        // ...
    }
}

Skipping validation on error

By default, if an error occurred during validation of an attribute, further rules for this attribute are processed. To change this behavior, use skipOnError: true when configuring rules:

use Yiisoft\Validator\Rule\Number;

new Number(asInteger: true, max: 100, skipOnError: true)

Skipping empty values

By default, empty values are validated. That is undesirable if you need to allow not specifying a field. To change this behavior, use skipOnEmpty: true:

use Yiisoft\Validator\Rule\Number;

new Number(asInteger: true, max: 100, skipOnEmpty: true);

What exactly to consider to be empty is vague and can vary depending on a scope of usage.

skipOnEmpty is more like a shortcut, skipOnEmptyCallback argument checks if a given value is empty:

  • If skipOnEmpty is false, Yiisoft\Validator\SkipOnEmptyCallback\SkipNone is used automatically for skipOnEmptyCallback - every value is considered non-empty and validated without skipping (default).
  • If skipOnEmpty is true, Yiisoft\Validator\SkipOnEmptyCallback\SkipOnEmpty is used automatically for skipOnEmptyCallback - only non-empty values (not null, [], or '') are validated.
  • If skipOnEmptyCallback is set, it takes precedence to determine emptiness.

Using first option is usually good for HTML forms. The second one is more suitable for APIs.

The empty values can be also limited to null only:

use Yiisoft\Validator\Rule\Number;
use Yiisoft\Validator\SkipOnEmptyCallback\SkipOnNull;

new Number(asInteger: true, max: 100, skipOnEmptyCallback: new SkipOnNull());

Note that in this case skipOnEmpty will be automatically set to true, so there is no need to do it manually.

For even more customization you can use your own class implementing __invoke() magic method:

use Yiisoft\Validator\Rule\Number;

final class SkipOnZero
{
    public function __invoke($value): bool
    {
        return $value === 0;
    }
}

new Number(asInteger: true, max: 100, skipOnEmptyCallback: new SkipOnZero());

or just a callable:

use Yiisoft\Validator\Rule\Number;

new Number(
    asInteger: true, 
    max: 100, 
    skipOnEmptyCallback: static function (mixed $value): bool {
        return $value === 0;
    }
);

For multiple rules this can be also set more conveniently at validator level:

use Yiisoft\Validator\SimpleRuleHandlerContainer;
use Yiisoft\Validator\Validator;

$validator = new Validator(new SimpleRuleHandlerContainer(), skipOnEmpty: true);
$validator = new Validator(
    new SimpleRuleHandlerContainer(),
    skipOnEmptyCallback: static function (mixed $value): bool {
        return $value === 0;
    }
);

Nested and related data

Basic usage

In many cases there is a need to validate related data in addition to current entity / model. There is a Nested rule for this purpose:

use Yiisoft\Validator\ValidatorInterface;
use Yiisoft\Validator\Rule\HasLength;
use Yiisoft\Validator\Rule\Nested;
use Yiisoft\Validator\Rule\Number;
use Yiisoft\Validator\Rule\Required;

// Usually obtained from container
$validator = $container->get(ValidatorInterface::class);

$data = ['author' => ['name' => 'Alexey', 'age' => '31']];
$rule = new Nested([
    'title' => [new Required()],
    'author' => new Nested([
        'name' => [new HasLength(min: 3)],
        'age' => [new Number(min: 18)],
    )];
]);
$errors = $validator->validate($data, [$rule])->getErrorMessagesIndexedByPath();
Other configuration options

A dot notation can be used as an alternative way of configuration. In this case the example above will be presented as following:

use Yiisoft\Validator\Rule\HasLength;
use Yiisoft\Validator\Rule\Nested;
use Yiisoft\Validator\Rule\Number;
use Yiisoft\Validator\Rule\Required;

$rule = new Nested([
    'title' => [new Required()],
    'author.name' => [new HasLength(min: 3)],
    'author.age' => [new Number(min: 18)],
)];

It's also possible to combine both of these approaches:

use Yiisoft\Validator\Rule\HasLength;
use Yiisoft\Validator\Rule\Nested;
use Yiisoft\Validator\Rule\Number;
use Yiisoft\Validator\Rule\Required;

$data = ['author' => ['name' => 'Alexey', 'age' => '31']];
$rule = new Nested([
    'author' => new Nested([
        'name' => [new HasLength(min: 3)],
        'age' => [new Number(min: 18)],
    )];
]);
Advanced usage

A more complex real-life example is a chart that is made of points. This data is represented as arrays. Nested can be combined with Each rule to validate such similar structures:

use Yiisoft\Validator\ValidatorInterface;
use Yiisoft\Validator\Rule\Count;
use Yiisoft\Validator\Rule\Each;
use Yiisoft\Validator\Rule\Nested;

// Usually obtained from container
$validator = $container->get(ValidatorInterface::class);

$data = [
    'charts' => [
        [
            'points' => [
                ['coordinates' => ['x' => -11, 'y' => 11], 'rgb' => [-1, 256, 0]],
                ['coordinates' => ['x' => -12, 'y' => 12], 'rgb' => [0, -2, 257]]
            ],
        ],
        [
            'points' => [
                ['coordinates' => ['x' => -1, 'y' => 1], 'rgb' => [0, 0, 0]],
                ['coordinates' => ['x' => -2, 'y' => 2], 'rgb' => [255, 255, 255]],
            ],
        ],
        [
            'points' => [
                ['coordinates' => ['x' => -13, 'y' => 13], 'rgb' => [-3, 258, 0]],
                ['coordinates' => ['x' => -14, 'y' => 14], 'rgb' => [0, -4, 259]],
            ],
        ],
    ],
];
$rule = new Nested([
    'charts' => new Each([
        new Nested([
            'points' => new Each([
                new Nested([
                    'coordinates' => new Nested([
                        'x' => [new Number(min: -10, max: 10)],
                        'y' => [new Number(min: -10, max: 10)],
                    ]),
                    'rgb' => new Each([
                        new Count(exactly: 3);
                        new Number(min: 0, max: 255),
                    ]),
                ]),
            ]),
        ]),
    ]),
]);
$errors = $rule->validate($data, [$rule])->getErrorMessagesIndexedByPath();

The contents of the errors will be:

$errors = [
    'charts.0.points.0.coordinates.x' => ['Value must be no less than -10.'],
    // ...
    'charts.0.points.0.rgb.0' => ['Value must be no less than 0. -1 given.'],
    // ...
];
Using shortcut

A shortcut can be used to simplify Nested and Each combinations:

use Yiisoft\Validator\Rule\Count;
use Yiisoft\Validator\Rule\Each;
use Yiisoft\Validator\Rule\Nested;

$rule = new Nested([
    'charts.*.points.*.coordinates.x' => [new Number(min: -10, max: 10)],
    'charts.*.points.*.coordinates.y' => [new Number(min: -10, max: 10)],
    'charts.*.points.*.rgb' => [
        new Count(exactly: 3);
        new Number(min: 0, max: 255),
    ]),
]);

With additional grouping it can also be rewritten like this:

use Yiisoft\Validator\Rule\Count;
use Yiisoft\Validator\Rule\Each;
use Yiisoft\Validator\Rule\Nested;

$rule = new Nested([
    'charts.*.points.*.coordinates' => new Nested([
        'x' => [new Number(min: -10, max: 10)],
        'y' => [new Number(min: -10, max: 10)],
    ]),
    'charts.*.points.*.rgb' => [
        new Count(exactly: 3);
        new Number(min: 0, max: 255),
    ]),
]);

This is less verbose, but the downside of this approach is that you can't additionally configure dynamically generated Nested and Each pairs. If you need to that, please refer to example provided in "Basic usage" section.

Using keys containing separator / shortcut

If a key contains the separator (.), it must be escaped with backslash (\) in rule config in order to work correctly. In the input data escaping is not needed. Here is an example with two nested keys named author.data and name.surname:

use Yiisoft\Validator\Rule\Nested;

$rule = new Nested([
    'author\.data.name\.surname' => [
        new HasLength(min: 3),
    ],
]);
$data = [
    'author.data' => [
        'name.surname' => 'Dmitry',
    ],
];

Note that in case of using multiple nested rules for configuration escaping is still required:

use Yiisoft\Validator\Rule\Nested;

$rule = new Nested([
    'author\.data' => new Nested([
        'name\.surname' => [
            new HasLength(min: 3),
        ],
    ]),
]);
$data = [
    'author.data' => [
        'name.surname' => 'Dmitriy',
    ],
];

The same applies to shortcut:

use Yiisoft\Validator\Rule\Nested;

$rule = new Nested([
    'charts\.list.*.points\*list.*.coordinates\.data.x' => [
        // ...
    ],
    'charts\.list.*.points\*list.*.coordinates\.data.y' => [
        // ...
    ],
    'charts\.list.*.points\*list.*.rgb' => [
        // ...
    ],
]);
$data = [
    'charts.list' => [
        [
            'points*list' => [
                [
                    'coordinates.data' => ['x' => -11, 'y' => 11], 'rgb' => [-1, 256, 0],
                ],
            ],
        ],
    ],
];

Using attributes

Basic usage

Common flow is the same as you would use usual classes:

  1. Declare property
  2. Add rules to it
use Yiisoft\Validator\Rule\Count;
use Yiisoft\Validator\Rule\Each;
use Yiisoft\Validator\Rule\Nested;
use Yiisoft\Validator\Rule\Number;

final class Chart
{
    #[Each([
        new Nested(Point::class),
    ])]
    private array $points;
}

final class Point
{
    #[Nested(Coordinates::class)]
    private $coordinates;
    #[Count(exactly: 3)]
    #[Each([
        new Number(min: 0, max: 255),
    ])]
    private array $rgb;
}

final class Coordinates
{
    #[Number(min: -10, max: 10)]
    private int $x;
    #[Number(min: -10, max: 10)]
    private int $y;
}

Here are some technical details:

  • In case of a flat array Point::$rgb, a property type array needs to be declared.

Pass object directly to validate() method:

use Yiisoft\Validator\ValidatorInterface;

// Usually obtained from container
$validator = $container->get(ValidatorInterface::class);

$coordinates = new Coordinates();
$errors = $validator->validate($coordinates)->getErrorMessagesIndexedByPath();
Traits

Traits are supported too:

use Yiisoft\Validator\Rule\HasLength;

trait TitleTrait
{
    #[HasLength(max: 255)]
    private string $title;
}

final class Post
{
    use TitleTrait;
}
Limitations
Callback rule and callable type

Callback rule is not supported, also you can't use callable type with attributes. Use custom rule instead.

use Attribute;
use Yiisoft\Validator\Exception\UnexpectedRuleException;
use Yiisoft\Validator\Result;
use Yiisoft\Validator\Rule\Number;
use Yiisoft\Validator\RuleHandlerInterface;
use Yiisoft\Validator\RuleInterface;
use Yiisoft\Validator\ValidationContext;

#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
final class ValidateXRule implements RuleInterface
{
    public function __construct()
    {    
    }
}

final class ValidateXRuleHandler implements RuleHandlerInterface
{    
    public function validate(mixed $value, object $rule, ?ValidationContext $context = null): Result
    {
        if (!$rule instanceof ValidateXRule) {
            throw new UnexpectedRuleException(ValidateXRule::class, $rule);
        }
        
        $result = new Result();
        $result->addError('Custom error.');

        return $result;
    }
}

final class Coordinates
{
    #[Number(min: -10, max: 10)]
    #[ValidateXRule()]
    private int $x;
    #[Number(min: -10, max: 10)]
    private int $y;
}
GroupRule

GroupRule is not supported, but it's unnecessary since multiple attributes can be used for one property.

use Yiisoft\Validator\Rule\HasLength;
use Yiisoft\Validator\Rule\Regex;

final class UserData
{
    #[HasLength(min: 2, max: 20)]
    #[Regex('~[a-z_\-]~i')]
    private string $name;    
}
Nested attributes

PHP 8.0 supports attributes, but nested declaration is allowed only in PHP 8.1 and above.

So such attributes as Each, Nested and Composite are not allowed in PHP 8.0.

The following example is not allowed in PHP 8.0:

use Yiisoft\Validator\Rule\Each;
use Yiisoft\Validator\Rule\Number;

final class Color
{
    #[Each([
        new Number(min: 0, max: 255),
    ])]
    private array $values;
}

But you can do this by creating a new composite rule from it.

namespace App\Validator\Rule;

use Attribute;
use Yiisoft\Validator\Rule\Each;
use Yiisoft\Validator\Rule\Composite;

#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
final class RgbRule extends Composite
{
    public function getRules(): array
    {
        return [
            new Each([
                new Number(min: 0, max: 255),
            ]),
        ];
    }
}

And use it after as attribute.

use App\Validator\Rule\RgbRule;

final class Color
{
    #[RgbRule]
    private array $values;
}
Function / method calls

You can't use a function / method call result with attributes. Like with Callback rule and callable, this problem can be overcome with custom rule.

use Attribute;
use Yiisoft\Validator\FormatterInterface;
use Yiisoft\Validator\Result;
use Yiisoft\Validator\Rule;
use Yiisoft\Validator\RuleInterface
use Yiisoft\Validator\Rule\Number;
use Yiisoft\Validator\ValidationContext;

final class CustomFormatter implements FormatterInterface
{
    public function format(string $message, array $parameters = []): string
    {
        // More complex logic
        // ...
        return $message;
    }
}

#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
final class ValidateXRule implements RuleInterface
{
    public function __construct(
        private $value,
    ) {
    }
    
    public function getValue()
    {
        return $this->value;
    }
}

final class Coordinates
{
    #[Number(min: -10, max: 10)]
    #[ValidateXRule()]
    private int $x;
    #[Number(min: -10, max: 10)]
    private int $y;
}
Passing instances

If you have PHP >= 8.1, you can utilize passing instances in attributes' scope. Otherwise, again fallback to custom rules approach described above.

use Yiisoft\Validator\Rule\HasLength;

final class Post
{
    #[HasLength(max: 255, formatter: new CustomFormatter())]
    private string $title;
}

Conditional validation

In some cases there is a need to apply rule conditionally. It could be performed by using when():

use Yiisoft\Validator\Rule\Number;

new Number(
    when: static function ($value, DataSetInterface $dataSet) {
        return $dataSet->getAttributeValue('country') === Country::USA;
    },
    asInteger: true, 
    min: 100
);

If callable returns true rule is applied, when the value returned is false, rule is skipped.

Validation rule handlers

Creating your own validation rule handlers

Basic usage

To create your own validation rule handler you should implement RuleHandlerInterface:

namespace MyVendor\Rules;

use Yiisoft\Validator\DataSetInterface;
use Yiisoft\Validator\Exception\UnexpectedRuleException;use Yiisoft\Validator\FormatterInterface;use Yiisoft\Validator\Result;
use Yiisoft\Validator\RuleHandlerInterface;use Yiisoft\Validator\RuleInterface;

final class PiHandler implements RuleHandlerInterface
{
    use FormatMessageTrait;
    
    private FormatterInterface $formatter;
    
    public function __construct(
        ?FormatterInterface $formatter = null,
    ) {
        $this->formatter = $this->createFormatter();
    }
    
    public function validate(mixed $value, object $rule, ?ValidationContext $context = null): Result
    {
        if (!$rule instanceof Pi) {
            throw new UnexpectedRuleException(Pi::class, $rule);
        }
        
        $result = new Result();
        $equal = \abs($value - M_PI) < PHP_FLOAT_EPSILON;

        if (!$equal) {
            $result->addError($this->formatter->format('Value is not PI.'));
        }

        return $result;
    }
    
    private function createFormatter(): FormatterInterface
    {
        // More complex logic
        // ...
        return CustomFormatter();
    }
}

Note that third argument in validate() is an instance of ValidationContext so you can use it if you need whole data set context. For example, implementation might be the following if you need to validate "company" property only if "hasCompany" is true:

namespace MyVendor\Rules;

use Yiisoft\Validator\DataSetInterface;
use Yiisoft\Validator\Result;
use Yiisoft\Validator\Rule;
use Yiisoft\Validator\ValidationContext;

final class CompanyNameHandler implements Rule\RuleHandlerInterface
{
    use FormatMessageTrait;
    
    private FormatterInterface $formatter;

    public function validate(mixed $value, object $rule, ?ValidationContext $context = null): Result
    {
        if (!$rule instanceof CompanyName) {
            throw new UnexpectedRuleException(CompanyName::class, $rule);
        }
        
        $result = new Result();
        $dataSet = $context->getDataSet();
        $hasCompany = $dataSet !== null && $dataSet->getAttributeValue('hasCompany') === true;

        if ($hasCompany && $this->isCompanyNameValid($value) === false) {
            $result->addError('Company name is not valid.');
        }

        return $result;
    }

    private function isCompanyNameValid(string $value): bool
    {
        // check company name    
    }
}

Note: Do not call handler's validate() method directly. It must be used via Validator only.

Resolving rule handler dependencies

Basically, you can use SimpleRuleHandlerResolver to resolve rule handler. In case you need extra dependencies, this can be done by ContainerRuleHandlerResolver.

That would work with the following implementation:

final class NoLessThanExistingBidRuleHandler implements RuleHandlerInterface
{
    use FormatMessageTrait;
    
    private FormatterInterface $formatter;

    public function __construct(    
        private ConnectionInterface $connection,        
        ?FormatterInterface $formatter = null
    ) {
        $this->formatter = $formatter ?? new Formatter();
    }
    }
    
    public function validate(mixed $value, object $rule, ?ValidationContext $context): Result
    {
        $result = new Result();
        
        $currentMax = $connection->query('SELECT MAX(price) FROM bid')->scalar();
        if ($value <= $currentMax) {
            $result->addError($this->formatter->format('There is a higher bid of {bid}.', ['bid' => $currentMax]));
        }

        return $result;
    }
}

$ruleHandlerContainer = new ContainerRuleHandlerResolver(new MyContainer());
$ruleHandler = $ruleHandlerContainer->resolve(NoLessThanExistingBidRuleHandler::class);

MyContainer is a container for resolving dependencies and must be an instance of Psr\Container\ContainerInterface. Yii Dependency Injection implementation also can be used.

Using Yii config
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;
use Yiisoft\Validator\RuleHandlerResolverInterface;
use Yiisoft\Validator\RuleHandlerContainer;

// Need to be defined in common.php
$config = [
    RuleHandlerResolverInterface::class => RuleHandlerContainer::class,
];

$containerConfig = ContainerConfig::create()->withDefinitions($config); 
$container = new Container($containerConfig);
$ruleHandlerResolver = $container->get(RuleHandlerResolverInterface::class);        
$ruleHandler = $ruleHandlerResolver->resolve(PiHandler::class);

Using common arguments for multiple rules of the same type

Because concrete rules' implementations (Number, etc.) are marked as final, you can not extend them to set up common arguments. For this and more complex cases use composition instead of inheritance:

use Yiisoft\Validator\RuleInterface;

final class Coordinate implements RuleInterface
{
    private Number $baseRule;
    
    public function __construct() 
    {
        $this->baseRule = new Number(min: -10, max: 10);
    }        

    public function validate(mixed $value, ?ValidationContext $context = null): Result
    {
        return $this->baseRule->validate($value, $context);
    }
}

Grouping multiple validation rules

To reuse multiple validation rules it is advised to group rules like the following:

use Yiisoft\Validator\Rule\HasLength;
use Yiisoft\Validator\Rule\Regex;
use \Yiisoft\Validator\Rule\Composite;

final class UsernameRule extends Composite
{
    public function getRules(): array
    {
        return [
            new HasLength(min: 2, max: 20),
            new Regex('~[a-z_\-]~i'),
        ];
    }
}

Then it could be used like the following:

use Yiisoft\Validator\Validator;
use Yiisoft\Validator\ValidatorInterface;
use Yiisoft\Validator\Rule\Email;

// Usually obtained from container
$validator = $container->get(ValidatorInterface::class);

$rules = [
    'username' => new UsernameRule(),
    'email' => [new Email()],
];
$result = $validator->validate($user, $rules);

if ($result->isValid() === false) {
    foreach ($result->getErrors() as $error) {
        // ...
    }
}

Testing

Unit testing

The package is tested with PHPUnit. To run tests:

./vendor/bin/phpunit

Mutation testing

The package tests are checked with Infection mutation framework with Infection Static Analysis Plugin. To run it:

./vendor/bin/roave-infection-static-analysis-plugin

Static analysis

The code is statically analyzed with Psalm. To run static analysis:

./vendor/bin/psalm

License

The Yii Validator is free software. It is released under the terms of the BSD License. Please see LICENSE for more information.

Maintained by Yii Software.

Support the project

Open Collective

Follow updates

Official website Twitter Telegram Facebook Slack

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].