# validate **Repository Path**: fiberphp/validate ## Basic Information - **Project Name**: validate - **Description**: ✅ FiberPHP 验证器组件 —— 基于规则的数据验证,支持场景验证、自定义规则、错误信息自定义,协程安全,开箱即用。 - **Primary Language**: PHP - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-23 - **Last Updated**: 2026-09-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # FiberPHP Validate FiberPHP 数据验证库。纯库实现(`ValidateException` 继承 `\RuntimeException`、仅依赖 contract 契约接口,不依赖 framework/http),支持多规则链式验证、场景/分组、批量错误收集、点号与通配符多维数据访问,内置 40+ 验证规则与自定义扩展。 ## 环境要求 - PHP >= 8.3 - `ext-json` - `ext-mbstring` - `fiberphp/support` dev-master(`Arr` / `Str`) ## 安装 ```bash composer require fiberphp/validate ``` ## 基本用法 ```php 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()` 只返回规则中定义的字段(已通过验证): ```php $clean = $v->checked($_POST); // 失败返回空数组(配合 failException 抛异常) ``` ### 单值单规则 ```php if ($v->checkRule('tom@example.com', 'email')) { /* ... */ } // 动态方法(__call) $v->isEmail('tom@example.com'); // → bool $v->checkIsEmail('bad'); // → false ``` ## 规则定义 `rule()` 支持三种形式: ```php // 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 : '手机号格式不符'; }); ``` ### 规则别名 ```php $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` / 内置正则。 ### 规则参数形式 ```php $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 规则) ``` ## 场景与分组 ### 场景 同一验证器在不同接口验证不同字段: ```php 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'])`。 ### 场景内增删改 ```php $v->scene('login') ->remove('password', 'length') // 移除 password 的 length 规则 ->append('username', 'email') // 追加规则 ->replace('password', 'require'); // 覆盖规则 ``` ### 规则分组 独立校验组(闭包隔离作用域): ```php $v->group('strict', function (Validate $v) { $v->rule('username', 'require|alphaDash|length:6,20'); }); $v->check($data, 'strict'); // 按 strict 组验证 ``` ## 批量与异常 ```php $v->batch(true)->check($data); // 收集所有字段错误(不遇错即停) $v->failException(true)->check($data); // 失败抛 ValidateException $v->getError(true); // [field => msg] 全量 $v->getErrors(); // 始终返回数组 ``` 捕获异常后业务可自行处理(纯库,不依赖 framework/http): ```php 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`: ```json {"code": 1, "msg": "参数校验失败", "errors": {"name": "name不能为空"}, "data": null} ``` - 数组错误(批量校验):消息为「参数校验失败」,`getErrors()` 返回 `[field => msg]` 全量明细; - 单条错误:消息即错误文案,`getErrors()` 返回 `[字段名 => 文案]`(未提供字段名时挂 `_` 键)。 ## 注解验证(#[Validate]) `#[Validate]` 是方法级属性注解,标注在控制器方法上,配合应用层的 `ValidateMiddleware` 实现自动验证——控制器无需手动调用 `check()`。 ### 基本用法 ```php 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())); } } ``` ### 指定场景 ```php #[Validate] // scene 默认取方法名 #[Validate('custom')] // scene = "custom" #[Validate(scene: null)] // 不使用 scene,全量验证 $rule ``` ### 命名约定 中间件按以下规则自动定位验证器: ``` App\Controller\GoodsController → App\Validate\GoodsValidate ``` - 将命名空间中的 `\Controller\` 替换为 `\Validate\` - 将类名后缀 `Controller` 替换为 `Validate` - 验证器类不存在时静默放行(该方法可能不需要验证) ### 中间件注册 在应用 `config/http.php` 中注册为全局中间件: ```php return [ 'middleware' => [ 'global' => [ \App\Middleware\ValidateMiddleware::class, ], ], ]; ``` ### 中间件实现 应用层创建 `ValidateMiddleware`,扫描方法注解并执行验证: ```php 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。 > 验证器不存在时静默放行而非报错,避免强制每个控制器方法都配验证器。 ## 多维数据 支持点号与 `*` 通配符取值: ```php $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` (逗号分隔的规则参数)。 ```php $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](LICENSE)。