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.
234 lines
5.5 KiB
234 lines
5.5 KiB
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Cfnpp;
|
|
|
|
use App\Dom\Node;
|
|
use App\Dom\NodeValue;
|
|
use App\Dom\NodeFunction;
|
|
use App\Dom\NodeFunctionValue;
|
|
|
|
/**
|
|
* Functions available in a cfnpp document.
|
|
*
|
|
* @author Adam Pippin <hello@adampippin.ca>
|
|
*/
|
|
class Functions
|
|
{
|
|
/**
|
|
* For now, generate incremental condition names.
|
|
* @todo
|
|
* @var int
|
|
*/
|
|
public static $condition_idx = 0;
|
|
|
|
/**
|
|
* cfnpp compiler.
|
|
* @var Compiler
|
|
*/
|
|
protected $compiler;
|
|
|
|
/**
|
|
* cfnpp compiler options, stores state.
|
|
* @var Options
|
|
*/
|
|
protected $options;
|
|
|
|
public function __construct(Compiler $compiler, Options $options)
|
|
{
|
|
$this->compiler = $compiler;
|
|
$this->options = $options;
|
|
}
|
|
|
|
/**
|
|
* Examines this class with reflection and registers all functions on a cfnpp
|
|
* compiler instance.
|
|
*
|
|
* @param Compiler $compiler
|
|
* @return void
|
|
*/
|
|
public function register(Compiler $compiler): void
|
|
{
|
|
$reflection = new \ReflectionClass(static::class);
|
|
$methods = $reflection->getMethods();
|
|
|
|
foreach ($methods as $method)
|
|
{
|
|
if (!stristr($method->name, '_'))
|
|
{
|
|
continue;
|
|
}
|
|
[$method_type, $method_name] = explode('_', $method->name, 2);
|
|
switch ($method_type)
|
|
{
|
|
case 'mf':
|
|
$compiler->registerMergeFunction($method_name, [$this, $method->name]);
|
|
break;
|
|
case 'f':
|
|
$compiler->registerFunction($method_name, [$this, $method->name]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Replace all children nodes in original with those from target, rather than
|
|
* merging them.
|
|
*
|
|
* @param Node $original
|
|
* @param Node $target
|
|
* @param NodeFunction $function
|
|
* @return ?Node
|
|
*/
|
|
public function mf_replace(Node $original, Node $target, NodeFunction $function): ?Node
|
|
{
|
|
// TODO: Deal with nodefunctionvalue
|
|
|
|
$replacement = new Node(null, $target->hasName() ? $target->getName() : null);
|
|
$replacement->setChildren($function->getChildren());
|
|
return $replacement;
|
|
}
|
|
|
|
/**
|
|
* Unset a node, completely removing it from the document.
|
|
*
|
|
* @param Node $node
|
|
* @param NodeFunction $function
|
|
* @return ?Node
|
|
*/
|
|
public function f_unset(Node $node, NodeFunction $function): ?Node
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get the value of a variable.
|
|
*
|
|
* @param Node $node
|
|
* @param NodeFunction $function
|
|
* @return ?Node
|
|
*/
|
|
public function f_var(Node $node, NodeFunction $function): ?Node
|
|
{
|
|
if (!($function instanceof NodeFunctionValue))
|
|
{
|
|
throw new \Exception('!var requires scalar argument');
|
|
}
|
|
$value = $this->options->getVariable($function->getValue());
|
|
return new NodeValue(null, $node->hasName() ? $node->getName() : null, $value);
|
|
}
|
|
|
|
/**
|
|
* Create a reference to a CloudFormation parameter.
|
|
*
|
|
* @param Node $node
|
|
* @param NodeFunction $function
|
|
* @return ?Node
|
|
*/
|
|
public function f_param(Node $node, NodeFunction $function): ?Node
|
|
{
|
|
if (!($function instanceof NodeFunctionValue))
|
|
{
|
|
throw new \Exception('!param requires scalar argument');
|
|
}
|
|
$node = new Node(null, $node->hasName() ? $node->getName() : null);
|
|
$ref = new NodeValue($node, 'Ref', $function->getValue());
|
|
$node->addChild($ref);
|
|
return $node;
|
|
}
|
|
|
|
/**
|
|
* Conditionally include part of the file.
|
|
*
|
|
* @param Node $node
|
|
* @param NodeFunction $function
|
|
* @return ?Node
|
|
*/
|
|
public function f_if(Node $node, NodeFunction $function): ?Node
|
|
{
|
|
if (!($function instanceof NodeFunction))
|
|
{
|
|
throw new \Exception('!if expects array of parameters');
|
|
}
|
|
|
|
$nodes = $function->getChildren();
|
|
|
|
$condition = $nodes[0];
|
|
|
|
if (!($condition instanceof NodeValue))
|
|
{
|
|
throw new \Exception('!if requires scalar condition');
|
|
}
|
|
$if_true = $nodes[1];
|
|
$if_false = sizeof($nodes) == 3 ? $nodes[2] : null;
|
|
|
|
$expression = new \App\Cfnpp\Expression\Expression($condition->getValue(), $this->options);
|
|
|
|
// We need a single resulting node as a final value either as a scalar or
|
|
// if we want to convert to cloudformation.
|
|
if ($expression->count() > 1)
|
|
{
|
|
throw new \Exception('!if expression must evaluate down to a single value: '.$condition->getValue());
|
|
}
|
|
|
|
if ($expression->isComplete())
|
|
{
|
|
// We know the final result, we can resolve this directly.
|
|
if ($expression->getValue())
|
|
{
|
|
$if_true->setName($node->hasName() ? $node->getName() : null);
|
|
return $if_true;
|
|
}
|
|
elseif (isset($if_false))
|
|
{
|
|
$if_false->setName($node->hasName() ? $node->getName() : null);
|
|
return $if_false;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Create condition
|
|
// Generate Cfn Fn::If
|
|
$condition_name = 'Condition'.(static::$condition_idx++);
|
|
$this->compiler->addCondition($condition_name, Node::fromPhp($expression->toCloudformation()));
|
|
|
|
$n_orig = new Node(null, $node->hasName() ? $node->getName() : null);
|
|
$n_if = new Node($n_orig, 'Fn::If');
|
|
$n_orig->addChild($n_if);
|
|
$n_if->addChild(new NodeValue($n_if, null, $condition_name));
|
|
$n_if->addChild($if_true);
|
|
if (isset($if_false))
|
|
{
|
|
$n_if->addChild($if_false);
|
|
}
|
|
return $n_orig;
|
|
}
|
|
|
|
/**
|
|
* Calculate an expression.
|
|
*
|
|
* @param Node $node
|
|
* @param NodeFunction $function
|
|
* @return ?Node
|
|
*/
|
|
/*
|
|
* TODO: Reimplement
|
|
public function f_expr(Node $node, NodeFunction $function): ?Node
|
|
{
|
|
if (!($function instanceof NodeFunctionValue))
|
|
{
|
|
throw new \Exception('!if requires scalar argument');
|
|
}
|
|
|
|
$parser = new \App\Cfnpp\Expression\Parser();
|
|
$expression = $parser->parse($function->getValue());
|
|
$result = $expression->evaluate($this->options->getVariables());
|
|
|
|
$result = Node::fromPhp($result);
|
|
$result->setName($node->hasName() ? $node->getName() : null);
|
|
|
|
return $result;
|
|
}
|
|
*/
|
|
}
|
|
|