*/ 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 $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); } } }