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