PHP 7.4 中 FilterIterator 比 glob() 更可控、省内存,但非绝对更快;关键在 accept() 中避免耗时操作、使用缓存属性、确保路径真实,并注意 OPcache 预加载。

直接说结论:PHP 7.4 下用 FilterIterator 及其子类(比如 php-file-iterator 的 Iterator)处理文件,比 glob() 或 scandir() + 手动 array_filter() 更可控、更省内存,但**不是无脑快**——关键在过滤逻辑是否提前剪枝、路径是否真实、是否误用 file_exists()。
为什么用 FilterIterator 而不是 glob()?
glob() 看似简单,但它底层调用的是系统级通配符展开,不支持动态条件(比如“排除所有以 .tmp 结尾且修改时间早于 24 小时的文件”),也无法复用迭代器链。而 FilterIterator 是 PHP 原生接口,可组合、可继承、可中断,适合构建可维护的文件发现流程。
-
glob("*.php")会一次性把匹配结果全加载进内存,文件多时易爆内存;FilterIterator是逐个accept()判断,天然惰性 -
glob()不区分文件/目录,FilterIterator的accept()方法里能用is_file()或is_dir()精确控制 -
glob()对路径中特殊字符(如空格、括号)容易出错;FilterIterator操作的是SplFileInfo对象,路径已转义、安全
php-file-iterator 的 accept() 方法怎么写才不拖慢?
常见错误是把耗时操作塞进 accept(),比如每轮都调用 file_exists() 或 stat()——这会让性能掉回原点。它本该只做轻量判断。
- 优先用
$file->getFilename()和$file->getExtension()做字符串匹配,避免系统调用 - 需要时间判断时,用
$file->getMTime()(已缓存),别再filemtime($file) - 绝对不要在
accept()里打开文件、读内容、计算 hash —— 这违背迭代器设计初衷 - 示例:只接受
.test.php且非隐藏文件
protected function accept(): bool
{
$filename = $this->current()->getFilename();
return !str_starts_with($filename, '.')
&& str_ends_with($filename, '.test.php');
}
Iterator 构造时传入的路径必须是真实路径
传相对路径或符号链接未解析路径,会导致 acceptPath() 判断失效,甚至跳过整个子目录。这不是 bug,是设计使然。
立即学习“PHP免费学习笔记(深入)”;
- 务必用
realpath($path)或dirname(__DIR__) . '/tests'拼出绝对路径 - 如果路径不存在,
php-file-iterator会静默跳过,不报错也不抛异常——容易误以为“没文件”,实际是路径错了 - 遇到 symlink 目录,
RecursiveDirectoryIterator默认不跟随,需显式传RecursiveIteratorIterator::SELF_FIRST | RecursiveIteratorIterator::CATCH_GET_CHILD标志
和 loophp/collection 混用时要注意什么?
可以混用,但别指望自动优化。loophp/collection 的 Collection::fromIterator() 能包装 Iterator,但若原始 Iterator 本身做了大量 I/O(比如每个 current() 都读文件头),collection 的惰性也没用。
- 先确保
php-file-iterator层只做路径筛选,I/O 留给后续 pipeline - 用
Collection::fromIterator($iterator)->filter(...)->map(...)是安全的,但filter()里别再调file_get_contents() - 真正的大文件日志处理场景,应优先用
yield from+Generator,而非把Iterator全转成 collection
最常被忽略的一点:PHP 7.4 的 FilterIterator 子类默认不启用 OPcache 预编译加速——因为它们常被动态 new 出来。如果高频使用(比如每次请求都 new 一个测试发现器),建议把自定义 Iterator 类预加载,否则每次都要重解析。



















