最近有一个场景一直让我郁闷,在一个基于 Wind 框架开发的长驻程序中,会经常往某个临时目录写入文件,但程序日志中每隔一段时间就会报找不到文件的错误,原因是那个目录不存在,后来想起来该临时目录每隔一段时间就会被一个脚本清理,而清理的方式是直接删除这个目录。这个临时目录在之前的程序中(传统的 php-fpm 模式的程序),每次写入会判断该临时目录是否存在,不存在则创建,于是在这个长驻的程序中也加入了这样的逻辑。代码很简单,如下:
$dir = dirname($realPath); if (!is_dir($dir)) { mkdir($dir); }
心想这下总该没问题了吧,但是程序运行一段时间之后,那个错误依然存在,这就很奇怪了,反复测试确认程序是能在该位置创建目录的。
经过一会琢磨,想起了 clearstatcache() 这个函数,此函数简单的说是可以清除 php 运行中关于文件系统的一些状态缓存,文档中描述是 stat(), lstat(), file_exists(), is_writable(), is_readable(), is_executable(), is_file(), is_dir(), is_link(), filectime(), fileatime(), filemtime (), fileinode(), filegroup(), fileowner(), filesize(), filetype() 和 fileperms() 这些函数都会受这个缓存的影响。
不过经过我的测试,is_file 和 is_dir 确实会受文件状态缓存的影响,而 file_exists 则不受影响,并且有时多次调用 is_dir 对多个目录进行判断,会有部分较早调用的 is_dir 不受缓存影响,这里有点琢磨不透,猜测这里的缓存机制应该并不是简单的全部缓存,而是具有某些类似于 LRU 的机制。
在长驻进程中(可以使用 php -a)进行测试,用 is_dir 对某个目录判断是否存在,当这个目录存在时,在另一处删除这个目录,只要之前的 php 进程并未关闭,那么该进程的 is_dir 会始终认为这个目录是存在的,除非主动调用了 clearstatcache() 清除状态缓存,或者该目录是由进程中的 unlink() 函数删除的,也会自动清除该目录的状态缓存。
所以保险起见,在常驻进程的 PHP 程序中应使用 file_exists() 来判断文件或目录是否存在,不过虽然 file_exists() 在实际测试中并不受缓存影响,但文档中描述它也是受影响的,真正的保险起见,在判断文件是否存在前应还是先调用一次 clearstatcache() 最佳。