Skip to content

Commit eeb7a6b

Browse files
committed
- update(unserialize): better error handling, and early exit if nothing is passed
1 parent 99d97fc commit eeb7a6b

File tree

2 files changed

+38
-22
lines changed

2 files changed

+38
-22
lines changed

Serializer/XmlSerializer.php

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
use function array_is_list;
2222
use function count;
2323
use function current;
24-
use function error_log;
24+
use function end;
2525
use function filter_var;
2626
use function is_array;
2727
use function is_bool;
@@ -30,14 +30,18 @@
3030
use function is_iterable;
3131
use function is_numeric;
3232
use function is_object;
33+
use function Koded\Stdlib\error_log;
3334
use function Koded\Stdlib\json_serialize;
3435
use function Koded\Stdlib\json_unserialize;
3536
use function key;
3637
use function preg_match;
3738
use function str_contains;
39+
use function str_replace;
3840
use function str_starts_with;
3941
use function substr;
4042
use function trim;
43+
use function xml_parse_into_struct;
44+
use function xml_parser_create;
4145

4246
/**
4347
* Class XmlSerializer is heavily modified Symfony encoder (XmlEncoder).
@@ -71,21 +75,20 @@ final public function val(): string
7175
}
7276

7377
/**
74-
* @param iterable $data
75-
*
78+
* @param iterable $value
7679
* @return string|null XML
7780
*/
78-
public function serialize(mixed $data): string|null
81+
public function serialize(mixed $value): string|null
7982
{
8083
$document = new DOMDocument('1.0', 'UTF-8');
8184
$document->formatOutput = false;
82-
if (is_iterable($data)) {
85+
if (is_iterable($value)) {
8386
$root = $document->createElement($this->root);
8487
$document->appendChild($root);
8588
$document->createAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'xsi:' . $this->root);
86-
$this->buildXml($document, $root, $data);
89+
$this->buildXml($document, $root, $value);
8790
} else {
88-
$this->appendNode($document, $document, $data, $this->root, null);
91+
$this->appendNode($document, $document, $value, $this->root, null);
8992
}
9093
return trim($document->saveXML());
9194
}
@@ -94,11 +97,13 @@ public function serialize(mixed $data): string|null
9497
* Unserialize a proper XML document into array, scalar value or NULL.
9598
*
9699
* @param string $xml XML
97-
*
98100
* @return mixed scalar|array|null
99101
*/
100102
public function unserialize(string $xml): mixed
101103
{
104+
if (empty($xml = trim($xml))) {
105+
return null;
106+
}
102107
try {
103108
$document = new DOMDocument('1.0', 'UTF-8');
104109
$document->preserveWhiteSpace = false;
@@ -111,15 +116,14 @@ public function unserialize(string $xml): mixed
111116
: [];
112117

113118
} catch (Throwable $e) {
114-
error_log(PHP_EOL . "[{$e->getLine()}]: " . $e->getMessage());
119+
$this->logUnserializeError(__METHOD__, $e->getMessage(), $xml);
115120
return null;
116121
}
117122
}
118123

119-
private function buildXml(
120-
DOMDocument $document,
121-
DOMNode $parent,
122-
iterable $data): void
124+
private function buildXml(DOMDocument $document,
125+
DOMNode $parent,
126+
iterable $data): void
123127
{
124128
foreach ($data as $key => $val) {
125129
$isKeyNumeric = is_numeric($key);
@@ -192,7 +196,6 @@ private function parseXmlAttributes(DOMNode $node): array
192196

193197
/**
194198
* @param DOMNode $node
195-
*
196199
* @return array|string|null
197200
* @throws \Exception
198201
*/
@@ -228,12 +231,11 @@ private function parseXmlValue(DOMNode $node): mixed
228231
* @param string $name
229232
* @param string|null $key
230233
*/
231-
private function appendNode(
232-
DOMDocument $document,
233-
DOMNode $parent,
234-
mixed $data,
235-
string $name,
236-
?string $key): void
234+
private function appendNode(DOMDocument $document,
235+
DOMNode $parent,
236+
mixed $data,
237+
string $name,
238+
?string $key): void
237239
{
238240
$element = $document->createElement($name);
239241
if (null !== $key) {
@@ -254,7 +256,7 @@ private function appendNode(
254256
$element->setAttribute('xsi:nil', 'true');
255257
} elseif ($data instanceof DateTimeInterface) {
256258
$element->setAttribute('type', 'xsd:dateTime');
257-
$element->appendChild($document->createTextNode($data->format(DateTimeInterface::ISO8601)));
259+
$element->appendChild($document->createTextNode($data->format(DateTimeInterface::RFC3339)));
258260
} elseif (is_object($data)) {
259261
$element->setAttribute('type', 'xsd:object');
260262
$element->appendChild($document->createCDATASection(json_serialize($data)));
@@ -331,4 +333,18 @@ private function extractValuesFromChildNodes(DOMNode $node, array &$value): void
331333
}
332334
}
333335
}
336+
337+
public function logUnserializeError(string $method,
338+
string $message,
339+
string $xml): void
340+
{
341+
$parser = xml_parser_create();
342+
xml_parse_into_struct($parser, $xml, $values);
343+
$last = end($values);
344+
unset($last['type'], $last['level']);
345+
error_log($method,
346+
str_replace('DOMDocument::loadXML(): ', '', $message),
347+
'hint: ' . json_serialize($last ?: ['<XML>' => $xml])
348+
);
349+
}
334350
}

tests/fixtures/error-message.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,6 @@
3434
<handled type="xsd:boolean">1</handled>
3535
<nothing xsi:nil="true"/>
3636
<empty></empty>
37-
<datetime type="xsd:dateTime">2167-04-30T09:16:15+0000</datetime>
37+
<datetime type="xsd:dateTime">2167-04-30T09:16:15+00:00</datetime>
3838
<object type="xsd:object"><![CDATA[{"key":"val"}]]></object>
3939
</payload>

0 commit comments

Comments
 (0)