早期範例使用已移除的 domxml 擴充套件(xmldocfile、walk_tree)。現代 PHP(5.0+)內建 DOMDocument 與 DOMXPath,提供標準、強大的 XML 操作 API。本文示範如何遞歸遍歷所有節點、屬性、文字內容,並處理命名空間、編碼等實務問題。
<?xml version="1.0" encoding="UTF-8"?>
<contact id="43956">
<personal>
<name>
<first>John</first>
<middle>Q</middle>
<last>Doe</last>
</name>
<title>Senior Manager</title>
<employer>National Corp</employer>
<dob>1971-12-22</dob>
</personal>
<contactInfo>
<email type="work">john.doe@national.com</email>
<phone type="mobile">+1-555-0199</phone>
</contactInfo>
</contact>
<?php
function walkDomNode(\DOMNode $node, int $depth = 0): void
{
$indent = str_repeat(' ', $depth);
switch ($node->nodeType) {
case XML_ELEMENT_NODE:
echo "{$indent}元素: <{$node->nodeName}>\n";
// 輸出屬性
if ($node->hasAttributes()) {
foreach ($node->attributes as $attr) {
echo "{$indent} 屬性: {$attr->name} = \"{$attr->value}\"\n";
}
}
break;
case XML_TEXT_NODE:
$text = trim($node->nodeValue);
if ($text !== '') {
echo "{$indent}文字: \"{$text}\"\n";
}
break;
case XML_CDATA_SECTION_NODE:
echo "{$indent}CDATA: {$node->nodeValue}\n";
break;
case XML_COMMENT_NODE:
echo "{$indent}註解: {$node->nodeValue}\n";
break;
}
// 遞歸處理子節點
if ($node->hasChildNodes()) {
foreach ($node->childNodes as $child) {
walkDomNode($child, $depth + 1);
}
}
}
// 載入並遍歷
$dom = new DOMDocument();
$dom->load('contact.xml'); // 或 loadXML($string)
echo "<pre>\n";
walkDomNode($dom->documentElement); // 從根元素開始
echo "</pre>\n";
?>
元素: <contact>
屬性: id = "43956"
元素: <personal>
元素: <name>
元素: <first>
文字: "John"
元素: <middle>
文字: "Q"
元素: <last>
文字: "Doe"
元素: <title>
文字: "Senior Manager"
...
<?php
$xpath = new DOMXPath($dom);
// 1. 取得所有 email 節點
$emails = $xpath->query('//email');
foreach ($emails as $email) {
echo "Email: {$email->nodeValue} (類型: {$email->getAttribute('type')})\n";
}
// 2. 取得特定路徑的文字
$firstName = $xpath->evaluate('string(/contact/personal/name/first)');
echo "First Name: $firstName\n";
// 3. 含命名空間查詢(需註冊前綴)
// $xpath->registerNamespace('ns', 'https://example.com/ns');
// $nodes = $xpath->query('//ns:element');
?>
| 問題 | 解決方法 |
|---|---|
| 載入報錯 "Entity not defined" | $dom->substituteEntities = true; 或預先定義實體 |
| 中文亂碼 | 確保 XML 宣告 encoding="UTF-8",PHP 腳本檔案也為 UTF-8 無 BOM |
| 大檔案記憶體不足 | 改用 XMLReader(拉式解析器),逐節點讀取,不建立完整 DOM 樹 |
| 預設移除空白文字節點 | $dom->preserveWhiteSpace = false;(預設 true),配合 $dom->formatOutput = true; 美化輸出 |
| 命名空間查詢失效 | 必須用 registerNamespace() 註冊前綴,XPath 中使用前綴 |
<?php
$reader = new XMLReader();
$reader->open('large.xml');
while ($reader->read()) {
switch ($reader->nodeType) {
case XMLReader::ELEMENT:
echo "開始元素: {$reader->name}";
if ($reader->hasAttributes) {
while ($reader->moveToNextAttribute()) {
echo " {$reader->name}=\"{$reader->value}\"";
}
$reader->moveToElement(); // 移回元素
}
echo "\n";
break;
case XMLReader::TEXT:
case XMLReader::CDATA:
$text = trim($reader->value);
if ($text) echo "文字: $text\n";
break;
case XMLReader::END_ELEMENT:
echo "結束元素: {$reader->name}\n";
break;
}
}
$reader->close();
?>
掌握 DOMDocument 與 XMLReader 兩大工具,即可應對從設定檔解析到大型 XML 資料交換的所有場景。