Search by

fiberphp / validate

fiberphp

✅ FiberPHP 数据验证 —— 多规则链式验证,支持内置规则与自定义扩展。

Package info

gitee.com/fiberphp/validate.git

Issues

pkg:composer/fiberphp/validate

Statistics

Installs: 0

Dependents: 1

Suggesters: 0

dev-master 2026-09-09 05:55 UTC

This package is auto-updated.

Last update: 2026-09-09 05:55:39 UTC


README

FiberPHP 数据验证库。纯库实现(ValidateException 继承 \RuntimeException、仅依赖 contract 契约接口,不依赖 framework/http),支持多规则链式验证、场景/分组、批量错误收集、点号与通配符多维数据访问,内置 40+ 验证规则与自定义扩展。

环境要求

  • PHP >= 8.3
  • ext-json
  • ext-mbstring
  • fiberphp/support dev-master(Arr / Str

安装

composer require fiberphp/validate

基本用法

use FiberPHP\Validate\Validate;

$v = new Validate();

$v->rule('name', 'require|length:2,20')
  ->rule('email', 'require|email')
  ->rule('age', 'integer|between:1,120', '年龄必须在 1-120 之间')
  ->field(['name' => '用户名', 'email' => '邮箱']);

$data = ['name' => 'tom', 'email' => 'tom@example.com', 'age' => 18];

if (!$v->check($data)) {
    echo $v->getError();   // 第一条错误(字符串)
    // $v->getError(true); // 全部错误([field => msg])
}

// 验证失败直接抛异常
$v->failException(true)->check($data);
// → throw \FiberPHP\Validate\Exception\ValidateException

取白名单数据

checked() 只返回规则中定义的字段(已通过验证):

$clean = $v->checked($_POST);   // 失败返回空数组(配合 failException 抛异常)

单值单规则

if ($v->checkRule('tom@example.com', 'email')) { /* ... */ }

// 动态方法(__call)
$v->isEmail('tom@example.com');           // → bool
$v->checkIsEmail('bad');                  // → false

规则定义

rule() 支持三种形式:

// 1. 规则串(| 分隔,参数用 : 或 , 传)
$v->rule('username', 'require|alphaDash|length:4,20');

// 2. 数组(key 为字段名)
$v->rule([
    'username|用户名' => 'require|alphaDash|length:4,20',
    'password'         => 'require|length:6,32',
]);

// 3. 闭包(返回 true/通过,false/失败,string/自定义消息)
$v->rule('mobile', function ($value) {
    return preg_match('/^1[3-9]\d{9}$/', $value) ? true : '手机号格式不符';
});

规则别名

$v->alias('pwd', 'require|length:6,32');
$v->rule('password', 'pwd');   // 等价于 require|length:6,32

类型别名映射:>gt>=egt<lt<=elt= / sameeq<>neq

内置规则

类别规则
必填require / must / requireIf / requireWith / requireWithout / requireCallback / accepted / declined / acceptedIf / declinedIf
类型number / integer / float / string / boolean / array / enum(BackedEnum / UnitEnum / 常量类)
字符集alpha / alphaNum / alphaDash / chs / chsAlpha / chsAlphaNum / chsDash
网络email / mobile / url / ip / activeUrl / macAddr / allowIp / denyIp
长度/范围length / max / min / in / notIn / between / notBetween / multipleOf
比较eq / neq / gt / egt / lt / elt / confirm / different
字符串startWith / endWith / contain / regex
日期date / dateFormat / after / before / expire
文件file / image / fileSize / fileExt / fileMime

兜底:找不到对应方法时走 is(),依次尝试 ctype_xxx / filter_var / 内置正则。

规则参数形式

$v->rule('age', 'between:1,120');           // 规则串
$v->rule('age', ['between' => [1, 120]]);   // 数组
$v->rule('status', ['in' => [0, 1, 2]]);
$v->rule('role', [RoleEnum::class]);         // 枚举验证(enum 规则)

场景与分组

场景

同一验证器在不同接口验证不同字段:

class UserValidate extends Validate
{
    public function __construct()
    {
        $this->rule([
            'username' => 'require|alphaDash|length:4,20',
            'password' => 'require|length:6,32',
            'email'    => 'require|email',
        ]);
    }

    // scene: login
    protected function sceneLogin(): void
    {
        $this->only(['username', 'password']);
    }
}

$v = (new UserValidate())->scene('login');
$v->check($data);   // 只验证 username + password

scene() 也可直接传字段数组临时限定:$v->scene(['username', 'password'])

场景内增删改

$v->scene('login')
  ->remove('password', 'length')        // 移除 password 的 length 规则
  ->append('username', 'email')          // 追加规则
  ->replace('password', 'require');      // 覆盖规则

规则分组

独立校验组(闭包隔离作用域):

$v->group('strict', function (Validate $v) {
    $v->rule('username', 'require|alphaDash|length:6,20');
});

$v->check($data, 'strict');   // 按 strict 组验证

批量与异常

$v->batch(true)->check($data);           // 收集所有字段错误(不遇错即停)
$v->failException(true)->check($data);   // 失败抛 ValidateException
$v->getError(true);                       // [field => msg] 全量
$v->getErrors();                          // 始终返回数组

捕获异常后业务可自行处理(纯库,不依赖 framework/http):

try {
    $v->failException(true)->check($data);
} catch (ValidateException $e) {
    $errors = $e->getError();   // array|string,原始形态
    $field  = $e->getKey();     // 失败字段名
}

ValidateException 通过 contract 接口(HttpCodeAware / UserFacingMessage / ValidationErrorsAware)参与框架渲染:在 FiberPHP 应用中未被 catch 时自动渲染为 HTTP 422,消息透传,字段明细写入响应体 errors

{"code": 1, "msg": "参数校验失败", "errors": {"name": "name不能为空"}, "data": null}
  • 数组错误(批量校验):消息为「参数校验失败」,getErrors() 返回 [field => msg] 全量明细;
  • 单条错误:消息即错误文案,getErrors() 返回 [字段名 => 文案](未提供字段名时挂 _ 键)。

注解验证(#[Validate])

#[Validate] 是方法级属性注解,标注在控制器方法上,配合应用层的 ValidateMiddleware 实现自动验证——控制器无需手动调用 check()

基本用法

use FiberPHP\Validate\Attribute\Validate;
use FiberPHP\Router\Attribute\Post;

class GoodsController extends BaseController
{
    #[Post('/goods')]
    #[Validate]              // scene = 方法名 "store"
    public function store(Request $request): Response
    {
        // 验证已通过,直接处理业务
        return $this->success($service->create($request->all()));
    }
}

指定场景

#[Validate]                  // scene 默认取方法名
#[Validate('custom')]        // scene = "custom"
#[Validate(scene: null)]     // 不使用 scene,全量验证 $rule

命名约定

中间件按以下规则自动定位验证器:

App\Controller\GoodsController → App\Validate\GoodsValidate
  • 将命名空间中的 \Controller\ 替换为 \Validate\
  • 将类名后缀 Controller 替换为 Validate
  • 验证器类不存在时静默放行(该方法可能不需要验证)

中间件注册

在应用 config/http.php 中注册为全局中间件:

return [
    'middleware' => [
        'global' => [
            \App\Middleware\ValidateMiddleware::class,
        ],
    ],
];

中间件实现

应用层创建 ValidateMiddleware,扫描方法注解并执行验证:

use FiberPHP\Http\Request;
use FiberPHP\Http\Response;
use FiberPHP\Validate\Attribute\Validate;
use FiberPHP\Validate\Exception\ValidateException;

class ValidateMiddleware
{
    public function process(Request $request, callable $handler): Response
    {
        $controller = $request->controller;
        $action     = $request->action;

        if (!$controller || !$action) {
            return $handler($request);
        }

        $attrs = (new \ReflectionMethod($controller, $action))
            ->getAttributes(Validate::class);

        if ($attrs === []) {
            return $handler($request);  // 无注解,放行
        }

        $attr  = $attrs[0]->newInstance();
        $scene = $attr->scene ?? $action;

        // 推导验证器类名
        $class = str_replace('\\Controller\\', '\\Validate\\', $controller);
        $class = preg_replace('/Controller$/', 'Validate', $class) ?? $class;

        if (!class_exists($class)) {
            return $handler($request);  // 验证器不存在,放行
        }

        $validate = new $class();
        if ($validate->hasScene($scene)) {
            $validate->scene($scene);
        }

        if (!$validate->check($request->all())) {
            throw new ValidateException($validate->getError());
        }

        return $handler($request);
    }
}

设计说明#[Validate] 属性类本身是纯标记(零依赖),由 validate 包提供。 ValidateMiddleware 属于应用层(skeleton),依赖 http + validate。 验证器不存在时静默放行而非报错,避免强制每个控制器方法都配验证器。

多维数据

支持点号与 * 通配符取值:

$v->rule('user.name', 'require');
$v->rule('items.*.sku', 'require|alphaDash');

$v->check([
    'user'  => ['name' => 'tom'],
    'items' => [['sku' => 'A1'], ['sku' => '']],
]);
// → items.*.sku 验证失败

错误消息

三级优先级:字段.规则 > 字段级 > typeMsg 默认。模板占位符::attribute(字段描述)、:rule(规则值)、:1 / :2 / :3 (逗号分隔的规则参数)。

$v->setTypeMsg(['email' => ':attribute 必须是合法邮箱']);
$v->message(['email.require' => '邮箱必填', 'email.email' => '邮箱格式不对']);
$v->field('email', '邮箱');

目录结构

src/
├── Validate.php                       # 核心:规则编排 + 验证执行 + 错误组装
├── Attribute/
│   └── Validate.php                   # #[Validate] 方法级注解(纯标记)
├── Exception/
│   └── ValidateException.php          # 继承 \RuntimeException(纯库化)
└── Rules/
    ├── ComparisonRulesTrait.php        # confirm/different/egt/gt/elt/lt/eq/neq
    ├── ConditionalRulesTrait.php       # requireIf/requireWith/requireWithout/acceptedIf/...
    ├── DateRulesTrait.php              # after/before/dateFormat/expire
    ├── FileRulesTrait.php              # file/image/fileSize/fileExt/fileMime
    ├── NetworkRulesTrait.php          # activeUrl/ip/allowIp/denyIp
    ├── StringRulesTrait.php            # length/max/min/in/notIn/between/regex/...
    └── TypeRulesTrait.php             # require/accepted/boolean/number/integer/enum/array/is()

License

MIT License (c) 2026 庞斌,详见 LICENSE