GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#109)
by
unknown
01:09
created

ArrayToXml::convertElement()   D

Complexity

Conditions 18
Paths 11

Size

Total Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 42
rs 4.8666
c 0
b 0
f 0
cc 18
nc 11
nop 2

How to fix   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
namespace Spatie\ArrayToXml;
4
5
use DOMElement;
6
use DOMDocument;
7
use DOMException;
8
use Exception;
9
10
class ArrayToXml
11
{
12
    protected $document;
13
14
    protected $replaceSpacesByUnderScoresInKeyNames = true;
15
16
    protected $numericTagNamePrefix = 'numeric_';
17
18
    public function __construct(
19
        array $array,
20
        $rootElement = '',
21
        $replaceSpacesByUnderScoresInKeyNames = true,
22
        $xmlEncoding = null,
23
        $xmlVersion = '1.0',
24
        $domProperties = []
25
    ) {
26
        $this->document = new DOMDocument($xmlVersion, $xmlEncoding);
27
28
        if (! empty($domProperties)) {
29
            $this->setDomProperties($domProperties);
30
        }
31
32
        $this->replaceSpacesByUnderScoresInKeyNames = $replaceSpacesByUnderScoresInKeyNames;
33
34
        if ($this->isArrayAllKeySequential($array) && ! empty($array)) {
35
            throw new DOMException('Invalid Character Error');
36
        }
37
38
        $root = $this->createRootElement($rootElement);
39
40
        $this->document->appendChild($root);
41
42
        $this->convertElement($root, $array);
43
    }
44
45
    public function setNumericTagNamePrefix(string $prefix)
46
    {
47
        $this->numericTagNamePrefix = $prefix;
48
    }
49
50
    public static function convert(
51
        array $array,
52
        $rootElement = '',
53
        bool $replaceSpacesByUnderScoresInKeyNames = true,
54
        string $xmlEncoding = null,
55
        string $xmlVersion = '1.0',
56
        array $domProperties = []
57
    ) {
58
        $converter = new static(
59
            $array,
60
            $rootElement,
61
            $replaceSpacesByUnderScoresInKeyNames,
62
            $xmlEncoding,
63
            $xmlVersion,
64
            $domProperties
65
        );
66
67
        return $converter->toXml();
68
    }
69
70
    public function toXml(): string
71
    {
72
        return $this->document->saveXML();
73
    }
74
75
    public function toDom(): DOMDocument
76
    {
77
        return $this->document;
78
    }
79
80
    protected function ensureValidDomProperties(array $domProperties) {
81
        foreach ($domProperties as $key => $value) {
82
            if (! property_exists($this->document, $key)) {
83
                throw new Exception($key.' is not a valid property of DOMDocument');
84
            }
85
        }
86
    }
87
88
    public function setDomProperties(array $domProperties)
89
    {
90
        $this->ensureValidDomProperties($domProperties);
91
92
        foreach ($domProperties as $key => $value) {
93
            $this->document->{$key} = $value;
94
        }
95
96
        return $this;
97
    }
98
99
    private function convertElement(DOMElement $element, $value)
100
    {
101
        $sequential = $this->isArrayAllKeySequential($value);
102
        
103
        if ($value === null) {
104
            return;
105
        }
106
107
        if (! is_array($value)) {
108
            $value = htmlspecialchars($value);
109
110
            $value = $this->removeControlCharacters($value);
111
112
            $element->nodeValue = $value;
113
114
            return;
115
        }
116
117
        foreach ($value as $key => $data) {
118
            if (! $sequential) {
119
                if (($key === '_attributes') || ($key === '@attributes')) {
120
                    $this->addAttributes($element, $data);
121
                } elseif ((($key === '_value') || ($key === '@value')) && is_string($data)) {
122
                    $element->nodeValue = htmlspecialchars($data);
123
                } elseif ((($key === '_cdata') || ($key === '@cdata')) && is_string($data)) {
124
                    $element->appendChild($this->document->createCDATASection($data));
125
                } elseif ((($key === '_mixed') || ($key === '@mixed')) && is_string($data)) {
126
                    $fragment = $this->document->createDocumentFragment();
127
                    $fragment->appendXML($data);
128
                    $element->appendChild($fragment);
129
                } elseif ($key === '__numeric') {
130
                    $this->addNumericNode($element, $data);
131
                } else {
132
                    $this->addNode($element, $key, $data);
133
                }
134
            } elseif (is_array($data)) {
135
                $this->addCollectionNode($element, $data);
136
            } else {
137
                $this->addSequentialNode($element, $data);
138
            }
139
        }
140
    }
141
142
    protected function addNumericNode(DOMElement $element, $value)
143
    {
144
        foreach ($value as $key => $item) {
145
            $this->convertElement($element, [$this->numericTagNamePrefix.$key => $value]);
146
        }
147
    }
148
149
    protected function addNode(DOMElement $element, $key, $value)
150
    {
151
        if ($this->replaceSpacesByUnderScoresInKeyNames) {
152
            $key = str_replace(' ', '_', $key);
153
        }
154
155
        $child = $this->document->createElement($key);
156
        $element->appendChild($child);
157
        $this->convertElement($child, $value);
158
    }
159
160
    protected function addCollectionNode(DOMElement $element, $value)
161
    {
162
        if ($element->childNodes->length === 0 && $element->attributes->length === 0) {
0 ignored issues
show
Bug introduced by
The property length does not seem to exist in DOMNamedNodeMap.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
163
            $this->convertElement($element, $value);
164
165
            return;
166
        }
167
168
        $child = $this->document->createElement($element->tagName);
169
        $element->parentNode->appendChild($child);
170
        $this->convertElement($child, $value);
171
    }
172
173
    protected function addSequentialNode(DOMElement $element, $value)
174
    {
175
        if (empty($element->nodeValue)) {
176
            $element->nodeValue = htmlspecialchars($value);
177
178
            return;
179
        }
180
181
        $child = new DOMElement($element->tagName);
182
        $child->nodeValue = htmlspecialchars($value);
183
        $element->parentNode->appendChild($child);
184
    }
185
186
    protected function isArrayAllKeySequential($value)
187
    {
188
        if (! is_array($value)) {
189
            return false;
190
        }
191
192
        if (count($value) <= 0) {
193
            return true;
194
        }
195
196
        if (\key($value) === '__numeric') {
197
            return false;
198
        }
199
200
        return array_unique(array_map('is_int', array_keys($value))) === [true];
201
    }
202
203
    protected function addAttributes(DOMElement $element, array $data)
204
    {
205
        foreach ($data as $attrKey => $attrVal) {
206
            $element->setAttribute($attrKey, $attrVal);
207
        }
208
    }
209
210
    protected function createRootElement($rootElement): DOMElement
211
    {
212
        if (is_string($rootElement)) {
213
            $rootElementName = $rootElement ?: 'root';
214
215
            return $this->document->createElement($rootElementName);
216
        }
217
218
        $rootElementName = $rootElement['rootElementName'] ?? 'root';
219
220
        $element = $this->document->createElement($rootElementName);
221
222
        foreach ($rootElement as $key => $value) {
223
            if ($key !== '_attributes' && $key !== '@attributes') {
224
                continue;
225
            }
226
227
            $this->addAttributes($element, $rootElement[$key]);
228
        }
229
230
        return $element;
231
    }
232
233
    protected function removeControlCharacters(string $value): string
234
    {
235
        return preg_replace('/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F]/', '', $value);
236
    }
237
}
238