自定义敏感词验证需继承think\validate并重写extend()注册规则,词库放app/common/sensitive_words.txt,用file()读取+array_filter()清洗,匹配用mb_stripos(),静态缓存词库,rule数组传参支持allow_empty、ignore_space等选项。

ThinkPHP 6 自定义验证规则加敏感词过滤
直接用 rule 配置 + 自定义验证器类,别碰 validate.php 全局配置或硬塞正则——词库变、规则变、提示语也要动态,硬编码扛不住。
核心是继承 think\Validate,重写 extend() 方法注册规则,再在验证器里调用。不这么做,后续换词库路径、加白名单、支持模糊匹配都会卡住。
- 敏感词文件建议放
app/common/sensitive_words.txt,每行一个词,避免 JSON/YAML 增加解析开销 - 验证器类命名按业务来,比如
App\Validate\ContentValidate,别叫SensitiveValidate——它只是校验环节一环,不是独立模块 - 别在
scene里写死规则,用rule数组动态传参更灵活,比如是否开启全匹配、是否忽略空格
如何读取词库并构建高效匹配逻辑
用 file() 读取后 array_filter() 去空行和注释,再 array_map('trim') 清空格——别用 file_get_contents() + explode(),Windows 换行符(\r\n)会导致多出空项。
匹配不用正则,用 stripos() 或 mb_stripos()(中文必须用后者)。正则写 /(关键词1|关键词2)/i 看似快,但词库超 500 条时编译慢、回溯风险高,且无法做「前后无字母数字」的边界控制。
- 词库加载建议加静态缓存:
static $words = null;,首次读取后复用,避免每次验证都 IO - 如果词库含“苹果”和“苹果手机”,需按长度倒序排序再匹配,否则短词先命中会漏掉长词逻辑
- 别默认开启「替换」或「脱敏」,验证阶段只返回
false+ 提示,处理动作交给控制器或事件监听
验证器中怎么写 rule 才能传参又不破坏框架约定
ThinkPHP 的 rule 支持数组格式:['content' => ['sensitive_words' => 'allow_empty,ignore_space']],其中字符串参数用逗号分隔,验证方法里用 explode(',', $param) 解析——这是唯一干净支持多选项的方式。
不要写成 'sensitive_words:allow_empty,ignore_space' 这种冒号语法,TP6 不识别,会当成字符串字面量传进验证方法,没法解构。
-
allow_empty:字段为空时不校验,避免表单未填就报敏感词错 -
ignore_space:匹配前把用户输入和词库都str_replace(' ', '', $str),防“敏 感 词”绕过 -
whole_word:启用后,用preg_match('/(? 控制边界,防止“和谐”匹配到“和谐社会”
常见报错和调试盲区
最常遇到的是 InvalidArgumentException: The validate rule [sensitive_words] is not exists,根本原因不是没注册规则,而是验证器类没被自动加载——检查命名空间是否和 app/validate/ 目录结构一致,TP6 默认只扫描 app\validate 下的类。
另一个坑是中文词匹配失败,错误日志里显示 mb_stripos(): Empty delimiter,其实是词库某行读出来是空字符串,mb_stripos($value, '') 触发警告并返回 false,导致整条验证跳过。
- 加一行
if (!$word) continue;在匹配循环开头,比 try-catch 更轻量 - 调试时临时加
Log::debug('sensitive check', ['input' => $value, 'words' => $this->words]);,别只看最终结果 - 部署后记得清缓存:
php think clear:config,否则新注册的规则不生效
词库更新频率低,但匹配逻辑一旦写死就难改;真正麻烦的不是“怎么拦”,而是“拦完怎么通知运营查漏、怎么给用户可理解的反馈”。这些不在验证器里做,但设计时得留好钩子。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










