PhpUnit单元测试中如何伪造时间过期的文件?
问题根因与解决方案
你遇到的问题和Alpine Linux本身无关,是两个PHP和文件属性的特性导致的:
- 你业务代码中调用的
filectime()获取的是inode变更时间,不是文件内容修改时间。当你用touch命令修改文件的访问时间、修改时间时,会触发inode元数据变更,ctime会自动被系统更新为当前时间,所以你读取到的filectime永远是当前操作的时间,不是你设置的历史时间。 - PHP默认开启了文件状态缓存,所有
stat系列函数(包括filectime、filemtime、file_exists等)的返回值都会被缓存,你修改完文件属性后如果不清理缓存,读取到的还是之前缓存的旧值。
修复方案
方案1:适配业务逻辑+清理缓存
如果你的业务逻辑实际需要判断的是文件内容是否超过1天未修改,将业务代码中的filectime替换为filemtime,filemtime获取的是文件内容修改时间,也就是touch -m参数修改的时间。
修改测试代码,每次操作文件前后清理指定文件的状态缓存:
$location = '/var/www/var/xls/myfile.xlsx'; $handle = fopen($location, 'wb'); fclose($handle); // 清理文件状态缓存,避免残留旧数据 clearstatcache(true, $location); // 注意用escapeshellarg转义路径,避免安全问题 exec('touch -m -t 202109231158 ' . escapeshellarg($location)); // 再次清理缓存,确保读取到最新属性 clearstatcache(true, $location); // 此时读取filemtime即可拿到设置的2021-09-23时间 var_dump(date('Ymd', filemtime($location)));
方案2:单元测试直接mock时间函数
不需要创建实际文件,用PHPUnit的函数mock功能或者uopz扩展直接mockfilectime/filemtime的返回值,测试更轻量,也不会受文件系统特性影响。
内容的提问来源于stack exchange,提问作者Calamity Jane
相关产品推荐
相关产品推荐

