OPcache 修改PHP文件未生效是因为其默认缓存字节码且不实时校验源文件变更;开发环境应设opcache.validate_timestamps=1并调低opcache.revalidate_freq,生产环境推荐reload PHP-FPM而非opcache_reset()。

OPcache 缓存为什么改了 PHP 文件却没生效
因为 OPcache 默认把编译后的字节码常驻内存,不检查源文件是否变更。哪怕你 touch 了文件、重启了 Web 服务(如 Apache),只要 OPcache 没被清空或重载,旧字节码就还在跑。
常见现象包括:修改了 index.php 但页面输出仍是旧逻辑;var_dump(__FILE__) 显示路径正确,但内容未更新;用 opcache_get_status() 查看发现 opcache.hit_rate 很高,但 opcache.files_count 和实际文件数对不上。
-
开发环境建议关闭
opcache.validate_timestamps(设为1),否则它只在每次请求时按固定间隔(默认 2 秒)检查一次时间戳,不是实时的 -
opcache.revalidate_freq值越大,延迟越明显;设为0才真正“每次请求都校验”,但会牺牲性能 - 某些 Docker 或容器化部署中,宿主机改文件、容器内 PHP 进程可能因挂载方式看不到 mtime 变更,导致 validate 失效
手动刷新 OPcache 的三种可靠方式
不是所有方式都“即时”——关键看是否触发了字节码重编译,以及是否影响全部 worker 进程。
- 调用
opcache_reset():必须在脚本中执行,且该脚本本身不能被 OPcache 缓存(否则函数根本不会运行)。推荐放在独立的opcache-reset.php中,并确保该文件不在opcache.blacklist_filename里,访问一次即重置全站缓存 - 调用
opcache_invalidate($script, $force):只清指定文件,$force = true才跳过时间戳校验强制重载。注意路径必须是绝对路径(可用realpath(__FILE__)获取) - 重启 PHP-FPM 进程:
sudo systemctl reload php*-fpm或kill -USR2 $(cat /var/run/php/php*-fpm.pid)。这是最彻底的方式,但会造成短暂请求失败(取决于进程管理策略)
别用 opcache_compile_file() 单独预热——它不触发自动依赖解析,require 的其他文件仍可能走旧缓存。
立即学习“PHP免费学习笔记(深入)”;
如何确认 OPcache 刷新已生效
不能只看页面输出,得查底层状态。
- 执行
var_dump(opcache_get_status()['scripts']),找目标文件的timestamp字段,对比filemtime()是否一致 - 检查
opcache_get_status()['opcache_enabled']是否为true,避免误操作关掉了 OPcache - 留意
opcache_get_status()['memory_usage']['used_memory']是否在opcache_reset()后显著下降,再缓慢回升,说明重载发生了 - 如果用了
opcache.file_cache(启用磁盘缓存),还需清空对应目录(如/tmp/opcache/),否则下次启动仍可能加载旧文件
生产环境慎用的“即时刷新”陷阱
所谓“即时”,在多 worker、多实例、FPM 动态子进程场景下根本不存在单点控制。
-
opcache_reset()只作用于当前请求所在的 PHP 进程,其他 worker 进程缓存未清——你以为刷完了,其实只有 1/8 的请求看到新代码 - 负载均衡后端有多个 PHP-FPM 实例?每个都要单独调用
opcache_reset(),或统一发信号 reload,否则必然出现新旧混跑 - 某些云平台(如 AWS Elastic Beanstalk、阿里云函数计算)禁止执行系统命令或写临时文件,
opcache.file_cache和opcache_reset()都不可用,只能靠部署时清空缓存目录 + 重启
最稳的做法其实是:部署流程中加入 systemctl reload php*-fpm,并配合健康检查确保所有 worker 已切换。临时调试才用 opcache-reset.php,且务必加 IP 白名单和随机 token 验证。











