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