You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
153 lines
3.8 KiB
153 lines
3.8 KiB
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Serialize;
|
|
|
|
use Symfony\Component\Yaml\Yaml as SymfonyYaml;
|
|
use Symfony\Component\Yaml\Tag\TaggedValue;
|
|
use App\Dom\Document;
|
|
use App\Dom\Node;
|
|
use App\Dom\NodeValue;
|
|
use App\Dom\NodeFunction;
|
|
use App\Dom\NodeFunctionValue;
|
|
|
|
/**
|
|
* Handles serializing and unserializing between documents and Yaml.
|
|
*
|
|
* @author Adam Pippin <hello@adampippin.ca>
|
|
*/
|
|
class Yaml implements ISerialize, IUnserialize
|
|
{
|
|
/**
|
|
* Convert a YAML string into a Dom\Document.
|
|
*
|
|
* @param string $document
|
|
* @return Document
|
|
*/
|
|
public function unserialize(string $document): Document
|
|
{
|
|
$data = SymfonyYaml::parse($document, SymfonyYaml::PARSE_CUSTOM_TAGS);
|
|
$document = new Document();
|
|
$this->arrayToDom($document, $data);
|
|
return $document;
|
|
}
|
|
|
|
/**
|
|
* Convert a Dom\Document into a YAML string.
|
|
*
|
|
* @param Document $document
|
|
* @return string
|
|
*/
|
|
public function serialize(Document $document): string
|
|
{
|
|
$data = $this->domToArray($document);
|
|
return SymfonyYaml::dump($data, 6);
|
|
}
|
|
|
|
/**
|
|
* Take a DOM node and return it turned into an array appropriate
|
|
* for serializing with the Symfony Yaml component.
|
|
*
|
|
* @param Node $node dom node to convert to array
|
|
* @return mixed
|
|
*/
|
|
protected function domToArray(Node $node)
|
|
{
|
|
$result = null;
|
|
foreach ($node as $child)
|
|
{
|
|
$child_ser = $this->serializeNode($child);
|
|
if (is_array($child_ser))
|
|
{
|
|
if (isset($result) && !is_array($result))
|
|
{
|
|
throw new \Exception('Invalid DOM: tagged value adjacent to property map');
|
|
}
|
|
$result = $result ?? [];
|
|
$result = array_merge($result, $child_ser);
|
|
}
|
|
else
|
|
{
|
|
$result = $child_ser;
|
|
}
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Serialize a single node, only meant to be used as part of/by domToArray.
|
|
*
|
|
* @param Node $node
|
|
* @return mixed
|
|
*/
|
|
private function serializeNode(Node $node)
|
|
{
|
|
switch (true)
|
|
{
|
|
case $node instanceof NodeFunctionValue:
|
|
return new TaggedValue($node->getName(), $node->getValue());
|
|
case $node instanceof NodeFunction:
|
|
return new TaggedValue($node->getName(), $this->domToArray($node));
|
|
case $node instanceof NodeValue:
|
|
return $node->hasName() ? [$node->getName() => $node->getValue()] : [$node->getValue()];
|
|
default:
|
|
return $node->hasName() ? [$node->getName() => $this->domToArray($node)] : [$this->domToArray($node)];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert an array returned from Symfony's Yaml component when parsing a
|
|
* yaml document and convert it into a Dom\Document.
|
|
*
|
|
* Children are pushed directly onto the passed $parent node
|
|
*
|
|
* @param Node $parent node that the data is to be unserialized under
|
|
* @param array<mixed,mixed> $data the data to unserialize
|
|
* @return void
|
|
*/
|
|
protected function arrayToDom(Node $parent, array $data)
|
|
{
|
|
foreach ($data as $k => $v)
|
|
{
|
|
if ($v instanceof TaggedValue)
|
|
{
|
|
$node = new Node($parent, is_string($k) ? $k : null);
|
|
$fun_node = null;
|
|
$fun_name = $v->getTag();
|
|
$fun_value = $v->getValue();
|
|
if (is_array($fun_value))
|
|
{
|
|
$fun_node = new NodeFunction($node, $fun_name);
|
|
$this->arrayToDom($fun_node, $fun_value);
|
|
}
|
|
elseif (is_scalar($fun_value))
|
|
{
|
|
$fun_node = new NodeFunctionValue($node, $fun_name, $fun_value);
|
|
}
|
|
else
|
|
{
|
|
throw new \Exception('Cannot unserialize value from function node--is not array or scalar');
|
|
}
|
|
$node->addChild($fun_node);
|
|
}
|
|
else
|
|
{
|
|
if (is_array($v))
|
|
{
|
|
$node = new Node($parent, is_string($k) ? $k : null);
|
|
$this->arrayToDom($node, $v);
|
|
}
|
|
elseif (is_scalar($v))
|
|
{
|
|
$node = new NodeValue($parent, is_string($k) ? $k : null, $v);
|
|
}
|
|
else
|
|
{
|
|
throw new \Exception('Cannot unserialize value from YAML--value is not TaggedValue, array or scalar');
|
|
}
|
|
}
|
|
$parent->addChild($node);
|
|
}
|
|
}
|
|
}
|
|
|