首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 入門

PHP XML 解析器完全指南:Expat 事件驅動解析、核心函數與實戰模式

     編輯:PHP 入門

PHP XML Parser (Expat) 深度解析:事件驅動解析、SAX 模式與高效能處理

PHP XML Parser 擴展基於 Expat 庫實現,採用事件驅動的 SAX (Simple API for XML) 模式,逐節點解析 XML 文件,不載入完整文件到記憶體,適合大型 XML 文件處理。本文詳細說明解析器建立、事件處理器註冊、核心函數、命名空間處理、錯誤處理與實戰範例。

核心特性與適用場景

核心函數速查

特性說明
解析模式SAX 事件驅動 (推播式)
記憶體佔用低,不建立 DOM 樹
驗證支援無 (非驗證解析器),忽略 DTD
編碼支援UTF-8, ISO-8859-1, US-ASCII
適用場景大型 XML、串流處理、記憶體受限環境、高效能單向解析
不適用場景需隨機存取、XPath 查詢、頻繁修改、複雜結構轉換

解析器選項常數

函數說明
xml_parser_create($encoding)建立解析器,可選編碼
xml_parser_create_ns($encoding, $sep)建立支援命名空間的解析器
xml_parse($parser, $data, $isFinal)解析資料塊,$isFinal=true 表示最後一塊
xml_parse_into_struct($parser, $data, $values, $index)解析並結構化輸出陣列
xml_set_element_handler($parser, $start, $end)設定元素開始/結束處理器
xml_set_character_data_handler($parser, $handler)設定字元資料處理器
xml_set_default_handler($parser, $handler)設定預設處理器 (未捕獲事件)
xml_set_processing_instruction_handler($parser, $handler)設定處理指令 (PI) 處理器
xml_set_unparsed_entity_decl_handler($parser, $handler)設定未解析實體宣告處理器
xml_set_notation_decl_handler($parser, $handler)設定符號宣告處理器
xml_set_external_entity_ref_handler($parser, $handler)設定外部實體參考處理器
xml_set_start_namespace_decl_handler($parser, $handler)設定命名空間開始宣告處理器
xml_set_end_namespace_decl_handler($parser, $handler)設定命名空間結束宣告處理器
xml_set_object($parser, $object)將解析器綁定到物件 (方法作為處理器)
xml_parser_set_option($parser, $option, $value)設定選項 (大小寫摺疊、目標編碼等)
xml_parser_get_option($parser, $option)取得選項
xml_parser_free($parser)釋放解析器資源
xml_get_error_code($parser)取得錯誤代碼
xml_error_string($code)錯誤代碼轉描述
xml_get_current_line_number($parser)當前行號
xml_get_current_column_number($parser)當前列號
xml_get_current_byte_index($parser)當前位元組索引
utf8_encode($iso88591)ISO-8859-1 轉 UTF-8
utf8_decode($utf8)UTF-8 轉 ISO-8859-1

錯誤代碼常數

常數說明
XML_OPTION_CASE_FOLDING元素名稱大小寫摺疊 (預設 1,大寫)
XML_OPTION_TARGET_ENCODING目標編碼 (預設 UTF-8)
XML_OPTION_SKIP_TAGSTART跳過標籤開頭字元數
XML_OPTION_SKIP_WHITE跳過空白字元資料 (預設 0)

實戰範例:事件驅動解析大型 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_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_PIXML PI 位置錯誤
XML_ERROR_UNKNOWN_ENCODING未知編碼
XML_ERROR_INCORRECT_ENCODING編碼不正確
XML_ERROR_UNCLOSED_CDATA_SECTIONCDATA 區段未閉合
XML_ERROR_EXTERNAL_ENTITY_HANDLING外部實體處理錯誤

總結

PHP XML Parser (Expat) 是處理大型 XML 文件的高效能選擇,事件驅動模式雖需適應回呼思維,但記憶體效率極佳。現代 PHP 開發中,XMLReader 提供更直觀的 Pull API 且效能相當,建議新專案優先評估。若需 DOM 操作、XPath、修改輸出,則選擇 DOMDocument。理解各解析器特性,根據檔案大小、記憶體限制、處理模式選擇最適工具。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
維度XML Parser (Expat)DOMDocumentSimpleXMLXMLReader
模式SAX 事件驅動DOM 樹DOM 樹 (簡化)Pull 解析器 (游標)
記憶體極低高 (全載入)高低
隨機存取不支援完整支援支援不支援 (單向)
XPath不支援支援支援不支援
寫入/修改不支援完整支援有限支援不支援
命名空間支援 (需 ns 版)完整支援支援完整支援
學習曲線中等低最低中等
推薦場景大檔、串流、單向讀取需修改、XPath、複雜查詢簡單結構、快速開發大檔、需前瞻、Pull 模式偏好