最直接批量处理流程是用 scandir() 列出文件并逐个处理,需用 is_file() 过滤目录项;大文件量改用 directoryiterator;按扩展名筛选优先用 glob();web 环境加 set_time_limit(0) 防超时;并发写日志须用 flock() 加锁;务必记录 error_log 处理结果。

用 scandir() 列出目录下所有文件再逐个处理最直接
批量流程本质就是「读取一批文件路径 → 对每个路径执行相同逻辑」。PHP 里最轻量、兼容性最好的方式是 scandir() 配合 is_file() 过滤掉 . 和 ..,避免误操作目录项。
常见错误是直接遍历 scandir() 返回的全部结果,导致对目录名(如 logs/)调用 fopen() 报错:Warning: fopen(): failed to open stream: Is a directory。
- 务必用
is_file($path)或!is_dir($path)显式判断 - 路径拼接建议统一用
dirname(__FILE__) . '/data/' . $filename,避免相对路径歧义 - 如果文件量大(比如上万),
scandir()会一次性加载全部文件名到内存,此时改用DirectoryIterator更省内存
用 glob() 按扩展名快速筛选文件
如果你只处理特定类型(比如所有 .csv 或 .log),glob() 比手动过滤更简洁,也天然规避了目录项干扰。
典型误用是写成 glob('*.log') 却没注意当前工作目录(getcwd())不是你预期的目录,结果什么也匹配不到。
- 推荐写死路径:
glob('/var/log/app/*.log') - Windows 下注意反斜杠转义,或统一用正斜杠:
glob('C:/temp/*.txt') -
glob()不支持递归,要查子目录得用glob('**/*.php', GLOB_BRACE | GLOB_ONLYDIR)(PHP 7.1+)或改用RecursiveDirectoryIterator
批量处理时加 set_time_limit(0) 防超时
默认 PHP 脚本执行时间限制是 30 秒,100 个文件哪怕每个只花 0.4 秒也会超时。不加这句,脚本中途静默失败,只留下部分文件被处理。
但要注意:CLI 模式下 set_time_limit() 默认无效(值为 0),只有 Web SAPI(如 Apache、FPM)才需要显式设为 0;CLI 反而要小心无限循环卡死。
- Web 环境下,放在批量逻辑开始前:
set_time_limit(0); - 更稳妥的做法是按批次处理,比如每 20 个文件
sleep(1),既防超时也降低服务器压力 - 如果涉及大文件读写,还要检查
memory_limit,必要时用unset($content)主动释放变量
用 flock() 避免并发写同一日志或状态文件
当多个请求或定时任务同时跑同一批文件时,如果都往一个 process.log 写进度,会出现内容错乱甚至覆盖——这是批量流程上线后最容易被忽略的坑。
flock() 是 PHP 原生支持的轻量级文件锁,不需要额外扩展,但必须配合 fopen() 的写模式使用,且锁在文件句柄关闭时自动释放。
- 正确姿势:
$fp = fopen('status.lock', 'c'); flock($fp, LOCK_EX); file_put_contents('process.log', "done: $file\n", FILE_APPEND); flock($fp, LOCK_UN); fclose($fp); - 不要用
file_put_contents(..., FILE_APPEND)单独操作日志文件,它不带锁 -
LOCK_NB可用于非阻塞尝试,适合做“抢占式任务分发”
批量流程真正难的不是循环本身,而是边界情况:空目录、权限不足、文件被其他进程占用、磁盘满、编码不一致导致 mb_strlen() 计算错误……这些往往在测试环境不暴露,一上生产就卡住。留个 error_log() 记录每个文件的处理结果,比事后翻日志快得多。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











