cloudformation-plus-plus: cfn template preprocessor
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

<?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);
}
}
}