# 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);
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://twig.shujuwajue.com/twig-2x/twig-nei-bu.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
