<?php
//
// Author: Mozart Fazito Rezende
// Email: hide@address.com
// Copywright : Free under Open Source Software rules
// PHP Version: 5 with XML
//
// Purpose: Convert PHP type array to XML object and vice-versa.
//
class xml_to_from_array {
//
// Instructions:
//
// The method "array_to_xml()" converts an array into XML object.
// Notes:
// 1. In order to satisfy the "unique root node" requirement of XML formalism, in the case
// of array with more than one key in the first dimension (vector), it will be created
// a node named "raiz" as root in the outcoming XML.
// 2. In order to satisfy the "not numeric node name" requirement of XML formalism, the
// numeric keys in the array will receive the prefix "A_", before conversion to node
// names in the outcoming XML.
// 3. The array keys, formed by number prefixed by "X_", will be converted to sequenced
// nodes, without a node sibling. This behavior reverses the occurrence of multiple
// child nodes in XML, transformed by the "xml_to_array()" method.
// 4. The array keys, named "valor" and "atributos", will be converted to a attributed
// XML node, with node name formed by the value of the array cell 'valor' and
// attributes generated from the array cell 'atributos'. This behavior reverses the
// conversion of attributed nodes made by the "xml_to_array()" method.
//
// The method "xml_to_array()" converts an XML object into array.
// Notes:
// 1. If the XML root node is named "raiz", it will not be converted to the outcoming array.
// This behavior reverses the root node created by the "array_to_xml()" method when the
// first dimension of an array is a vector.
// 2. If the XML node name is a number prefixed by "A_", it will be convert to a numeric
// key in the outcoming array. This behavior restore the numbered key, converted by the
// "array_to_xml()" method.
// 3. The multiple XML child nodes, with the same node Name, will be converted to numbered
// array keys prefixed by "X_", in the outcoming array.
// 4. The atributed XML node will generate two array keys in the outcoming array: one, named
// "valor", to receive the node name and other named "atributos" to receive the atributes.
//
// Limitations:
// In order to guarantee the reversibility of the created XML back to array and vice-versa,
// the incoming array cannot use key names, and XML object cannot usenode names nor
// attribute names like:
// . "raiz"
// . "atributos"
// . "valor"
// . number prefixed by "A_"
// . number prefixed by "X_".
// Note: Contributions attempting the elimination of these limitations will be well received.
//
// Test:
// Run the "arr2xm.example.php" file
//
var $arrXML = Array();
var $arrObjXML = Array();
var $msg = "";
//
function xml_to_array(&$dom){
reset($this->arrXML);
$this->parse_node($dom);
return ($this->arrXML);
}
private function parse_node($node, $s_arr='', $seq_dup=0) {
$a_dup = Array(); // Array with duplicated node names in a XML tree level
$a_uni = Array(); // Array with all node names in a XML tree level
// Process the children of the current node
if ($node->hasChildNodes()) {
//
foreach($node->childNodes as $n) {
// Skip the document root node:
if ($node->nodeName == "#document" and strlen($s_arr) == 0){
$s_arr = "\$this->arrXML";
$this->parse_node($n, $s_arr);
return;
}
// Create the two arrays in order to find multiple XML child nodes
if(in_array($n->nodeName, $a_uni)){
$a_dup[$n->nodeName] = 1;
}else{
$a_uni[] = $n->nodeName;
}
}
// Eliminate the "A_" previx created by the array_to_xml() method
$nodeName = utf8_decode($node->nodeName);
$tam = strlen($nodeName);
if ($tam > 2){
$prefix = substr($nodeName, 0, 2);
$sufix = substr($nodeName, 2, ($tam-2));
if ($prefix == "A_"){
$nodeName = $sufix;
}
}
// Skip the first XML node, if their name is 'raiz'
if (! ($node->nodeName == "raiz" and $s_arr == "\$this->arrXML")){
if ($seq_dup == 0){
$s_arr .= "['" . $nodeName . "']";
}else{
$s_arr .= "['" . $nodeName . "']['X_" . $seq_dup . "']";
}
}
// Process the attributes of a XML node
if ($node->hasAttributes()) {
foreach ($node->attributes as $attr){
$com = $s_arr . "['atributos']['" . utf8_decode($attr->name) . "'] = '"
. utf8_decode($attr->value) . "';";
eval ($com);
}
}
// Create the prefixed numbered array cells for multiple child nodes in this level
foreach($node->childNodes as $n) {
if ($n->nodeName == '#text') {
$this->cria_array($node, $s_arr);
continue;
}
// Put the node name
if ($a_dup[$n->nodeName] > 0){
$this->parse_node($n, $s_arr, $a_dup[$n->nodeName]);
$a_dup[$n->nodeName] = $a_dup[$n->nodeName] + 1;
}else{
$this->parse_node($n, $s_arr);
}
}
}
}
function cria_array($node, $s_arr){
if ($node->hasAttributes()) {
foreach ($node->attributes as $attr){
$com = $s_arr . "['atributos']['" . utf8_decode($attr->name) . "'] = '"
. utf8_decode($attr->value) . "';";
eval ($com);
}
$com = $s_arr . "['valor'] = '" . utf8_decode($node->nodeValue) . "';";
}else{
$com = $s_arr . " = '" . utf8_decode($node->nodeValue) . "';";
}
eval ($com);
}
function array_to_xml(&$arr_xml){
reset($this->arrObjXML);
// Create the document.
$this->arrObjXML[0] = new DOMDocument( "1.0", "ISO-8859-1" );
$i = 0;
if (count($arr_xml) > 1){
$this->arrObjXML[1] = $this->arrObjXML[0]->createElement("raiz");
$this->parse_arr($arr_xml, 2);
$this->arrObjXML[0]->appendChild($this->arrObjXML[1]);
}else{
$this->parse_arr($arr_xml, 1);
}
return ($this->arrObjXML[0]);
}
private function parse_arr($arrCell, $i) {
//
if (is_array($arrCell)) {
foreach ($arrCell as $cellName => $cellValue){
// Reverse the attributed node
if ($cellName == "valor"){
$this->arrObjXML[$i-1]->nodeValue = utf8_encode($cellValue);
continue;
}
// The atributes
if ($cellName == "atributos"){
foreach ($cellValue as $attrName => $attrValue){
$this->arrObjXML[$i-1]->setAttribute( utf8_encode($attrName), utf8_encode($attrValue) );
}
continue;
}
// Create the prefixed cell for numbered key
if (is_numeric($cellName)){
$cellName = "A_" . $cellName;
}
// Reverse the prefixed numbered cell to multiple childs
if (is_array($cellValue)){
$ha_X = false;
foreach ($cellValue as $subCellName => $subCellValue){
$tam = strlen($subCellName);
if ($tam > 2){
$prefix = substr($subCellName, 0, 2);
$sufix = substr($subCellName, 2, ($tam-2));
if ($prefix == "X_"){
$this->arrObjXML[$i] = $this->arrObjXML[0]->createElement(utf8_encode($cellName));
$this->parse_arr($subCellValue, $i+1);
$this->arrObjXML[$i-1]->appendChild($this->arrObjXML[$i]);
$ha_X = true;
}
}
}
if ($ha_X == true){ continue; }
}
// Process the common array cell
if (is_array($cellValue)){
$this->arrObjXML[$i] = $this->arrObjXML[0]->createElement(utf8_encode($cellName));
$this->parse_arr($cellValue, $i+1);
$this->arrObjXML[$i-1]->appendChild($this->arrObjXML[$i]);
}else{
$this->arrObjXML[$i] = $this->arrObjXML[0]->createElement(utf8_encode($cellName), utf8_encode($cellValue) );
$this->arrObjXML[$i-1]->appendChild($this->arrObjXML[$i]);
}
}
}
}
}
?>