如何通过PHP动态获取Twig模板中的所有变量及对象属性?
如何通过PHP动态获取Twig模板中使用的所有变量
可以实现这个需求,核心思路是利用Twig的词法分析器和解析器遍历模板的抽象语法树(AST),提取所有显式引用的变量,包括嵌套属性和被包含模板中的变量,无需在模板内添加dump代码。
实现步骤
1. 初始化Twig环境并解析模板为AST
首先加载目标模板,将其解析为Twig的抽象语法树,这是提取变量的基础:
use Twig\Environment; use Twig\Loader\FilesystemLoader; use Twig\Parser; // 初始化Twig文件加载器和环境 $loader = new FilesystemLoader('/path/to/your/templates'); $twig = new Environment($loader); // 指定要分析的模板路径 $targetTemplate = 'page.html.twig'; // 获取模板源代码 $source = $twig->getLoader()->getSourceContext($targetTemplate); // 词法分析生成Token,再解析为AST $lexer = $twig->getLexer(); $tokens = $lexer->tokenize($source); $parser = new Parser($twig); $ast = $parser->parse($tokens);
2. 编写AST访问者提取变量
实现Twig的节点访问者接口,遍历AST节点,提取普通变量、嵌套属性,同时递归处理被包含的模板:
use Twig\Node\Node; use Twig\NodeVisitor\NodeVisitorInterface; use Twig\Node\Expression\NameExpression; use Twig\Node\Expression\GetAttrExpression; use Twig\Node\IncludeNode; class TemplateVariableExtractor implements NodeVisitorInterface { private $collectedVars = []; private $twigEnv; public function __construct(Environment $env) { $this->twigEnv = $env; } public function getCollectedVariables(): array { return array_unique($this->collectedVars); } public function enterNode(Node $node, Environment $env): Node { // 提取普通变量(如 {{ username }}) if ($node instanceof NameExpression) { $this->collectedVars[] = $node->getAttribute('name'); } // 提取嵌套属性(如 {{ user.profile.email }}) if ($node instanceof GetAttrExpression) { $this->extractNestedPath($node); } // 处理被包含的模板,递归提取变量 if ($node instanceof IncludeNode) { $templateExpr = $node->getNode('template'); if ($templateExpr instanceof NameExpression) { $includedTemplate = $templateExpr->getAttribute('name'); $this->extractFromIncludedTemplate($includedTemplate); } } return $node; } private function extractNestedPath(GetAttrExpression $node) { $currentNode = $node; $pathParts = []; // 回溯嵌套节点,拼接完整属性路径 while ($currentNode instanceof GetAttrExpression) { $attr = $currentNode->getNode('attribute'); if (method_exists($attr, 'getAttribute')) { $pathParts[] = $attr->getAttribute('value'); } $currentNode = $currentNode->getNode('node'); } if ($currentNode instanceof NameExpression) { $pathParts[] = $currentNode->getAttribute('name'); $fullPath = implode('.', array_reverse($pathParts)); $this->collectedVars[] = $fullPath; } } private function extractFromIncludedTemplate(string $templatePath) { $source = $this->twigEnv->getLoader()->getSourceContext($templatePath); $lexer = $this->twigEnv->getLexer(); $tokens = $lexer->tokenize($source); $parser = new Parser($this->twigEnv); $includedAst = $parser->parse($tokens); $visitor = new self($this->twigEnv); $visitor->enterNode($includedAst, $this->twigEnv); $this->collectedVars = array_merge($this->collectedVars, $visitor->getCollectedVariables()); } public function leaveNode(Node $node, Environment $env): ?Node { return $node; } public function getPriority(): int { return 0; } }
3. 执行提取并获取结果
将访问者加入Twig环境,触发AST遍历,最终得到所有变量:
// 初始化提取器 $extractor = new TemplateVariableExtractor($twig); $twig->addNodeVisitor($extractor); // 触发解析流程,自动调用访问者 $twig->parse($tokens); // 获取所有提取到的变量 $requiredVariables = $extractor->getCollectedVariables(); // 输出结果示例 print_r($requiredVariables);
注意事项
- 仅能提取显式引用的变量,无法处理动态生成的变量(如
{{ attribute(user, dynamicKey) }}),这类变量在模板编译前无法确定具体名称。 - 若模板使用了宏(Macro),需要在访问者中添加对
MacroCallNode的处理逻辑,以提取宏调用中的变量。 - 确保Twig加载器能访问到所有被包含的模板,否则递归提取会失败。
内容的提问来源于stack exchange,提问作者Fatemeh Gharri
相关产品推荐
相关产品推荐

