在thinkphp 6验证器中,须于验证器类的init()方法内调用validator::extend()注册ismobile等自定义规则,规则名需与验证场景中字符串完全一致,闭包返回布尔值,正则推荐/^1[3-9]\d{9}$/,支持动态参数如ismobile:86。

ThinkPHP 6 验证器里怎么加自定义手机号规则
直接在验证器类里用 extend 方法注册规则最稳妥,别硬塞进全局验证器配置或试图改框架源码。TP6 的验证器设计是“类内注册 + 实例调用”,脱离这个上下文容易失效。
常见错误现象:写了正则但 mobile|isMobile 校验始终通过;或者提示 Rule not found: isMobile;还有人把规则写在 app/validate/Validate.php 基类里却没被子类识别——本质是没走对注册路径。
- 必须在验证器类的
init方法或构造函数中调用Validator::extend() - 规则名(如
isMobile)要和验证场景里写的字符串完全一致,大小写敏感 - 闭包函数返回
true表示通过,false或抛异常表示失败;推荐返回布尔值,避免异常打断流程 - 正则建议用
1[3-9]\d{9},别用网上抄的带空格、横线、括号的“宽松匹配”,真实业务里前端应已格式化
为什么不能只靠内置的 regex 规则写手机号
能写,但不推荐。每次用都得重复粘贴一长串正则,比如 regex:/^1[3-9]\d{9}$/,既难维护又无法复用错误提示。更关键的是:TP6 的 regex 不支持动态参数(比如不同国家区号),而自定义规则可以接收额外参数,例如 isMobile:86 表示中国号码。
性能上没差异,但可读性和扩展性差很多。你以后加港澳台号码支持、或对接短信平台要求前缀校验时,会发现 regex 已经卡死在字符串里了。
-
regex是静态字符串匹配,不支持运行时参数传入 - 自定义规则函数签名是
function($value, $rule, $data, $field),其中$rule可以是['isMobile', '86']这样的数组 - 错误提示模板统一配在
ruleMsg属性里,比如'isMobile' => ':attribute 必须是中国大陆手机号'
验证器类里怎么正确注册并使用 isMobile
别在控制器或中间件里注册,必须在验证器类初始化阶段完成。TP6 的验证器实例是按需创建的,每次 new 都是新对象,所以注册动作要确保执行一次且仅一次——放在 init() 最安全。
<?php namespace app\validate;
use think\Validate;
use think\validation\Validator;
class UserValidate extends Validate
{
protected $rule = [
'mobile' => 'require|isMobile:86',
];
protected $message = [
'mobile.require' => '手机号不能为空',
'mobile.isMobile' => '手机号格式不正确',
];
protected function init()
{
Validator::extend('isMobile', function ($value, $rule, $data, $field) {
$prefix = $rule[1] ?? '86';
if ($prefix === '86') {
return (bool) preg_match('/^1[3-9]\d{9}$/', $value);
}
// 其他区号逻辑可在此扩展
return false;
});
}
}
注意 $rule 是数组,isMobile:86 会被自动拆成 ['isMobile', '86'];如果只写 isMobile,$rule[1] 就是 null,所以要用空合并操作符。
验证器复用时容易忽略的兼容点
如果你把验证器抽成公共基类(比如 BaseValidate),再让多个子类继承,那 init() 里的 extend 会被反复执行。TP6 不会去重注册,但多次注册同一规则名会导致后一次覆盖前一次——看起来没问题,实则隐藏风险。
更隐蔽的问题是:某些部署环境下(如 Swoole 长连接),验证器类可能被复用,init() 被多次调用,而 Validator::extend() 是全局静态注册,重复调用不会报错,但会浪费内存、增加查找开销。
- 推荐做法:用静态变量标记是否已注册,例如
if (static::$isMobileRegistered) return; - 不要在
scene方法里注册规则,它只控制字段和规则组合,不负责规则定义 - 如果项目用了多语言,
$this->language()设置语言后,message才会生效,否则默认用中文提示
真正麻烦的从来不是写对一行正则,而是当验证器分散在七八个模块、又被单元测试反复 new 出来时,谁还记得那个 extend 是不是只跑了一次。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











