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 (#137)
by
unknown
01:06
created

ArrayToXml::addNode()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 10
rs 9.9332
cc 2
nc 2
nop 3
1
<?php
2
3
namespace Spatie\ArrayToXml;
4
5
use DOMDocument;
6
use DOMElement;
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
        /**
35
         * Commented this out to allow collections within the root node.
36
         *
37
         * if ($this->isArrayAllKeySequential($array) && ! empty($array)) {
38
         *     throw new DOMException('Invalid Character Error');
39
         * }
40
         */
41
42
        $root = $this->createRootElement($rootElement);
43
44
        $this->document->appendChild($root);
45
46
        $this->convertElement($root, $array);
47
    }
48
49
    public function setNumericTagNamePrefix(string $prefix)
50
    {
51
        $this->numericTagNamePrefix = $prefix;
52
    }
53
54
    public static function convert(
55
        array $array,
56
        $rootElement = '',
57
        bool $replaceSpacesByUnderScoresInKeyNames = true,
58
        string $xmlEncoding = null,
59
        string $xmlVersion = '1.0',
60
        array $domProperties = []
61
    ) {
62
        $converter = new static(
63
            $array,
64
            $rootElement,
65
            $replaceSpacesByUnderScoresInKeyNames,
66
            $xmlEncoding,
67
            $xmlVersion,
68
            $domProperties
69
        );
70
71
        return $converter->toXml();
72
    }
73
74
    public function toXml(): string
75
    {
76
        return $this->document->saveXML();
77
    }
78
79
    public function toDom(): DOMDocument
80
    {
81
        return $this->document;
82
    }
83
84
    protected function ensureValidDomProperties(array $domProperties)
85
    {
86
        foreach ($domProperties as $key => $value) {
87
            if (! property_exists($this->document, $key)) {
88
                throw new Exception($key.' is not a valid property of DOMDocument');
89
            }
90
        }
91
    }
92
93
    public function setDomProperties(array $domProperties)
94
    {
95
        $this->ensureValidDomProperties($domProperties);
96
97
        foreach ($domProperties as $key => $value) {
98
            $this->document->{$key} = $value;
99
        }
100
101
        return $this;
102
    }
103
104
    public function prettify()
105
    {
106
        $this->document->preserveWhiteSpace = false;
107
        $this->document->formatOutput = true;
108
109
        return $this;
110
    }
111
112
    private function convertElement(DOMElement $element, $value)
113
    {
114
        $sequential = $this->isArrayAllKeySequential($value);
115
116
        if (! is_array($value)) {
117
            $value = htmlspecialchars($value);
118
119
            $value = $this->removeControlCharacters($value);
120
121
            $element->nodeValue = $value;
122
123
            return;
124
        }
125
126
        foreach ($value as $key => $data) {
127
            if (! $sequential) {
128
                if (($key === '_attributes') || ($key === '@attributes')) {
129
                    $this->addAttributes($element, $data);
130
                } elseif ((($key === '_value') || ($key === '@value')) && is_string($data)) {
131
                    $element->nodeValue = htmlspecialchars($data);
132
                } elseif ((($key === '_cdata') || ($key === '@cdata')) && is_string($data)) {
133
                    $element->appendChild($this->document->createCDATASection($data));
134
                } elseif ((($key === '_mixed') || ($key === '@mixed')) && is_string($data)) {
135
                    $fragment = $this->document->createDocumentFragment();
136
                    $fragment->appendXML($data);
137
                    $element->appendChild($fragment);
138
                } elseif ($key === '__numeric') {
139
                    $this->addNumericNode($element, $data);
140
                } else {
141
                    $this->addNode($element, $key, $data);
142
                }
143
            } elseif (is_array($data)) {
144
                $this->addCollectionNode($element, $data);
145
            } else {
146
                $this->addSequentialNode($element, $data);
147
            }
148
        }
149
    }
150
151
    protected function addNumericNode(DOMElement $element, $value)
152
    {
153
        foreach ($value as $key => $item) {
154
            $this->convertElement($element, [$this->numericTagNamePrefix.$key => $item]);
155
        }
156
    }
157
158
    protected function addNode(DOMElement $element, $key, $value)
159
    {
160
        if ($this->replaceSpacesByUnderScoresInKeyNames) {
161
            $key = str_replace(' ', '_', $key);
162
        }
163
164
        $child = $this->document->createElement($key);
165
        $element->appendChild($child);
166
        $this->convertElement($child, $value);
167
    }
168
169
    protected function addCollectionNode(DOMElement $element, $value)
170
    {
171
        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...
172
            $this->convertElement($element, $value);
173
174
            return;
175
        }
176
177
        $child = $this->document->createElement($element->tagName);
178
        $element->parentNode->appendChild($child);
179
        $this->convertElement($child, $value);
180
    }
181
182
    protected function addSequentialNode(DOMElement $element, $value)
183
    {
184
        if (empty($element->nodeValue) && ! is_numeric($element->nodeValue)) {
185
            $element->nodeValue = htmlspecialchars($value);
186
187
            return;
188
        }
189
190
        $child = new DOMElement($element->tagName);
191
        $child->nodeValue = htmlspecialchars($value);
192
        $element->parentNode->appendChild($child);
193
    }
194
195
    protected function isArrayAllKeySequential($value)
196
    {
197
        if (! is_array($value)) {
198
            return false;
199
        }
200
201
        if (count($value) <= 0) {
202
            return true;
203
        }
204
205
        if (\key($value) === '__numeric') {
206
            return false;
207
        }
208
209
        return array_unique(array_map('is_int', array_keys($value))) === [true];
210
    }
211
212
    protected function addAttributes(DOMElement $element, array $data)
213
    {
214
        foreach ($data as $attrKey => $attrVal) {
215
            $element->setAttribute($attrKey, $attrVal);
216
        }
217
    }
218
219
    protected function createRootElement($rootElement): DOMElement
220
    {
221
        if (is_string($rootElement)) {
222
            $rootElementName = $rootElement ?: 'root';
223
224
            return $this->document->createElement($rootElementName);
225
        }
226
227
        $rootElementName = $rootElement['rootElementName'] ?? 'root';
228
229
        $element = $this->document->createElement($rootElementName);
230
231
        foreach ($rootElement as $key => $value) {
232
            if ($key !== '_attributes' && $key !== '@attributes') {
233
                continue;
234
            }
235
236
            $this->addAttributes($element, $rootElement[$key]);
237
        }
238
239
        return $element;
240
    }
241
242
    protected function removeControlCharacters(string $value): string
243
    {
244
        return preg_replace('/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F]/', '', $value);
245
    }
246
}
247