Browse Source

Add generic methods for converting DOM<->PHP, updated places where this is useful

master
Adam Pippin 3 years ago
parent
commit
145b89da1e
  1. 66
      app/Dom/Document.php
  2. 89
      app/Dom/Node.php
  3. 19
      app/Engine/Engine.php

66
app/Dom/Document.php

@ -11,11 +11,40 @@ namespace App\Dom;
*/
class Document extends Node
{
public function __construct()
/**
* The original name of this document, as they will relate to each other
* via include/stack refs, etc.
* @var string
*/
protected $document_name;
public function __construct(?string $document_name = null)
{
$this->document_name = $document_name;
$this->children = [];
}
/**
* Get this document's name.
*
* @return string
*/
public function getDocumentName(): string
{
return $this->document_name;
}
/**
* Set this document's name.
*
* @param string $document_name
* @return void
*/
public function setDocumentName(string $document_name): void
{
$this->document_name = $document_name;
}
/**
* Retrieve a value from the meta `cfnpp` block in the document.
*
@ -29,29 +58,18 @@ class Document extends Node
{
throw new \Exception('Path not found: '.$path);
}
if ($node instanceof NodeValue)
{
return $node->getValue();
}
elseif ($node->isArray())
{
$values = [];
foreach ($node as $child)
{
$values[] = $child->getValue();
}
return $values;
}
elseif ($node->isMap())
{
$values = [];
foreach ($node as $child)
{
$values[$child->getName()] = $child->getValue();
}
return $values;
}
return Node::toPhp($node);
}
throw new \Exception('Cannot retrieve meta value: '.$path);
/**
* Set a value on the meta `cfnpp` block in the document.
*
* @param string $path a dot-separate path to the value to set/replace
* @param mixed $data the data to set on that node
* @return void
*/
public function setMeta(string $path, $data): void
{
$this->setChildByPath('cfnpp.'.$path, Node::fromPhp($data));
}
}

89
app/Dom/Node.php

@ -199,17 +199,17 @@ class Node implements \ArrayAccess, \Iterator
}
/**
* Set a child of this node by a dot-separate path.
* Add/replace a child of this node by a dot-separated path.
*
* Each element of the path can either be a node name or a numeric
* index representing the offset into an array
*
* @todo will explode if you try to write using a numeric index to an array
* element that doesn't exist
* @param string $path
* @param Node $node
* @param string $path
* @param Node $node
* @return void
*/
public function addChildUnderPath(string $path, Node $node): void
public function setChildByPath(string $path, Node $node): void
{
$path = explode('.', $path);
$current = $this;
@ -229,17 +229,24 @@ class Node implements \ArrayAccess, \Iterator
{
$next_node = new Node($current, $next);
$current->addChild($next_node);
$current = $next_node;
}
$current = $next_node;
}
if (!isset($current))
{
return;
throw new \Exception('Cannot write to non-existent array index by path');
}
}
$current->addChild($node);
// Replace current with the passed in node
$current->remove();
$current->getParent()->addChild($node);
$node->setParent($current->getParent());
if ($current->hasName())
{
$node->setName($current->getName());
}
}
/**
@ -343,6 +350,72 @@ class Node implements \ArrayAccess, \Iterator
$this->getParent()->removeChild($this);
}
public static function toPhp(Node $node)
{
if ($node instanceof NodeValue)
{
return $node->getValue();
}
elseif ($node->isArray())
{
$result = [];
foreach ($node as $child)
{
$result[] = static::toPhp($child);
}
return $result;
}
elseif ($node->isMap())
{
$result = [];
foreach ($node as $child)
{
$result[$child->getName()] = static::toPhp($child);
}
return $result;
}
throw new \Exception('Cannot convert from DOM node');
}
public static function fromPhp($value): Node
{
if (is_scalar($value))
{
return new NodeValue(null, null, $value);
}
elseif (is_array($value))
{
$contains_only_numeric_keys = array_reduce(array_keys($value), static function($carry, $item) {
return $carry && is_numeric($item);
}, true);
if ($contains_only_numeric_keys)
{
$node = new Node(null, null);
foreach ($value as $item)
{
$child = static::fromPhp($item);
$child->setParent($node);
$node->addChild($child);
}
return $node;
}
$node = new Node(null, null);
foreach ($value as $key => $item)
{
$child = static::fromPhp($item);
$child->setName($key);
$child->setParent($node);
$node->addChild($child);
}
return $node;
}
throw new \Exception('Cannot convert passed value into DOM nodes');
}
// ArrayAccess
public function offsetExists($offset): bool

19
app/Engine/Engine.php

@ -56,13 +56,14 @@ class Engine
$documents = [];
// Figure out which files to process and load all files.
$files = [basename($input_file)];
$files = [realpath($input_file)];
for ($i = 0; $i < sizeof($files); $i++)
{
if (!isset($documents[$files[$i]]))
{
$documents[$files[$i]] = $document = $this->unserialize->unserialize(file_get_contents($file_helper->resolve($files[$i])));
$documents[$files[$i]] = $document = $this->unserialize->unserialize(file_get_contents($files[$i]));
$document->setDocumentName($files[$i]);
$includes = [];
try
{
@ -73,17 +74,27 @@ class Engine
}
if (sizeof($includes))
{
$file_helper->clear();
$file_helper->appendPath(dirname($files[$i]));
foreach ($includes as &$include)
{
$include = $file_helper->resolve($include);
}
unset($include);
$document->setMeta('stack', $includes);
array_splice($files, $i, 0, $includes);
--$i;
}
}
}
// Now actually process all the documents
$documents = array_values($documents);
// Pass it all in and compile
$document = $this->compileDocuments($documents, $options);
// Set metadata in output
$document->addChildUnderPath('Metadata', new \App\Dom\NodeValue(null, 'Stack', implode(', ', $files)));
$document->setChildByPath('Metadata.Stack', $files);
// Remove cfnpp block, that doesn't go to CloudFormation
$cfnpp = $document->getChildByName('cfnpp');

Loading…
Cancel
Save