This class library provides two XML related functions. One to convert an XML Tree into a PHP array and another to convert a complex PHP object into XML.
The first is the xml_to_array function witch will read through an entire xml tree and convert it into a single multi-dimensional array. That is each node in the root node will be added as items in the array and all children and properties will be added to the respective items. This is done in a completely recursive manner so that nodes within nodes within nodes will be fully read and created as arrays within arrays within arrays. The exact format of the generated array is dependent on the options selected.
The default options will merge all the properties and children directly into the node but this may need to be disabled if you have children with the same name as properties or if you need to distinguish between the two types.
Here is a sample XML file, from a svn dump.
<b>svn log --non-interactive --xml</b>
<?xml version="1.0"?>
<log>
<logentry
revision="114">
<author>pynej</author>
<date>2010-01-11T14:20:42.200771Z</date>
<msg>Removed smarty 2.0 code.
</msg>
</logentry>
<logentry
revision="113">
<author>pynej</author>
<date>2008-08-08T05:14:31.046815Z</date>
<msg>* Changed videos to search recursively. This may be configured by an extra variable in the future.</msg>
</logentry>
</log>
Calling the conversion with the default options would look like so.
<b>print_r(xmlutils::xml_to_array($xml));</b>
Array
(
[name] => log
[0] => Array
(
[name] => logentry
[revision] => 114
[author] => pynej
[date] => 2010-01-11T14:20:42.200771Z
[msg] => Removed smarty 2.0 code.
)
[1] => Array
(
[name] => logentry
[revision] => 113
[author] => pynej
[date] => 2008-08-08T05:14:31.046815Z
[msg] => * Changed videos to search recursively. This may be configured by an extra variable in the future.
)
)
Where as the call with no options generates the more detailed but less user friendly output.
<b>print_r(xmlutils::xml_to_array($xml), 0);</b>
Array
(
[name] => log
[children] => Array
(
[0] => Array
(
[name] => logentry
[attributes] => Array
(
[revision] => 114
)
[children] => Array
(
[0] => Array
(
[name] => author
[value] => pynej
)
[1] => Array
(
[name] => date
[value] => 2010-01-11T14:20:42.200771Z
)
[2] => Array
(
[name] => msg
[value] => Removed smarty 2.0 code.
)
)
)
[1] => Array
(
[name] => logentry
[attributes] => Array
(
[revision] => 113
)
[children] => Array
(
[0] => Array
(
[name] => author
[value] => pynej
)
[1] => Array
(
[name] => date
[value] => 2008-08-08T05:14:31.046815Z
)
[2] => Array
(
[name] => msg
[value] => * Changed videos to search recursively. This may be configured by an extra variable in the future.
)
)
)
)
)
A more useful format might be to retail the attribute/children breakdown but processes the rest.
<b>print_r(xmlutils::xml_to_array($xml), xmlutils::XML_MERGE_ATTRIBUTES | xmlutils::XML_VALUE_PAIRS | xmlutils::XML_MERGE_VALUES);</b>
Array
(
[name] => log
[children] => Array
(
[0] => Array
(
[name] => logentry
[revision] => 114
[children] => Array
(
[author] => pynej
[date] => 2010-01-11T14:20:42.200771Z
[msg] => Removed smarty 2.0 code.
)
)
[1] => Array
(
[name] => logentry
[revision] => 113
[children] => Array
(
[author] => pynej
[date] => 2008-08-08T05:14:31.046815Z
[msg] => * Changed videos to search recursively. This may be configured by an extra variable in the future.
)
)
)
)
The second function array_to_xml will to the inverse of this and will generate a XML Tree from a complex anonymous array object. This object can contain any amount of data and will be recursively traversed and added to the tree.
Also note that XML Tree's cant have shared nodes or references so if any exist in the source object the data in them will be duplicated in the XML Tree. There is no way to preserve these references with this tool. Also note that no recursion checks are done so an object with circular recursion will lock up the process.
The following array converted to XML will look like so.
<b>print_r($logList);</b>
Array
(
[name] => log
[0] => Array
(
[name] => logentry
[revision] => 114
[author] => pynej
[date] => 2010-01-11T14:20:42.200771Z
[msg] => Removed smarty 2.0 code.
)
[1] => Array
(
[name] => logentry
[revision] => 113
[author] => pynej
[date] => 2008-08-08T05:14:31.046815Z
[msg] => Array
(
[0] => * Added links to the debug section to view the contents of ajax calls.
[1] => * Added query->get_md5 to calculate the md5 hash of a query result.
)
)
<b>print_r(xmlutils::array_to_xml($logList));</b>
<?xml version="1.0" encoding="utf-8"?>
<data>
<name>log</name>
<data-item>
<name>logentry</name>
<revision>114</revision>
<author>pynej</author>
<date>2010-01-11T14:20:42.200771Z</date>
<msg>Removed smarty 2.0 code.</msg>
</data-item>
<data-item>
<name>logentry</name>
<revision>113</revision>
<author>pynej</author>
<date>2008-08-08T05:14:31.046815Z</date>
<msg>
<msg-item>* Added links to the debug section to view the contents of ajax calls.</msg-item>
<msg-item>* Added query->get_md5 to calculate the md5 hash of a query result.</msg-item>
</msg>
</data-item>
</data>
Source Code:
<b>xmlutils.php</b>
/**
* This class contains mothods to convert a xml tree into a complex array with nested properties and to convert a complex array object into an xml tree.
*
* @package xml-utils
* @author Jeremy Pyne <jeremy.pyne@gmail.com>
* @license CC:BY/NC/SA http://creativecommons.org/licenses/by-nc-sa/3.0/
* @lastupdate February 16 2010
* @version 1.5
*/
final class xmlutils
{
/**
* Add a levels attributes directly to the levels node instead of into an attributes array.
*
*/
const XML_MERGE_ATTRIBUTES = 1;
/**
* Merge the attribures of a level into the parent level that they belong to.
*
*/
const XML_MERGE_VALUES = 2;
/**
* Add a levels children directly to the levels node instead of into a children array.
*
*/
const XML_MERGE_CHIILDREN = 4;
/**
* Process value as a lone entry under their level and ignore the other attributes and children.
*
*/
const XML_VALUE_PAIRS = 8;
/**
* Split the value of a node into an array on newlines.
*
*/
const XML_SPLIT_VALUES = 16;
/**
* If a value is an array with a single item, just use the item.
*
*/
const XML_SPLIT_SHIFT = 32;
/**
* This function will convert an XML tree into a multi-dimentional array.
*
* @param SimpleXMLElement $xml
* @param bitfield $ops
* @return array
*/
public static function xml_to_array($xml, $ops=63) {
// Store the name of this level.
$level = array();
$level["name"] = $xml->getName();
// Grab the value of this level.
$value = trim((string)$xml);
// If we have a value, process it.
if($value) {
// Split the value into an array on newlines.
if($ops & self::XML_SPLIT_VALUES)
$value = explode("\n", $value);
// If the value is an array with one item, remove the array.
if($ops & self::XML_SPLIT_SHIFT)
if(sizeof($value) == 1)
$value = array_shift($value);
// Store the value of this level.
$level["value"] = $value;
}
// If this level had a value just return the name/value as an array.
if($ops & self::XML_VALUE_PAIRS && array_key_exists("value", $level))
return array($level["name"] => $level["value"]);
// Loop through each atribute of this level.
foreach($xml->attributes() as $attribute) {
// Add each attribure directly to this level in the array.
if($ops & self::XML_MERGE_ATTRIBUTES)
$level[$attribute->getName()] = (string)$attribute;
// Add all the attributes to an attributes array under this level in the array.
else
$level["attributes"][$attribute->getName()] = (string)$attribute;
}
// Loop through each child of this level.
foreach($xml->children() as $children) {
// Get an array of this childs data.
$child = self::xml_to_array($children, $ops);
if($ops & self::XML_MERGE_VALUES) {
// Add each child directly to this level or to the children array of this level in the array.
if(sizeof($child) == 1) {
// If there is only one child then merge it up.
if($ops & self::XML_MERGE_CHIILDREN)
$level[array_shift(array_keys($child))] = $child[array_shift(array_keys($child))];
else
$level["children"][array_shift(array_keys($child))] = $child[array_shift(array_keys($child))];
} elseif(array_key_exists("value", $child)) {
// If there is a value key then merge it up.
if($ops & self::XML_MERGE_CHIILDREN)
$level[$child["name"]] = $child["value"];
else
$level["children"][$child["name"]] = $child["value"];
} elseif(array_key_exists("children", $child)) {
// If there are children, then merge them up.
if($ops & self::XML_MERGE_CHIILDREN)
$level[] = $child;
else
$level["children"][] = $child;
} else {
// Otherwise just assigne yourself.
if($ops & self::XML_MERGE_CHIILDREN)
$level[] = $child;
else
$level["children"][] = $child;
}
} else {
$level["children"][] = $child;
}
}
return $level;
}
/**
* The main function for converting to an XML document.
* Pass in a multi dimensional array and this recrusively loops through and builds up an XML document.
*
* @param array $data
* @param string $rootNodeName - what you want the root node to be - defaultsto data.
* @param SimpleXMLElement $xml - should only be used recursively
* @return string XML
*/
public static function array_to_xml($data, $rootNodeName = 'data', $xml=null, $parentXml=null)
{
// turn off compatibility mode as simple xml throws a wobbly if you don't.
if (ini_get('zend.ze1_compatibility_mode') == 1)
{
ini_set ('zend.ze1_compatibility_mode', 0);
}
//if ($rootNodeName == false) {
// $xml = simplexml_load_string("<s/>");
//}
if ($xml == null)
{
$xml = simplexml_load_string("<?xml version='1.0' encoding='utf-8'?><$rootNodeName />");
}
// loop through the data passed in.
foreach($data as $key => $value)
{
// Create a name for this item based off the attribute name or if this is a item in an array then the parent nodes name.
$nodeName = is_numeric($key) ? $rootNodeName . '-item' : $key;
$nodeName = preg_replace('/[^a-z1-9_-]/i', '', $nodeName);
// If this item is an array then we will be recursine to the logic is more complex.
if (is_array($value)) {
// If this node is part of an array we have to proccess is specialy.
if (is_numeric($key)) {
// Another exception if this is teh root node and is an array. In this case we don't have a parent node to use so we must use the current node and not update the reference.
if($parentXml == null) {
$childXml = $xml->addChild($nodeName);
self::array_to_xml($value, $nodeName, $childXml, $xml);
// If this is a array node then we want to add the item under the parent node instead of out current node. Also we have to update $xml to reflect the change.
} else {
$xml = $parentXml->addChild($nodeName);
self::array_to_xml($value, $nodeName, $xml, $parentXml);
}
} else {
// For a normal attribute node just add it to the parent node.
$childXml = $xml->addChild($nodeName);
self::array_to_xml($value, $nodeName, $childXml, $xml);
}
// If not then it is a simple value and can be directly appended to the XML tree.
} else {
$value = htmlentities($value);
$xml->addChild($nodeName, $value);
}
}
// Pass back as string or simple xml object.
return $xml->asXML();
}
Copyright © 2011 - All Rights Reserved - Softron.in
Template by Softron Technology