-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The library updated with new functionalities
- adds new `Serializer\XmlSerializer` class - adds new functions: json_serialize(), json_unserialize(), xml_serialize(), xml_unserialize(), php_serialize() and php_unserialize() - updates the composer.json (some extensions requirements, new tags, branch alias) - updates the unit test (some files are renamed)
- Loading branch information
Showing
16 changed files
with
481 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Koded package. | ||
* | ||
* (c) Mihail Binev <mihail@kodeart.com> | ||
* | ||
* Please view the LICENSE distributed with this source code | ||
* for the full copyright and license information. | ||
* | ||
*/ | ||
|
||
namespace Koded\Stdlib\Serializer; | ||
|
||
use DateTime; | ||
use DateTimeInterface; | ||
use DOMDocument; | ||
use DOMElement; | ||
use Exception; | ||
use Koded\Stdlib\Interfaces\StringSerializable; | ||
|
||
/** | ||
* Class XmlSerializer is heavily copied from excellent | ||
* Propel 3 runtime parser (XmlParser) and modified. | ||
* | ||
*/ | ||
final class XmlSerializer implements StringSerializable | ||
{ | ||
|
||
private $root; | ||
|
||
public function __construct(string $root) | ||
{ | ||
$this->root = $root; | ||
} | ||
|
||
/** | ||
* @param iterable $data | ||
* | ||
* @return string XML | ||
*/ | ||
public function serialize($data): string | ||
{ | ||
$xml = new DOMDocument('1.0', 'UTF-8'); | ||
$xml->preserveWhiteSpace = false; | ||
$xml->formatOutput = true; | ||
|
||
$root = $xml->createElement($this->root); | ||
$xml->appendChild($root); | ||
$this->parseFromArray($data, $root); | ||
|
||
return $xml->saveXML(); | ||
} | ||
|
||
/** | ||
* @param string $document XML string | ||
* | ||
* @return array | ||
*/ | ||
public function unserialize(string $document) | ||
{ | ||
$xml = new DOMDocument('1.0', 'UTF-8'); | ||
|
||
try { | ||
$xml->loadXML(utf8_encode($document)); | ||
} catch (Exception $e) { | ||
return []; | ||
} | ||
|
||
return $this->parseFromElement($xml->documentElement); | ||
} | ||
|
||
private function parseFromArray(iterable $data, DOMElement $element): DOMElement | ||
{ | ||
foreach ($data as $key => $value) { | ||
if (is_numeric($key)) { | ||
$key = $element->nodeName; | ||
if ('s' === mb_substr($key, -1, 1)) { | ||
$key = mb_substr($key, 0, mb_strlen($key) - 1); | ||
} | ||
} | ||
|
||
try { | ||
$child = $element->ownerDocument->createElement($key); | ||
} catch (Exception $e) { | ||
error_log(sprintf('[%s] thrown while parsing the data into XML, with message "%s" for the key %s and value %s', | ||
get_class($e), | ||
$e->getMessage(), | ||
var_export($key, true), | ||
var_export($value, true) | ||
)); | ||
continue; | ||
} | ||
|
||
if (is_array($value)) { | ||
$child = $this->parseFromArray($value, $child); | ||
} elseif (is_string($value)) { | ||
$value = htmlentities($value, ENT_QUOTES | ENT_HTML5, 'UTF-8'); | ||
$child->appendChild($child->ownerDocument->createCDATASection($value)); | ||
} elseif ($value instanceof DateTimeInterface) { | ||
$child->setAttribute('type', 'xsd:dateTime'); | ||
$child->appendChild($child->ownerDocument->createTextNode($value->format(DateTime::ISO8601))); | ||
} elseif (is_object($value)) { | ||
$child->setAttribute('type', 'xsd:token'); | ||
$child->appendChild($child->ownerDocument->createCDATASection(serialize($value))); | ||
} else { | ||
$child->appendChild($child->ownerDocument->createTextNode($value)); | ||
} | ||
|
||
$element->appendChild($child); | ||
} | ||
|
||
return $element; | ||
} | ||
|
||
private function parseFromElement(DOMElement $element): array | ||
{ | ||
$result = []; | ||
$names = []; | ||
|
||
/** @var DOMElement $node */ | ||
foreach ($element->childNodes as $node) { | ||
if (XML_TEXT_NODE === $node->nodeType) { | ||
continue; | ||
} | ||
|
||
$name = $node->nodeName; | ||
|
||
if (isset($names[$name])) { | ||
if (isset($result[$name])) { | ||
$result[$names[$name]] = $result[$name]; | ||
unset($result[$name]); | ||
} | ||
|
||
$names[$name] += 1; | ||
$index = $names[$name]; | ||
} else { | ||
$names[$name] = 0; | ||
$index = $name; | ||
} | ||
|
||
$hasChildNodes = $node->hasChildNodes(); | ||
|
||
if (false === $hasChildNodes) { | ||
$result[$index] = null; | ||
} elseif ('xsd:token' === $node->getAttribute('type')) { | ||
$result[$index] = unserialize($node->firstChild->textContent); | ||
} elseif ($hasChildNodes && false === $this->hasOnlyTextNodes($node)) { | ||
$result[$index] = $this->parseFromElement($node); | ||
} elseif ($hasChildNodes && XML_CDATA_SECTION_NODE === $node->firstChild->nodeType) { | ||
$result[$index] = html_entity_decode($node->firstChild->textContent, ENT_QUOTES | ENT_HTML5); | ||
} elseif ('xsd:dateTime' === $node->getAttribute('type')) { | ||
$result[$index] = new DateTime($node->textContent); | ||
} else { | ||
$result[$index] = $node->textContent; | ||
} | ||
} | ||
|
||
return $result; | ||
} | ||
|
||
private function hasOnlyTextNodes(DOMElement $node): bool | ||
{ | ||
foreach ($node->childNodes as $child) { | ||
if (($child->nodeType !== XML_CDATA_SECTION_NODE) && ($child->nodeType !== XML_TEXT_NODE)) { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<?php | ||
|
||
namespace Koded\Stdlib; | ||
|
||
use Koded\Exceptions\KodedException; | ||
use Koded\Stdlib\Serializer\JsonSerializerTest; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
class JsonSerializerFunctionsTest extends TestCase | ||
{ | ||
|
||
/** @dataProvider data */ | ||
public function test_serialize_to_json($data) | ||
{ | ||
$this->assertEquals(JsonSerializerTest::SERIALIZED_JSON, json_serialize($data)); | ||
} | ||
|
||
public function test_unserialize_json() | ||
{ | ||
$this->assertEquals( | ||
json_decode(JsonSerializerTest::SERIALIZED_JSON, true), | ||
json_unserialize(JsonSerializerTest::SERIALIZED_JSON) | ||
); | ||
} | ||
|
||
public function test_unserialize_error() | ||
{ | ||
$this->expectException(KodedException::class); | ||
$this->expectExceptionMessage('[Exception] Syntax error'); | ||
|
||
json_unserialize(''); | ||
} | ||
|
||
public function data() | ||
{ | ||
return [ | ||
[ | ||
require __DIR__ . '/fixtures/config-test.php' | ||
] | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?php | ||
|
||
namespace Koded\Stdlib; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
|
||
class PhpSerializerFunctionsTest extends TestCase | ||
{ | ||
|
||
/** @var array */ | ||
private $original; | ||
|
||
/** @var string */ | ||
private $serialized; | ||
|
||
public function test_serialize_php() | ||
{ | ||
$this->assertEquals($this->serialized, php_serialize($this->original)); | ||
} | ||
|
||
public function test_unserialize_php() | ||
{ | ||
$this->assertEquals($this->original, php_unserialize($this->serialized)); | ||
} | ||
|
||
protected function setUp() | ||
{ | ||
$this->original = require __DIR__ . '/fixtures/config-test.php'; | ||
$this->serialized = php_serialize($this->original); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
<?php | ||
|
||
namespace Koded\Stdlib\Serializer; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
|
||
class XmlSerializerTest extends TestCase | ||
{ | ||
|
||
const XML_FILE = __DIR__ . '/../fixtures/error-message.xml'; | ||
const PHP_FILE = __DIR__ . '/../fixtures/error-message.php'; | ||
|
||
/** @var XmlSerializer */ | ||
private $SUT; | ||
|
||
public function test_serialize() | ||
{ | ||
$xml = $this->SUT->serialize(require self::PHP_FILE); | ||
$this->assertXmlStringEqualsXmlFile(self::XML_FILE, $xml); | ||
} | ||
|
||
public function test_unserialize() | ||
{ | ||
$array = $this->SUT->unserialize(file_get_contents(self::XML_FILE)); | ||
$this->assertEquals(require self::PHP_FILE, $array); | ||
} | ||
|
||
public function test_unserialize_error_should_return_empty_array() | ||
{ | ||
$this->assertSame([], $this->SUT->unserialize('')); | ||
} | ||
|
||
public function test_frankenstein_array() | ||
{ | ||
$array = require __DIR__ . '/../fixtures/nested-array.php'; | ||
$this->SUT->serialize($array); | ||
$this->assertEquals(require __DIR__ . '/../fixtures/nested-array.php', $array); | ||
} | ||
|
||
protected function setUp() | ||
{ | ||
$this->SUT = new XmlSerializer('payload'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?php | ||
|
||
namespace Koded\Stdlib; | ||
|
||
use Koded\Stdlib\Serializer\XmlSerializerTest; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
class XmlSerializerFunctionsTest extends TestCase | ||
{ | ||
|
||
public function test_serialize_to_xml() | ||
{ | ||
$this->assertXmlStringEqualsXmlFile( | ||
XmlSerializerTest::XML_FILE, | ||
xml_serialize('payload', require XmlSerializerTest::PHP_FILE) | ||
); | ||
} | ||
|
||
public function test_unserialize() | ||
{ | ||
$this->assertEquals( | ||
require XmlSerializerTest::PHP_FILE, | ||
xml_unserialize('payload', file_get_contents(XmlSerializerTest::XML_FILE)) | ||
); | ||
} | ||
|
||
public function test_unserialize_error_should_return_empty_array() | ||
{ | ||
$this->assertSame([], xml_unserialize('', '')); | ||
} | ||
} |
Oops, something went wrong.