PHP里做敏感词过滤,感词过滤最直接的感词过滤办法不是正则,是感词过滤数组。
正则这东西,感词过滤写个`preg_replace`匹配几个词看着挺美,感词过滤词库一上五百条,感词过滤性能立刻崩给你看。感词过滤我去年维护过一个老项目,感词过滤后台填了三百多个违禁词,感词过滤前端发一条评论,感词过滤服务器要卡两秒。感词过滤用户早跑了。感词过滤后来换成`str_replace`配合关键词哈希,感词过滤毫秒级完成。感词过滤
核心逻辑就三步:把敏感词表加载进内存,感词过滤用`array_map`做一次`trim`和去重,然后跑一个循环,逐个`str_replace`。别笑,真的够用。PHP的`str_replace`是C层实现的,比正则引擎快一个数量级。你写一百个词替换,它内部有优化,不会傻乎乎扫描一百遍字符串。
但光替换不够。敏感词过滤有个坑——谐音和拆字。你过滤了“赌博”,用户写“赌 博”或者“du博”,照样漏。这时候需要二次处理:先把用户输入里的空格、特殊符号全剥掉,比如`preg_replace('/[\s\W]+/u', '', $text)`,然后再跑词库。代价是误伤,比如“中 华 人 民”会被连成“中华人民共和国”,但这在敏感词场景里通常可以接受。
另一个坑是词库更新。别把词硬编码在代码里,存数据库或者单独文件。我见过有人把词直接写死在控制器里,运营要加个词还得发工单找开发。正确做法是启动时加载一次,缓存到`apcu`或者文件,词库更新时清缓存。PHP-FPM每个请求都会重新执行脚本,但`opcache`能帮你省下解析时间,词库加载这步必须做缓存。
性能再往上走,可以用`trie`树。PHP里实现一个简单的前缀树并不难,每个节点存一个字符,叶子节点标记为敏感词。匹配时逐字符走树,遇到叶子就替换。这比`str_replace`好在能处理部分重叠词,比如“政治”和“政治家”,前者是后者的前缀。用`str_replace`先替换“政治”会把“政治家”搞成“***家”,用trie就不会。
但说实话,中小项目用不着trie。我做过一个日活十万的社区,词库一千条,`str_replace`方案峰值CPU占用不到百分之五。瓶颈从来不在过滤本身,而在你每次请求都重新加载词库。用`apcu_fetch`判断版本号,版本没变就直接用缓存数组,这才是关键优化。
最后说个现实问题:敏感词过滤永远跟不上造词速度。你堵了“翻墙”,人家写“fan qiang”;你堵了“代购”,人家写“dai gou”。真要较真,得配合人工审核和举报机制。代码能做的,是把已知的、明确的词拦住,把漏网的交给运营。别指望一个函数解决所有问题,那是产品经理的幻觉。
写代码时记得加个日志,记录命中了哪个词、替换成了什么。这玩意儿能帮运营调整词库,也能帮你排查误杀。我习惯把替换前后的文本都存下来,方便回溯。别小看这个细节,出了问题能少背一半锅。
过滤函数本身别写太长,二十行以内搞定。拆成两个方法:一个负责清洗输入(去空格、转小写),一个负责实际替换。测试的时候多准备几个边界用例——空字符串、纯标点、超长文本、emoji混排。emoji这玩意儿容易被`\W`误删,记得在清洗正则里保留`\x{ 1F300}-\x{ 1F9FF}`范围。
就这些。PHP没那么玄乎,敏感词过滤更没那么玄乎。把基础函数用扎实,比追新框架管用。

喜欢
讨厌