@@ -24,37 +24,47 @@ class XmlContentTypeSerializer implements ContentTypeSerializerInterface
24
24
25
25
const ATTRIBUTE_NAMESPACE_SEPARATOR = ': ' ;
26
26
27
- protected array $ config = ['version ' => '1.0 ' , 'encoding ' => 'UTF-8 ' , 'attributesKey ' => '@attributes ' , 'cdataKey ' => '@cdata ' , 'valueKey ' => '@value ' , 'namespacesOnRoot ' => true ];
27
+ /** @var array */
28
+ private $ config = ['version ' => '1.0 ' , 'encoding ' => 'UTF-8 ' , 'attributesKey ' => '@attributes ' , 'cdataKey ' => '@cdata ' , 'valueKey ' => '@value ' , 'namespacesOnRoot ' => true ];
28
29
29
- protected DOMDocument $ xml ;
30
+ private $ xml ;
30
31
31
- protected array $ namespaces = [];
32
+ /** @var array */
33
+ private $ namespaces = [];
32
34
33
- protected array $ array = [];
35
+ /** @var array */
36
+ private $ items = [];
34
37
35
38
public function decode (StreamInterface $ body ): array
36
39
{
37
40
$ body ->rewind ();
38
41
$ this ->loadXml ($ body ->getContents ());
42
+ if ($ this ->xml ->documentElement === null ) {
43
+ return [];
44
+ }
39
45
// Convert the XML to an array, omitting the root node, as it is the name of the entity
40
- $ child = $ this ->xml ->documentElement ->firstChild ;
46
+ $ child = $ this ->xml ->documentElement ->firstChild ;
47
+ if ($ child === null ) {
48
+ return [];
49
+ }
41
50
$ childValue = $ this ->parseNode ($ child );
42
51
$ childNodeName = $ child ->nodeName ;
43
- $ this ->array [$ childNodeName ] = $ childValue ;
52
+ $ this ->items [$ childNodeName ] = $ childValue ;
44
53
// Add namespacing information to the root node
45
54
if (! empty ($ this ->namespaces ) && $ this ->config ['namespacesOnRoot ' ]) {
46
- if (! isset ($ this ->array [$ childNodeName ][$ this ->config ['attributesKey ' ]])) {
47
- $ this ->array [$ childNodeName ][$ this ->config ['attributesKey ' ]] = [];
55
+ if (! isset ($ this ->items [$ childNodeName ][$ this ->config ['attributesKey ' ]])) {
56
+ $ this ->items [$ childNodeName ][$ this ->config ['attributesKey ' ]] = [];
48
57
}
49
58
foreach ($ this ->namespaces as $ uri => $ prefix ) {
50
- if ($ prefix ) {
51
- $ prefix = self :: ATTRIBUTE_NAMESPACE_SEPARATOR . $ prefix ;
59
+ if (! \is_string ( $ prefix) ) {
60
+ continue ;
52
61
}
53
- $ this ->array [$ childNodeName ][$ this ->config ['attributesKey ' ]][self ::ATTRIBUTE_NAMESPACE . $ prefix ] = $ uri ;
62
+ $ prefix = \sprintf ('%s%s%s ' , self ::ATTRIBUTE_NAMESPACE , self ::ATTRIBUTE_NAMESPACE_SEPARATOR , $ prefix );
63
+ $ this ->items [$ childNodeName ][$ this ->config ['attributesKey ' ]][$ prefix ] = $ uri ;
54
64
}
55
65
}
56
66
57
- return $ this ->array ;
67
+ return $ this ->items ;
58
68
}
59
69
60
70
public function encode (SerializableInterface $ body ): string
@@ -66,16 +76,25 @@ public function encode(SerializableInterface $body): string
66
76
$ rootKey = \substr (\get_class ($ body ), \strrpos (\get_class ($ body ), '\\' ) + 1 );
67
77
}
68
78
$ this ->xml ->appendChild ($ this ->buildNode ($ rootKey , $ body ->toArray ()));
79
+ $ result = $ this ->xml ->saveXML ();
80
+ if ($ result === false ) {
81
+ throw new SerializeException ('Failed to save xml during serialization ' );
82
+ }
69
83
70
- return $ this -> xml -> saveXML () ;
84
+ return $ result ;
71
85
}
72
86
73
87
public function getMimeType (): string
74
88
{
75
89
return self ::MIME_TYPE ;
76
90
}
77
91
78
- protected function loadXml (string $ inputXml )
92
+ /**
93
+ * @throws SerializeException
94
+ *
95
+ * @return void
96
+ */
97
+ private function loadXml (string $ inputXml )
79
98
{
80
99
$ this ->xml = new DOMDocument ($ this ->config ['version ' ], $ this ->config ['encoding ' ]);
81
100
$ parse = @$ this ->xml ->loadXML ($ inputXml );
@@ -84,10 +103,12 @@ protected function loadXml(string $inputXml)
84
103
}
85
104
}
86
105
87
- protected function parseNode (DOMNode $ node )
106
+ /**
107
+ * @throws SerializeException
108
+ */
109
+ private function parseNode (DOMNode $ node )
88
110
{
89
- $ output = [];
90
- $ output = $ this ->collectNodeNamespaces ($ node , $ output );
111
+ $ output = $ this ->collectNodeNamespaces ($ node , []);
91
112
switch ($ node ->nodeType ) {
92
113
case XML_CDATA_SECTION_NODE :
93
114
$ output [$ this ->config ['cdataKey ' ]] = $ this ->normalizeTextContent ($ node ->textContent );
@@ -108,7 +129,10 @@ protected function parseNode(DOMNode $node)
108
129
return $ output ;
109
130
}
110
131
111
- protected function parseChildNodes (DOMNode $ node , $ output )
132
+ /**
133
+ * @throws SerializeException
134
+ */
135
+ private function parseChildNodes (DOMNode $ node , $ output )
112
136
{
113
137
foreach ($ node ->childNodes as $ child ) {
114
138
if ($ child ->nodeType === XML_CDATA_SECTION_NODE ) {
@@ -122,8 +146,8 @@ protected function parseChildNodes(DOMNode $node, $output)
122
146
$ output [$ this ->config ['cdataKey ' ]] = $ this ->normalizeTextContent ($ child ->textContent );
123
147
} else {
124
148
$ value = $ this ->parseNode ($ child );
125
- if ($ child ->nodeType == XML_TEXT_NODE ) {
126
- if ($ value != '' ) {
149
+ if ($ child ->nodeType === XML_TEXT_NODE ) {
150
+ if ($ value !== '' ) {
127
151
if (! empty ($ output )) {
128
152
$ output [$ this ->config ['valueKey ' ]] = $ value ;
129
153
} else {
@@ -143,34 +167,45 @@ protected function parseChildNodes(DOMNode $node, $output)
143
167
return $ output ;
144
168
}
145
169
146
- protected function normalizeTextContent ($ textContent ): string
170
+ /**
171
+ * @param string|string[] $textContent
172
+ *
173
+ * @throws SerializeException
174
+ */
175
+ private function normalizeTextContent ($ textContent ): string
147
176
{
148
- return \trim (\preg_replace (['/ \\n+ \\s+/ ' , '/ \\r+ \\s+/ ' , '/ \\n+ \\t+/ ' , '/ \\r+ \\t+/ ' ], ' ' , $ textContent ));
177
+ $ normalized = \preg_replace (['/ \\n+ \\s+/ ' , '/ \\r+ \\s+/ ' , '/ \\n+ \\t+/ ' , '/ \\r+ \\t+/ ' ], ' ' , $ textContent );
178
+ if (! \is_string ($ normalized )) {
179
+ throw new SerializeException (\sprintf ('Normalization of %s failed ' , \json_encode ($ textContent )));
180
+ }
181
+
182
+ return \trim ($ normalized );
149
183
}
150
184
151
- protected function normalizeNodeValues ($ values )
185
+ private function normalizeNodeValues ($ values )
152
186
{
153
- if (\is_array ($ values )) {
154
- // if only one node of its kind, assign it directly instead if array($value);
155
- foreach ($ values as $ key => $ value ) {
156
- if (\is_array ($ value ) && \count ($ value ) === 1 ) {
157
- $ keyName = \array_keys ($ value )[0 ];
158
- if (\is_numeric ($ keyName )) {
159
- $ values [$ key ] = $ value [$ keyName ];
160
- }
187
+ if (! \is_array ($ values )) {
188
+ return $ values ;
189
+ }
190
+ if (empty ($ values )) {
191
+ return '' ;
192
+ }
193
+ // if there is only one node of its kind, assign it directly instead of array($value);
194
+ foreach ($ values as $ key => $ value ) {
195
+ if (\is_array ($ value ) && \count ($ value ) === 1 ) {
196
+ $ keyName = \array_keys ($ value )[0 ];
197
+ if (\is_numeric ($ keyName )) {
198
+ $ values [$ key ] = $ value [$ keyName ];
161
199
}
162
200
}
163
- if (empty ($ values )) {
164
- $ values = '' ;
165
- }
166
201
}
167
202
168
203
return $ values ;
169
204
}
170
205
171
- protected function collectAttributes (DOMNode $ node , $ output )
206
+ private function collectAttributes (DOMNode $ node , $ output )
172
207
{
173
- if (! $ node ->attributes ->length ) {
208
+ if ($ node -> attributes === null || ! $ node ->attributes ->length ) {
174
209
return $ output ;
175
210
}
176
211
$ attributes = [];
@@ -179,12 +214,10 @@ protected function collectAttributes(DOMNode $node, $output)
179
214
$ attributeName = $ attributeNode ->nodeName ;
180
215
$ attributes [$ attributeName ] = (string ) $ attributeNode ->value ;
181
216
if ($ attributeNode ->namespaceURI ) {
182
- $ nsUri = $ attributeNode ->namespaceURI ;
183
- $ nsPrefix = $ attributeNode ->lookupPrefix ($ nsUri );
184
217
$ namespaces = $ this ->collectNamespaces ($ attributeNode );
185
218
}
186
219
}
187
- // if its a leaf node, store the value in @value instead of directly it.
220
+ // if it is a leaf node, store the value in @value
188
221
if (! \is_array ($ output )) {
189
222
if (! empty ($ output )) {
190
223
$ output = [$ this ->config ['valueKey ' ] => $ output ];
@@ -199,7 +232,7 @@ protected function collectAttributes(DOMNode $node, $output)
199
232
return $ output ;
200
233
}
201
234
202
- protected function collectNodeNamespaces (DOMNode $ node , array $ output ): array
235
+ private function collectNodeNamespaces (DOMNode $ node , array $ output ): array
203
236
{
204
237
$ namespaces = $ this ->collectNamespaces ($ node );
205
238
if (! empty ($ namespaces )) {
@@ -209,7 +242,7 @@ protected function collectNodeNamespaces(DOMNode $node, array $output): array
209
242
return $ output ;
210
243
}
211
244
212
- protected function collectNamespaces (DOMNode $ node ): array
245
+ private function collectNamespaces (DOMNode $ node ): array
213
246
{
214
247
$ namespaces = [];
215
248
if ($ node ->namespaceURI ) {
@@ -229,25 +262,36 @@ protected function collectNamespaces(DOMNode $node): array
229
262
return $ namespaces ;
230
263
}
231
264
232
- protected function buildNode ($ nodeName , $ data )
265
+ /**
266
+ * @throws SerializeException
267
+ */
268
+ private function buildNode (string $ nodeName , $ data ): DOMElement
233
269
{
234
270
if (! $ this ->isValidTagName ($ nodeName )) {
235
271
throw new SerializeException ('Invalid character in the tag name being generated: ' . $ nodeName );
236
272
}
237
273
$ node = $ this ->xml ->createElement ($ nodeName );
274
+ if ($ data === false ) {
275
+ throw new SerializeException ('Failed to create a node for: ' . $ nodeName );
276
+ }
238
277
if (\is_array ($ data )) {
239
- $ this ->parseArray ($ node , $ nodeName , $ data );
278
+ $ this ->parseArray ($ node , $ data );
240
279
} else {
241
280
$ node ->appendChild ($ this ->xml ->createTextNode ($ this ->normalizeValues ($ data )));
242
281
}
243
282
244
283
return $ node ;
245
284
}
246
285
247
- protected function parseArray (DOMElement $ node , $ nodeName , array $ array )
286
+ /**
287
+ * @throws SerializeException
288
+ *
289
+ * @return void
290
+ */
291
+ private function parseArray (DOMElement $ node , array $ array )
248
292
{
249
- // get the attributes first.;
250
- $ array = $ this ->parseAttributes ($ node , $ nodeName , $ array );
293
+ // get the attributes first
294
+ $ array = $ this ->parseAttributes ($ node , $ array );
251
295
// get value stored in @value
252
296
$ array = $ this ->parseValue ($ node , $ array );
253
297
// get value stored in @cdata
@@ -258,7 +302,7 @@ protected function parseArray(DOMElement $node, $nodeName, array $array)
258
302
throw new SerializeException ('Invalid character in the tag name being generated: ' . $ key );
259
303
}
260
304
if (\is_array ($ value ) && \is_numeric (\key ($ value ))) {
261
- // MORE THAN ONE NODE OF ITS KIND;
305
+ // MORE THAN ONE NODE OF ITS KIND
262
306
// if the new array is numeric index, means it is array of nodes of the same kind
263
307
// it should follow the parent key name
264
308
foreach ($ value as $ v ) {
@@ -272,7 +316,10 @@ protected function parseArray(DOMElement $node, $nodeName, array $array)
272
316
}
273
317
}
274
318
275
- protected function parseAttributes (DOMElement $ node , $ nodeName , array $ array )
319
+ /**
320
+ * @throws SerializeException
321
+ */
322
+ private function parseAttributes (DOMElement $ node , array $ array ): array
276
323
{
277
324
$ attributesKey = $ this ->config ['attributesKey ' ];
278
325
if (\array_key_exists ($ attributesKey , $ array ) && \is_array ($ array [$ attributesKey ])) {
@@ -288,7 +335,7 @@ protected function parseAttributes(DOMElement $node, $nodeName, array $array)
288
335
return $ array ;
289
336
}
290
337
291
- protected function parseValue (DOMElement $ node , array $ array )
338
+ private function parseValue (DOMElement $ node , array $ array ): array
292
339
{
293
340
$ valueKey = $ this ->config ['valueKey ' ];
294
341
if (\array_key_exists ($ valueKey , $ array )) {
@@ -299,7 +346,7 @@ protected function parseValue(DOMElement $node, array $array)
299
346
return $ array ;
300
347
}
301
348
302
- protected function parseCdata (DOMElement $ node , array $ array )
349
+ private function parseCdata (DOMElement $ node , array $ array ): array
303
350
{
304
351
$ cdataKey = $ this ->config ['cdataKey ' ];
305
352
if (\array_key_exists ($ cdataKey , $ array )) {
@@ -310,7 +357,7 @@ protected function parseCdata(DOMElement $node, array $array)
310
357
return $ array ;
311
358
}
312
359
313
- protected function normalizeValues ($ value )
360
+ private function normalizeValues ($ value ): string
314
361
{
315
362
$ value = $ value === true ? 'true ' : $ value ;
316
363
$ value = $ value === false ? 'false ' : $ value ;
@@ -319,7 +366,7 @@ protected function normalizeValues($value)
319
366
return (string ) $ value ;
320
367
}
321
368
322
- protected function isValidTagName (string $ tag ): bool
369
+ private function isValidTagName (string $ tag ): bool
323
370
{
324
371
$ pattern = '/^[a-zA-Z_][ \\w \\: \\- \\.]*$/ ' ;
325
372
0 commit comments