在 PHP 中处理大型 XML 文本节点


Process large XML text nodes in PHP

这个问题中,讨论了如何使用流解析PHP中的大型XML文档,以便不必将整个文档放入内存中。

但是,XMLReader类似乎不适合分析 XML 文档中的大型文本节点。由于我使用的 API 将 base64 编码的文件作为 XML 文档的值以及一些元数据发送,因此我正在寻找一种流式传输这些文本节点的方法,而不是将值作为字符串返回:

<?php
$reader = XMLReader::open($someStream);
// $reader->read() until a node is reached
// The following puts the whole text node in memory, rather than creating a stream
$content = $reader->value; 
?>

是否可以将$reader->value变成溪流?

我想出的是使用PHP的低级XML解析器和一些流函数。

$input = fopen('input.xml', 'r');
$output = fopen('output.txt', 'w');
stream_filter_append($output, 'convert.base64-decode');

这些被传递给创建 XML 解析器的类:

public function __construct($input, $output) {
    // ...
    $this->xml = xml_parser_create();
    xml_set_object($this->xml, $this);
    xml_set_element_handler($this->xml, 'start', 'end');
    xml_set_character_data_handler($this->xml, 'character');
}

startend 方法用于在 XML 中查找正确的元素,character 方法将内容写入输出流:

protected function character($parser, $data)
{
    if ($this->match()) {
        fwrite($this->output, $data);
    }
}

高效的部分是我们调用解析器的地方,它一次只读取可管理的块:

while ($data = fread($this->input, $bufferSize = 1024)) {
    xml_parse($this->xml, $data, feof($this->input) or $this->done);
}

$this->done可以在startend处理程序中设置,就我而言,一旦找到匹配项,我就会完全删除处理程序。

由于这些旧的 php 函数不会抛出,因此当然仍然必须实现一些安全检查。