Completed
Push — master ( e9ba9e...8032f4 )
by Rafael
02:45
created

Array2XML::validateTagName()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 10
rs 9.4285
cc 3
eloc 5
nc 2
nop 3
1
<?php
2
3
/**
4
 * LICENSE: This file is subject to the terms and conditions defined in
5
 * file 'LICENSE', which is part of this source code package.
6
 *
7
 * @copyright 2016 Copyright(c) - All rights reserved.
8
 */
9
10
namespace Rafrsr\LibArray2Xml;
11
12
/**
13
 * Array2XML: A class to convert array in PHP to XML
14
 * It also takes into account attributes names unlike SimpleXML in PHP
15
 * It returns the XML in form of DOMDocument class for further manipulation.
16
 * It throws exception if the tag name or attribute name has illegal chars.
17
 *
18
 * Based on http://www.lalit.org/lab/convert-xml-to-array-in-php-xml2array/
19
 *
20
 *  - some minor bug fixes
21
 *  - support for php7
22
 *  - tests
23
 *
24
 * Usage:
25
 *       $xml = Array2XML::createXML('root_node_name', $php_array);
26
 *       echo $xml->saveXML();
27
 */
28
class Array2XML
29
{
30
    use CommonsTrait;
31
32
    /**
33
     * Convert an Array to XML
34
     *
35
     * @param string $nodeName - name of the root node to be converted
36
     * @param mixed  $data     - content to put into the xml
37
     *
38
     * @return \DOMDocument
39
     * @throws \InvalidArgumentException
40
     */
41
    public static function createXML($nodeName, $data)
42
    {
43
        $xml = self::getXMLRoot();
44
        $xml->appendChild(self::convert($nodeName, $data));
45
46
        self::$xml = null;// clear the xml node in the class for 2nd time use.
47
48
        return $xml;
49
    }
50
51
    /**
52
     * Convert an Array to XML
53
     *
54
     * @param string $nodeName - name of the root node to be converted
55
     * @param mixed  $data     - content to put into the xml
56
     *
57
     * @return \DOMNode
58
     * @throws \InvalidArgumentException
59
     */
60
    private static function convert($nodeName, $data)
61
    {
62
        //print_arr($nodeName);
63
        $xml = self::getXMLRoot();
64
        $node = $xml->createElement($nodeName);
65
66
        if (is_array($data)) {
67
            // get the attributes first.;
68
            if (array_key_exists(self::$prefixAttributes . 'attributes', $data)) {
69
                foreach ($data[self::$prefixAttributes . 'attributes'] as $key => $value) {
70
                    self::validateTagName($key, $nodeName, 'attribute');
71
                    $node->setAttribute($key, self::bool2str($value));
72
                }
73
                unset($data[self::$prefixAttributes . 'attributes']); //remove the key from the array once done.
74
            }
75
76
            // check if it has a value stored in @value, if yes store the value and return
77
            // else check if its directly stored as string
78
            if (array_key_exists(self::$prefixAttributes . 'value', $data)) {
79
                $node->appendChild($xml->createTextNode(self::bool2str($data[self::$prefixAttributes . 'value'])));
80
                unset($data[self::$prefixAttributes . 'value']);    //remove the key from the array once done.
81
                //return from recursion, as a note with value cannot have child nodes.
82
                return $node;
83
            } else {
84
                if (array_key_exists(self::$prefixAttributes . 'cdata', $data)) {
85
                    $node->appendChild($xml->createCDATASection(self::bool2str($data[self::$prefixAttributes . 'cdata'])));
86
                    unset($data[self::$prefixAttributes . 'cdata']);    //remove the key from the array once done.
87
                    //return from recursion, as a note with cdata cannot have child nodes.
88
                    return $node;
89
                }
90
            }
91
        }
92
93
        //create sub-nodes using recursion
94
        if (is_array($data)) {
95
            // recurse to get the node for that key
96
            foreach ($data as $key => $value) {
97
                self::validateTagName($key, $nodeName, 'tag');
98
99
                if (is_array($value) && reset($value) && is_numeric(key($value))) {
100
                    // MORE THAN ONE NODE OF ITS KIND;
101
                    // if the new array is numeric index, means it is array of nodes of the same kind
102
                    // it should follow the parent key name
103
                    foreach ($value as $k => $v) {
104
                        $node->appendChild(self::convert($key, $v));
105
                    }
106
                } else {
107
                    // ONLY ONE NODE OF ITS KIND
108
                    $node->appendChild(self::convert($key, $value));
109
                }
110
                unset($data[$key]); //remove the key from the array once done.
111
            }
112
        }
113
114
        // after we are done with all the keys in the array (if it is one)
115
        // we check if it has any text value, if yes, append it.
116
        if (!is_array($data)) {
117
            $node->appendChild($xml->createTextNode(self::bool2str($data)));
118
        }
119
120
        return $node;
121
    }
122
123
    /*
124
     * Get string representation of boolean value
125
     */
126
    private static function bool2str($v)
127
    {
128
        //convert boolean to text value.
129
        $v = $v === true ? 'true' : $v;
130
        $v = $v === false ? 'false' : $v;
131
132
        return $v;
133
    }
134
135
    /*
136
     * Check if the tag name or attribute name contains illegal characters
137
     * Ref: http://www.w3.org/TR/xml/#sec-common-syn
138
     *
139
     * @param string $tag      tag name to verify
140
     * @param string $nodeName node name for informational purposes on error
141
     * @param string $type     verify "tag" or "attribute"
142
     */
143
    private static function validateTagName($tag, $nodeName, $type = 'tag')
144
    {
145
        $pattern = '/^[a-z_]+[a-z0-9\:\-\.\_]*[^:]*$/i';
146
147
        if (!(preg_match($pattern, $tag, $matches) && $matches[0] === $tag)) {
148
            $msg = sprintf('[Array2XML] Illegal character in %s name. %s: %s in node: %s', $type, $type, $tag, $nodeName);
149
150
            throw new \InvalidArgumentException($msg);
151
        }
152
    }
153
}