PHP流式读取XML文件

时间:2021-03-15 11:59:54

  之前在项目中有读取XML的需求,一开始采用的是simplexml将xml文件全部load到内存里,然后一个节点一个节点读的方式,后来随着XML文件的增大,内存被撑爆了。于是赶紧想办法,于是有了流式读取。一开始老大给了我一个封装XMLReader的类,但是我发现这个类不是很符合我的要求,于是又把XMLReader按我的要求重新封装了一遍,在此感谢老大moon。

  目前我的类非常简单就提供几个方法,我提供了Demo供大家参考使用。话不多说上代码

<?php
/**
 *
 * @author gaoran
 * Another new parser for (X|HT)ML(stream version)
 * @version 1.0 2014-06-02 18:10:00
 * 感谢我的老大moon,之前moon写了一个XMLParser的类,但是我觉得不是我想要的,于是自己重新封装了一遍,自我感觉简单,好用
 */
class New_XMLParser extends XMLReader{
    private $_tagName;

    /**
     * 设置打开xml文件的路径
     */
    public function open($xml,$encoding=null,$option=null){
        parent::open($xml, $encoding, $option);
    }
    /**
     * 设置要读取数据的节点名称
     */
    public function setTagName($tagName){
        $this->_tagName = $tagName;
    }
    /**
     * 每次读取一个节点数据,以数组格式返回
     */
    public function getData(){
        if(!$this->_tagName){return false;}
        while($this->read()){
            $tagName = $this->name;
            if($this->nodeType == XMLReader::ELEMENT && $this->_tagName == $tagName){
                $dom = new DOMDocument('1.0', 'utf-8');
                $d = $dom->importNode($this->expand(), true);
                //$dom->appendChild($d);
                $appinfo = $this->domToArray($d);
                return $appinfo;
            }
        }
        return false;
    }
    /**
     *
     * @param SimpleXMLElement|DOMDocument|DOMNode $obj SimpleXMLElement, DOMDocument or DOMNode instance
     * @return array Array representation of the XML structure.
     */
    public static function domToArray($obj) {
        if ($obj instanceof DOMNode) {
            $obj = simplexml_import_dom($obj);
        }

        $result = array();
        $namespaces = array_merge(array('' => ''), $obj->getNamespaces(true));

        self::_toArray($obj, $result, '', array_keys($namespaces));
        return $result;
    }
    protected static function _toArray($xml, &$parentData, $ns, $namespaces) {
        $data = array();

        foreach ($namespaces as $namespace) {
            foreach ($xml->attributes($namespace, true) as $key => $value) {
                if (!empty($namespace)) {
                    $key = $namespace . ':' . $key;
                }
                $data['@' . $key] = (string)$value;
            }

            foreach ($xml->children($namespace, true) as $child) {
                self::_toArray($child, $data, $namespace, $namespaces);
            }
        }

        $asString = trim((string)$xml);
        if (empty($data)) {
            $data = $asString;
        } elseif (!empty($asString)) {
            $data['@'] = $asString;
        }

        if (!empty($ns)) {
            $ns .= ':';
        }
        $name = $ns . $xml->getName();
        if (isset($parentData[$name])) {
            if (!is_array($parentData[$name]) || !isset($parentData[$name][0])) {
                $parentData[$name] = array($parentData[$name]);
            }
            $parentData[$name][] = $data;
        } else {
            $parentData[$name] = $data;
        }
    }
    /*
     * 关闭XML流
     */
    public function close(){
        parent::close();
    }
}
class Demo{
    public function run(){
        $im = new New_XMLParser();
        $im->open('xmlfile');

        $im->setTagName("webName");
        print_r($im->getData());

        $im->setTagName("webSiteUrl");
        print_r($im->getData());

        $im->setTagName("app");
        print_r($im->getData());

        //$im->open('xmlfile');

        $i = 0;
        while($i < 3 && $appinfo = $im->getData()){
            print_r($appinfo);
            $i ++;
        }
        $im->close();
    }
}
$d = new Demo();
$d->run();

  

目前够用了,以后继续改进吧,欢迎大家拍砖,一起进步完善。