一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

PHP 8.5.7 解析 JSON 大文件不再内存溢出【数据处理】

时间:2026-07-12 09:15:51 编辑:袖梨 来源:一聚教程网

json-machine通过流式解析实现恒定内存占用,解决传统json_decode因全量加载导致的内存溢出问题;它基于Generator逐项处理JSON,峰值内存与文件大小无关,实测100GB文件仅需3–5MB内存。

直接用 json_decode(file_get_contents($file)) 解析 25MB+ 的 JSON 文件,内存必然爆——这不是 PHP 版本问题,是数据规模和处理模型根本错配。

为什么 json_decode() 一读就崩?

它必须把整个 JSON 字符串加载进内存,再递归构建嵌套数组/对象。25MB 原始 JSON 解码后常占 100MB+ 内存;500MB 文件基本无解。更隐蔽的是:Web SAPI 下还叠加了 max_execution_time 和输出缓冲限制,脚本可能静默中断,连错误都不报。

  • 别信 ini_set('memory_limit', '2G') —— 单请求吃掉 2GB 内存,高并发时 PHP-FPM 进程池直接雪崩
  • json_last_error() 返回 JSON_ERROR_NONE 也不代表成功:若因超时被 kill,函数根本没执行完
  • 用 CLI 运行只是绕过 Web 限制,不解决内存模型缺陷——仍会 OOM

用 json-machine 实现恒定内存解析

它基于生成器(Generator)做流式解析,只在内存中保留当前 JSON 项,峰值内存与文件大小无关。实测解析 100GB JSON,内存稳定在 3–5MB。

  • 安装:composer require halaxa/json-machine
  • 基础用法:$items = JsonMachineItems::fromFile('data.json');,然后 foreach ($items as $key => $value) 迭代
  • 只取子路径(如 /results/users):Items::fromFile('data.json', ['pointer' => '/results/users'])
  • 若 JSON 是“多行 JSON”(每行一个独立对象),改用 Items::fromIterable(new JsonMachineLineReader('data.json'))

遇到嵌套深、含 BOM、非 UTF-8 怎么办?

流式解析器不自动处理编码和结构异常,这些得前置清理:

立即学习“PHP免费学习笔记(深入)”;

  • BOM 必须手动剔除:$content = file_get_contents('data.json'); $content = ltrim($content, "xEFxBBxBF");,再喂给 Items::fromString()
  • 非 UTF-8 编码(如 GBK 中文)需转码:$content = mb_convert_encoding($content, 'UTF-8', 'GBK');
  • 嵌套超 512 层?传统 json_decode() 会报 JSON_ERROR_DEPTH,但 json-machine 不受此限——它根本不预建完整树
  • 字段缺失或类型混乱?在 foreach 循环里用空合并操作符:$name = $user->name ?? 'unknown';

比 json-machine 更轻量的替代方案

若项目不能引入第三方依赖,可用 pcrov/jsonreader(纯拉式解析器,零依赖)或手写简易状态机:

  • pcrov/jsonreader 安装:composer require pcrov/jsonreader;核心是 $reader->read() + $reader->value(),适合需精细控制节点遍历的场景
  • 手写方案仅适用于“每行一个 JSON 对象”的日志类文件:用 fopen() + fgets() 逐行读,每行调 json_decode($line, null, 512, JSON_THROW_ON_ERROR) ——注意显式传 JSON_THROW_ON_ERROR 避免 silent fail
  • 绝对不要用 substr() 或正则硬切 JSON,结构稍复杂就崩溃

真正卡住人的从来不是库选不对,而是把“大文件”当成“一个整体”来对待。只要记住:任何需要 file_get_contents() 全量加载的方案,在 25MB+ 场景下都该被标记为危险操作。

热门栏目