| 常數 | 說明 |
XML_ERROR_NONE | 無錯誤 |
XML_ERROR_NO_MEMORY | 記憶體不足 |
XML_ERROR_SYNTAX | 語法錯誤 |
XML_ERROR_NO_ELEMENTS | 無元素 |
XML_ERROR_INVALID_TOKEN | 無效標記 |
XML_ERROR_UNCLOSED_TOKEN | 未閉合標記 |
XML_ERROR_PARTIAL_CHAR | 部分字元 (編碼問題) |
XML_ERROR_TAG_MISMATCH | 標籤不匹配 |
XML_ERROR_DUPLICATE_ATTRIBUTE | 重複屬性 |
XML_ERROR_JUNK_AFTER_DOC_ELEMENT | 根元素後有垃圾資料 |
XML_ERROR_PARAM_ENTITY_REF | 參數實體參考錯誤 |
XML_ERROR_UNDEFINED_ENTITY | 未定義實體 |
XML_ERROR_RECURSIVE_ENTITY_REF | 遞迴實體參考 |
XML_ERROR_ASYNC_ENTITY | 非同步實體 |
XML_ERROR_BAD_CHAR_REF | 無效字元參考 |
XML_ERROR_BINARY_ENTITY_REF | 二進制實體參考 |
XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF | 屬性外部實體參考 |
XML_ERROR_MISPLACED_XML_PI | XML PI 位置錯誤 |
XML_ERROR_UNKNOWN_ENCODING | 未知編碼 |
XML_ERROR_INCORRECT_ENCODING | 編碼不正確 |
XML_ERROR_UNCLOSED_CDATA_SECTION | CDATA 區段未閉合 |
XML_ERROR_EXTERNAL_ENTITY_HANDLING | 外部實體處理錯誤 |
實戰範例:事件驅動解析大型 XML
<?php
class LargeXmlParser {
private $parser;
private $currentElement = '';
private $data = [];
private $itemCount = 0;
public function __construct() {
$this->parser = xml_parser_create('UTF-8');
xml_set_object($this->parser, $this);
xml_set_element_handler($this->parser, 'startElement', 'endElement');
xml_set_character_data_handler($this->parser, 'characterData');
// 可選:關閉大小寫摺疊保持原始大小寫
xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
}
public function parseFile($filename, $chunkSize = 8192) {
$fp = fopen($filename, 'r');
if (!$fp) throw new Exception("無法開啟檔案: $filename");
while (!feof($fp)) {
$data = fread($fp, $chunkSize);
if (!xml_parse($this->parser, $data, feof($fp))) {
$this->handleError();
}
}
fclose($fp);
xml_parser_free($this->parser);
return $this->data;
}
private function startElement($parser, $name, $attrs) {
$this->currentElement = $name;
if ($name === 'item') {
$this->itemCount++;
$this->data[$this->itemCount] = ['attrs' => $attrs];
}
}
private function endElement($parser, $name) {
$this->currentElement = '';
}
private function characterData($parser, $data) {
if ($this->currentElement && $this->itemCount > 0) {
$this->data[$this->itemCount][$this->currentElement] =
($this->data[$this->itemCount][$this->currentElement] ?? '') . $data;
}
}
private function handleError() {
$code = xml_get_error_code($this->parser);
$line = xml_get_current_line_number($this->parser);
$col = xml_get_current_column_number($this->parser);
throw new Exception("XML 解析錯誤 [$code]: " . xml_error_string($code) . " at line $line, col $col");
}
}
// 使用
$parser = new LargeXmlParser();
$items = $parser->parseFile('large-feed.xml');
print_r($items);
xml_parse_into_struct 簡易結構化輸出
$xml = '<?xml version="1.0"?><root><item id="1"><name>Test</name></item></root>';
$parser = xml_parser_create();
xml_parse_into_struct($parser, $xml, $values, $index);
xml_parser_free($parser);
print_r($values);
/* 輸出結構:
Array
(
[0] => Array
(
[tag] => ROOT
[type] => open
[level] => 1
[attributes] => Array()
)
[1] => Array
(
[tag] => ITEM
[type] => open
[level] => 2
[attributes] => Array([ID] => 1)
)
[2] => Array
(
[tag] => NAME
[type] => complete
[level] => 3
[value] => Test
)
[3] => Array
(
[tag] => ITEM
[type] => close
[level] => 2
)
[4] => Array
(
[tag] => ROOT
[type] => close
[level] => 1
)
)
*/
print_r($index); // 索引:標籤名 => 陣列索引列表
命名空間處理
$xml = '<?xml version="1.0"?><root xmlns:ns="https://example.com/ns"><ns:item/></root>';
$parser = xml_parser_create_ns('UTF-8', ':'); // 分隔符為 :
xml_set_start_namespace_decl_handler($parser, function($parser, $prefix, $uri) {
echo "NS Start: $prefix => $uri\n";
});
xml_set_end_namespace_decl_handler($parser, function($parser, $prefix) {
echo "NS End: $prefix\n";
});
xml_set_element_handler($parser, function($parser, $name, $attrs) {
echo "Element: $name\n"; // 名稱格式: prefix:localname
}, function($parser, $name) {});
xml_parse($parser, $xml, true);
xml_parser_free($parser);
XML Parser vs DOMDocument vs SimpleXML vs XMLReader 比較
| 維度 | XML Parser (Expat) | DOMDocument | SimpleXML | XMLReader |
| 模式 | SAX 事件驅動 | DOM 樹 | DOM 樹 (簡化) | Pull 解析器 (游標) |
| 記憶體 | 極低 | 高 (全載入) | 高 | 低 |
| 隨機存取 | 不支援 | 完整支援 | 支援 | 不支援 (單向) |
| XPath | 不支援 | 支援 | 支援 | 不支援 |
| 寫入/修改 | 不支援 | 完整支援 | 有限支援 | 不支援 |
| 命名空間 | 支援 (需 ns 版) | 完整支援 | 支援 | 完整支援 |
| 學習曲線 | 中等 | 低 | 最低 | 中等 |
| 推薦場景 | 大檔、串流、單向讀取 | 需修改、XPath、複雜查詢 | 簡單結構、快速開發 | 大檔、需前瞻、Pull 模式偏好 |
總結
PHP XML Parser (Expat) 是處理大型 XML 文件的高效能選擇,事件驅動模式雖需適應回呼思維,但記憶體效率極佳。現代 PHP 開發中,XMLReader 提供更直觀的 Pull API 且效能相當,建議新專案優先評估。若需 DOM 操作、XPath、修改輸出,則選擇 DOMDocument。理解各解析器特性,根據檔案大小、記憶體限制、處理模式選擇最適工具。
- 上一篇文章: PHP Zip 壓縮檔操作完全指南:安裝、核心函數與實戰應用
- 下一篇文章: PHP 字串函數完整參考:核心函數分類、常用操作與效能技巧
Copyright ©
程式碼之家 All Rights Reserved