必须先校验文件可读且非空再替换:用is_readable()和filesize()>0双重判断,读取后检查$content!==false&&$content!=='';禁用二进制文件文本替换,用finfo_file()判MIME类型;词表需array_filter($badwords,'strlen')清理空项;替换后校验$content!==$original_content;HTML替换须跳过标签,用(*SKIP)(*FAIL)语法;preg_quote()转义搜索字符;写入前验证路径合法性、锁定UTF-8编码、确认目录可写,并恢复原始权限。

替换前必须校验原始内容是否可读且非空
直接 file_get_contents() 读取失败或返回空字符串时,preg_replace() 或 str_replace() 仍会执行,但结果不可靠——比如文件不存在、权限不足、或被锁住,都可能导致空内容被误写回磁盘,覆盖原有数据。
- 先用
is_readable($path)和filesize($path) > 0双重判断 - 读取后立刻检查
$content !== false && $content !== '',避免空内容参与后续替换 - 对二进制文件(如图片、PDF)禁用文本替换逻辑,可用
finfo_file()判断 MIME 类型,仅对text/*或application/json等纯文本类型操作
敏感词替换时别漏掉 array_filter 清理空项
str_replace() 遇到空字符串或 null 元素会触发 PHP Warning 并跳过整个数组替换,导致过滤失效。常见于从配置文件或数据库加载词表后未清洗。
- 词表数组必须提前用
array_filter($badwords, 'strlen')去掉空值 - 确保每个词都是字符串:
array_map('strval', $badwords) - 替换后加一层校验:
if ($content === $original_content) { throw new RuntimeException('No replacement occurred'); },防止静默失败
正则替换 HTML 文本内容必须跳过标签本身
直接用 /J2/ 替换,会把 <img title="J2" alt="PHP替换文件内容时的安全过滤技巧" > 或 <div id="j2-container"> 里的 J2 也改掉,破坏 HTML 结构甚至引入 XSS 风险。
<ul>
<li>正确做法是用 PCRE 的 <code>(*SKIP)(*FAIL) 跳过所有标签:'/]*>(*SKIP)(*FAIL)|' . preg_quote($search, '/') . '/i'
preg_quote($search, '/') 必须加,否则搜索词含 .、[、? 会变成正则元字符DOMDocument + XPath 遍历文本节点写回文件前要锁定编码并验证路径合法性
替换后的中文乱码、写入截断、甚至路径穿越(如文件名含 ../),往往不是替换逻辑的问题,而是写入环节失控。
立即学习“PHP免费学习笔记(深入)”;
- 强制指定 UTF-8:用
file_put_contents($path, $content, LOCK_EX | FILE_TEXT),其中FILE_TEXT在 PHP 8.4+ 可显式声明文本模式 - 路径校验不能只靠
basename():需提前用realpath()解析并比对根目录前缀,防止../../etc/passwd绕过 - 写入前确认目标目录可写:
is_writable(dirname($path)),否则file_put_contents可能静默失败
真正容易被忽略的是:替换操作本身不改变文件权限和所有权,但写回时若原文件属主与 Web 进程不同,可能触发 SELinux 或 ACL 限制——建议用 chown() 和 chmod() 显式恢复原始权限快照。



















