Windows下PHP CLI终止运行时损坏丢失主执行PHP文件问题咨询
问题描述
运行PHP CLI后,有时被执行的主PHP文件会被直接擦除、彻底消失,出现文件丢失/损坏问题。通过Netbeans历史记录的「撤销删除」功能仍可看到该文件记录,但无法完成恢复,也无法在同路径下使用完全相同的文件名替换/重建该文件。

试错排查记录
- 该问题在3台不同设备上均有复现,设备系统为Windows 10或Windows 11;测试过多个PHP版本(php-7.3.10-Win32-VC15-x64、php-8.1.4-Win32-vs16-x64)均存在该问题。
- 出问题的代码无任何文件IO操作,是基于React事件循环实现的WebSocket监听服务,文件名为
server_worklistobserver.php,代码如下:
<?php require __DIR__ . '/vendor/autoload.php'; $context = array( 'tls' => array( 'local_cert' => "certificate.crt", 'local_pk' => "certificate.key", 'allow_self_signed' => true, // 生产环境请设置为false 'verify_peer' => false ) ); // 初始化websocket服务 $loop = React\EventLoop\Factory::create(); $application = new Ratchet\App('my.domain.com', 8443, 'tls://0.0.0.0', $loop, $context); // 初始化控制器组件 $controller = new WorklistObserver(); $application->route('/checkworklist', $controller, array('*')); $application->run(); die('-server stopped-');
- 文件丢失均发生在PHP执行被终止的场景:包括CMD窗口按Ctrl-Break中断进程、将程序作为服务运行时执行服务停止/重启操作。
- 程序启动方式为在CMD命令行中执行:
php.exe server_worklistobserver.php。 - 测试使用管理员权限运行程序无改善,执行磁盘扫描未发现磁盘异常。该问题持续存在但无稳定复现规律,属于偶发问题;同目录下其他关联PHP文件均保持完好,Apache模式下运行PHP从未出现过该异常。
求助内容
请问可以调整哪些配置或操作规避该问题?是否有其他开发者遇到过同类问题?目前在公开网络未检索到相似问题案例,提前感谢各位解答。
解答
这是Windows平台下PHP CLI + Opcache搭配长驻进程、强制终止场景下的冷门已知bug,和代码逻辑、磁盘硬件无关。
问题根因
Windows的文件锁机制和类Unix系统逻辑不同,CLI模式下开启Opcache时,PHP会对直接执行的主入口脚本做内存映射并加排他文件锁。当你通过Ctrl+Break、强制停止服务等方式硬终止进程时,Opcache的资源回收逻辑会因为进程上下文异常中断出现错乱,原本应该释放文件句柄、清空缓存的流程,会误触发临时缓存文件的删除逻辑,直接把主脚本文件的MFT记录标记为删除。
此时文件句柄还被残留的PHP进程占用没有释放,所以会出现能在IDE历史里看到文件记录、但无法恢复、也无法新建同名文件的现象。
这个问题的触发特征和实际场景完全吻合:
- 仅CLI模式出现:Apache/FPM等SAPI模式下Opcache的缓存策略不同,不会对单个入口脚本加排他锁,因此不会触发
- 仅主入口文件丢失:require引入的依赖文件、其他业务脚本不会被Opcache做这种级别的内存映射,因此不会受影响
- 偶发无稳定规律:只有进程终止的时机刚好卡在Opcache的缓存检查窗口期才会触发,短生命周期CLI脚本执行速度快,正常退出前就完成资源释放,只有React/Ratchet这类长驻进程才会长期占用文件锁,给硬终止触发bug留了条件
规避方案(按优先级排序)
- 最彻底的解决方案:关闭CLI模式下的Opcache
在命令行执行php --ini找到CLI模式对应的php.ini文件(注意不要改到Apache用的php.ini),修改配置项:
修改后重启命令行窗口即可生效。这个改动对WebSocket这类长驻服务几乎没有性能影响:Opcache的加速作用主要体现在短脚本重复请求场景,长驻进程启动后代码已经全部加载到内存,运行期不会重复读取磁盘文件,仅启动时会有几毫秒的差异,完全可以忽略。opcache.enable_cli=0 - 如果需要保留CLI Opcache,调整配置降低触发概率
在php.ini中修改以下配置,把锁文件移出脚本目录、关闭文件更新保护逻辑:opcache.file_update_protection=0 opcache.lockfile_path = "C:\Windows\Temp" - 尽量避免硬终止进程
可以给代码增加信号监听逻辑,收到终止信号时先优雅停止事件循环,再退出进程,避免Windows强杀进程:
如果Windows环境没有安装pcntl扩展,停止服务时尽量用// 放在$application->run()之前 if (function_exists('pcntl_signal')) { pcntl_signal(SIGINT, function() use ($loop) { $loop->stop(); }); pcntl_signal(SIGTERM, function() use ($loop) { $loop->stop(); }); }taskkill /im php.exe命令终止,不要直接在任务管理器结束进程树、不要按Ctrl+Break,能大幅降低触发概率。 - 故障应急处理
如果已经出现文件丢失、无法新建同名文件的情况,不需要重启电脑,打开任务管理器结束所有残留的php.exe进程,释放被占用的文件句柄后,就可以正常恢复文件、新建同名文件了。
内容的提问来源于stack exchange,提问作者Ido
相关产品推荐
相关产品推荐

