> For the complete documentation index, see [llms.txt](https://twig.shujuwajue.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://twig.shujuwajue.com/twig-2x/twig-nei-bu.md).

# Twig 内部机制

> 原文：[Twig Internals](https://twig.sensiolabs.org/doc/2.x/internals.html)\
> 翻译：小虾米（QQ:509129）

## Twig 内部机制

Twig是非常可扩展的，您可以轻松攻击它。请记住，您应该尝试在黑客攻击核心之前创建扩展，因为大多数特性和增强都可以通过扩展来处理。这一章也对那些想了解Twig如何在引擎盖下工作的人有用。

### Twig是如何工作的呢?

Twig模板的呈现可以概括为四个关键步骤:

* **加载**模板:如果模板已经编译，加载它并进入评估步骤，否则:
  * 首先，**lexer**将模板源代码标记为小块，以便进行更简单的处理;
  * 然后，解析器将令牌流转换为有意义的节点树(抽象语法树);
  * 最终，编译器将AST转换为PHP代码。
* **评估**模板:它基本上意味着调用已编译模板的display()方法并将其传递给上下文。

### Lexer

lexer将模板源代码标记为令牌流(每个令牌是Twig\_Token的一个实例，而流是Twig\_TokenStream的实例)。默认的lexer识别13种不同的标记类型:

* 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::PUNCTUATION\_TYPE:标点符号;
* Twig\_Token::插值\_start\_type,Twig\_Token::插值函数:字符串插值的分隔符;
* Twig\_Token::EOF\_TYPE:结束的模板。

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

```php
$stream = $twig->tokenize(new Twig_Source($source, $identifier));
```

由于流有一个\_\_toString()方法，因此可以通过与对象相呼应来对它进行文本表示:

```php
echo $stream."\n";
```

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

```
TEXT_TYPE(Hello )
VAR_START_TYPE()
NAME_TYPE(name)
VAR_END_TYPE()
EOF_TYPE()
```

> 可以通过调用setLexer()方法来更改默认的lexer(Twig\_Lexer):

```php
$twig->setLexer($lexer);
```

### 解析器(Parser)

解析器将令牌流转换为AST(抽象语法树)或节点树(Twig\_Node\_Module的实例)。核心扩展定义了基本的节点，例如:for，如果，…表达式节点。

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

```php
$nodes = $twig->parse($stream);
```

与节点对象相呼应，可以得到树的良好表示:

```php
echo $nodes."\n";
```

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

```
Twig_Node_Module(
  Twig_Node_Text(Hello )
  Twig_Node_Print(
    Twig_Node_Expression_Name(name)
  )
)
```

> 默认解析器(Twig\_TokenParser)可以通过调用setParser()方法来更改:

```php
$twig->setParser($parser);
```

### 编译器(Compiler)

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

您可以通过一个环境的compile()方法手工地将节点树编译为PHP代码:

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

Hello { { name }模板的生成模板如下(根据所使用的Twig版本，实际的输出会有所不同):

```php
/* Hello {{ name }} */
class __TwigTemplate_1121b6f109fe93ebe8c6e22e3712bceb extends Twig_Template
{
    protected function doDisplay(array $context, array $blocks = array())
    {
        // line 1
        echo "Hello ";
        echo twig_escape_filter($this->env, isset($context["name"]) ? $context["name"] : null), "html", null, true);
    }

    // some more code
}
```

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

```php
$twig->setCompiler($compiler);
```
