str_replace更适合简单敏感词过滤,preg_replace适用于复杂匹配;需注意赋值、空值清理、编码与权限校验;词表应外置并配合Redis或时间戳实现热更新。

敏感词过滤用 str_replace 还是 preg_replace?
直接替换简单词用 str_replace 更快、更安全;含通配、大小写混排或需模糊匹配(如“和-谐”“h3xie”)才考虑正则。但正则容易写错边界,比如没加 \b 导致“和谐号”被误拦,或漏掉 Unicode 变体(如全角空格、零宽字符)。生产环境建议先用 str_replace + 精确词表,再按需补正则规则。
写文件前必须校验过滤是否生效
常见错误是调用 str_replace 后没接返回值,原字符串未变就直接写入:
$content = str_replace($badwords, $replace, $content); // ✅ 正确赋值另一个坑是敏感词数组含空字符串或
// ❌ 忘了这行:$content = ...,结果写的是原始内容
null,导致 str_replace 报 Warning 并跳过过滤。建议过滤前加判断:- 用
array_filter($badwords, 'strlen')清理无效项 - 用
is_string($content)确保输入类型正确 - 写文件前
if ($content !== $original)检查是否真有替换发生
写文件时注意权限与编码一致性
过滤后写文件失败,常因两件事:一是目标目录无写权限,二是敏感词含中文却用 file_put_contents 默认 ASCII 写入,造成乱码甚至截断。解决方法:
- 写前用
is_writable($path)检查路径可写 - 强制指定 UTF-8 编码:
file_put_contents($file, mb_convert_encoding($content, 'UTF-8', 'auto'), LOCK_EX) - 避免直接拼接用户输入进文件名,否则可能路径穿越(如
../../etc/passwd),应限定后缀并过滤路径分隔符
敏感词更新后如何不重启服务就生效?
硬编码词表改一次就得重部署,线上应把词表放独立文件(如 sensitive_words.php 返回数组),每次过滤前 require_once 加载。但注意 PHP opcode 缓存(如 OPcache)会缓存该文件,导致修改不生效。解决方案:
- 开发期关 OPcache,或设
opcache.file_update_protection=0 - 生产期用时间戳校验:
if (filemtime($wordfile) > $cached_time) { require $wordfile; } - 更稳的方式是走 Redis 缓存词表,用
GET sensitive:words读取,更新时SET即刻生效











