temkaa/simple-validator

简单的验证器实现

v0.0.5 2024-08-14 20:48 UTC

This package is auto-updated.

Last update: 2024-09-16 09:46:34 UTC


README

一个兼容PSR容器的验证器实现。

安装

composer require temkaa/simple-validator

此包提供以下约束

#[Count]

检查特定值是否与给定计数完全匹配。

#[GreaterThan]

检查特定值是否大于预期。

#[Initialized]

检查对象的特定属性是否已用任何值初始化。

#[Length]

检查特定值是否在指定的长度范围内。

#[LessThan]

检查特定值是否小于预期。

#[Negative]

检查特定值是否为负数(严格小于0)。

#[NotBlank]

检查特定值是否不为空(空数组/空字符串/未初始化)。

#[Positive]

检查特定值是否为正数(严格大于0)。

#[Range]

与Length相同,但针对int和float。

#[Regex]

检查特定值是否与给定的正则表达式匹配。

#[Cascade]

如果你的对象包含其他对象作为属性,你希望对其进行验证或数组|可迭代对象,你可以放置此属性,验证器将根据其自己的约束或对象数组验证此对象。

用法

<?php

declare(strict_types=1);

namespace App;

use Temkaa\SimpleValidator\Constraint\Assert;
use Temkaa\SimpleValidator\Constraint\ViolationInterface;
use Temkaa\SimpleValidator\Constraint\ViolationListInterface;
use Temkaa\SimpleValidator\Validator;

final class Test
{
    #[Assert\Length(minLength: 2, minMessage: 'min message')]
    #[Assert\NotBlank(message: 'message')]
    public string $name;
    
    #[Assert\Count(expected: 1, message: 'message')]
    public array $preferences;
    
    #[Assert\Positive(message: 'message')]
    #[Assert\GreaterThan(threshold: 18, message: 'message', allowEquality: true)]
    public int $age;
    
    #[Assert\Initialized(message: 'message')]
    public string $middleName;
    
    #[Assert\LessThan(threshold: 95.5, message: 'message')]
    public float $weight;
    
    #[Assert\Regex('/any_pattern/', message: 'message')]
    public string $username;

    #[Assert\Cascade]
    public iterable $arrayOfObjects;
    
    public function __construct()
    {
        $testObject = new TestObject();
        $testObject->string = 'string';

        $this->arrayOfObjects = [new TestObject()];
    }
}

final class TestObject
{
    #[Assert\Length(minLength: 2, minMessage: 'min message')]
    public string $string;
}

$validator = new Validator();
/** @var ViolationListInterface<ViolationInterface> $errors */
$errors = $validator->validate(new Test());

// or to perform specific assertions

$validator = new Validator();
/** @var ViolationListInterface<ViolationInterface> $errors */
$errors = $validator->validate(new Test(), new CustomAssertion());
$errors = $validator->validate(new Test(), [new CustomAssertion1(), new CustomAssertion2()]);

编写自定义验证器

<?php

declare(strict_types=1);

namespace App;

use Attribute;
use Temkaa\SimpleValidator\AbstractConstraintValidator;
use Temkaa\SimpleValidator\Constraint\ConstraintInterface;
use Temkaa\SimpleValidator\Constraint\ConstraintValidatorInterface;
use Temkaa\SimpleValidator\Constraint\ViolationInterface;
use Temkaa\SimpleValidator\Constraint\ViolationListInterface;
use Temkaa\SimpleValidator\Model\ValidatedValueInterface;
use Temkaa\SimpleValidator\Validator;

#[Attribute(Attribute::TARGET_CLASS)]
final readonly class Constraint implements ConstraintInterface
{
    public function __construct(
        public string $message,
    ) {
    }

    public function getHandler(): string
    {
        return ConstraintHandler::class;
    }
}

final class ConstraintHandler extends AbstractConstraintValidator
{
    public function validate(ValidatedValueInterface $value, ConstraintInterface $constraint): void
    {
        if (!$constraint instanceof Constraint) {
            throw new UnexpectedTypeException(actualType: $constraint::class, expectedType: Constraint::class);
        }

        if ($value->isInitialized() && $value->getValue()->age !== 18) {
            $this->addViolation(
                new Violation(invalidValue: $value->getValue(), message: $constraint->message, path: $value->getPath()),
            );
        }
    }
}

// OR

final class ConstraintHandler implements ConstraintValidatorInterface
{
    public function getViolations(): ViolationListInterface;

    public function validate(mixed $value, ConstraintInterface $constraint): void;
    
    public function __construct(
        private readonly ViolationListInterface $violationList = new ViolationList(),
    ) {
    }

    public function getViolations(): ViolationListInterface
    {
        return $this->violationList;
    }

    public function validate(ValidatedValueInterface $value, ConstraintInterface $constraint): void
    {
        if (!$constraint instanceof Constraint) {
            throw new UnexpectedTypeException(actualType: $constraint::class, expectedType: Constraint::class);
        }

        if ($value->isInitialized() && $value->getValue()->age !== 18) {
            $this->violationList->add(
                new Violation(invalidValue: $value->getValue(), message: $constraint->message, path: $value->getPath()),
            );
        }
    }
}

#[Constraint(message: 'message')]
final class Test
{
    public int $age = 17;
}

$validator = new Validator();
/** @var ViolationListInterface<ViolationInterface> $errors */
$errors = $validator->validate(new Test());

向验证器类传递 Psr\Container\ContainerInterface 的选项

如果你在框架内部或具有di容器的项目中使用此验证器,可以将此容器传递给 Validator 构造函数。在这种情况下,当验证器为你的自定义约束实例化约束验证器时,它首先会检查目标约束验证器是否存在于容器中。如果不存在,它将尝试搜索你的约束验证器的所有依赖项,如果找不到任何依赖项,则将抛出异常。示例

<?php

declare(strict_types=1);

use Temkaa\SimpleValidator\Validator;

$validator = new Validator($container);

注意

  1. 当将 ValidatedValueInterface 传递到验证器时,如果验证的属性未初始化,则将 null 作为值传递。在这些情况下,建议添加 NotBlankInitialized 验证器与你的自定义验证器一起使用;
  2. 除了 NotBlankInitialized 之外的所有验证器都不会在未初始化的属性上触发验证错误。