最轻量的文件修改监控方式是轮询filemtime():先用is_file()确认存在,保存上次时间戳并设1秒容错阈值,Web端轮询间隔≥1s;Linux下可用inotify扩展实现内核级事件驱动监听,需注意递归配置与资源限制。

用 filemtime() 检查文件是否被修改
最轻量的监控方式就是轮询检查文件最后修改时间。PHP 本身没有原生文件监听机制(不像 Node.js 的 fs.watch),所以得靠定时读取 filemtime() 来判断变化。
关键点在于:不能只比对一次,必须保存上一次的时间戳做差值判断;且要避免因系统时钟回拨或 NFS 延迟导致误判。
- 每次检查前先用
is_file()确认文件存在,防止filemtime()报Warning: filemtime(): stat failed - 把上次的
filemtime()存在内存变量或临时文件里(别用 session,不跨请求) - 建议加 1 秒容错阈值,比如
abs($now - $last) > 1才认为是真实变更 - 不要高频轮询(如 100ms 一次),Web 场景下建议 ≥1s,CLI 脚本可缩至 200ms
用 inotify 扩展实现真正的内核级监听(Linux)
如果服务器是 Linux 且能装扩展,inotify 是唯一接近实时、低开销的选择。它依赖内核 inotify 子系统,不轮询,事件驱动。
但要注意:inotify 不是默认开启的,需确认 PHP 编译时带 --enable-inotify,或通过 pecl install inotify 安装。
立即学习“PHP免费学习笔记(深入)”;
- 一个
Inotify实例最多监听 8192 个文件/目录(受/proc/sys/fs/inotify/max_user_watches限制) -
inotify_add_watch()对目录加IN_MODIFY不会触发子文件变化,必须用IN_CREATE | IN_MOVED_TO | IN_MODIFY组合,并递归监听子目录 - 监听后必须调用
inotify_read()阻塞等待事件,否则 CPU 占用飙升 - 返回的事件数组里,
$event['name']是相对路径,$event['mask']要用位运算判断,比如($event['mask'] & IN_MODIFY)
用 tail -f + proc_open() 监控日志类追加写入文件
如果是监控不断追加内容的日志文件(如 access.log),用 tail -f 比轮询更省资源,也比 inotify 更兼容旧环境。
核心是用 proc_open() 启动后台 tail -f 进程,持续读取 stdout 流,适合 CLI 模式长期运行。
- 必须设置
stream_set_blocking($pipes[1], false),否则fread()会卡住 -
tail -f在文件被 logrotate 切割后可能失效,加--follow=name --retry参数可恢复 - PHP 进程退出前务必用
proc_terminate()+proc_close()清理子进程,否则残留tail进程会占句柄 - 注意编码问题:若日志含中文,
fread()返回的是原始字节流,需按实际编码处理(通常 UTF-8 或 locale 编码)
为什么不用 scandir() 或 glob() 做文件增删监控
有人想用定期 scandir() 对比文件列表来发现新增/删除,这在小目录可行,但一到大目录就暴露问题:
-
scandir()会一次性读取全部文件元信息,10 万文件目录下耗时可能超秒级,CPU 和 I/O 压力陡增 - 无法区分“重命名”和“删除+新建”,因为文件名变了,但
inode可能未变(尤其 ext4 默认启用dir_index) -
glob('*.log')在通配符复杂时(如嵌套**)性能更差,且不返回修改时间,还得额外调用filemtime() - Windows 下
scandir()性能比 Linux 差一个数量级,且 NTFS 时间精度为 100ns,PHPfilemtime()只精确到秒,容易漏判
真正需要监控增删场景,优先走 inotify(Linux)或放弃 PHP 改用 rsync --watch / entr 等外部工具,PHP 只做事件回调处理。



















