Quantcast
Channel: CSDN博客推荐文章
Viewing all articles
Browse latest Browse all 35570

XMLReader强大的XML解析器

$
0
0

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


作者:andybegin 发表于2013-3-27 0:43:50 原文链接
阅读:137 评论:0 查看评论

Viewing all articles
Browse latest Browse all 35570

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>