Twig

灵活、快速且安全的
PHP 模板引擎

a Symfony Product
文档 Twig 内部结构
您正在阅读 Twig 3.x 的文档。切换到 Twig 1.x, 2.x 的文档。

问题与反馈

许可

Twig documentation is licensed under the new BSD license.

Twig 内部结构

Twig 非常易于扩展,您可以对其进行 hack。请记住,在 hack 核心之前,您应该尝试创建一个扩展,因为大多数特性和增强功能都可以通过扩展来处理。本章对于想要了解 Twig 底层工作原理的人也很有用。

Twig 是如何工作的?

Twig 模板的渲染可以概括为四个关键步骤

  • 加载模板:如果模板已编译,则加载它并转到评估步骤,否则

    • 首先,词法分析器将模板源代码标记化为小块,以便于处理;
    • 然后,语法分析器将 token 流转换为有意义的节点树(抽象语法树);
    • 最后,编译器将 AST 转换为 PHP 代码。
  • 评估模板:这意味着调用已编译模板的 display() 方法并传递上下文。

词法分析器

词法分析器将模板源代码标记化为 token 流(每个 token 都是 \Twig\Token 的实例,而流是 \Twig\TokenStream 的实例)。默认词法分析器识别 15 种不同的 token 类型

  • \Twig\Token::BLOCK_START_TYPE, \Twig\Token::BLOCK_END_TYPE:块的分隔符 ({% %})
  • \Twig\Token::VAR_START_TYPE, \Twig\Token::VAR_END_TYPE:变量的分隔符 ({{ }})
  • \Twig\Token::TEXT_TYPE:表达式之外的文本;
  • \Twig\Token::NAME_TYPE:表达式中的名称;
  • \Twig\Token::NUMBER_TYPE:表达式中的数字;
  • \Twig\Token::STRING_TYPE:表达式中的字符串;
  • \Twig\Token::OPERATOR_TYPE:运算符;
  • \Twig\Token::ARROW_TYPE:箭头函数运算符 (=>);
  • \Twig\Token::SPREAD_TYPE:展开运算符 (...);
  • \Twig\Token::PUNCTUATION_TYPE:标点符号;
  • \Twig\Token::INTERPOLATION_START_TYPE, \Twig\Token::INTERPOLATION_END_TYPE:字符串插值的分隔符;
  • \Twig\Token::EOF_TYPE:模板结束。

您可以通过调用环境的 tokenize() 方法手动将源代码转换为 token 流

1
$stream = $twig->tokenize(new \Twig\Source($source, $identifier));

由于该流具有 __toString() 方法,您可以通过回显对象来获得它的文本表示

1
echo $stream."\n";

这是 Hello {{ name }} 模板的输出

1
2
3
4
5
TEXT_TYPE(Hello )
VAR_START_TYPE()
NAME_TYPE(name)
VAR_END_TYPE()
EOF_TYPE()

注意

可以通过调用 setLexer() 方法来更改默认词法分析器 (\Twig\Lexer)

1
$twig->setLexer($lexer);

语法分析器

语法分析器将 token 流转换为 AST(抽象语法树),或节点树(\Twig\Node\ModuleNode 的实例)。核心扩展定义了基本节点,例如:forif,... 以及表达式节点。

您可以通过调用环境的 parse() 方法手动将 token 流转换为节点树

1
$nodes = $twig->parse($stream);

回显节点对象会为您提供树的良好表示

1
echo $nodes."\n";

这是 Hello {{ name }} 模板的输出

1
2
3
4
5
6
\Twig\Node\ModuleNode(
  \Twig\Node\TextNode(Hello )
  \Twig\Node\PrintNode(
    \Twig\Node\Expression\NameExpression(name)
  )
)

注意

可以通过调用 setParser() 方法来更改默认语法分析器 (\Twig\TokenParser\AbstractTokenParser)

1
$twig->setParser($parser);

编译器

最后一步由编译器完成。它以节点树作为输入,并生成可用于模板运行时执行的 PHP 代码。

您可以使用环境的 compile() 方法手动将节点树编译为 PHP 代码

1
$php = $twig->compile($nodes);

Hello {{ name }} 模板生成的模板内容如下(实际输出可能因您使用的 Twig 版本而异)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Hello {{ name }} */
class __TwigTemplate_1121b6f109fe93ebe8c6e22e3712bceb extends Template
{
    protected function doDisplay(array $context, array $blocks = []): iterable
    {
        $macros = $this->macros;
        // line 1
        yield "Hello ";
        // line 2
        yield $this->env->getRuntime('Twig\Runtime\EscaperRuntime')->escape((isset($context["name"]) || array_key_exists("name", $context) ? $context["name"] : (function () { throw new RuntimeError('Variable "name" does not exist.', 2, $this->source); })()), "html", null, true);
        return; yield '';
    }

    // some more code
}

注意

可以通过调用 setCompiler() 方法来更改默认编译器 (\Twig\Compiler)

1
$twig->setCompiler($compiler);