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 (#112)
by Martin
01:02
created

ArrayToXml::addAttributes()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 6
rs 10
cc 2
nc 2
nop 2
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 (! is_array($value)) {
104
            $value = htmlspecialchars($value);
105
106
            $value = $this->removeControlCharacters($value);
107
108
            $element->nodeValue = $value;
109
110
            return;
111
        }
112
113
        foreach ($value as $key => $data) {
114
            if (! $sequential) {
115
                if (($key === '_attributes') || ($key === '@attributes')) {
116
                    $this->addAttributes($element, $data);
117
                } elseif ((($key === '_value') || ($key === '@value')) && is_string($data)) {
118
                    $element->nodeValue = htmlspecialchars($data);
119
                } elseif ((($key === '_cdata') || ($key === '@cdata')) && is_string($data)) {
120
                    $element->appendChild($this->document->createCDATASection($data));
121
                } elseif ((($key === '_mixed') || ($key === '@mixed')) && is_string($data)) {
122
                    $fragment = $this->document->createDocumentFragment();
123
                    $fragment->appendXML($data);
124
                    $element->appendChild($fragment);
125
                } elseif ($key === '__numeric') {
126
                    $this->addNumericNode($element, $data);
127
                } else {
128
                    $this->addNode($element, $key, $data);
129
                }
130
            } elseif (is_array($data)) {
131
                $this->addCollectionNode($element, $data);
132
            } else {
133
                $this->addSequentialNode($element, $data);
134
            }
135
        }
136
    }
137
138
    protected function addNumericNode(DOMElement $element, $value)
139
    {
140
        foreach ($value as $key => $item) {
141
            $this->convertElement($element, [$this->numericTagNamePrefix.$key => $value]);
142
        }
143
    }
144
145
    protected function addNode(DOMElement $element, $key, $value)
146
    {
147
        if ($this->replaceSpacesByUnderScoresInKeyNames) {
148
            $key = str_replace(' ', '_', $key);
149
        }
150
151
        $child = $this->document->createElement($key);
152
        $element->appendChild($child);
153
        $this->convertElement($child, $value);
154
    }
155
156
    protected function addCollectionNode(DOMElement $element, $value)
157
    {
158
        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...
159
            $this->convertElement($element, $value);
160
161
            return;
162
        }
163
164
        $child = $this->document->createElement($element->tagName);
165
        $element->parentNode->appendChild($child);
166
        $this->convertElement($child, $value);
167
    }
168
169
    protected function addSequentialNode(DOMElement $element, $value)
170
    {
171
        if (empty($element->nodeValue) && ! is_numeric($element->nodeValue)) {
172
            $element->nodeValue = htmlspecialchars($value);
173
174
            return;
175
        }
176
177
        $child = new DOMElement($element->tagName);
178
        $child->nodeValue = htmlspecialchars($value);
179
        $element->parentNode->appendChild($child);
180
    }
181
182
    protected function isArrayAllKeySequential($value)
183
    {
184
        if (! is_array($value)) {
185
            return false;
186
        }
187
188
        if (count($value) <= 0) {
189
            return true;
190
        }
191
192
        if (\key($value) === '__numeric') {
193
            return false;
194
        }
195
196
        return array_unique(array_map('is_int', array_keys($value))) === [true];
197
    }
198
199
    protected function addAttributes(DOMElement $element, array $data)
200
    {
201
        foreach ($data as $attrKey => $attrVal) {
202
            $element->setAttribute($attrKey, $attrVal);
203
        }
204
    }
205
206
    protected function createRootElement($rootElement): DOMElement
207
    {
208
        if (is_string($rootElement)) {
209
            $rootElementName = $rootElement ?: 'root';
210
211
            return $this->document->createElement($rootElementName);
212
        }
213
214
        $rootElementName = $rootElement['rootElementName'] ?? 'root';
215
216
        $element = $this->document->createElement($rootElementName);
217
218
        foreach ($rootElement as $key => $value) {
219
            if ($key !== '_attributes' && $key !== '@attributes') {
220
                continue;
221
            }
222
223
            $this->addAttributes($element, $rootElement[$key]);
224
        }
225
226
        return $element;
227
    }
228
229
    protected function removeControlCharacters(string $value): string
230
    {
231
        return preg_replace('/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F]/', '', $value);
232
    }
233
}
234