1. 程式人生 > >XMLReader強大的XML解析器

XMLReader強大的XML解析器

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>';
?>