Skip to content

Validate

Testing Is Documentation

tests/Validate/ValidatorTest.php

构造器函数原型

php
public function __construct(array $data = [], array $rules = [], array $names = [], array $messages = []);
  • $data 验证的数据
  • $rules 验证规则
  • $names 校验名字隐射
  • $messages 校验失败消息

可以通过构造器传递参数,也可以通过 name,message 等方法传入。

Uses

php
<?php

use Leevel\Di\Container;
use Leevel\Kernel\Utils\Api;
use Leevel\Validate\IValidator;
use Leevel\Validate\Validate;
use Leevel\Validate\Validator;
use Leevel\Validate\ValidatorException;
use PHPUnit\Framework\Attributes\DataProvider;

验证器基本使用方法

可以通过 success 判断是否通过验证,error 返回错误消息。

php
public function testBaseUse(): void
{
    $validate = new Validator(
        [
            'name' => '小牛哥',
        ],
        [
            'name' => 'required|max_length:10',
        ],
        [
            'name' => '用户名',
        ]
    );

    $this->assertInstanceof(IValidator::class, $validate);

    $rule = <<<'eot'
        {
            "name": [
                [
                    "required",
                    []
                ],
                [
                    "max_length",
                    [
                        "10"
                    ]
                ]
            ]
        }
        eot;

    self::assertTrue($validate->success());
    self::assertFalse($validate->fail());
    self::assertSame([], $validate->error());
    self::assertSame([], $validate->getMessage());
    self::assertSame(['name' => '小牛哥'], $validate->getData());

    self::assertSame(
        $rule,
        $this->varJson(
            $validate->getRule()
        )
    );
}

验证器规则支持数组写法

可以通过 success 判断是否通过验证,error 返回错误消息。

php
public function testRuleIsArray(): void
{
    $validate = new Validator(
        [
            'name' => '小牛哥',
        ],
        [
            'name' => ['required', 'max_length:10'],
        ],
        [
            'name' => '用户名',
        ]
    );

    $this->assertInstanceof(IValidator::class, $validate);

    $rule = <<<'eot'
        {
            "name": [
                [
                    "required",
                    []
                ],
                [
                    "max_length",
                    [
                        "10"
                    ]
                ]
            ]
        }
        eot;

    self::assertTrue($validate->success());
    self::assertFalse($validate->fail());
    self::assertSame([], $validate->error());
    self::assertSame([], $validate->getMessage());
    self::assertSame(['name' => '小牛哥'], $validate->getData());

    self::assertSame(
        $rule,
        $this->varJson(
            $validate->getRule()
        )
    );
}

验证器规则支持数组写法:每一项都是一个数组(第一个是规则,第一个是参数非数组兼容为数组)

可以通过 success 判断是否通过验证,error 返回错误消息。

php
public function testRuleIsArray2(): void
{
    $validate = new Validator(
        [
            'name' => '小牛哥',
        ],
        [
            'name' => ['required', ['max_length', 10]],
        ],
        [
            'name' => '用户名',
        ]
    );

    $this->assertInstanceof(IValidator::class, $validate);

    $rule = <<<'eot'
        {
            "name": [
                [
                    "required",
                    []
                ],
                [
                    "max_length",
                    [
                        10
                    ]
                ]
            ]
        }
        eot;

    self::assertTrue($validate->success());
    self::assertFalse($validate->fail());
    self::assertSame([], $validate->error());
    self::assertSame([], $validate->getMessage());
    self::assertSame(['name' => '小牛哥'], $validate->getData());

    self::assertSame(
        $rule,
        $this->varJson(
            $validate->getRule()
        )
    );
}

验证器规则支持数组写法:每一项都是一个数组(第一个是规则,第一个是参数数组用法)

可以通过 success 判断是否通过验证,error 返回错误消息。

php
public function testRuleIsArray3(): void
{
    $validate = new Validator(
        [
            'name' => '小牛哥',
        ],
        [
            'name' => ['required', ['max_length', [10]]],
        ],
        [
            'name' => '用户名',
        ]
    );

    $this->assertInstanceof(IValidator::class, $validate);

    $rule = <<<'eot'
        {
            "name": [
                [
                    "required",
                    []
                ],
                [
                    "max_length",
                    [
                        10
                    ]
                ]
            ]
        }
        eot;

    self::assertTrue($validate->success());
    self::assertFalse($validate->fail());
    self::assertSame([], $validate->error());
    self::assertSame([], $validate->getMessage());
    self::assertSame(['name' => '小牛哥'], $validate->getData());

    self::assertSame(
        $rule,
        $this->varJson(
            $validate->getRule()
        )
    );
}

验证器规则支持数组每一项字符串支持分隔:可以用于实际业务中合并验证规则的需求

可以通过 success 判断是否通过验证,error 返回错误消息。

php
public function testRuleIsArrayStringMixed(): void
{
    $validate = new Validator(
        [
            'name' => '小牛哥',
        ],
        [
            'name' => ['required|chinese|min_length:1', ['max_length', [10]]],
        ],
        [
            'name' => '用户名',
        ]
    );

    $this->assertInstanceof(IValidator::class, $validate);

    $rule = <<<'eot'
        {
            "name": [
                [
                    "required",
                    []
                ],
                [
                    "chinese",
                    []
                ],
                [
                    "min_length",
                    [
                        "1"
                    ]
                ],
                [
                    "max_length",
                    [
                        10
                    ]
                ]
            ]
        }
        eot;

    self::assertTrue($validate->success());
    self::assertFalse($validate->fail());
    self::assertSame([], $validate->error());
    self::assertSame([], $validate->getMessage());
    self::assertSame(['name' => '小牛哥'], $validate->getData());

    self::assertSame(
        $rule,
        $this->varJson(
            $validate->getRule()
        )
    );
}

make 创建验证器

php
public function testMake(): void
{
    $validate = Validator::make(
        [
            'name' => '小牛哥',
        ],
        [
            'name' => 'required|max_length:10',
        ],
        [
            'name' => '用户名',
        ]
    );

    $rule = <<<'eot'
        {
            "name": [
                [
                    "required",
                    []
                ],
                [
                    "max_length",
                    [
                        "10"
                    ]
                ]
            ]
        }
        eot;

    self::assertTrue($validate->success());
    self::assertFalse($validate->fail());
    self::assertSame([], $validate->error());
    self::assertSame([], $validate->getMessage());
    self::assertSame(['name' => '小牛哥'], $validate->getData());

    self::assertSame(
        $rule,
        $this->varJson(
            $validate->getRule()
        )
    );
}

验证器校验错误

php
public function testError(): void
{
    $validate = new Validator(
        [
            'name' => '小牛哥',
        ],
        [
            'name' => 'required|min_length:20',
        ],
        [
            'name' => '用户名',
        ]
    );

    $error = <<<'eot'
        {
            "name": [
                "用户名 不满足最小长度 20"
            ]
        }
        eot;

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );
}

设置校验数据

php
public function testData(): void
{
    $validate = new Validator(
        [
            'name' => '中国',
        ],
        [
            'name' => 'required|min_length:20',
        ],
        [
            'name' => '用户名',
        ]
    );

    $error = <<<'eot'
        {
            "name": [
                "用户名 不满足最小长度 20"
            ]
        }
        eot;

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );

    $validate->data(['name' => '12345678901234567890']);

    self::assertTrue($validate->success());
    self::assertFalse($validate->fail());
}

添加校验数据

php
public function testAddData(): void
{
    $validate = new Validator(
        [
        ],
        [
            'name' => 'required|min_length:20|'.IValidator::OPTIONAL,
        ],
        [
            'name' => '用户名',
        ]
    );

    $error = <<<'eot'
        {
            "name": [
                "用户名 不满足最小长度 20"
            ]
        }
        eot;

    self::assertTrue($validate->success());
    self::assertFalse($validate->fail());

    $validate->addData(['name' => '中国']);

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );
}

设置校验规则

php
public function testRule(): void
{
    $validate = new Validator(
        [
            'name' => '中国',
        ],
        [
        ],
        [
            'name' => '用户名',
        ]
    );

    $error = <<<'eot'
        {
            "name": [
                "用户名 不满足最小长度 20"
            ]
        }
        eot;

    self::assertTrue($validate->success());
    self::assertFalse($validate->fail());

    $validate->rule(['name' => 'required|min_length:20']);

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );

    $validate->rule(['name' => 'required|max_length:20']);

    self::assertTrue($validate->success());
    self::assertFalse($validate->fail());
}

设置校验规则支持条件

第一个闭包条件参数不为空,如果闭包返回 true 则添加改验证规则,否则忽略。

php
public function testRuleIf(): void
{
    $validate = new Validator(
        [
            'name' => '中国',
        ],
        [
        ],
        [
            'name' => '用户名',
        ]
    );

    self::assertTrue($validate->success());
    self::assertFalse($validate->fail());

    $validate->rule(['name' => 'required|min_length:20'], function (array $data) {
        $this->assertSame(['name' => '中国'], $data);

        return false;
    });

    $rule = <<<'eot'
        []
        eot;

    self::assertSame(
        $rule,
        $this->varJson(
            $validate->getRule()
        )
    );

    self::assertTrue($validate->success());
    self::assertFalse($validate->fail());
}

添加校验规则

php
public function testAddRule(): void
{
    $validate = new Validator(
        [
            'name' => '中国',
        ],
        [
        ],
        [
            'name' => '用户名',
        ]
    );

    self::assertTrue($validate->success());
    self::assertFalse($validate->fail());

    $validate->addRule(['name' => 'required|min_length:20']);

    $rule = <<<'eot'
        {
            "name": [
                [
                    "required",
                    []
                ],
                [
                    "min_length",
                    [
                        "20"
                    ]
                ]
            ]
        }
        eot;

    self::assertSame(
        $rule,
        $this->varJson(
            $validate->getRule()
        )
    );

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    $error = <<<'eot'
        {
            "name": [
                "用户名 不满足最小长度 20"
            ]
        }
        eot;

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );
}

添加校验规则支持条件

第一个闭包条件参数不为空,如果闭包返回 true 则添加改验证规则,否则忽略。

php
public function testAddRuleIf(): void
{
    $validate = new Validator(
        [
            'name' => '中国',
        ],
        [
        ],
        [
            'name' => '用户名',
        ]
    );

    self::assertTrue($validate->success());
    self::assertFalse($validate->fail());

    $validate->addRule(['name' => 'required|min_length:20'], function (array $data) {
        $this->assertSame(['name' => '中国'], $data);

        return false;
    });

    $rule = <<<'eot'
        []
        eot;

    self::assertSame(
        $rule,
        $this->varJson(
            $validate->getRule()
        )
    );

    self::assertTrue($validate->success());
    self::assertFalse($validate->fail());
}

设置验证消息

php
public function testMessage(): void
{
    $validate = new Validator(
        [
            'name' => '中国',
        ],
        [
            'name' => 'required|min_length:20',
        ],
        [
            'name' => '用户名',
        ]
    );

    $error = <<<'eot'
        {
            "name": [
                "用户名 不满足最小长度 20"
            ]
        }
        eot;

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );

    $validate->message(['min_length' => '{field} not min {rule}']);

    $error = <<<'eot'
        {
            "name": [
                "用户名 not min 20"
            ]
        }
        eot;

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );
}

添加验证消息

设置规则所有字段的验证消息。

php
public function testAddMessage(): void
{
    $validate = new Validator(
        [
            'name' => '中国',
        ],
        [
            'name' => 'required|min_length:20',
        ],
        [
            'name' => '用户名',
        ]
    );

    $error = <<<'eot'
        {
            "name": [
                "用户名 不满足最小长度 20"
            ]
        }
        eot;

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );

    $validate->addMessage(['min_length' => '{field} foo bar {rule}']);

    $error = <<<'eot'
        {
            "name": [
                "用户名 foo bar 20"
            ]
        }
        eot;

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );
}

添加指定字段验证规则消息

可以单独为某个字段指定验证消息规则,其它字段验证消息保持不变。

php
public function testAddMessageForOneField(): void
{
    $validate = new Validator(
        [
            'name' => '中国',
        ],
        [
            'name' => 'required|min_length:20',
        ],
        [
            'name' => '用户名',
        ]
    );

    $error = <<<'eot'
        {
            "name": [
                "用户名 不满足最小长度 20"
            ]
        }
        eot;

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );

    $validate->addMessage(['name' => ['min_length' => '{field} hello world {rule}']]);

    $error = <<<'eot'
        {
            "name": [
                "用户名 hello world 20"
            ]
        }
        eot;

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );
}

添加指定字段验证规则消息(圆点分隔)

通过圆点 . 分隔开来。

php
public function testAddMessageForOneFieldSeparateByDot(): void
{
    $validate = new Validator(
        [
            'name' => '中国',
        ],
        [
            'name' => 'required|min_length:20',
        ],
        [
            'name' => '用户名',
        ]
    );

    $error = <<<'eot'
        {
            "name": [
                "用户名 不满足最小长度 20"
            ]
        }
        eot;

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );

    $validate->addMessage(['name.min_length' => '{field} hehe {rule}']);

    $error = <<<'eot'
        {
            "name": [
                "用户名 hehe 20"
            ]
        }
        eot;

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );
}

添加指定多层子字段验证规则消息(圆点分隔)

通过圆点 . 分隔开来。

php
public function testSubDataWithSubMessage(): void
{
    $validate = new Validator(
        [
            'name' => ['sub' => ['sub' => '']],
        ],
        [
            'name.sub.sub' => 'required|'.IValidator::MUST,
        ],
        [
            'name' => '歌曲',
        ]
    );

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    $error = <<<'eot'
        {
            "name.sub.sub": [
                "name.sub.sub 不能为空"
            ]
        }
        eot;

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );

    $validate->addMessage(['name.sub.sub' => ['required' => '字段 {field} 不能为空']]);

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    $error = <<<'eot'
        {
            "name.sub.sub": [
                "字段 name.sub.sub 不能为空"
            ]
        }
        eot;

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );
}

添加通配符字段验证规则消息

通过 * 来代表通配符。

php
public function testWildcardSubDataWithSubMessage(): void
{
    $validate = new Validator(
        [
            'name' => ['sub' => ['sub' => '']],
        ],
        [
            'name.sub.sub' => 'required|'.IValidator::MUST,
        ],
        [
            'name' => '歌曲',
        ]
    );

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    $error = <<<'eot'
        {
            "name.sub.sub": [
                "name.sub.sub 不能为空"
            ]
        }
        eot;

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );

    $validate->addMessage(['name*' => ['required' => 'sub {field} must have value']]);

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    $error = <<<'eot'
        {
            "name.sub.sub": [
                "sub name.sub.sub must have value"
            ]
        }
        eot;

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );
}

设置验证字段隐射

php
public function testName(): void
{
    $validate = new Validator(
        [
            'name' => '中国',
        ],
        [
            'name' => 'required|min_length:20',
        ],
        [
            'name' => '用户名',
        ]
    );

    $error = <<<'eot'
        {
            "name": [
                "用户名 不满足最小长度 20"
            ]
        }
        eot;

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());
    self::assertSame(['name' => '用户名'], $validate->getName());

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );

    $validate->name(['name' => 'username']);

    $error = <<<'eot'
        {
            "name": [
                "username 不满足最小长度 20"
            ]
        }
        eot;

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );
}

添加验证字段隐射

php
public function testAddName(): void
{
    $validate = new Validator(
        [
            'name' => '中国',
        ],
        [
            'name' => 'required|min_length:20',
        ],
        [
            'name' => '用户名',
        ]
    );

    $error = <<<'eot'
        {
            "name": [
                "用户名 不满足最小长度 20"
            ]
        }
        eot;

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());
    self::assertSame(['name' => '用户名'], $validate->getName());

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );

    $validate->addName(['name' => 'hello world']);

    $error = <<<'eot'
        {
            "name": [
                "hello world 不满足最小长度 20"
            ]
        }
        eot;

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );
}

验证后回调

无论成功或者失败都会执行回调。

php
public function testAfter(): void
{
    $validate = new Validator(
        [
            'name' => '成都',
        ],
        [
            'name' => 'required|max_length:10',
        ],
        [
            'name' => '地名',
        ]
    );

    $validate->after(function ($v): void {
        $this->assertSame(['name' => '地名'], $v->getName());
    });

    self::assertTrue($validate->success());
    self::assertFalse($validate->fail());
}

自定义扩展验证规则

php
public function testExtend(): void
{
    $validate = new Validator(
        [
            'name' => 1,
        ],
        [
            'name' => 'required|custom_rule:10',
        ],
        [
            'name' => '地名',
        ]
    );

    $validate->extend('custom_rule', static function ($value, array $param, IValidator $validator, string $field): bool {
        if (1 === $value) {
            return true;
        }

        return false;
    });

    self::assertTrue($validate->success());
    self::assertFalse($validate->fail());

    $validate->data(['name' => 0]);

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());
}

直接调用验证规则

php
public function testCall(): void
{
    $validate = new Validator();

    self::assertTrue($validate->minLength('成都', 1));
    self::assertTrue($validate->minLength('成都', 2));
    self::assertFalse($validate->minLength('成都', 3));
    self::assertFalse($validate->alpha('成都'));
    self::assertTrue($validate->alpha('cd'));
}

直接调用自定义验证规则

php
public function testCallCustom(): void
{
    $validate = new Validator();

    $validate->extend('custom_foo_bar', static function (string $field, $value, array $param): bool {
        if ('成都' === $value) {
            return true;
        }

        return false;
    });

    self::assertTrue($validate->customFooBar('成都'));
    self::assertFalse($validate->customFooBar('魂之挽歌'));
}

自定义扩展验证规则(类)

自定义扩展规则可以为一个独立的类,例如下面的例子。

php
namespace Tests\Validate;

class ExtendClassTest1
{
    public function handle($value, array $param, IValidator $validator, string $field): bool
    {
        if (1 === $value) {
            return true;
        }

        return false;
    }

    public function handle2($value, array $param, IValidator $validator, string $field): bool
    {
        if (2 === $value) {
            return true;
        }

        return false;
    }
}

默认情况下,此时自定义类的 handle 方法将作为验证入口。

php
public function testCallExtendClass(): void
{
    $validate = new Validator(
        [
            'name' => 1,
        ],
        [
            'name' => 'custom_foobar',
        ],
        [
            'name' => '地名',
        ]
    );

    $container = new Container();
    $validate->setContainer($container);
    $validate->extend('custom_foobar', ExtendClassTest1::class);
    self::assertTrue($validate->success());
    $validate->data(['name' => 'foo']);
    self::assertFalse($validate->success());
}

自定义扩展验证规则(类),指定验证方法

自定义扩展规则可以为一个独立的类,例如下面的例子。

php
namespace Tests\Validate;

class ExtendClassTest1
{
    public function handle($value, array $param, IValidator $validator, string $field): bool
    {
        if (1 === $value) {
            return true;
        }

        return false;
    }

    public function handle2($value, array $param, IValidator $validator, string $field): bool
    {
        if (2 === $value) {
            return true;
        }

        return false;
    }
}

指定方法情况下,通过 @ 分隔开来,此时自定义类的 handle2 方法将作为验证入口。

php
public function testCallExtendClassWithCustomMethod(): void
{
    $validate = new Validator(
        [
            'name' => 2,
        ],
        [
            'name' => 'custom_foobar',
        ],
        [
            'name' => '地名',
        ]
    );

    $container = new Container();
    $validate->setContainer($container);
    $validate->extend('custom_foobar', ExtendClassTest1::class.'@handle2');
    self::assertTrue($validate->success());
    $validate->data(['name' => 'foo']);
    self::assertFalse($validate->success());
}

验证失败则跳过其它验证规则

只需要在校验规则中加入 SKIP_OTHER 即可。

php
public function testShouldSkipOther(): void
{
    $validate = new Validator(
        [
            'name' => '',
            'value' => '',
        ],
        [
            'name' => 'required|alpha',
            'value' => 'required',
        ],
        [
            'name' => '地名',
            'value' => '值',
        ]
    );

    $error = <<<'eot'
        {
            "name": [
                "地名 不能为空",
                "地名 只能是字母"
            ],
            "value": [
                "值 不能为空"
            ]
        }
        eot;

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());
    self::assertSame(['name' => '地名', 'value' => '值'], $validate->getName());

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );

    $validate->addRule(['name' => 'required|alpha|'.IValidator::SKIP_OTHER]);

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    $error = <<<'eot'
        {
            "name": [
                "地名 不能为空"
            ]
        }
        eot;

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );
}

验证失败则跳过自身其它验证规则

只需要在校验规则中加入 SKIP_SELF 即可,只会跳过当前字段的其他验证规则,而其它字段的验证规则不受影响。

php
public function testShouldSkipSelf(): void
{
    $validate = new Validator(
        [
            'name' => '',
            'value' => '',
        ],
        [
            'name' => 'required|alpha',
            'value' => 'required',
        ],
        [
            'name' => '地名',
            'value' => '值',
        ]
    );

    $error = <<<'eot'
        {
            "name": [
                "地名 不能为空",
                "地名 只能是字母"
            ],
            "value": [
                "值 不能为空"
            ]
        }
        eot;

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());
    self::assertSame(['name' => '地名', 'value' => '值'], $validate->getName());

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );

    $validate->addRule(['name' => 'required|alpha|'.IValidator::SKIP_SELF]);

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    $error = <<<'eot'
        {
            "name": [
                "地名 不能为空"
            ],
            "value": [
                "值 不能为空"
            ]
        }
        eot;

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );
}

值为 null 会跳过可选验证规则

如果校验规则中有 OPTIONAL ,那么字段值为 null 则不会执行验证规则。

php
public function testOptional(): void
{
    $validate = new Validator(
        [
            'name' => null,
        ],
        [
            'name' => 'required|'.IValidator::OPTIONAL,
        ],
        [
            'name' => '地名',
        ]
    );

    self::assertTrue($validate->success());
    self::assertFalse($validate->fail());
    self::assertSame(['name' => '地名'], $validate->getName());
}

值为 null或者空字符串 会跳过可选字符串验证规则

如果校验规则中有 OPTIONAL_STRING ,那么字段值为 null 或者空字符串则不会执行验证规则。

php
public function testOptionalString(): void
{
    $validate = new Validator(
        [
            'name' => null,
        ],
        [
            'name' => 'required|'.IValidator::OPTIONAL_STRING,
        ],
        [
            'name' => '地名',
        ]
    );

    self::assertTrue($validate->success());
    self::assertFalse($validate->fail());
    self::assertSame(['name' => '地名'], $validate->getName());

    $validate = new Validator(
        [
            'name' => '',
        ],
        [
            'name' => 'required|'.IValidator::OPTIONAL_STRING,
        ],
        [
            'name' => '地名',
        ]
    );

    self::assertTrue($validate->success());
    self::assertFalse($validate->fail());
    self::assertSame(['name' => '地名'], $validate->getName());
}

值为 null 默认必须验证

我们加入 MUST 或者默认不指定,那么 null 也会执行验证。

php
public function testMustRequired(): void
{
    $validate = new Validator(
        [
            'name' => null,
        ],
        [
            'name' => 'required|'.IValidator::OPTIONAL,
        ],
        [
            'name' => '地名',
        ]
    );

    self::assertTrue($validate->success());
    self::assertFalse($validate->fail());
    self::assertSame(['name' => '地名'], $validate->getName());

    $validate->rule(['name' => 'required']);
    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    $error = <<<'eot'
        {
            "name": [
                "地名 不能为空"
            ]
        }
        eot;

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );

    $validate->data(['name' => null]);
    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());

    $error = <<<'eot'
        {
            "name": [
                "地名 不能为空"
            ]
        }
        eot;

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );
}

通配符验证规则支持

可以通过 * 来表示通配符验证规则。

php
public function testWildcardRule(): void
{
    $validate = new Validator(
        [
            'name' => '',
            'nafoo' => '',
            'nabar' => '',
        ],
        [
        ],
        [
            'name' => '地名',
            'nafoo' => 'foo',
            'nabar' => 'bar',
        ]
    );

    self::assertTrue($validate->success());
    self::assertFalse($validate->fail());

    $validate->rule(['na*' => 'required']);

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());
    self::assertSame(['name' => '地名', 'nafoo' => 'foo', 'nabar' => 'bar'], $validate->getName());

    $data = <<<'eot'
        {
            "name": "",
            "nafoo": "",
            "nabar": ""
        }
        eot;

    self::assertSame(
        $data,
        $this->varJson(
            $validate->getData()
        )
    );

    $rule = <<<'eot'
        {
            "name": [
                [
                    "required",
                    []
                ]
            ],
            "nafoo": [
                [
                    "required",
                    []
                ]
            ],
            "nabar": [
                [
                    "required",
                    []
                ]
            ]
        }
        eot;

    self::assertSame(
        $rule,
        $this->varJson(
            $validate->getRule(),
            1
        )
    );

    $error = <<<'eot'
        {
            "name": [
                "地名 不能为空"
            ],
            "nafoo": [
                "foo 不能为空"
            ],
            "nabar": [
                "bar 不能为空"
            ]
        }
        eot;

    self::assertSame(
        $error,
        $this->varJson(
            $validate->error(),
            2
        )
    );
}

类的静态方法验证规则支持

可以直接指定类的静态方法为验证规则,例如下面的例子。

php
namespace Tests\Validate;

class ClassStaticDemo1
{
    public static function handle(mixed $value, array $param, Validator $validator): bool
    {
        return false;
    }

    public static function demoValidator1(mixed $value, array $param, Validator $validator): bool
    {
        return true;
    }

    public static function demoValidator2(mixed $value, array $param, Validator $validator): bool
    {
        return false;
    }

    public static function demoValidator3(mixed $value, array $param, Validator $validator): bool
    {
        return 'foo' === $value;
    }

    public static function demoValidator4(mixed $value, array $param, Validator $validator): bool
    {
        throw new ValidatorException('我的名字验证失败');
    }
}

指定方法情况下,通过 @ 分隔开来,此时自定义类的 demoValidator1 方法将作为验证入口。

php
public function test2(): void
{
    $validate = new Validator(
        [
            'name' => 2,
        ],
        [
            'name' => [
                ClassStaticDemo1::class.'@demoValidator1',
            ],
        ],
        [
            'name' => '地名',
        ]
    );

    $container = new Container();
    $validate->setContainer($container);
    self::assertTrue($validate->success());
    $validate->data(['name' => 'foo']);
    self::assertTrue($validate->success());
}

类的静态方法验证规则支持通过异常抛出错误

可以在类的静态方法中抛出异常消息,异常必须为\Leevel\Validate\ValidatorException,例如下面的例子。

php
namespace Tests\Validate;

class ClassStaticDemo1
{
    public static function handle(mixed $value, array $param, Validator $validator): bool
    {
        return false;
    }

    public static function demoValidator1(mixed $value, array $param, Validator $validator): bool
    {
        return true;
    }

    public static function demoValidator2(mixed $value, array $param, Validator $validator): bool
    {
        return false;
    }

    public static function demoValidator3(mixed $value, array $param, Validator $validator): bool
    {
        return 'foo' === $value;
    }

    public static function demoValidator4(mixed $value, array $param, Validator $validator): bool
    {
        throw new ValidatorException('我的名字验证失败');
    }
}

指定方法情况下,通过 @ 分隔开来,此时自定义类的 demoValidator4 方法将作为验证入口。

php
public function test6(): void
{
    $validate = new Validator(
        [
            'name' => 2,
        ],
        [
            'name' => [
                ClassStaticDemo1::class.'@demoValidator4',
            ],
        ],
        [
            'name' => '地名',
        ]
    );

    $container = new Container();
    $validate->setContainer($container);
    self::assertFalse($validate->success());
    $error = <<<'eot'

"name": [
    "我的名字验证失败"
]



    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );
    $validate->data(['name' => 'foo']);
    self::assertFalse($validate->success());
}

验证器支持反义规则

定义反义规则,只需要在规则前面加上英文感叹号即可。

php
public function test7(): void
{
    $validate = new Validator(
        [
            'name' => 8,
        ],
        [
            'name' => '!min:5',
        ],
        [
            'name' => '地名',
        ]
    );

    self::assertFalse($validate->success());
    self::assertTrue($validate->fail());
    self::assertSame(['name' => '地名'], $validate->getName());
    $error = <<<'eot'

"name": [
    "不满足【地名 值不能小于 5】"
]



    self::assertSame(
        $error,
        $this->varJson(
            $validate->error()
        )
    );
}