• 首页
  • css3教程
  • html5教程
  • jQuery手册
  • php7教程
  • MySQL手册
  • apache手册
  • 位置: php7教程 -> php7外部扩展库

    SimpleXML类

    SimpleXML 函数允许您把 XML 转换为对象。通过普通的属性选择器或数组迭代器,可以处理这个对象,就像处理任何其他对象一样。

    此扩展需要 libxml PHP 扩展。这表示需要使用--enable-libxml,尽管这将隐式完成因为 libxml 是缺省开启的。

    安装

    此扩展默认为启用,编译时可通过下列选项禁用:--disable-simplexml。注意:在PHP 5.1.2之前,需要--enable-simplexml来启用此扩展。

    Installing simpleXml using composer:
    "composer require ext-simplexml"
    The extension is enabled by default in most distros. You can check the list of php modules using:
    "php -m"
    
    This XML module is not provided on a default php (v7) install on Debian GNU/Linux stretch (9.3)
    Add it with: sudo apt-get install php-xml
    
    To install on CentOS and Redhat, run: 
    sudo yum -y install php-xml
    

    SimpleXML基本用法

    此引用中的许多示例需要XML字符串。我们没有在每个示例中重复这个字符串,而是将它放入一个包含在每个示例中的文件中。下面的示例部分显示了包含的文件。或者,可以创建一个XML文档,并使用simplexml加载文件()读取它。

    Include file example.php with XML string

    <?php
    $xmlstr = <<<XML
    <?xml version='1.0' standalone='yes'?>
    <movies>
     <movie>
      <title>PHP: Behind the Parser</title>
      <characters>
       <character>
        <name>Ms. Coder</name>
        <actor>Onlivia Actora</actor>
       </character>
       <character>
        <name>Mr. Coder</name>
        <actor>El ActÓr</actor>
       </character>
      </characters>
      <plot>
       So, this language. It's like, a programming language. Or is it a
       scripting language? All is revealed in this thrilling horror spoof
       of a documentary.
      </plot>
      <great-lines>
       <line>PHP solves all my web problems</line>
      </great-lines>
      <rating type="thumbs">7</rating>
      <rating type="stars">5</rating>
     </movie>
    </movies>
    XML;
    ?>
    

    Getting <plot>

    <?php
    include 'example.php';
    $movies =newSimpleXMLElement($xmlstr);
    echo $movies->movie[0]->plot;
    ?>
    

    以上例程会输出:

    So, this language. It's like, a programming language. Or is it a
       scripting language? All is revealed in this thrilling horror spoof
       of a documentary.
    

    Getting <line>

    <?php
    include 'example.php';
    $movies =newSimpleXMLElement($xmlstr);
    echo$movies->movie->{'great-lines'}->line;
    ?>
    

    以上例程会输出:

    PHP solves all my web problems
    

    Accessing non-unique elements in SimpleXML

    <?php
    include 'example.php';
    $movies = new SimpleXMLElement($xmlstr);
    /* For each  node, we echo a separate . */
    foreach ($movies->movie->characters->character as $character) 
    {
       echo $character->name, ' played by ', $character->actor, PHP_EOL;
    }
    ?>
    

    以上例程会输出:

    Ms. Coder played by Onlivia Actora
    Mr. Coder played by El ActÓr
    
    Note:
    Properties ($movies->movie in previous example) are not arrays. They are iterable and accessible objects.

    Using attributes
    到目前为止,我们只讨论了读取元素名及其值的工作。SimpleXML还可以访问元素属性。像访问数组元素一样访问元素的属性。

    <?php
    include 'example.php';
    $movies = new SimpleXMLElement($xmlstr);
    /* Access the  nodes of the first movie.
     * Output the rating scale, too. */
    foreach ($movies->movie[0]->rating as $rating)
    {
        switch((string) $rating['type']) { // Get attributes as element indices
        case 'thumbs':
            echo $rating, ' thumbs up';
            break;
        case 'stars':
            echo $rating, ' stars';
            break;
        }
    }
    ?>
    

    以上例程会输出:

    7 thumbs up5 stars
    

    Setting values

    SimpleXML中的数据不必是常量。对象允许操作其所有元素。

    <?php
    include 'example.php';
    $movies = new SimpleXMLElement($xmlstr);
    $movies->movie[0]->characters->character[0]->name = 'Miss Coder';
    echo $movies->asXML();
    ?>
    

    以上例程会输出:

    <?xml version="1.0" standalone="yes"?>
    <movies>
     <movie>
      <title>PHP: Behind the Parser</title>
      <characters>
       <character>
        <name>Miss Coder</name>
        <actor>Onlivia Actora</actor>
       </character>
       <character>
        <name>Mr. Coder</name>
        <actor>El ActÓr</actor>
       </character>
      </characters>
      <plot>
       So, this language. It's like, a programming language. Or is it a
       scripting language? All is revealed in this thrilling horror spoof
       of a documentary.
      </plot>
      <great-lines>
       <line>PHP solves all my web problems</line>
      </great-lines>
      <rating type="thumbs">7</rating>
      <rating type="stars">5</rating>
     </movie>
    </movies>
    

    处理XML错误

    在加载文档时处理XML错误是一项非常简单的任务。使用libxml功能,可以在加载文档时抑制所有XML错误,然后遍历这些错误。libxml_get_errors()返回的libXMLError对象包含几个属性,包括错误的消息、行和列(位置)。

    Loading broken XML string

    <?php
    libxml_use_internal_errors(true);
    $sxe = simplexml_load_string("");
    if ($sxe === false) 
    {
        echo "Failed loading XML\n";
        foreach(libxml_get_errors() as $error) 
        {
            echo "\t",$error->message;
        }
    }
    ?>
    

    以上例程会输出:

    Failed loading XML
        Blank needed here
        parsing XML declaration: '?>' expected
        Opening and ending tag mismatch: xml line 1 and broken
        Premature end of data in tag broken line 1
    

    参见

    Now that the /e modifier is considered deprecated in preg_replace, you can use a negative lookahead to replace unescaped ampersands with & without throwing warnings:
    $str = preg_replace('/&(?!;{6})/', '&', $str);
    You probably should have been doing this before /e was deprecated, actually.
    
    If you are trying to load an XML string with some escaped and some unescaped ampersands, you can pre-parse the string to ecsape the unescaped ampersands without modifying the already escaped ones:
    <?php
    $s = preg_replace('/&[^; ]{0,6}.?/e', "((substr('\\0',-1) == ';') ? '\\0' : '&'.substr('\\0',1))",$s);
    ?>
    

    SimpleXMLElement类

    (PHP 5, PHP 7)

    表示XML文档中的元素

    SimpleXMLElement implements Traversable
    {
    	/* 方法 */
    	final public __construct ( string $data [, int $options = 0 [, bool $data_is_url = FALSE [, string $ns = "" [, bool $is_prefix = FALSE ]]]] )
    	public addAttribute ( string $name [, string $value [, string $namespace ]] ) : void
    	public addChild ( string $name [, string $value [, string $namespace ]] ) : SimpleXMLElement
    	public asXML ([ string $filename ] ) : mixed
    	public attributes ([ string $ns = NULL [, bool $is_prefix = FALSE ]] ) : SimpleXMLElement
    	public children ([ string $ns [, bool $is_prefix = FALSE ]] ) : SimpleXMLElement
    	public count ( void ) : int
    	public getDocNamespaces ([ bool $recursive = FALSE [, bool $from_root = TRUE ]] ) : array
    	public getName ( void ) : string
    	public getNamespaces ([ bool $recursive = FALSE ] ) : array
    	public registerXPathNamespace ( string $prefix , string $ns ) : bool
    	public __toString ( void ) : string
    	public xpath ( string $path ) : array
    }
    

    SimpleXMLIterator

    (PHP 5 >= 5.1.3, PHP 7)

    SimpleXMLIterator在simplexmlement对象的所有节点上提供递归迭代。

    SimpleXMLIterator extends SimpleXMLElement implements RecursiveIterator , Countable 
    {
    	/* 方法 */
    	public current ( void ) : mixed
    	public getChildren ( void ) : SimpleXMLIterator
    	public hasChildren ( void ) : bool
    	public key ( void ) : mixed
    	public next ( void ) : void
    	public rewind ( void ) : void
    	public valid ( void ) : bool
    	/* 继承的方法 */
    	final public SimpleXMLElement::__construct ( string $data [, int $options = 0 [, bool $data_is_url = FALSE [, string $ns = "" [, bool $is_prefix = FALSE ]]]] )
    	public SimpleXMLElement::addAttribute ( string $name [, string $value [, string $namespace ]] ) : void
    	public SimpleXMLElement::addChild ( string $name [, string $value [, string $namespace ]] ) : SimpleXMLElement
    	public SimpleXMLElement::asXML ([ string $filename ] ) : mixed
    	public SimpleXMLElement::attributes ([ string $ns = NULL [, bool $is_prefix = FALSE ]] ) : SimpleXMLElement
    	public SimpleXMLElement::children ([ string $ns [, bool $is_prefix = FALSE ]] ) : SimpleXMLElement
    	public SimpleXMLElement::count ( void ) : int
    	public SimpleXMLElement::getDocNamespaces ([ bool $recursive = FALSE [, bool $from_root = TRUE ]] ) : array
    	public SimpleXMLElement::getName ( void ) : string
    	public SimpleXMLElement::getNamespaces ([ bool $recursive = FALSE ] ) : array
    	public SimpleXMLElement::registerXPathNamespace ( string $prefix , string $ns ) : bool
    	public SimpleXMLElement::__toString ( void ) : string
    	public SimpleXMLElement::xpath ( string $path ) : array
    }
    

    SimpleXMLIterator

    (PHP 5 >= 5.1.3, PHP 7)

    SimpleXMLIterator在simplexmlement对象的所有节点上提供递归迭代

    SimpleXMLIterator extends SimpleXMLElement implements RecursiveIterator , Countable 
    {
    	/* 方法 */
    	public current ( void ) : mixed
    	public getChildren ( void ) : SimpleXMLIterator
    	public hasChildren ( void ) : bool
    	public key ( void ) : mixed
    	public next ( void ) : void
    	public rewind ( void ) : void
    	public valid ( void ) : bool
    	/* 继承的方法 */
    	final public SimpleXMLElement::__construct ( string $data [, int $options = 0 [, bool $data_is_url = FALSE [, string $ns = "" [, bool $is_prefix = FALSE ]]]] )
    	public SimpleXMLElement::addAttribute ( string $name [, string $value [, string $namespace ]] ) : void
    	public SimpleXMLElement::addChild ( string $name [, string $value [, string $namespace ]] ) : SimpleXMLElement
    	public SimpleXMLElement::asXML ([ string $filename ] ) : mixed
    	public SimpleXMLElement::attributes ([ string $ns = NULL [, bool $is_prefix = FALSE ]] ) : SimpleXMLElement
    	public SimpleXMLElement::children ([ string $ns [, bool $is_prefix = FALSE ]] ) : SimpleXMLElement
    	public SimpleXMLElement::count ( void ) : int
    	public SimpleXMLElement::getDocNamespaces ([ bool $recursive = FALSE [, bool $from_root = TRUE ]] ) : array
    	public SimpleXMLElement::getName ( void ) : string
    	public SimpleXMLElement::getNamespaces ([ bool $recursive = FALSE ] ) : array
    	public SimpleXMLElement::registerXPathNamespace ( string $prefix , string $ns ) : bool
    	public SimpleXMLElement::__toString ( void ) : string
    	public SimpleXMLElement::xpath ( string $path ) : array
    }
    
    Three line xml2array:
    <?php
    $xml = simplexml_load_string($xmlstring);
    $json = json_encode($xml);
    $array = json_decode($json,TRUE);
    ?>
    Ta da!
    
    The BIGGEST differece between an XML and a PHP array is that in an XML file, the name of elements can be the same even if they are siblings, eg. "", while in an PHP array, the key of which must be different.
    I think the array structure developed by svdmeer can fit for XML, and fits well.
    here is an example array converted from an xml file:
    array(
    "@tag"=>"name",
    "@attr"=>array(
        "id"=>"1","class"=>"2")
    "@text"=>"some text",
    )
    or if it has childrens, that can be:
    array(
    "@tag"=>"name",
    "@attr"=>array(
        "id"=>"1","class"=>"2")
    "@items"=>array(
        0=>array(
            "@tag"=>"name","@text"=>"some text"
        )
    )
    Also, I wrote a function that can change that array back to XML.
    <?php
    function array2XML($arr,$root) {
    $xml = new SimpleXMLElement("");
    $f = create_function('$f,$c,$a','
            foreach($a as $v) {
                if(isset($v["@text"])) {
                    $ch = $c->addChild($v["@tag"],$v["@text"]);
                } else {
                    $ch = $c->addChild($v["@tag"]);
                    if(isset($v["@items"])) {
                        $f($f,$ch,$v["@items"]);
                    }
                }
                if(isset($v["@attr"])) {
                    foreach($v["@attr"] as $attr => $val) {
                        $ch->addAttribute($attr,$val);
                    }
                }
            }');
    $f($f,$xml,$arr);
    return $xml->asXML();
    }
    ?>
    
    Here's a quick way to dump the nodeValues from SimpleXML into an array using the path to each nodeValue as key. The paths are compatible with e.g. DOMXPath. I use this when I need to update values externally (i.e. in code that doesn't know about the underlying xml). Then I use DOMXPath to find the node containing the original value and update it.
    <?php
    function XMLToArrayFlat($xml, &$return, $path='', $root=false)
    {
        $children = array();
        if ($xml instanceof SimpleXMLElement) {
            $children = $xml->children();
            if ($root){ // we're at root
                $path .= '/'.$xml->getName();
            }
        }
        if ( count($children) == 0 ){
            $return[$path] = (string)$xml;
            return;
        }
        $seen=array();
        foreach ($children as $child => $value) {
            $childname = ($child instanceof SimpleXMLElement)?$child->getName():$child;
            if ( !isset($seen[$childname])){
                $seen[$childname]=0;
            }
            $seen[$childname]++;
            XMLToArrayFlat($value, $return, $path.'/'.$child.'['.$seen[$childname].']');
        }
    }
    ?>
    Use like this:
    <?php
    $xml = simplexml_load_string(...some xml string...);
    $xmlarray = array(); // this will hold the flattened data
    XMLToArrayFlat($xml, $xmlarray, '', true);
    ?>
    You can also pull multiple files in one array:
    <?php
    foreach($files as $file){
        $xml = simplexml_load_file($file);
        XMLToArrayFlat($xml, $xmlarray, $file.':', true);
    }
    ?>
    The respective filename/path is thus prefixed to each key.
    
    Here is a recursive function that will convert a given SimpleXMLElement object into an array, preserving namespaces and attributes.
    <?php
    function xmlObjToArr($obj)
    {
            $namespace = $obj->getDocNamespaces(true);
            $namespace[NULL] = NULL;
           
            $children = array();
            $attributes = array();
            $name = strtolower((string)$obj->getName());
           
            $text = trim((string)$obj);
            if( strlen($text) $nsUrl ) {
                    // atributes
                    $objAttributes = $obj->attributes($ns, true);
                    foreach( $objAttributes as $attributeName => $attributeValue ) {
                        $attribName = strtolower(trim((string)$attributeName));
                        $attribVal = trim((string)$attributeValue);
                        if (!empty($ns)) {
                            $attribName = $ns . ':' . $attribName;
                        }
                        $attributes[$attribName] = $attribVal;
                    }
                   
                    // children
                    $objChildren = $obj->children($ns, true);
                    foreach( $objChildren as $childName=>$child ) {
                        $childName = strtolower((string)$childName);
                        if( !empty($ns) ) {
                            $childName = $ns.':'.$childName;
                        }
                        $children[$childName][] = xmlObjToArr($child);
                    }
                }
            }
           
            return array(
                'name'=>$name,
                'text'=>$text,
                'attributes'=>$attributes,
                'children'=>$children
            );
    }
    ?<
    
    Here is an example of an easy mapping between xml and classes defined by user.
    <?php
    class XmlClass extends SimpleXMLElement
    {
        /**
         * Returns this object as an instance of the given class.
         */
        public function asInstanceOf($class_name)
        {
            // should check that class_name is valid
            return simplexml_import_dom(dom_import_simplexml($this), $class_name);
        }
        public function __call($name, array $arguments)
        {
            echo "magic __call called for method $name on instance of ".get_class()."\n";
            // class could be mapped according $this->getName()
            $class_name = 'Test';
            $instance = $this->asInstanceOf($class_name);
            return call_user_func_array(array($instance, $name), $arguments);
        }
    }
    class Test extends XmlClass
    {
        public function setValue($string)
        {
            $this->{0} = $string;
        }
    }
    $xml = new XmlClass('');
    $test = $xml->test->asInstanceOf('Test');
    echo get_class($xml->test), "\n";
    echo get_class($test), "\n";
    $test->setValue('value set directly by instance of Test');
    echo (string)$xml->test, "\n";
    echo (string)$test, "\n";
    $xml->test->setValue('value set by instance of XmlClass and magic __call');
    echo (string)$xml->test, "\n";
    echo (string)$test, "\n";
    ?>
    XmlClass
    Test
    value set directly by instance of Test
    value set directly by instance of Test
    magic __call called for method setValue on instance of XmlClass
    value set by instance of XmlClass and magic __call
    value set by instance of XmlClass and magic __call