PHP的XML分析函數 首先我得承認我喜歡計算機標准。如果每個人都遵從這個行業的標准,互聯網將會是一個更好的媒體。使用標准化的數據交換格式才能使開放的和獨立於平台的計算模式切實可行。這就是我作為XML愛好者的原因。 幸運的是,我最喜愛的腳本語言不但支持XML而且對其支持正不斷加強。PHP可以讓我迅速將XML文檔發布到互聯網上,收集XML文檔的統計信息,將XML文檔轉換成其它格式。例如,我時常用PHP的XML處理能力來管理我用XML所寫的文章和書。 本文中,我將討論任何用PHP內建的Expat解析器來處理XML文檔。通過范例,我將演示Expat的處理方法。同時,范例可以告訴你如何: 建立你自己的處理函數 將XML文檔轉換成你自己的PHP數據結構 介紹Expat XML的解析器,同樣稱為XML處理器,可以使程序訪問XML文檔的結構和內容。Expat是PHP腳本語言的XML解析器。它同時也運用在其它項目中,例如Mozilla、Apache和Perl。 什麼是基於事件的解析器? XML解析器的兩種基本類型: 基於樹型的解析器:將XML文檔轉換成樹型結構。這類解析器分析整篇文章,同時提供一個API來訪問所產生樹的每個元素。其通用的標准為DOM(文檔對象模式)。 基於事件的解析器:將XML文檔視為一系列的事件。當一個特殊事件發生時,解析器將調用開發者提供的函數來處理。 基於事件的解析器有一個XML文檔的數據集中視圖,也就是說它集中在XML文檔的數據部分,而不是其結構。這些解析器從頭到尾處理文檔,並將類似於-元素的開始、元素的結尾、特征數據的開始等等-事件通過回調(callback)函數報告給應用程序。以下是一個"Hello-World"的XML文檔范例: Hello World 基於事件的解析器將報告為三個事件: 開始元素:greeting CDATA項的開始,值為:Hello World 結束元素:greeting 不像基於樹型的解析器,基於事件的解析器不產生描述文檔的結構。在CDATA項中,基於事件的解析器不會讓你得到父元素greeting的信息。 然而,它提供一個更底層的訪問,這就使得可以更好地利用資源和更快地訪問。通過這種方式,就沒有必要將整個文檔放入內存;而事實上,整個文檔甚至可以大於實際內存值。 Expat就是這樣的一種基於事件的解析器。當然如果使用Expat,必要時它一樣可以在PHP中生成完全的原生樹結構。 上面Hello-World的范例包括完整的XML格式。但它是無效的,因為既沒有DTD(文檔類型定義)與其聯系,也沒有內嵌DTD。 對於Expat,這並沒有區別:Expat是一個不檢查有效性的解析器,因此忽略任何與文檔聯系的DTD。但應注意的是文檔仍然需要完整的格式,否則Expat(和其他符合XML標准的解析器一樣)將會隨著出錯信息而停止。 作為不檢查有效性的解析器,Exapt的快速性和輕巧性使其十分適合互聯網程序。 編譯Expat Expat可以編譯進PHP3.0.6版本(或以上)中。從Apache1.3.9開始,Expat已經作為Apache的一部分。在Unix系統中,通過-with-xml選項配置PHP,你可以將其編譯入PHP。 如果你將PHP編譯為Apache的模塊,而Expat將默認作為Apache的一部分。在Windows中,你則必須要加載XML動態連接庫。 XML范例:XMLstats 了解Expat的函數的一個辦法就是通過范例。我們所要討論的范例是使用Expat來收集XML文檔的統計數據。 對於文檔中每個元素,以下信息都將被輸出: 該元素在文檔中使用的次數 該元素中字符數據的數量 元素的父元素 元素的子元素 注意:為了演示,我們利用PHP來產生一個結構來保存元素的父元素和子元素 准備 用於產生XML解析器實例的函數為xml_parser_create()。該實例將用於以後的所有函數。這個思路非常類似於PHP中MySQL函數的連接標記。在解析文檔前,基於事件的解析器通常要求你注冊回調函數-用於特定的事件發生時調用。Expat沒有例外事件,它定義了如下七個可能事件: 對象 XML解析函數 描述 元素 xml_set_element_handler() 元素的開始和結束 字符數據 xml_set_character_data_handler() 字符數據的開始 外部實體 xml_set_external_entity_ref_handler() 外部實體出現 未解析外部實體 xml_set_unparsed_entity_decl_handler() 未解析的外部實體出現 處理指令 xml_set_processing_instruction_handler() 處理指令的出現 記法聲明 xml_set_notation_decl_handler() 記法聲明的出現 默認 xml_set_default_handler() 其它沒有指定處理函數的事件 所有的回調函數必須將解析器的實例作為其第一個參數(此外還有其它參數)。 對於本文最後的范例腳本。你需要注意的是它既用到了元素處理函數又用到了字符數據處理函數。元素的回調處理函數通過xml_set_element_handler()來注冊。 這個函數需要三個參數: 解析器的實例 處理開始元素的回調函數的名稱 處理結束元素的回調函數的名稱 當開始解析XML文檔時,回調函數必須存在。它們必須定義為與PHP手冊中所描述的原型一致。 例如,Expat將三個參數傳遞給開始元素的處理函數。在腳本范例中,其定義如下: function start_element($parser, $name, $attrs) 第一個參數是解析器標示,第二個參數是開始元素的名稱,第三參數為包含元素所有屬性和值的數組。 一旦你開始解析XML文檔,Expat在遇到開始元素是都將調用你的start_element()函數並將參數傳遞過去。 XML的Case Folding選項 用xml_parser_set_option()函數將Case folding選項關閉。這個選項默認是打開的,使得傳遞給處理函數的元素名自動轉換為大寫。但XML對大小寫是敏感的(所以大小寫對統計XML文檔是非常重要的)。對於我們的范例,case folding選項必須關閉。 解析文檔 在完成所有的准備工作後,現在腳本終於可以解析XML文檔: Xml_parse_from_file(),一個自定義的函數,打開參數中指定的文件,並以4kb的大小進行解析 xml_parse()和xml_parse_from_file()一樣,當發生錯誤時,即XML文檔的格式不完全時,將會返回false。 你可以使用xml_get_error_code()函數來得到最後一個錯誤的數字代碼。將此數字代碼傳遞給xml_error_string()函數即可得到錯誤的文本信息。 輸出XML當前的行數,使得調試更容易。 在解析的過程中,調用回調函數。 描述文檔結構 當解析文檔時,對於Expat需要強調問題的是:如何保持文檔結構的基本描述? 如前所述,基於事件的解析器本身並不產生任何結構信息。 不過標簽(tag)結構是XML的重要特性。例如,元素序列表示的意思不同於