Skip to content

Commit f0a0c22

Browse files
committed
Fix bugs
1 parent 9e24201 commit f0a0c22

File tree

5 files changed

+218
-105
lines changed

5 files changed

+218
-105
lines changed

src/Xml2Array.php

Lines changed: 58 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,11 @@ protected function loadXml($inputXml)
191191
$this->xml = new DOMDocument($this->config['version'], $this->config['encoding']);
192192

193193
if (is_string($inputXml)) {
194-
$this->xml->loadXML($inputXml);
194+
$parse = @$this->xml->loadXML($inputXml);
195+
196+
if ($parse === false) {
197+
throw new DOMException('Error parsing XML string, input is not a well-formed XML string.');
198+
}
195199
} elseif ($inputXml instanceof SimpleXMLElement) {
196200
$this->xml->loadXML($inputXml->asXML());
197201
} elseif ($inputXml instanceof DOMDocument) {
@@ -215,21 +219,16 @@ protected function parseNode(DOMNode $node)
215219

216220
switch ($node->nodeType) {
217221
case XML_CDATA_SECTION_NODE:
218-
$output[$this->config['cdataKey']] = trim($node->textContent);
222+
$output[$this->config['cdataKey']] = $this->normalizeTextContent($node->textContent);
219223
break;
220224

221225
case XML_TEXT_NODE:
222-
$output = trim(preg_replace([
223-
'/\n+\s+/',
224-
'/\r+\s+/',
225-
'/\n+\t+/',
226-
'/\r+\t+/'
227-
], ' ', $node->textContent));
226+
$output = $this->normalizeTextContent($node->textContent);
228227
break;
229228

230229
case XML_ELEMENT_NODE:
231230
$output = $this->parseChildNodes($node, $output);
232-
$output = $this->normalizeValues($output);
231+
$output = $this->normalizeNodeValues($output);
233232
$output = $this->collectAttributes($node, $output);
234233
break;
235234
}
@@ -247,32 +246,36 @@ protected function parseNode(DOMNode $node)
247246
*/
248247
protected function parseChildNodes(DOMNode $node, $output)
249248
{
250-
if ($node->childNodes->length == 1) {
251-
if (!empty($output)) {
252-
$output[$this->config['valueKey']] = $this->parseNode($node->firstChild);
249+
foreach ($node->childNodes as $child) {
250+
if ($child->nodeType === XML_CDATA_SECTION_NODE) {
251+
if (!is_array($output)) {
252+
if (!empty($output)) {
253+
$output = [$this->config['valueKey'] => $output];
254+
} else {
255+
$output = [];
256+
}
257+
}
258+
259+
$output[$this->config['cdataKey']] = $this->normalizeTextContent($child->textContent);
253260
} else {
254-
$output = $this->parseNode($node->firstChild);
255-
}
256-
} else {
257-
foreach ($node->childNodes as $child) {
258-
if ($child->nodeType === XML_CDATA_SECTION_NODE) {
259-
$output[$this->config['cdataKey']] = trim($child->textContent);
260-
} else {
261-
$value = $this->parseNode($child);
261+
$value = $this->parseNode($child);
262262

263-
if ($child->nodeType == XML_TEXT_NODE) {
264-
if ($value != '') {
263+
if ($child->nodeType == XML_TEXT_NODE) {
264+
if ($value != '') {
265+
if (!empty($output)) {
265266
$output[$this->config['valueKey']] = $value;
267+
} else {
268+
$output = $value;
266269
}
267-
} else {
268-
$nodeName = $child->nodeName;
269-
270-
if (!isset($output[$nodeName])) {
271-
$output[$nodeName] = [];
272-
}
270+
}
271+
} else {
272+
$nodeName = $child->nodeName;
273273

274-
$output[$nodeName][] = $value;
274+
if (!isset($output[$nodeName])) {
275+
$output[$nodeName] = [];
275276
}
277+
278+
$output[$nodeName][] = $value;
276279
}
277280
}
278281
}
@@ -281,32 +284,49 @@ protected function parseChildNodes(DOMNode $node, $output)
281284
}
282285

283286
/**
284-
* Normalize values
287+
* Clean text content of text node
288+
*
289+
* @param string $textContent
290+
*
291+
* @return string
292+
*/
293+
protected function normalizeTextContent($textContent)
294+
{
295+
return trim(preg_replace([
296+
'/\n+\s+/',
297+
'/\r+\s+/',
298+
'/\n+\t+/',
299+
'/\r+\t+/'
300+
], ' ', $textContent));
301+
}
302+
303+
/**
304+
* Normalize values of node
285305
*
286-
* @param mixed $output
306+
* @param mixed $values
287307
*
288308
* @return mixed
289309
*/
290-
protected function normalizeValues($output)
310+
protected function normalizeNodeValues($values)
291311
{
292-
if (is_array($output)) {
312+
if (is_array($values)) {
293313
// if only one node of its kind, assign it directly instead if array($value);
294-
foreach ($output as $key => $value) {
314+
foreach ($values as $key => $value) {
295315
if (is_array($value) && count($value) === 1) {
296316
$keyName = array_keys($value)[0];
297317

298318
if (is_numeric($keyName)) {
299-
$output[$key] = $value[$keyName];
319+
$values[$key] = $value[$keyName];
300320
}
301321
}
302322
}
303323

304-
if (empty($output)) {
305-
$output = '';
324+
if (empty($values)) {
325+
$values = '';
306326
}
307327
}
308328

309-
return $output;
329+
return $values;
310330
}
311331

312332
/**

tests/Array2XmlTest.php

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,29 +59,75 @@ class Array2XmlTest extends TestCase
5959
];
6060

6161
/**
62-
* Expected xml string
62+
* Throw DOMException when there are more than one root node
6363
*
64-
* @var string
64+
* @test
65+
*
66+
* @return void
6567
*/
66-
protected $expected_xml_string = '<?xml version="1.0" encoding="UTF-8"?><root_node><tag>Example tag</tag><attribute_tag description="This is a tag with attribute">Another tag with attributes</attribute_tag><cdata_section><![CDATA[This is CDATA section]]></cdata_section><tag_with_subtag><sub_tag>Sub tag 1</sub_tag><sub_tag>Sub tag 2</sub_tag></tag_with_subtag><mixed_section>Hello<![CDATA[This is another CDATA section]]><section id="sec_1">Section number 1</section><section id="sec_2">Section number 2</section><section id="sec_3">Section number 3</section></mixed_section><example:with_namespace xmlns:example="http://example.com"><example:sub>Content</example:sub></example:with_namespace></root_node>';
68+
public function throw_dom_exception_when_there_are_more_than_one_root_node()
69+
{
70+
$this->expectException(DOMException::class);
71+
$this->expectExceptionMessage('XML documents are allowed only one root element. Wrap your elements in a key or set the `rootElement` parameter in the configuration.');
72+
73+
$process = Array2Xml::convert([
74+
'root' => 'content',
75+
'another_root' => 'Another content'
76+
]);
77+
}
6778

6879
/**
69-
* Convert array to xml string
80+
* Throw DOMException when node name is invalid
7081
*
7182
* @test
7283
*
7384
* @return void
7485
*/
75-
public function convert_array_to_xml_string()
86+
public function throw_dom_exception_when_node_name_is_invalid()
7687
{
77-
$dom = new DOMDocument;
78-
$dom->loadXML($this->expected_xml_string);
88+
$this->expectException(DOMException::class);
89+
$this->expectExceptionMessage('Invalid character in the tag name being generated: 0');
90+
91+
$process = Array2Xml::convert(['content']);
92+
}
93+
94+
/**
95+
* Throw DOMException when attribute name is invalid
96+
*
97+
* @test
98+
*
99+
* @return void
100+
*/
101+
public function throw_dom_exception_when_attaribute_name_is_invalid()
102+
{
103+
$this->expectException(DOMException::class);
104+
$this->expectExceptionMessage('Invalid character in the attribute name being generated: invalid attribute');
105+
106+
$process = Array2Xml::convert([
107+
'root' => [
108+
'sub' => [
109+
'@attributes' => [
110+
'invalid attribute' => 'Attribute value'
111+
]
112+
]
113+
]
114+
]);
115+
}
79116

80-
$this->assertSame($dom->saveXML(), Array2Xml::convert($this->input_array)->toXml());
117+
/**
118+
* Convert array to XML string
119+
*
120+
* @test
121+
*
122+
* @return void
123+
*/
124+
public function convert_array_to_xml_string()
125+
{
126+
$this->assertXmlStringEqualsXmlFile(__DIR__ . '/resources/example.xml', Array2Xml::convert($this->input_array)->toXml());
81127
}
82128

83129
/**
84-
* Convert array to dom
130+
* Convert array to DOM
85131
*
86132
* @test
87133
*
@@ -90,8 +136,8 @@ public function convert_array_to_xml_string()
90136
public function convert_array_to_dom()
91137
{
92138
$dom = new DOMDocument;
93-
$dom->loadXML($this->expected_xml_string);
139+
$dom->loadXML(file_get_contents(__DIR__ . '/resources/example.xml'));
94140

95-
$this->assertSame($dom->saveXML(), Array2Xml::convert($this->input_array)->toDom()->saveXML());
141+
$this->assertEquals($dom, Array2Xml::convert($this->input_array)->toDom());
96142
}
97143
}

0 commit comments

Comments
 (0)