 |
CXXXIV. SimpleXML functions
The SimpleXML extension provides a very simple and easily usable
toolset to convert XML to an object that can be processed with
normal property selectors and array iterators.
The SimpleXML extension requires PHP 5.
The SimpleXML extension is enabled by default. To disable it, use the
--disable-simplexml configure option.
Many examples in this reference require an XML string. Instead of
repeating this string in every example, we put it into a file which
we include in each example. This included file is shown in the
following example section. Alternatively, you could create an XML
document and read it with simplexml_load_file().
例子 1. 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> <rating type="thumbs">7</rating> <rating type="stars">5</rating> </movie> </movies> XML; ?>
|
|
The simplicity of SimpleXML appears most clearly when one extracts
a string or number from a basic XML document.
例子 2. Getting <plot>
<?php include 'example.php';
$xml = simplexml_load_string($xmlstr);
echo $xml->movie[0]->plot; // "So this language. It's like..." ?>
|
|
例子 3. Accessing non-unique elements in SimpleXML
When multiple instances of an element exist as children of
a single parent element, normal iteration techniques apply.
<?php include 'example.php';
$xml = simplexml_load_string($xmlstr);
/* For each <movie> node, we echo a separate <plot>. */ foreach ($xml->movie as $movie) { echo $movie->plot, '<br />'; }
?>
|
|
例子 4. Using attributes
So far, we have only covered the work of reading element names
and their values. SimpleXML can also access element attributes.
Access attributes of an element just as you would elements
of an array.
<?php include 'example.php';
$xml = simplexml_load_string($xmlstr);
/* Access the <rating> nodes of the first movie. * Output the rating scale, too. */ foreach ($xml->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; } } ?>
|
|
例子 5. Comparing Elements and Attributes with Text
To compare an element or attribute with a string or pass it into a
function that requires a string, you must cast it to a string using
(string). Otherwise, PHP treats the element as an object.
<?php include 'example.php';
$xml = simplexml_load_string($xmlstr);
if ((string) $xml->movie->title == 'PHP: Behind the Parser') { print 'My favorite movie.'; }
htmlentities((string) $xml->movie->title); ?>
|
|
例子 6. Using Xpath
SimpleXML includes builtin Xpath support.
To find all <character> elements:
<?php include 'example.php'; $xml = simplexml_load_string($xmlstr);
foreach ($xml->xpath('//character') as $character) { echo $character->name, 'played by ', $character->actor, '<br />'; } ?>
|
'//' serves as a wildcard. To specify absolute
paths, omit one of the slashes.
|
例子 7. Setting values
Data in SimpleXML doesn't have to be constant. The object allows
for manipulation of all of its elements.
<?php include 'example.php'; $xml = simplexml_load_string($xmlstr);
$xml->movie[0]->characters->character[0]->name = 'Miss Coder';
echo $xml->asXML(); ?>
|
The above code will output a new XML document, just like the original,
except that the new XML will change Ms. Coder to Miss Coder.
|
例子 8. DOM Interoperability
PHP has a mechanism to convert XML nodes between SimpleXML
and DOM formats. This example shows how one might change
a DOM element to SimpleXML.
<?php $dom = new domDocument; $dom->loadXML('<books><book><title>blah</title></book></books>'); if (!$dom) { echo 'Error while parsing the document'; exit; }
$s = simplexml_import_dom($dom);
echo $s->book[0]->title; ?>
|
|
Jason Sheets <jsheets at shadonet dot com>
01-Jun-2006 01:31
Here is my Simple XML to array function, it is recursive and has the benefit of maintaining key value relationships and has worked well for me.
function XMLToArray($xml)
{
if ($xml instanceof SimpleXMLElement) {
$children = $xml->children();
$return = null;
}
foreach ($children as $element => $value) {
if ($value instanceof SimpleXMLElement) {
$values = (array)$value->children();
if (count($values) > 0) {
$return[$element] = XMLToArray($value);
} else {
if (!isset($return[$element])) {
$return[$element] = (string)$value;
} else {
if (!is_array($return[$element])) {
$return[$element] = array($return[$element], (string)$value);
} else {
$return[$element][] = (string)$value;
}
}
}
}
}
if (is_array($return)) {
return $return;
} else {
return $false;
}
}
djib
21-May-2006 10:02
I wrote my own simplexml_to_array function which collects all information about attributes - cdata and childnodes of any level. I think it is much more simple compared to those I ran into so far, maybe it lacks a few things but it did the job for me...
XML FILE:
<?xml version="1.0" encoding="UTF-8" ?>
<employees>
<employee ID="1" FOO="BAR">
<empname>Anthony Clarke</empname>
<emptitle>Chief Information Officer</emptitle>
<empage>48</empage>
</employee>
<employee ID="2" BAZ="WOM">
<empname>Laura Pollard</empname>
<emptitle>Chief Executive Officer</emptitle>
<empage>54</empage>
</employee>
</employees>
PHP CODE:
function sxml_to_array($xml) {
$arr = array();
$x = 0;
foreach($xml as $a=>$b) {
$arr[$a][$x] = array();
// Looking for ATTRIBUTES
$att = $b->attributes();
foreach($att as $c=>$d) {
$arr[$a][$x]['@'][$c] = (string) $d;
}
// Getting CDATA
$arr[$a][$x]['cdata'] = trim((string) utf8_decode($b));
// Processing CHILD NODES
$arr[$a][$x]['nodes'] = sxml_to_array($b);
$x++;
}
return $arr;
}
$xml = simplexml_load_file('myfile.xml');
$arr = sxml_to_array($xml);
print("<pre>");
print_r($arr);
print("</pre>");
daniel dot oconnor at gmail dot com
18-May-2006 09:21
SimpleXML handles namespaces, but it's not documented very well here at all.
If you wanted to parse, say, an open office manifest file or a piece of RDF/XML, you have to get elements and attributes by the namespace url.
Example:
<?php
function display($in) {
if (file_exists($in)) {
$xml = simplexml_load_file($in);
} else {
throw new Exception($in . " does not exist");
}
$manifest = $xml->children('http://openoffice.org/2001/manifest');
foreach ($manifest->xpath('//manifest:file-entry') as $file) {
foreach ($file->attributes('http://openoffice.org/2001/manifest') as $key => $value) {
print "Key:" . $key . "\n";
print "Value:" . $value . "\n";
}
}
}
?>
File: manifest.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE manifest:manifest PUBLIC "-//OpenOffice.org//DTD Manifest 1.0//EN" "Manifest.dtd">
<manifest:manifest xmlns:manifest="http://openoffice.org/2001/manifest">
<manifest:file-entry manifest:media-type="application/vnd.sun.xml.writer" manifest:full-path="/"/>
<manifest:file-entry manifest:media-type="application/vnd.sun.xml.ui.configuration" manifest:full-path="Configurations2/"/>
<manifest:file-entry manifest:media-type="application/binary" manifest:full-path="layout-cache"/>
<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="content.xml"/>
<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="styles.xml"/>
<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="meta.xml"/>
<manifest:file-entry manifest:media-type="" manifest:full-path="Thumbnails/thumbnail.png"/>
<manifest:file-entry manifest:media-type="" manifest:full-path="Thumbnails/"/>
<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="settings.xml"/>
</manifest:manifest>
JaskaS
05-May-2006 03:11
More new stuff.
<?php
$xml = "
<test></test>
";
$xml = simplexml_load_string($xml);
$xml->addChild('new','value','namespace');
echo $xml->asXml();
?>
output:
<?xml version="1.0"?>
<test><new xmlns="namespace">value</new></test>
Very nice and clean. DOM is so awkward compared to this.
Vaedan
03-May-2006 01:04
This is one new stuff to get the root name (since PHP 5.1.3) :
<?php
$xml = simplexml_load_string( '<foo name="bar" />' );
echo $xml->getName(); // displays foo
?>
chad 0x40 herballure 0x2e com
31-Mar-2006 11:47
It's mentioned in Example 5, but it's counterintuitive, so I'll highlight it here: attribute values are actually SimpleXMLElement objects and (like actual elements) they need to be cast to string before use as an array index. Otherwise PHP throws an "Illegal offset type" warning.
Jason
28-Jan-2006 10:45
As noted by paul at preinheimer dot com in a lower note ( http://us3.php.net/manual/en/ref.simplexml.php#52583 );
If you want to access an element that has a dash in its name, (as is common with the XML documents provided by the Library of Congress, as well as the NWS) you will need to handle it a little bit differently.
You can either use XPATH, which works fine, but will return an array of results every time, even if there is a single result.
eg.
$xml->xpath('/data/time-layout/start-valid-time'}
You can also choose just to encapsulate the element names containing a dash:
$xml->data->{'time-layout'}->{'start-valid-time'}
greyl at greyworld dot net
17-Jan-2006 02:56
If you want to use XML elements containing a '-', you can use a variable:
<?php
$xml = simplexml_load_file("test.xml");
$tag = 'first-name';
echo $xml->$tag->asxml();
?>
t at wantzen dot com
17-Oct-2005 05:00
Thanks to Daniel Faivre! Great work!
I modified it to an array filled with ISO-8859-1 text and not UTF-8. Haven't tested it in detail - so be cautious!
Tobias
<?php
/**
* Convert SimpleXMLElement object to ISO array
* Copyright Daniel FAIVRE 2005 - www.geomaticien.com
* Copyleft GPL license
*
* Modifications by Tobias Wantzen
* Returned array will contain single-byte ISO-8859-1 using utf8_decode()
*/
function simplexml2ISOarray($xml) {
if (get_class($xml) == 'SimpleXMLElement') {
$attributes = $xml->attributes();
foreach($attributes as $k=>$v) {
if ($v) $a[$k] = (string) $v;
}
$x = $xml;
$xml = get_object_vars($xml);
}
if (is_array($xml)) {
if (count($xml) == 0) return (string) $x; // for CDATA
foreach($xml as $key=>$value) {
$r[$key] = simplexml2ISOarray($value);
// original line instead of the following if statement:
//$r[$key] = simplexml2ISOarray($value);
if ( !is_array( $r[$key] ) ) $r[$key] = utf8_decode( $r[$key] );
}
if (isset($a)) $r['@'] = $a; // Attributes
return $r;
}
return (string) $xml;
}
?>
js1 at qq dot com (js5131)
12-Oct-2005 09:27
modify the content of simpleXML:
<?php
$str="<?xml version='1.0'?>
<root>
<name>tom</name>
<sex>man</sex>
</root>";
$sx = simplexml_load_string($str);
$sx -> name = "simpleXML";
echo $sx->asxml();
?>
lists at cyberlot dot net
08-Sep-2005 03:51
While you can't add new elements to a SimpleXML object you can however add new attributes
<?php
$string = '<doc channel="chat"><test1>Hello</test1></doc>';
$xml = simplexml_load_string($string);
$xml->test1['sub'] = 'No';
echo $xml->asXML();
?>
Will return output
<doc channel="chat"><test1 sub="No">Hello</test1></doc>
galaxiaguy at hotmail dot com
03-Sep-2005 10:52
I tried extending SimpleXMLElement to add some functionality and found you can't set new properties. I assume this is something to do with built-in magic methods or something.
So I wrote a class that behaves the same as SimpleXMLElement (by delegating most of it's work) but is arbitrarily extendable.
http://www.oliverbrown.me.uk/2005/09/03/extending-simplexml/
It's very new and not very well tested but could be useful to somebody.
mmacadams at casslin dot com
03-Sep-2005 12:46
it's important to note that PHP seems to disregard to first "all encompassing" tag...
for example:
$xmlstr="<order>
<billing_name>Matt</billing_name>
<billing_address>123 Main St.</billing_address>
</order>";
the 'billing_name' would be accessed by:
$xml = simplexml_load_string($xmlstr);
$billing_name = $xml->billing_name;
NOT:
$xml = simplexml_load_string($xml);
$billing_name = $xml->order->billing_name;
Maybe I'm just an idiot, but this took me a few hours to figure out.. (Partly due to the similiarity between the words "movie" and "movies" in the Movie example at the top of the page)
haha
hope this helps save someone some time! :)
emdeboas at bronstee dot com
30-Jun-2005 06:51
It seems not to be possible to find the name of the first XML tag in a SimpleXML document. Therefore here a simple way to find out (using the example XML):
<?php
include 'example.php';
$xml = simplexml_load_string($xmlstr);
$dom = dom_import_simplexml ($xml);
$root = $dom->tagName;
echo "-- the tagname is...: $root<br/>"; //should be: movies
?>
Maciek Ruckgaber <maciekrb at gmai dot com>
08-Jun-2005 08:07
after wondering around some time, i just realized something (maybe obvious, not very much for me). Hope helps someone to not waste time as i did :-P
when you have something like:
$xmlstr = <<<XML
<?xml version="1.0" encoding="utf-8"?>
<double xmlns="http://foosite.foo/">2328</double>
XML;
you will have the simpleXML object "transformed" to the text() content:
$xml = simplexml_load_string($xmlstr);
echo $xml; // this will echo 2328 (string)
cyberlot at cyberlot dot net
10-May-2005 04:24
outping a SimpleXML object is deceptive
For example
<doc channel="chat"><mess>Hello</mess></doc>
print_r returns
SimpleXMLElement Object
(
[mess] => Hello
)
One might assume you are "losing" the attribute of doc BUT
echo $object['channel']
Does return the value of chat
No xpath /doc/@channel mess needed to access it, Its there you just can't "see" it until you access it directly
Everything is there, sometimes you just have to hunt for it.
paul at preinheimer dot com
06-May-2005 08:11
Hi,
If you want to access an element that has a dash in its name, (as is common with the XML documents provided by the Library of Congress, as well as the NWS) you will need to handle it a little bit differently.
You can either use XPATH, which works fine, but will return an array of results every time, even if there is a single result.
eg.
$xml->xpath('/data/time-layout/start-valid-time'}
You can also choose just to encapsulate the element names containing a dash:
$xml->data->{'time-layout'}->{'start-valid-time'}
--
On a only partially related note, dealing with SimpleXML is one of the only times I have employed casting with PHP. While iterating (foreach) through the valid times, echo'ing the element worked great (it merely echo'ed the apropriate time), assigning it to another variable resulted in a SimpleXML object containing the time to be assigned, rather than just the time itself. This was resolved by casting the time to a string:
foreach($xml->data->{'time-layout'}->{'start-valid-time'} AS $time)
{
$weatherDates[] = (string) $time;
}
T CHASSAGNETTE t_chassagnette at yahoo dot fr
04-May-2005 04:57
Another method to parse an XML Document into a PHP array with SIMPLEXML inspired from Daniel FAIVRE !
function xml2php($xml)
{
$fils = 0;
$tab = false;
$array = array();
foreach($xml->children() as $key => $value)
{
$child = xml2php($value);
//To deal with the attributes
foreach($node->attributes() as $ak=>$av)
{
$child[$ak] = (string)$av;
}
//Let see if the new child is not in the array
if($tab==false && in_array($key,array_keys($array)))
{
//If this element is already in the array we will create an indexed array
$tmp = $array[$key];
$array[$key] = NULL;
$array[$key][] = $tmp;
$array[$key][] = $child;
$tab = true;
}
elseif($tab == true)
{
//Add an element in an existing array
$array[$key][] = $child;
}
else
{
//Add a simple element
$array[$key] = $child;
}
$fils++;
}
if($fils==0)
{
return (string)$xml;
}
return $array;
}
nelson_menezes at yahoo dot co dot uk
01-Mar-2005 09:05
Note that SimpleXML expects to both read and output XML in UTF-8 encoding. You'll need to add a line such as this at the top of your input XML file if it isn't saved in UTF-8 (adjust to whatever encoding used):
<?xml version="1.0" encoding="ISO-8859-1" ?>
On the output side of things, if you're not serving/handling UTF-8, you'll need to use utf8_decode() [red. but that will only work for ISO-8859-1, not other encodings]. Common mistake: http://bugs.php.net/bug.php?id=28154
webmarks
14-Feb-2005 01:57
I'm new to PHP and XML, but I think others might pull their hair out over this too. I forgot about XML and whitespace when I was populating an array with node values, and was getting what appeared to be duplicate values despite using array_unique(). Use trim() when populating an array or assigning a variable.
<?php
$xml_array = array();
foreach ($resource->xpath($node_match) as $option){
$xml_array[$i] = trim($option);
$i++;
}
$xml_unique_array = array_unique($xml_array);
?>
roland dot swingler at transversal dot com
08-Feb-2005 06:24
Just a quick note - if you wish to get the name of the tag you are operating on, use the the key bit of foreach, i.e in the example:
<?php
// Outputs title characters plot rating rating
foreach($xml->movie[0]->children() as $tag => $child){
echo $tag;
}
?>
This might seem obvious but it stumped me for a bit.
Daniel FAIVRE - geomaticien.com
15-Jan-2005 07:46
I've searched a while for a convenient "to array" function, and finally wrote it. These one provide at least 5 cool features:
- the MOST IMPORTANT: return a correct structure when several childs nodes have the same name: nodes are numbered properly
- read CDATA values
- returned array is very easy-to-use
- attributes returned in sub-arrays with key [@]
- very fast
When you create a "xml2array" function, you need to manage nodes with attributes, but without children: i've choosed to ignore their attributes to avoid a "virtual ['value'] node" for such nodes in the returned array.
<?php
/**
* Convert SimpleXMLElement object to array
* Copyright Daniel FAIVRE 2005 - www.geomaticien.com
* Copyleft GPL license
*/
function simplexml2array($xml) {
if (get_class($xml) == 'SimpleXMLElement') {
$attributes = $xml->attributes();
foreach($attributes as $k=>$v) {
if ($v) $a[$k] = (string) $v;
}
$x = $xml;
$xml = get_object_vars($xml);
}
if (is_array($xml)) {
if (count($xml) == 0) return (string) $x; // for CDATA
foreach($xml as $key=>$value) {
$r[$key] = simplexml2array($value);
}
if (isset($a)) $r['@'] = $a; // Attributes
return $r;
}
return (string) $xml;
}
?>
igor kraus
08-Jan-2005 02:37
A simple way to merge two SimpleXML objects.
<?php
/**
* Pumps all child elements of second SimpleXML object into first one.
*
* @param object $xml1 SimpleXML object
* @param object $xml2 SimpleXML object
* @return void
*/
function simplexml_merge (SimpleXMLElement &$xml1, SimpleXMLElement $xml2)
{
// convert SimpleXML objects into DOM ones
$dom1 = new DomDocument();
$dom2 = new DomDocument();
$dom1->loadXML($xml1->asXML());
$dom2->loadXML($xml2->asXML());
// pull all child elements of second XML
$xpath = new domXPath($dom2);
$xpathQuery = $xpath->query('/*/*');
for ($i = 0; $i < $xpathQuery->length; $i++)
{
// and pump them into first one
$dom1->documentElement->appendChild(
$dom1->importNode($xpathQuery->item($i), true));
}
$xml1 = simplexml_import_dom($dom1);
}
$xml1 = simplexml_load_string('<root><child>child 1</child></root>');
$xml2 = simplexml_load_string('<root><child>child 2</child></root>');
simplexml_merge($xml1, $xml2);
echo($xml1->asXml());
?>
Will output:
<?xml version="1.0"?>
<root>
<child>child 1</child>
<child>child 2</child>
</root>
brcavanagh AT NO SPAM hotmail.com
03-Jan-2005 01:18
If you are looking to use SimpleXML for anything but reading XML documents, you should really reconsider, and use the XML DOM library. By the time you get enough utilities implemented in DOM to handle all the set backs in SimpleXML, you will have defeated the purpose of using SimpleXML. There are a few reasons for this, and there are already many workrounds, but the primairy issues are this
1) No complex node assignment. You cannot switch nodes or replace them.
2) No appending new child nodes
3) Whenever you do something like $new_node = $xml_doc->node you will always get a reference even if you use a clone method, which will crash the script.
Other than that, its a great tool for reading docs.
lajos dot arpasi at maxxlogic dot hu
08-Oct-2004 08:31
If you use PHP4 and miss the features of SimpleXML try MiniXML (http://minixml.psychogenic.com).
MiniXML is a PHP class library for generating and parsing XML.
MiniXML have similar abilities like creating XML files from Arrays and importing XML files into Arrays.
You can manipulate the XML files more easily than SimpleXML.
It saved my life:).
jam from Russia
24-Sep-2004 11:23
Example:
<?xml version="1.0"?>
<root status="ok">
<login status="logining"/>
<user:name xmlns:user="http://user.com/">jam</user:name>
</root>
You can get value from "user:name" element:
$user_name = $xml->xpath('/root/user:name');
You can get value from "status" attribute "root" element:
$status = $xml->xpath('/root/@status');
You can get value from "status" attribute "login" element:
$logined = $xml->xpath('/root/login/@status');
SimpleXML work with namespace :-)
cellog at php dot net
01-Sep-2004 07:11
simplexml does not simply handle CDATA sections in a foreach loop.
<?php
$sx = simplexml_load_string('
<test>
<one>hi</one>
<two><![CDATA[stuff]]></two>
<t>
<for>two</for>
</t>
<multi>one</multi>
<multi>two</multi>
</test>');
foreach((array) $sx as $tagname => $val) {
if (is_string($val)) {
// <one> will go here
} elseif (is_array($val)) {
// <multi> will go here because it happens multiple times
} elseif (is_object($val)) {
// <t> will go here because it contains tags
// <two> will go here because it contains CDATA!
}
}
?>
To test in the loop, do this
<?php
if (count((array) $val) == 0) {
// this is not a tag that contains other tags
$val = '' . $val;
// now the CDATA is revealed magically.
}
?>
rishad at kaluma dot com
02-Jul-2004 12:12
To test whether a child node exists I used the following code:
<?php
function child_exists($xml, $childpath)
{
$result = $xml->xpath($childpath);
if (count($result)) {
return true;
} else {
return false;
}
}
?>
greg dot steffensen at spamless dot richmond dot edu
20-Feb-2004 07:04
Simplexml's simplicity can be deceptive. Simplexml elements behave either as objects or strings, depending on the context in which they're used (through overloading of the __toString() method, I assume). Statements implying conversion to string treat them as strings, while assignment operations treat them as objects. This can lead to unexpected behavior if, for example, you are trying to compare the values of two Simplexml elements. The expected syntax will not work. To force conversion to strings, just "typecast' whatever Simplexml element you're using. For example:
<?php
$s = simplexml_load_string('<foo>43</foo> <bar>43</bar>');
// Evaluates to false by comparing object IDs instead of strings
($s->foo == $s->bar);
// Evaluates to true
((string)$s->foo == (string)$s->bar);
?>
[Ed. Note: Changed from quotes to casts because casts provide a quicker and more explicit conversion than do double quotes.]
|  |