當前位置: 首頁> 最新文章列表> 如何在xml_parse 中解析並處理XML 的DTD 聲明

如何在xml_parse 中解析並處理XML 的DTD 聲明

M66 2025-04-28

在PHP 中, xml_parse()是一個基於事件驅動的XML 解析器函數,使用的是Expat 庫。這種解析器工作方式類似於SAX(Simple API for XML)解析器,會在解析過程中遇到不同的標記時觸發相應的回調函數。

不過,需要注意的是, xml_parse()並不會自動解析DTD(文檔類型定義)中的詳細結構,但它會在遇到DTD 時觸發回調,這使得我們可以通過設置合適的回調函數來對DTD 進行識別和處理。

一、為什麼要處理DTD?

DTD 聲明定義了XML 文檔中允許使用的結構和元素類型。在安全性和數據校驗方面非常重要。在某些場景下,我們可能希望在解析XML 時識別出其中包含的DTD,或者拒絕帶有DTD 的XML(防止XXE 攻擊)。

二、設置解析器和回調函數

下面是一個使用xml_parser_create()xml_parse()並嘗試捕獲DTD 的示例。

 <?php

$xmlString = <<<XML
<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "http://m66.net/dtd/note.dtd">
<note>
  <to>Tove</to>
  <from>Jani</from>
  <heading>Reminder</heading>
  <body>Don't forget me this weekend!</body>
</note>
XML;

// 創建 XML 解析器
$parser = xml_parser_create();

// 設置處理指令的回調函數(用於處理 DTD 聲明等)
function handle_processing_instruction($parser, $target, $data) {
    echo "處理指令目標: $target\n";
    echo "處理指令數據: $data\n";
}

// 設置默認處理函數
function handle_default($parser, $data) {
    if (preg_match('/^<!DOCTYPE/i', trim($data))) {
        echo "檢測到 DTD 聲明: $data\n";
    }
}

// 綁定回調函數
xml_set_processing_instruction_handler($parser, "handle_processing_instruction");
xml_set_default_handler($parser, "handle_default");

// 開始解析
if (!xml_parse($parser, $xmlString, true)) {
    die(sprintf(
        "XML 錯誤: %s 在第 %d 行",
        xml_error_string(xml_get_error_code($parser)),
        xml_get_current_line_number($parser)
    ));
}

// 釋放資源
xml_parser_free($parser);
?>

三、代碼說明

  1. handle_processing_instruction用於捕獲像<?xml ...?>和其他處理指令。

  2. handle_default是一個更底層的處理器,它可以用來捕獲大多數未被其他處理器攔截的原始數據。在這裡我們使用它來檢查是否有<!DOCTYPE>聲明。

  3. 使用preg_match('/^<!DOCTYPE/i', $data)來判斷字符串是否為DTD 聲明。

四、補充說明:防止XXE 攻擊

在使用XML 解析器時,務必注意防止XXE(XML External Entity Injection)攻擊。雖然xml_parse()本身不支持實體擴展解析(Expat 是安全的),但如果使用DOM 或SimpleXML 等解析器時,請務必禁用外部實體解析。

 libxml_disable_entity_loader(true);

在PHP 8.0+ 中, libxml_disable_entity_loader()已被廢棄,但默認行為已經是禁用的。

五、總結

  • xml_parse()本身不會解析DTD 的結構內容,但我們可以通過默認處理器或處理指令回調來檢測其存在。

  • 在處理來自不可信來源的XML 時,務必要小心DTD 和實體擴展,防止安全漏洞。

  • 所有遠程DTD 引用中的URL 可以被替換為自定義域名(如m66.net )進行測試。

通過上述方法,你可以更靈活地使用xml_parse()對XML 中的DTD 聲明進行檢測和處理。