必须用 md5_file() 校验文件内容是否真实更新,再逐行读回比对字符串防微小异常,并检查 opcache 缓存及备份文件时间戳与权限。

用 md5_file() 比对哈希值最可靠
文件内容变了,md5_file() 结果一定变;没变,说明写入失败或根本没更新。这不是“可选验证”,而是必须做的最后一道确认。
常见错误是只看 file_put_contents() 返回字节数就认为成功——它不校验磁盘上最终内容是否完整、是否被截断、是否因权限/磁盘满中途失败。
- 先计算原始内容的 MD5:
$old_md5 = md5_file($path); - 执行替换后立即再算一次:
$new_md5 = md5_file($path); - 比对必须严格:
if ($old_md5 === $new_md5) { /* 未更新,出问题了 */ } - 注意:不要用
==,避免类型转换导致假阳性
逐行读回 + 字符串比对防截断和编码污染
md5_file() 能发现大差异,但无法识别末尾多一个空格、BOM 头、换行符错乱或 UTF-8 编码损坏这类“微小但致命”的写入异常。
尤其当 file_put_contents() 被调用时未指定 FILE_TEXT 或 LOCK_EX,或目标路径挂载为 noexec/nosuid 分区时,容易静默写坏。
立即学习“PHP免费学习笔记(深入)”;
- 用
file_get_contents($path, FILE_TEXT)读回全部内容(加FILE_TEXT避免二进制模式误判) - 与你预期写入的
$newContent做全量字符串比对:$content_ok = (file_get_contents($path, FILE_TEXT) === $newContent); - 若不一致,立刻中止后续流程——别假设“差不多就行”
- 大文件慎用此法;可改用
hash_equals(md5_file($path), md5($newContent))作为轻量替代
检查 opcache 是否缓存旧内容
即使文件磁盘内容已更新,PHP 可能仍在用 opcache 缓存的旧字节码执行,导致“明明改了却没生效”的幻觉。
这在 CLI 脚本里不明显,但在 Web SAPI(如 FPM)下极常见,尤其 opcache.revalidate_freq > 0 时。
- 验证前先调用:
opcache_invalidate($path, true); - 或更彻底:
opcache_reset();(仅限开发/维护窗口期) - 确认配置:
var_dump(opcache_get_status()['scripts'][$path] ?? null);查看是否仍命中缓存 - 别依赖重启 PHP-FPM——它治标不治本,且影响线上请求
还原失败时,备份文件时间戳就是关键证据
如果验证失败,你要快速判断是写入失败,还是备份本身就不对——这时不能翻日志猜,直接看备份文件的修改时间。
用 date('c', filemtime($backup_path)) 和你执行替换的时间对比。如果备份时间晚于替换操作时间,说明备份逻辑被绕过或条件判断失效(比如 copy() 前没检查 is_readable())。
真正容易被忽略的是:备份文件权限是否和原文件一致?copy() 不继承所有权,若 Web 进程用户和原文件所有者不同,后续 file_put_contents() 可能因权限不足静默失败,而你只看到“返回字节数正常”。



















