fiberphp / validate
✅ FiberPHP 数据验证 —— 多规则链式验证,支持内置规则与自定义扩展。
Requires
- php: >=8.3
- ext-json: *
- ext-mbstring: *
- fiberphp/contract: dev-master
- fiberphp/support: dev-master
Requires (Dev)
- phpunit/phpunit: ^11.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
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-jsonext-mbstringfiberphp/supportdev-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、= / same → eq、<> → 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。