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

ArrayToXml::ensureValidDocTypeProperties()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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