PHP XMLWriter 怎样分段取出内存缓冲区的内容
来源:17golang原创
时间:2026-11-10 08:15:50 189浏览 收藏
需要把大量记录写成 XML 时,XMLWriter 的内存模式可以按批次取出已经生成的字符串。关键不是“每写一个标签就调用一次输出”,而是先用 openMemory() 创建内存写入器,再在合适的批次边界调用 outputMemory(true) 或 flush(true)。参数为 true 时,取出的内容会从内部缓冲区移走;如果传入 false,内容仍会保留,下一次取出可能再次包含它。
本文只讨论 XMLWriter 的内存输出,不把中间片段冒充成完整 XML 文档。官方参考地址:https://www.php.net/manual/en/book.xmlwriter.php
先分清 outputMemory 与 flush
XMLWriter::outputMemory(bool $flush = true) 返回当前缓冲区字符串,$flush 控制返回后是否清空缓冲区;XMLWriter::flush(bool $empty = true) 也会刷新当前缓冲区,在内存模式下同样返回生成的 XML 字符串,第二个参数控制是否清空。两者最容易混淆的地方是:方法名不同不代表一个会“自动分批”、另一个不会,真正决定下一批是否重复旧内容的是清空参数。
| 调用 | 取出结果 | 内部缓冲区 | 适用场景 |
|---|---|---|---|
outputMemory(true) | 当前 XML 字符串 | 清空 | 分批消费,控制内存 |
outputMemory(false) | 当前 XML 字符串 | 保留 | 只读取快照,继续拼同一份结果 |
flush(true) | 当前 XML 字符串 | 清空 | 使用 flush 语义输出批次 |
flush(false) | 当前 XML 字符串 | 保留 | 查看当前内容但不移动游标 |

按批次取出内存缓冲区
最稳妥的分段模式是把“生成一批元素”和“取出这一批字符串”放在同一个循环边界。下面的示例每两条记录取一次,outputMemory(true) 让已经交给下游的内容不再留在 XMLWriter 的内部缓冲区。
openMemory();
$writer->startDocument('1.0', 'UTF-8');
$writer->startElement('items');
$records = [
['id' => 101, 'name' => '键盘'],
['id' => 102, 'name' => '鼠标'],
['id' => 103, 'name' => '显示器'],
['id' => 104, 'name' => '耳机'],
];
$chunks = [];
foreach ($records as $index => $record) {
// 每个 item 是一个完整子元素,避免在元素内部截断。
$writer->startElement('item');
$writer->writeAttribute('id', (string) $record['id']);
$writer->writeElement('name', $record['name']);
$writer->endElement();
// 每两条记录取出一次,并清空已交付的缓冲区。
if (($index + 1) % 2 === 0) {
$chunks[] = $writer->outputMemory(true);
}
}
// 处理不足一个批次的尾部记录。
$tail = $writer->outputMemory(true);
if ($tail !== '') {
$chunks[] = $tail;
}
// 关闭根元素和文档;这部分属于文档收尾,不再当作业务批次。
$writer->endElement();
$writer->endDocument();
$documentEnd = $writer->outputMemory(true);
foreach ($chunks as $chunk) {
// 这里可以写入队列、响应流或临时文件。
processXmlChunk($chunk);
}
processXmlChunk($documentEnd);
?>
这个示例的重点是边界:批次中拿到的是已经生成的 XML 片段,根元素尚未闭合,所以它们未必能单独作为完整文档交给严格的 XML 解析器。如果下游要求“每次都是完整 XML”,应让每个批次拥有自己的根元素,或者在业务层先封装后再发送。
为什么 outputMemory(true) 能控制内存
当 true 传给 outputMemory() 时,方法返回当前缓冲区并清空它。下一轮写入从空缓冲区继续,因而适合生成大量数据时把片段写到队列、文件或网络层。它不会替你关闭尚未结束的 XML 元素,也不会替你判断片段是否满足下游协议;它只负责返回并按参数处理当前缓冲区。
调用时机也很重要。刚创建 writer 或刚清空缓冲区时调用,得到空字符串是正常现象;如果在 startElement() 之后立刻取出,拿到的可能只有未闭合的开始标签。把取出点放在一个完整业务元素之后,通常更容易让下游按片段处理。
flush=true 与 flush=false 怎么选
flush(true) 与 outputMemory(true) 都适合“取出后继续写新内容”的分段场景;而 flush(false) 与 outputMemory(false) 更像读取当前快照。后者不会清掉旧内容,若循环中反复读取,返回字符串会包含之前保留的内容,不能直接当作“下一批”。

openMemory();
$writer->startElement('root');
$writer->writeElement('message', '第一段');
$snapshot = $writer->flush(false);
// 继续写入后,flush(true) 会返回保留的旧内容加上新内容,并清空缓冲区。
$writer->writeElement('message', '第二段');
$chunk = $writer->flush(true);
// 结束元素后再取收尾内容,避免把根元素的关闭标签遗漏。
$writer->endElement();
$writer->endDocument();
$end = $writer->flush(true);
consume($snapshot, $chunk, $end);
?>
如果只是想把同一份完整结果不断追加到一个变量,保留缓冲区可以工作,但内存会随着输出增长。若目标是流式传输或大批量导出,通常应优先清空,并把返回值及时交给下游。
文档完整性要自己设计
XMLWriter 是向前写入器,分段取出并不等于自动生成多个独立文档。像 这样的根元素如果在所有业务数据完成后才关闭,那么中间片段只是一份连续输出流的一部分。可以采用三种策略:
- 下游接受 XML 片段流:保持一个根元素,按完整子元素分批取出。
- 下游要求独立文档:每批重新创建 writer,并为每批写入独立根元素。
- 下游要求一个完整文件:不清空,最终统一取出,或直接使用
openUri()写入目标 URI。
不要把“已经取到字符串”误认为“已经完成 XML 文档”。尤其是把片段放进消息队列时,要同时传递批次序号、是否首批、是否末批等元数据,消费端才能正确拼接或解析。
常见问题:为什么拿到空字符串或重复内容
为什么刚调用 outputMemory 就是空的
调用时缓冲区可能还没有产生可输出内容,或者上一次调用使用了默认的 true 并已清空。把调用点移到写入完整元素之后,并确认是否有意清空。
为什么每次取出都包含上一批
通常是使用了 outputMemory(false) 或 flush(false)。这两个调用只返回当前内容,不负责删除旧内容;分批消费时改成 true,或在业务层自行记录已处理偏移。
为什么片段无法直接解析
片段可能缺少根元素、关闭标签或 XML 声明。分段输出时要明确下游协议:接受连续片段,还是要求每一批都是独立文档;两者的 writer 生命周期不同。
小结
PHP XMLWriter 分段取内存内容的核心只有两点:先用 openMemory() 建立内存写入器,再根据消费模型选择是否清空。outputMemory(true) 与 flush(true) 适合把已生成片段交给下游并控制内存;false 适合查看快照,但会保留旧内容。最后,缓冲区边界和 XML 文档边界不是一回事,根元素的生命周期必须由业务代码明确管理。
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
129 收藏
-
293 收藏
-
163 收藏
-
381 收藏
-
178 收藏
-
331 收藏
-
384 收藏
-
399 收藏
-
378 收藏
-
219 收藏
-
204 收藏
-
447 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习