XMLReader強大的XML解析器
阿新 • • 發佈:2019-02-19
PHP中有兩種主要的XML解析器
1)基於樹的解析器。它是把整個文件儲存為樹的資料結構中,即需要把整個文件都載入到記憶體中才能工作。所以,當處理大型XML文件時候,效能劇減。SimpleXML和DOM擴充套件屬於此型別解析器。
2)基於流的解析器。它不會一次把整個文件載入到記憶體中,而是每次分別讀取其中的一個節點並允許實時與之互動(當移向下一個節點時,上一個節點是被丟棄,但也設定為保留)。很明顯,其效率要高且佔記憶體少,不便之處程式碼量大點。
所以,PHP中處理大型XML文件可以用XMLReader擴充套件方案(基於流的解析器)。它在PHP 5.1中預設是啟用的。
下面是我結合手冊與程式碼整理出來的筆記,希望多交流交流。
部落格地址:http://blogforit.sinaapp.com/View/index/b_id/304.html
<?php class xmlRead { // ================================================================== // // 前三個屬性用來儲存RSS頻道資訊,$items陣列儲存來自指定頻道的所有RSS專案,$xml // 儲存頻道的原始XML源 // // ------------------------------------------------------------------ public $channelTitle = ''; public $channelDesc = ''; public $test = ''; public $items = array(); public $xml; public function __construct($url = NULL) { if($url !== NULL) { $this->load($url); } } public function load($url) { // $this->xml = file_get_contents($url); $this->xml = $url; //我們使用XMLReader來解析XML資料 $xr = new XMLReader(); $xr->XML($this->xml); while ($xr->read()) { // ================================================================== // // XMLReader::ELEMENT常量通過PHP手冊知道這個代表節點的開始(值是1),所以當 // $xr->nodeType也是1(即節點的開始),我們就可以通過localName屬性得到節點的名字: // 如<channel>是一個開始節點,nodeType = 1,並且localName = channel.這裡我們 // 列印所有節點屬性如下(如果要看所有節點,一定要註釋掉switch,不然它會執行相應函式,就 // 不能輸出已經在函式裡執行過的屬性了)。 // // ------------------------------------------------------------------ // echo '<pre>'; // var_dump($xr->nodeType.' '.$xr->localName.' '.$xr->depth.' '.$xr->value); // echo '</pre>'; if(XMLReader::ELEMENT == $xr->nodeType) { // 這裡我們得到$xr->nodeType=1,即所有的開始標籤如<channel>. // echo '<pre>'; // var_dump($xr->nodeType.' '.$xr->localName.' '.$xr->depth.' '.$xr->value); // echo '</pre>'; // ================================================================== // // 這裡需要特別注意一下,為什麼你把switch註釋與不註釋,var_dump出來的值不同呢,原因 // 就是因為XMLReader是類似遊標一行一行讀取的,讀取完了之後會銷燬已經讀過的,所以當你 // 開啟了switch執行程式碼後,由於執行到了channel時,會跳轉到函式_getChannelInfo($xr) // 繼續執行函式裡面的程式碼,我們在函式裡面就把RSS的title,description讀過了,所以 // 打印出來就不存在那些屬性了,如果把switch註釋掉,由於沒有任何執行,則會一行一行輸出 // 所有的開始標籤如<title>,<descrition>.。 // // ------------------------------------------------------------------ switch ($xr->localName) { //如果是channel,我們就得到它的下級屬性值,如title,description等 case 'channel': $this->_getChannelInfo($xr); break; //如果是item(即RSS文摘的開始屬性),我們就得到所有的文章內容並存入到陣列中 case 'item': $this->_getItemInfo($xr); break; } } } } /** * 獲取channel型別的資料,比如標題,描述等 */ protected function _getChannelInfo($xr) { // ================================================================== // // depth代表節點樹的深度,最開始是0即<rss version="2.0">,當出現了第一個存在深度 // 為3的節點時,while迴圈就結束了。這裡指明一下,由於在<xiaozhe>下面的文字值是屬於 // 3節點,由於下面迴圈到了xiaozhe時,我讀取了那個值節點(深度是3),所以while迴圈才能 // 繼續執行。當執行到pubDate時,由於沒有得到它的值節點(在函式內部沒有將那個節點讀取, // while迴圈就會讀取到那個節點),即讀取的節點深度就是3(pubDate的值節點)。 // 所以迴圈結束,並且此節點已經被讀取下次就不會輸出出來了。永遠記住,read()一次,遊標 // 向下走一個節點。 // // ------------------------------------------------------------------ while ( ($xr->read()) && ($xr->depth == 2) ) { // echo '<pre>'; // print_r($xr->nodeType.' '.$xr->localName.' '.$xr->depth); // echo '</pre>'; if(XMLReader::ELEMENT == $xr->nodeType) { switch ($xr->localName) { case 'title': //這裡使用read繼續讀取下個遊標,即屬性值 $xr->read(); $this->channelTitle = $xr->value; break; case 'description': $xr->read(); $this->channelDesc = $xr->value; break; case 'xiaozhe': $xr->read(); $this->test = $xr->value; break; } } } } /** * 獲取Item資料,一個Item在一個RSS裡面相當於一篇文章 */ protected function _getItemInfo($xr) { $title = ''; $link = ''; $desc = ''; while( ($xr->read()) && ($xr->depth > 2) ) { if(XMLReader::ELEMENT == $xr->nodeType) { switch ($xr->localName) { case 'title': $xr->read(); $title = $xr->value; break; case 'description': $xr->read(); $desc = $xr->value; break; case 'link': $xr->read(); $link = $xr->value; break; } } } //將資料放入陣列中,因為一個RSS可能會有很多的item //當呼叫一次這個函式,就增加一次資料 $this->items[] = array( 'title' => $title, 'link' => $link, 'desc' => $desc, ); } } // $url = 'http://blog.sina.com.cn/rss/2022595450.xml'; //$url = 'http://www.huxiu.com/rss/0.xml'; $url = "<rss> <channel> <title>feed title</title> <description>feed description</description> <xiaozhe>123</xiaozhe> <pubDate>Mon, 29 Oct 2012 13:30:00 +0100</pubDate> <copyright>123</copyright> <item> <title>item title</title> <description>item description</description> <link>http://itemlink</link> </item> <item> <title>item title</title> <description>item description</description> <link>http://bla</link> </item> </channel> </rss>"; $obj = new xmlRead($url); // echo '<pre>'; // print_r($obj->items); // echo '</pre>'; ?>