ZetCode

Twig

最后修改于 2025 年 2 月 16 日

Twig 教程展示了如何在 PHP 应用程序中使用 Twig 模板引擎来生成文档。

Twig

Twig 是一个 PHP 模板引擎。它由 Symfony 开发人员创建。Twig 文件的扩展名为 .html.twig;它们是静态数据(如 HTML)和 Twig 构造的混合体。

Twig 使用双大括号分隔符 {{ }} 用于输出,使用大括号百分比分隔符 {% %} 用于逻辑。 {# #} 用于注释。

<ul>
    {% for word in words %}
        <li>{{ word }}</li>
    {% endfor %}
</ul>

此代码是一个示例 Twig 语法。在此代码中,我们使用 for 标签来创建一个循环。

Twig 语法由标签、过滤器、函数、运算符和测试组成。

设置 Twig

首先,我们设置 Twig。

$ composer require twig/twig

我们使用 composer 安装 Twig。

$ mkdir templates

我们将模板文件放置在 template 目录中。

require __DIR__ . '/vendor/autoload.php';

我们需要将 autoload.php 文件添加到我们的脚本中。

模板引擎

模板引擎或模板处理器是一个旨在将模板与数据模型结合起来以生成文档的库。模板引擎通常用于生成大量电子邮件、源代码预处理或生成动态 HTML 页面。

我们创建一个模板引擎,在其中定义静态部分和动态部分。动态部分稍后将被数据替换。渲染函数随后会将模板与数据结合起来。

Twig 第一个例子

以下是 Twig 模板系统的一个简单演示。

first.php
<?php

require __DIR__ . '/vendor/autoload.php';

use Twig\Environment;
use Twig\Loader\FilesystemLoader;

$loader = new FilesystemLoader(__DIR__ . '/templates');
$twig = new Environment($loader);

echo $twig->render('first.html.twig', ['name' => 'John Doe', 
    'occupation' => 'gardener']);

我们使用 FilesystemLoader 从指定的目录加载模板。

echo $twig->render('first.html.twig', ['name' => 'John Doe', 
    'occupation' => 'gardener']);

输出使用 render 生成。它有两个参数:模板文件和数据。

templates/first.html.twig
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>

    <p>
        {{ name }} is a {{ occupation }}
    </p>

</body>

</html>

这是模板文件。变量使用 {{}} 语法输出。

$ php first.php
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>

    <p>
        John Doe is a gardener
    </p>

</body>

</html>

Twig 过滤器

过滤器允许我们以各种方式修改数据。

filters.php
<?php

require __DIR__ . '/vendor/autoload.php';

use Twig\Environment;
use Twig\Loader\FilesystemLoader;

$loader = new FilesystemLoader(__DIR__ . '/templates');
$twig = new Environment($loader);

$words = ['sky', 'mountain', 'falcon', 'forest', 'rock', 'blue'];
$sentence = 'today is a windy day';

echo $twig->render('filters.html.twig', 
    ['words' => $words, 'sentence' => $sentence]);

在这个例子中,我们有一个数组和一个字符串作为模板数据。

templates/filters.html.twig
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Filters</title>
</head>

<body>

    <p>
     The array has {{ words | length }} elements
    </p>

    <p>
     Joined array elements: {{ words | join(',') }} 
    </p>    

    <p>
     {{ sentence | title }} 
    </p>        

</body>

</html>

过滤器使用 | 字符应用。该示例使用 length 计数单词,使用 join 连接数组元素,并使用 title 修改字符。

Twig 自定义过滤器

我们可以使用 Twig_Filter 创建自定义过滤器。

customfilter.php
<?php

require __DIR__ . '/vendor/autoload.php';

use Twig\Environment;
use Twig\Loader\FilesystemLoader;

$loader = new FilesystemLoader(__DIR__ . '/templates');
$twig = new Environment($loader);
$twig->addFilter(new Twig_Filter('accFirst', 'accFirst'));

$sentence = 'šumivé víno';

echo $twig->render('customfilter.html.twig',
    ['sentence' => $sentence]);

function accFirst($value, $encoding = 'UTF8')
{
    $strlen = mb_strlen($value, $encoding);
    $firstChar = mb_substr($value, 0, 1, $encoding);
    $rest = mb_substr($value, 1, $strlen - 1, $encoding);

    return mb_strtoupper($firstChar, $encoding) . $rest;
}

我们添加了一个名为 accFirst 的新过滤器。它仅修改第一个字母,并且还可以处理重音符号。

templates/customfilter.html.twig
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Custom filter</title>
</head>

<body>  

    <p>
     {{ sentence | accFirst }} 
    </p>      

</body>

</html>

这是模板文件,它使用自定义的 accFirst 过滤器。

Twig 循环

要创建循环,我们使用 for 标签。

looping.php
<?php

require __DIR__ . '/vendor/autoload.php';

use Twig\Environment;
use Twig\Loader\FilesystemLoader;

$loader = new FilesystemLoader(__DIR__ . '/templates');
$twig = new Environment($loader);

$words = ['sky', 'mountain', 'falcon', 'forest', 
    'rock', 'blue', 'solid', 'book', 'tree'];

echo $twig->render('words.html.twig', ['words' => $words]);

我们将循环一个单词数组。

templates/words.html.twig
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Words</title>
</head>

<body>

    <ul>
    {% for word in words %}
        <li>{{ word }}</li>
    {% endfor %}
    </ul>

    <ul>
    {% for word in words|slice(2, 4) %}
        <li>{{ word }}</li>
    {% endfor %}    
    </ul>

</body>

</html>

在模板文件中,我们循环遍历 words 数组并生成一个 HTML 列表。使用 slice 过滤器,我们可以循环遍历数组的一部分。

Twig 使用 if & else 循环

我们可以将 for 标签与 if 标签和 else 标签结合使用。

looping2.php
<?php

require __DIR__ . '/vendor/autoload.php';

use Twig\Environment;
use Twig\Loader\FilesystemLoader;

$loader = new FilesystemLoader(__DIR__ . '/templates');
$twig = new Environment($loader);

$users = [ 
    ['name' => 'John Doe', 'active' => false],
    ['name' => 'Lucy Smith', 'active' => false],
    ['name' => 'Peter Holcombe', 'active' => false],
    ['name' => 'Barry Collins', 'active' => false]
];

echo $twig->render('activeusers.html.twig', ['users' => $users]);

我们将一个用户数组发送到模板文件。

templates/activeusers.html.twig
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>

<p>Active users</p>

<ul>
    {% for user in users if user.active %}
        <li>{{ user.name }}</li>
    {% else %}
        <li>No users found</li>
    {% endfor %}
</ul>

</body>

</html>

如果 user.active 属性为 true,我们输出用户的姓名。当没有活动用户时,会显示 else 标签的输出。

Twig set 标签

set 标签允许在模板内为变量设置一个值。

$words = ['sky', 'mountain', 'falcon', 'forest',
    'rock', 'blue', 'solid', 'book', 'tree'];
    
echo $twig->render('test.html.twig', ['words' => $words]);

我们有一系列单词。

{% set sorted = words | sort %}

<ul>
{% for word in sorted %}
    <li>{{ word }}</li>
{% endfor %}
</ul>

我们使用 sort 过滤器对数组进行排序,并将排序后的数组使用 set 分配给 sorted 变量。

Twig verbatim 标签

verbatim 将部分标记为不应解析的原始文本。

{% verbatim %}
    <ul>
    {% for word in words %}
        <li>{{ word }}</li>
    {% endfor %}
    </ul>
{% endverbatim %}

例如,如果我们有一个解释某些 Twig 标签的教程,我们将不需要解析演示文稿的一部分。

Twig format 过滤器

format 过滤器通过替换占位符来格式化给定的字符串。它类似于 sprintf 函数。

$name = "John Doe";
$age = 34;
        
echo $twig->render('formatfil.html.twig', ['name' => $name, 'age' => $age]);

我们将两个变量发送到模板。

{{ "%s is %d years old" | format(name, age) }}

我们使用 format 构建字符串。

Twig date 函数

date 函数将一个参数转换为日期,以允许日期比较。

$user = ['name' => 'John Doe', 'created_at' => '2011/11/10'];

echo $twig->render('datefun.html.twig', ['user' => $user]);

用户数组有一个 created_at 键。

{% if date(user.created_at) < date('-5years') %}
    <p>{{ user.name }} is a senior user</p>
{% endif %} 

在模板中,我们比较两个日期。

Twig 自动转义

Twig 自动转义某些字符,例如 < 或 >。

$twig = new Environment($loader, [
    'autoescape' => false
]);

可以使用 autoescape 选项关闭自动转义。

$data = "<script src='http::/example.com/nastyscript.js'></script>";

echo $twig->render('autoescape.html.twig', ['data' => $data]);

用户可能会向应用程序添加潜在的危险输入。包含未知 JS 文件可以通过自动转义来防止。

<p>
The data is {{ data }}
</p>

<p>
The data is {{ data | raw }}
</p>

如果启用了自动转义,我们可以使用 raw 过滤器显示原始输入。

<p>
The data is <script src=&#039;http::/example.com/nastyscript.js&#039;></script>
</p>

<p>
The data is <script src='http::/example.com/nastyscript.js'></script>
</p>

此部分输出显示了字符的转义方式。

Twig 测试

Twig 测试允许测试数据。测试使用 is 运算符应用。

$words = ['', null, 'rock', '   ', 'forest'];
echo $twig->render('tests.html.twig', ['words' => $words]);

我们有一个单词数组,其中包含空元素和空白元素。

<ul>
{% for word in words %}

    {% if word is null %}
    <p>null element</p>
    {% elseif word | trim is empty %}
    <p>Empty element</p>
    {% else %}
    <li>{{ word }}</li>
    {% endif %}

{% endfor %}
</ul>

为了处理空元素和空白元素,Twig 有 emptynull 测试。

Twig 继承

Twig 的模板继承是一个强大的功能,它可以消除重复并促进维护。

inheritance.php
<?php

require __DIR__ . '/vendor/autoload.php';

use Twig\Environment;
use Twig\Loader\FilesystemLoader;

$loader = new FilesystemLoader(__DIR__ . '/templates');
$twig = new Environment($loader);

echo $twig->render('derived.html.twig');

这是 inheritance.php 文件。它呈现 derived.html.twig,该文件从 base.html.twig 扩展而来。

templates/base.html.twig
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}{% endblock %}</title>
</head>

<body>

{% block body %}{% endblock %}

</body>

</html>

基本布局定义了两个将被子项替换的块:titlebody

templates/derived.html.twig
{% extends 'base.html.twig' %}

{% block title %}Some title{% endblock %}

{% block body %}

The body contents

{% endblock %}

派生子模板使用 extends 关键字从基本模板继承。这两个块定义了自定义文本。

$ php inheritance.php
<!DOCTYPE html><html lang="en">
<head>
    <meta charset="UTF-8">    
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Some title</title>
</head>

<body>


The body contents


</body>

</html>

Symfony 示例

Twig 是 Symfony 框架不可或缺的一部分。下一个例子展示了在 Symfony 骨架应用程序中使用 Twig 的步骤。

$ composer create-project symfony/skeleton simple
$ cd simple

我们创建一个新的 Symfony 骨架应用程序并移动到项目目录。

$ composer require server --dev

我们包含开发服务器。

$ composer require maker annotations twig

我们包含一些基本的 Symfony 组件,包括 Twig。

$ php bin/console make:controller HomeController

我们创建一个 home 控制器。

src/Controller/HomeController.php
<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;

class HomeController extends AbstractController
{
    /**
     * @Route("/home", name="home")
     */
    public function index()
    {
        $words = ['sky', 'blue', 'cloud', 'symfony', 'forest'];

        return $this->render('home/index.html.twig', [
            'words' => $words
        ]);
    }
}

在 home 控制器中,我们呈现 index.html.twig 模板,并将 $words 数组传递给它进行处理。

templates/base.html.twig
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>{% block title %}Welcome!{% endblock %}</title>
        {% block stylesheets %}{% endblock %}
    </head>
    <body>
        {% block body %}{% endblock %}
        {% block javascripts %}{% endblock %}
    </body>
</html>

这是基本布局页面。

templates/home/index.html.twig
{% extends 'base.html.twig' %}

{% block title %}Home page{% endblock %}

{% block body %}

<h2>List of words</h2>

<ul>
{% for word in words  %}
    <li>{{ word }}</li>
{% endfor %}
</ul>

{% endblock %}

这是主页模板。它使用 for 标签遍历单词,并将它们输出到无序列表中。

$ php bin/console server:run

我们启动服务器。

我们导航到 https://:8000/home 以查看结果。

来源

Twig 模板引擎

在本文中,我们使用 Twig 从模板和数据生成文档。我们介绍了 Twig 标签、过滤器、测试和继承。我们在 Symfony 应用程序中展示了 Twig。

作者

我叫 Jan Bodnar,是一位充满激情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。到目前为止,我撰写了 1,400 多篇文章和 8 本电子书。我拥有超过十年的编程教学经验。

列出所有 PHP 教程。