Xml::convert()   C
last analyzed

Complexity

Conditions 14
Paths 20

Size

Total Lines 65
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 35
dl 0
loc 65
rs 6.2666
c 0
b 0
f 0
cc 14
nc 20
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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