APIDatabaseError class for SQL error, which send debug info to slim\log
[dolibarr-api.git] / Array2XML.php
1 <?php
2 /**
3  * Array2XML: A class to convert array in PHP to XML
4  * It also takes into account attributes names unlike SimpleXML in PHP
5  * It returns the XML in form of DOMDocument class for further manipulation.
6  * It throws exception if the tag name or attribute name has illegal chars.
7  *
8  * Author : Lalit Patel
9  * Website: http://www.lalit.org/lab/convert-php-array-to-xml-with-attributes
10  * License: Apache License 2.0
11  *          http://www.apache.org/licenses/LICENSE-2.0
12  * Version: 0.1 (10 July 2011)
13  * Version: 0.2 (16 August 2011)
14  *          - replaced htmlentities() with htmlspecialchars() (Thanks to Liel Dulev)
15  *          - fixed a edge case where root node has a false/null/0 value. (Thanks to Liel Dulev)
16  * Version: 0.3 (22 August 2011)
17  *          - fixed tag sanitize regex which didn't allow tagnames with single character.
18  * Version: 0.4 (18 September 2011)
19  *          - Added support for CDATA section using @cdata instead of @value.
20  * Version: 0.5 (07 December 2011)
21  *          - Changed logic to check numeric array indices not starting from 0.
22  * Version: 0.6 (04 March 2012)
23  *          - Code now doesn't @cdata to be placed in an empty array
24  * Version: 0.7 (24 March 2012)
25  *          - Reverted to version 0.5
26  * Version: 0.8 (02 May 2012)
27  *          - Removed htmlspecialchars() before adding to text node or attributes.
28  *
29  * Usage:
30  *       $xml = Array2XML::createXML('root_node_name', $php_array);
31  *       echo $xml->saveXML();
32  */
33
34 class Array2XML {
35
36     private static $xml = null;
37         private static $encoding = 'UTF-8';
38
39     /**
40      * Initialize the root XML node [optional]
41      * @param $version
42      * @param $encoding
43      * @param $format_output
44      */
45     public static function init($version = '1.0', $encoding = 'UTF-8', $format_output = true) {
46         self::$xml = new DomDocument($version, $encoding);
47         self::$xml->formatOutput = $format_output;
48                 self::$encoding = $encoding;
49     }
50
51     /**
52      * Convert an Array to XML
53      * @param string $node_name - name of the root node to be converted
54      * @param array $arr - aray to be converterd
55      * @return DomDocument
56      */
57     public static function &createXML($node_name, $arr=array()) {
58         $xml = self::getXMLRoot();
59         $xml->appendChild(self::convert($node_name, $arr));
60
61         self::$xml = null;    // clear the xml node in the class for 2nd time use.
62         return $xml;
63     }
64
65     /**
66      * Convert an Array to XML
67      * @param string $node_name - name of the root node to be converted
68      * @param array $arr - aray to be converterd
69      * @return DOMNode
70      */
71     private static function &convert($node_name, $arr=array()) {
72
73         //print_arr($node_name);
74         $xml = self::getXMLRoot();
75         $node = $xml->createElement($node_name);
76
77         if(is_array($arr)){
78             // get the attributes first.;
79             if(isset($arr['@attributes'])) {
80                 foreach($arr['@attributes'] as $key => $value) {
81                     if(!self::isValidTagName($key)) {
82                         throw new Exception('[Array2XML] Illegal character in attribute name. attribute: '.$key.' in node: '.$node_name);
83                     }
84                     $node->setAttribute($key, self::bool2str($value));
85                 }
86                 unset($arr['@attributes']); //remove the key from the array once done.
87             }
88
89             // check if it has a value stored in @value, if yes store the value and return
90             // else check if its directly stored as string
91             if(isset($arr['@value'])) {
92                 $node->appendChild($xml->createTextNode(self::bool2str($arr['@value'])));
93                 unset($arr['@value']);    //remove the key from the array once done.
94                 //return from recursion, as a note with value cannot have child nodes.
95                 return $node;
96             } else if(isset($arr['@cdata'])) {
97                 $node->appendChild($xml->createCDATASection(self::bool2str($arr['@cdata'])));
98                 unset($arr['@cdata']);    //remove the key from the array once done.
99                 //return from recursion, as a note with cdata cannot have child nodes.
100                 return $node;
101             }
102         }
103
104         //create subnodes using recursion
105         if(is_array($arr)){
106             // recurse to get the node for that key
107             foreach($arr as $key=>$value){
108                 if(!self::isValidTagName($key)) {
109                     throw new Exception('[Array2XML] Illegal character in tag name. tag: '.$key.' in node: '.$node_name);
110                 }
111                 if(is_array($value) && is_numeric(key($value))) {
112                     // MORE THAN ONE NODE OF ITS KIND;
113                     // if the new array is numeric index, means it is array of nodes of the same kind
114                     // it should follow the parent key name
115                     foreach($value as $k=>$v){
116                         $node->appendChild(self::convert($key, $v));
117                     }
118                 } else {
119                     // ONLY ONE NODE OF ITS KIND
120                     $node->appendChild(self::convert($key, $value));
121                 }
122                 unset($arr[$key]); //remove the key from the array once done.
123             }
124         }
125
126         // after we are done with all the keys in the array (if it is one)
127         // we check if it has any text value, if yes, append it.
128         if(!is_array($arr)) {
129             $node->appendChild($xml->createTextNode(self::bool2str($arr)));
130         }
131
132         return $node;
133     }
134
135     /*
136      * Get the root XML node, if there isn't one, create it.
137      */
138     private static function getXMLRoot(){
139         if(empty(self::$xml)) {
140             self::init();
141         }
142         return self::$xml;
143     }
144
145     /*
146      * Get string representation of boolean value
147      */
148     private static function bool2str($v){
149         //convert boolean to text value.
150         $v = $v === true ? 'true' : $v;
151         $v = $v === false ? 'false' : $v;
152         return $v;
153     }
154
155     /*
156      * Check if the tag name or attribute name contains illegal characters
157      * Ref: http://www.w3.org/TR/xml/#sec-common-syn
158      */
159     private static function isValidTagName($tag){
160         $pattern = '/^[a-z_]+[a-z0-9\:\-\.\_]*[^:]*$/i';
161         return preg_match($pattern, $tag, $matches) && $matches[0] == $tag;
162     }
163 }
164 ?>