Is it possible to retrieve all variables inside a Twig template with PHP?
Example someTemplate.twig.php:
Hello {{ name }},
your new email is {{ ema
I built a Twig2Schema class to infer variables from a Twig AST. To get the variables in a document, you need to recursively "walk" through the Twig AST and have rules in place when you encounter certain types of language nodes.
This class extracts variable names from Nodes if they are not always defined, and also grabs variables from the value used in ForLoopNodes and IfStatements.
To use it, you can either call infer
for the whole template, or a subset of the tree using inferFromAst
.
getLoader()->getSourceContext($twigTemplateName);
$tokens = $twig->tokenize($source);
$ast = $twig->parse($tokens);
return $this->inferFromAst($ast);
}
/**
* @param \Twig\Node\ModuleNode $ast - An abstract syntax tree parsed from Twig
* @return array - The variables used in the Twig template
*/
public function inferFromAst(\Twig\Node\ModuleNode $ast)
{
$keys = $this->visit($ast);
foreach ($keys as $key => $value) {
if ($value['always_defined'] || $key === '_self') {
unset($keys[$key]);
}
}
return $keys;
}
/**
* @param \Twig\Node\Node $ast - The tree to traverse and extract variables
* @return array - The variables found in this tree
*/
private function visit(\Twig\Node\Node $ast)
{
$vars = [];
switch (get_class($ast)) {
case \Twig\Node\Expression\AssignNameExpression::class:
case \Twig\Node\Expression\NameExpression::class:
$vars[$ast->getAttribute('name')] = [
'type' => get_class($ast),
'always_defined' => $ast->getAttribute('always_defined'),
'is_defined_test' => $ast->getAttribute('is_defined_test'),
'ignore_strict_check' => $ast->getAttribute('ignore_strict_check')
];
break;
case \Twig\Node\ForNode::class:
foreach ($ast as $key => $node) {
switch ($key) {
case 'value_target':
$vars[$node->getAttribute('name')] = [
'for_loop_target' => true,
'always_defined' => $node->getAttribute('always_defined')
];
break;
case 'body':
$vars = array_merge($vars, $this->visit($node));
break;
default:
break;
}
}
break;
case \Twig\Node\IfNode::class:
foreach ($ast->getNode('tests') as $key => $test) {
$vars = array_merge($vars, $this->visit($test));
}
foreach ($ast->getNode('else') as $key => $else) {
$vars = array_merge($vars, $this->visit($else));
}
break;
default:
if ($ast->count()) {
foreach ($ast as $key => $node) {
$vars = array_merge($vars, $this->visit($node));
}
}
break;
}
return $vars;
}
}