PHP的XMLReader扩展提供了拉模式解析XML的能力,这种模式不需要一次性将整个XML文件加载到内存中,而是通过程序主动控制读取节奏,逐节点处理内容,非常适合解析体积较大的XML文件。拉模式解析的核心是由开发者决定何时读取下一个节点,而不是像事件驱动模式那样被动等待回调,逻辑更清晰可控。

XMLReader拉模式解析基础
拉模式解析的工作流程是:先打开XML文件,然后循环调用read()方法读取下一个节点,根据当前节点的类型做对应的处理,直到文件读取完毕。XMLReader支持多种节点类型,常用的有元素节点、文本节点、属性节点等,我们可以通过nodeType属性判断当前节点的类型。
常用节点类型常量
- XMLReader::ELEMENT:元素开始节点
- XMLReader::TEXT:文本节点
- XMLReader::END_ELEMENT:元素结束节点
- XMLReader::ATTRIBUTE:属性节点
基础使用示例
下面是一个简单的示例,解析一个包含书籍信息的XML文件,读取每本书的标题和作者信息。
首先准备测试用的XML文件books.xml,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book category="编程">
<title>PHP从入门到精通</title>
<author>张三</author>
<price>89.9</price>
</book>
<book category="文学">
<title>平凡的世界</title>
<author>路遥</author>
<price>59.8</price>
</book>
</bookstore>
使用XMLReader拉模式解析该文件的代码如下:
<?php
// 初始化XMLReader对象
$reader = new XMLReader();
// 打开XML文件,也可以是远程URL,如http://ipipp.com/books.xml
$reader->open('books.xml');
// 循环读取节点
while ($reader->read()) {
// 判断当前节点是book元素的开始节点
if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'book') {
// 读取book元素的category属性
$category = $reader->getAttribute('category');
echo "书籍分类:$category" . PHP_EOL;
// 继续读取book内部的子节点
while ($reader->read()) {
// 遇到book元素的结束节点,跳出内层循环
if ($reader->nodeType == XMLReader::END_ELEMENT && $reader->name == 'book') {
break;
}
// 处理title子元素
if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'title') {
// 读取文本节点内容
$reader->read();
echo "标题:" . $reader->value . PHP_EOL;
}
// 处理author子元素
if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'author') {
$reader->read();
echo "作者:" . $reader->value . PHP_EOL;
}
// 处理price子元素
if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'price') {
$reader->read();
echo "价格:" . $reader->value . PHP_EOL;
}
}
echo "-------------------" . PHP_EOL;
}
}
// 关闭XMLReader,释放资源
$reader->close();
?>
进阶操作技巧
读取属性值
除了使用getAttribute()方法获取指定属性,还可以通过moveToAttribute()方法移动到指定属性节点,再读取属性值:
<?php
$reader = new XMLReader();
$reader->open('books.xml');
while ($reader->read()) {
if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'book') {
// 移动到category属性
if ($reader->moveToAttribute('category')) {
echo "分类:" . $reader->value . PHP_EOL;
// 移动回元素节点
$reader->moveToElement();
}
}
}
$reader->close();
?>
设置解析选项
XMLReader支持设置解析选项,比如是否验证XML格式、是否忽略空白节点等,通过setParserProperty()方法设置:
<?php
$reader = new XMLReader();
// 忽略空白节点,减少不必要的处理
$reader->setParserProperty(XMLReader::VALIDATE, false);
$reader->setParserProperty(XMLReader::SUBST_ENTITIES, false);
$reader->open('books.xml');
// 解析逻辑同上
while ($reader->read()) {
// 处理逻辑
}
$reader->close();
?>
注意事项
- 使用
open()方法打开文件后,一定要在最后调用close()方法释放资源,避免内存泄漏。 - 拉模式解析中,读取文本节点前需要先调用一次
read()方法,因为元素的开始节点和内部的文本节点是两个独立的节点。 - 如果XML文件中有命名空间,需要通过
namespaceURI属性判断节点的命名空间,避免解析错误。 - 解析本地文件时需要注意文件路径的正确性,解析远程文件时需要确保PHP的
allow_url_fopen配置是开启状态。