敏感词过滤应优先用str_replace批量替换高频固定词,需预处理防变体;正则仅用于边界控制和大小写忽略;词库超2000条且qps高时应上dfa树并缓存;词表热更新失败多因opcache、权限或bom头。

敏感词过滤该用 str_replace 还是 preg_replace?
直接用 str_replace 最快,但只适合“原样匹配”;preg_replace 能加 \b 控制词边界、用 i 忽略大小写,可防“苹果手机”被误杀成“***手机”,但性能差、正则写错容易绕过或崩溃。
- 高频固定词(如“傻逼”“尼玛”)优先用
str_replace($badWords, '***', $text)批量替换——一次调用完成全部词,比循环调用快 5–10 倍 - 需防“shǎb傻@逼”“s h a b i”的场景,必须先预处理:用
preg_replace('/[\s\p{P}]+/u', '', $text)清空空格和标点,再进核心匹配 - 错误写法:
preg_replace('/苹果/', '*', $text)→ 会把“苹果手机”变成“*手机”;正确写法:preg_replace('/\b苹果\b/i', '*', $text) - 中文词必须加
u修饰符,否则stripos或正则可能截断 UTF-8 字节,导致漏匹配
ThinkPHP 行为类里怎么安全遍历 POST 数据?
在 Application/Home/Behavior/WordsFilterBehavior.class.php 中用递归遍历 $_POST 是常见做法,但容易崩在非字符串类型、深层嵌套或 JSON 字段上。
- 必须先判断类型:
if (is_string($val)) { $params[$key] = $this->filter($val); },不能对数组或对象直接传入filter() -
$this->filter()内部若用preg_replace,记得把敏感词数组用array_map('preg_quote', $words)转义,否则词里含.、[会触发正则报错 - 别在行为里写日志到文件——高并发时
file_put_contents抢锁卡死;改用Log::record()交由 ThinkPHP 日志驱动统一处理 - 如果 POST 数据已由框架自动做了
htmlspecialchars或html_entity_decode,过滤前得还原,否则“"傻逼"”根本匹配不上原始词库
DFA/Trie 树方案真有必要上吗?
词库超 2000 条、QPS 过 50、且出现 CPU 持续 >70%,就该怀疑是暴力遍历在拖垮服务;这时 DFA(字典树)不是“高级玩法”,而是刚需。
- 网上抄的“完整版 Trie 类”常带冗余方法和递归遍历,实际只需实现「单次扫描 + 最短命中」,结构精简到 30 行内就能跑通
- 错误现象:
APCu is not enabled导致每次请求重建 Trie 树,CPU 突增;解决办法:检查apcu.enabled=1,并用apcu_store('sensitive_trie', $root)缓存根节点 - 缓存 key 别硬编码成
'trie'——词库一更新,旧缓存不失效;建议用词文件md5_file('public/static/censorwords.txt')作 key 后缀 - 别为了“技术正确”强行上 Aho-Corasick 扩展;90% 的业务,用
str_replace+ 长度倒序排序(usort($words, function($a,$b){return mb_strlen($b) - mb_strlen($a);}))已足够稳
词表热更新为什么总不生效?
不是代码写错了,大概率是 PHP 缓存或文件权限卡住了——线上改完 sensitive_words.txt 却没反应,9 成发生在这一环。
- OPcache 默认缓存
include的文件;开发期关掉它,或设opcache.file_update_protection=0,否则改了词表要等缓存过期或重启 PHP-FPM - 用
file_get_contents加载词表时,没做is_readable()检查;路径错、权限不足、SELinux 限制都会静默失败,返回空数组 - 词表里混入空行或 BOM 头,
file()读出来带"\xEF\xBB\xBF",导致stripos($content, $word)永远失败;加array_filter($words, 'trim')清理 - 最稳的做法:把词表放 Redis,用
GET sensitive:words读取;更新时SET sensitive:words "傻逼,尼玛,操",PHP 端explode(',', $redis->get('sensitive:words'))即可,毫秒级生效
真正难的从来不是“怎么写过滤函数”,而是词表从哪来、编码怎么穿、缓存何时破——这三个点串起来出问题,日志里连报错都没有,只能靠 var_dump 打桩看每一步的输入输出。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










