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

PHP XML 操作完整指南:DOMDocument、SimpleXML、XMLReader 三大解析器深度對比與最佳實踐

     編輯:PHP 綜合知識

前言:選對解析器,事半功倍

PHP 內建三種 XML 解析器,各有優劣。本文深度對比 **DOMDocument (樹狀)**、**SimpleXML (易用)**、**XMLReader (流式/SAX)**,並提供生產級封裝建議。

三大解析器核心對比

維度DOMDocumentSimpleXMLXMLReader
解析模式DOM 樹 (全量載入記憶體)DOM 樹 (全量載入)流式拉取 (SAX-like,逐節點)
記憶體佔用高 (文件大小 5-10x)高極低 (常數級)
操作風格W3C DOM 標準 API屬性訪問語法糖游標迭代
寫入/修改完整支援有限支援不可 (唯讀)
XPath 支援原生 DOMXPathxpath() 方法無 (需配合 DOM)
命名空間完整支援需 registerXPathNamespace原生支援
適用場景複雜修改、XPath 查詢、文檔構建讀取已知結構、配置文件、小檔案大檔案 (GB 級)、高併發、低記憶體

DOMDocument:功能最完整的標準選擇

讀取與 XPath 查詢



    
        PHP 8 實戰
        張三
        580
        2023-03-15
    
    
        Python 數據科學
        李四
        620
        2023-05-20
    

XML;

$dom = new DOMDocument();
$dom->loadXML($xml);
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;

$xpath = new DOMXPath($dom);

// 1. 查詢所有 PHP 類書籍
$books = $xpath->query('//book[@category="PHP"]');
foreach ($books as $book) {
    echo "ID: " . $book->getAttribute('id') . "\n";
    echo "標題: " . $xpath->evaluate('string(title)', $book) . "\n";
    echo "價格: " . $xpath->evaluate('number(price)', $book) . "\n";
}

// 2. 聚合查詢:平均價格
$avgPrice = $xpath->evaluate('number(//book/price)');
echo "平均價格: $avgPrice\n";

構建與修改 XML

$dom = new DOMDocument('1.0', 'UTF-8');
$dom->formatOutput = true;

$catalog = $dom->createElement('catalog');
$dom->appendChild($catalog);

$data = [
    ['id' => 'bk103', 'cat' => 'Go', 'title' => 'Go 微服務', 'author' => '王五', 'price' => 680],
];

foreach ($data as $item) {
    $book = $dom->createElement('book');
    $book->setAttribute('id', $item['id']);
    $book->setAttribute('category', $item['cat']);
    
    foreach (['title', 'author', 'price', 'publish_date'] as $field) {
        $elem = $dom->createElement($field, $item[$field] ?? date('Y-m-d'));
        $book->appendChild($elem);
    }
    $catalog->appendChild($book);
}

// 儲存
$dom->save('new_catalog.xml');
// 或輸出字串
$xmlString = $dom->saveXML();

SimpleXML:最直觀的讀取體驗

$xml = simplexml_load_string($xmlString); // 或 simplexml_load_file('file.xml')

// 直接屬性訪問
echo $xml->book[0]->title; // "PHP 8 實戰"
echo $xml->book[0]['id'];  // "bk101" (屬性)

// 迭代
foreach ($xml->book as $book) {
    echo (string)$book->title . " - " . (string)$book->author . "\n";
    // 強制轉型 (string) 獲取文本內容
}

// XPath 查詢
$phpBooks = $xml->xpath('//book[@category="PHP"]');
foreach ($phpBooks as $book) {
    echo $book->title . "\n";
}

// 修改 (有限)
$xml->book[0]->price = 590;
$xml->book[0]->addChild('isbn', '978-7-111-12345-6');

// 命名空間處理
$xml->registerXPathNamespace('ns', 'https://example.com/ns');
$items = $xml->xpath('//ns:item');

XMLReader:大檔案流式處理王者

open('huge_catalog.xml'); // 或 $reader->XML($hugeString);

$books = [];
while ($reader->read()) {
    // 只關心  元素開始
    if ($reader->nodeType === XMLReader::ELEMENT && $reader->name === 'book') {
        // 展開當前節點為 DOMNode (可選,需記憶體)
        $domNode = $reader->expand(); 
        $dom = new DOMDocument();
        $bookNode = $dom->importNode($domNode, true);
        $dom->appendChild($bookNode);
        
        $xpath = new DOMXPath($dom);
        $books[] = [
            'id' => $xpath->evaluate('string(/book/@id)'),
            'category' => $xpath->evaluate('string(/book/@category)'),
            'title' => $xpath->evaluate('string(/book/title)'),
            'price' => (float)$xpath->evaluate('number(/book/price)'),
        ];
        
        // 釋放展開的 DOM 節點
        $domNode = null;
        $dom = null;
    }
}

// 處理 $books ...
echo "共解析 " . count($books) . " 本書\n";

生產級封裝:統一介面 XmlParser

interface XmlParserInterface {
    public function parse(string $source, callable $handler): void; // source: file path or XML string
    public function query(string $xpath): iterable;
}

class DomXmlParser implements XmlParserInterface {
    private DOMDocument $dom;
    private DOMXPath $xpath;
    
    public function __construct(private bool $preserveWhiteSpace = false) {}
    
    public function parse(string $source, callable $handler): void {
        $this->dom = new DOMDocument();
        $this->dom->preserveWhiteSpace = $this->preserveWhiteSpace;
        if (is_file($source)) {
            $this->dom->load($source);
        } else {
            $this->dom->loadXML($source);
        }
        $this->xpath = new DOMXPath($this->dom);
        $handler($this);
    }
    
    public function query(string $xpath): iterable {
        return $this->xpath->query($xpath);
    }
    
    public function evaluate(string $xpath): mixed {
        return $this->xpath->evaluate($xpath);
    }
}

class StreamingXmlParser implements XmlParserInterface {
    public function parse(string $source, callable $handler): void {
        $reader = new XMLReader();
        $reader->open($source);
        while ($reader->read()) {
            if ($reader->nodeType === XMLReader::ELEMENT) {
                $handler($reader); // 傳遞 reader 讓調用方決定是否 expand()
            }
        }
        $reader->close();
    }
    
    public function query(string $xpath): iterable {
        throw new \BadMethodCallException('Streaming parser does not support XPath query');
    }
}

// 使用
$parser = new DomXmlParser();
$parser->parse('catalog.xml', function($p) {
    foreach ($p->query('//book[@category="PHP"]') as $book) {
        echo $book->getAttribute('id') . ': ' . $p->evaluate('string(title)', $book) . "\n";
    }
});

常見坑點與解決

問題原因解決
載入失敗/亂碼XML 宣告編碼與實際不符、含 BOM、非法字元`$dom->loadXML(mb_convert_encoding($xml, 'UTF-8', 'auto'))`、移除 BOM、過濾控制字元
SimpleXML 丟失屬性/命名空間轉 JSON 時 `json_encode` 不處理屬性手動遞歸轉陣列、或用 DOMDocument
XMLReader expand() 記憶體暴增大量節點展開為 DOM僅展開必要節點、用完即釋放、或改用純游標讀取屬性
XPath 查無結果命名空間未註冊、大小寫不匹配`$xpath->registerNamespace('pfx', 'uri')`、確認節點名稱大小寫
實體引用錯誤XML 含未定義實體 `©` 等`$dom->substituteEntities = true`、或預先定義 DTD

結語

**DOMDocument** 為通用首選 (功能全、標準化);**SimpleXML** 適配簡單讀取;**XMLReader** 專治大檔案。封裝統一介面,根據文件大小動態選擇解析器,是架構層面的最佳實踐。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved