Skip to content

模板引擎概览

PHP 生态中有多个成熟的模板引擎,如 Smarty、Twig(Symfony)、Blade(Laravel)等。它们在安全自动转义、模板继承、缓存编译等方面提供了比原生 PHP 模板更强的能力。本节对主流 PHP 模板引擎进行概览比较,帮助开发者根据项目需求选择合适的方案。

前置知识

阅读本节前,建议先了解:PHP 原生模板

基础概念

为什么需要模板引擎

需求原生 PHP模板引擎
自动转义需手动调用 htmlspecialchars默认自动转义
模板继承需手动实现内置支持
沙箱安全完全可执行 PHP限制可用的函数和语法
设计师友好需要懂 PHP简化的语法
编译缓存自动编译和缓存
可扩展性手动实现内置标签/过滤器/函数扩展

选择建议

如果你的团队中设计师参与前端开发,或者项目需要模板沙箱安全,使用模板引擎是更好的选择。如果团队全是 PHP 开发者,原生 PHP 模板配合规范的编码约定也能满足需求。

Twig(Symfony)

简介

Twig 是 Symfony 框架的默认模板引擎,也是 PHP 生态中最成熟的独立模板引擎之一。它提供了自动转义、模板继承、宏定义、砂箱模式等丰富功能。

安装

bash
composer require twig/twig

基本用法

php
<?php

declare(strict_types=1);

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

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

$loader = new FilesystemLoader(__DIR__ . '/templates');
$twig = new Environment($loader, [
    'cache' => __DIR__ . '/cache',     // 生产环境开启缓存
    'debug' => false,                   // 生产环境关闭调试
    'auto_reload' => true,              // 开发环境自动重载
    'strict_variables' => true,         // 访问未定义变量时抛出异常
]);

// === 渲染模板 ===
echo $twig->render('index.html.twig', [
    'title' => '首页',
    'users' => [
        ['id' => 1, 'name' => 'Alice'],
        ['id' => 2, 'name' => 'Bob'],
    ],
]);

Twig 模板语法

twig
{# templates/index.html.twig #}

{# 继承布局 #}
{% extends 'layouts/base.html.twig' %}

{# 覆盖区块 #}
{% block title %}{{ title }}{% endblock %}

{% block content %}
    <h1>{{ title }}</h1>

    {# 自动转义(默认) #}
    <p>{{ user.name }}</p>

    {# 原始输出(已验证的 HTML) #}
    <div>{{ richContent|raw }}</div>

    {# 属性值自动转义 #}
    <a href="{{ url }}" title="{{ description }}">Link</a>

    {# 条件判断 #}
    {% if users|length > 0 %}
        <ul>
        {% for user in users %}
            <li>{{ user.name }} (ID: {{ user.id }})</li>
        {% endfor %}
        </ul>
    {% else %}
        <p>暂无用户</p>
    {% endif %}

    {# 过滤器 #}
    <p>{{ content|nl2br }}</p>
    <p>{{ price|number_format(2) }}</p>
    <p>{{ date|date('Y-m-d') }}</p>
    <p>{{ text|truncate(100) }}</p>
    <p>{{ name|upper }}</p>
    <p>{{ email|lower }}</p>

    {# 循环变量 #}
    {% for user in users %}
        {{ loop.index }}    {# 当前索引(从1开始) #}
        {{ loop.index0 }}   {# 当前索引(从0开始) #}
        {{ loop.first }}    {# 是否第一个 #}
        {{ loop.last }}     {# 是否最后一个 #}
        {{ loop.length }}   {# 总数 #}
    {% endfor %}

    {# 宏定义(可复用的模板片段) #}
    {% macro userCard(user) %}
        <div class="user-card">
            <h3>{{ user.name }}</h3>
            <p>{{ user.email }}</p>
        </div>
    {% endmacro %}

    {# 使用宏 #}
    {{ _self.userCard(alice) }}

    {# 或从模板导入 #}
    {% import 'macros/user.html.twig' as macros %}
    {{ macros.userCard(alice) }}

    {# 设置变量 #}
    {% set isActive = true %}
    {% set greeting = 'Hello, ' ~ name %}
{% endblock %}

Twig 布局

twig
{# templates/layouts/base.html.twig #}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <title>{% block title %}My App{% endblock %}</title>
    <link rel="stylesheet" href="/assets/css/style.css">
    {% block styles %}{% endblock %}
</head>
<body>
    <header>
        {% block header %}{% endblock %}
    </header>

    <main>
        {% block content %}{% endblock %}
    </main>

    <footer>
        {% block footer %}{% endblock %}
    </footer>

    {% block scripts %}{% endblock %}
</body>
</html>

Twig 安全特性

php
<?php

declare(strict_types=1);

// 自动转义策略
$twig = new Environment($loader, [
    // autoescape: false -- 关闭自动转义(不推荐)
    // autoescape: 'html' -- HTML 转义(默认)
    // autoescape: 'js' -- JavaScript 转义
    // autoescape: 'css' -- CSS 转义
    // autoescape: 'url_param' -- URL 参数转义
    // autoescape: 'html_attr' -- HTML 属性转义
    'autoescape' => 'html',
]);

// 砂箱模式(限制模板中可用的函数和标签)
$sandbox = new \Twig\Extension\SandboxExtension();
$sandboxPolicy = new \Twig\Sandbox\SecurityPolicy(
    allowedTags: ['if', 'for', 'block', 'extends'],
    allowedFilters: ['escape', 'upper', 'lower', 'length', 'date'],
    allowedFunctions: ['range', 'max', 'min'],
    allowedProperties: [],
    allowedMethods: [],
);
$twig->addExtension(new \Twig\Extension\SandboxExtension($sandboxPolicy, true));

Blade(Laravel)

简介

Blade 是 Laravel 框架内置的模板引擎,语法简洁,与 Laravel 生态深度集成。它编译模板为 PHP 缓存文件,性能接近原生 PHP。

基本语法

blade
{{-- resources/views/users/index.blade.php --}}

@extends('layouts.app')

@section('title', '用户列表')

@section('content')
    <h1>用户列表</h1>

    {{-- 自动转义 --}}
    <p>{{ $user->name }}</p>

    {{-- 原始输出 --}}
    <div>{!! $htmlContent !!}</div>

    {{-- 条件 --}}
    @if($users->count() > 0)
        @foreach($users as $user)
            <div class="user">
                <h2>{{ $user->name }}</h2>
                <p>{{ $user->email }}</p>
            </div>
        @endforeach
    @else
        <p>暂无用户</p>
    @endif

    {{-- 循环 --}}
    @foreach($items as $item)
        @if($loop->first) <p>第一个</p> @endif
        <p>{{ $loop->iteration }} / {{ $loop->count }}</p>
        @if($loop->last) <p>最后一个</p> @endif
    @endforeach

    {{-- 布局区块 --}}
    @section('sidebar')
        @parent
        <p>额外的侧边栏内容</p>
    @endsection
@endsection

Smarty

简介

Smarty 是最经典的 PHP 模板引擎,以 {} 定界符为特征。虽然设计较老,但在维护旧项目时仍可能遇到。

基本用法

php
<?php

declare(strict_types=1);

require_once 'smarty/Smarty.class.php';

$smarty = new Smarty();
$smarty->setTemplateDir(__DIR__ . '/templates');
$smarty->setCompileDir(__DIR__ . '/templates_c');
$smarty->setCacheDir(__DIR__ . '/cache');
$smarty->setConfigDir(__DIR__ . '/configs');

$smarty->assign('title', '首页');
$smarty->assign('users', [
    ['name' => 'Alice', 'email' => 'alice@example.com'],
    ['name' => 'Bob', 'email' => 'bob@example.com'],
]);

$smarty->display('index.tpl');

Smarty 模板语法

smarty
{* templates/index.tpl *}
{include file="header.tpl" title=$title}

<h1>{$title}</h1>

{*$name 自动转义*}
<p>{$user.name|escape}</p>

{*$htmlContent 不转义*}
<div>{$htmlContent nofilter}</div>

{foreach from=$users item=user}
    <div class="user">
        <h2>{$user.name}</h2>
        <p>{$user.email}</p>
    </div>
{foreachelse}
    <p>暂无用户</p>
{/foreach}

{if $count > 0}
    <p>共 {$count} 条记录</p>
{else}
    <p>暂无记录</p>
{/if}

{include file="footer.tpl"}

模板引擎对比

特性原生 PHPTwigBladeSmarty
自动转义手动默认默认手动
模板继承手动内置内置内置
编译缓存内置内置内置
沙箱模式内置内置
独立性原生独立Laravel 专属独立
性能最快中等
语法PHP 语法 {% %} @{$} {* *}
宏/组件
社区活跃度--
学习曲线

注意事项

1. 模板引擎不是安全银弹

php
<?php

// 即使使用 Twig 的自动转义
// 仍需注意:
// 1. 使用 |raw 时必须确保数据安全
// 2. JSON 数据传递到 JS 需要额外编码
// 3. URL 参数中的特殊字符需要正确处理
// 4. 模板中不能替代服务端验证

2. 编译缓存的安全性

php
<?php

// 确保编译缓存目录不在 Web 可访问的路径下
// Twig 缓存: /var/cache/twig/(不在 document_root 下)
// Blade 缓存: storage/framework/views/(Laravel 默认)

// Smarty 编译目录同样需要保护
$smarty->setCompileDir('/var/cache/smarty/templates_c');

最佳实践

1. 选择建议

- Laravel 项目 -> Blade(无缝集成)
- Symfony 项目 -> Twig(默认集成)
- 非 Laravel/Symfony 的现代项目 -> Twig(功能丰富)
- 简单项目或微服务 -> 原生 PHP 模板(零依赖)
- 维护旧项目 -> 保持现有模板引擎

下一节

继续学习:模板最佳实践

参考链接