模板引擎概览
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
@endsectionSmarty
简介
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"}模板引擎对比
| 特性 | 原生 PHP | Twig | Blade | Smarty |
|---|---|---|---|---|
| 自动转义 | 手动 | 默认 | 默认 | 手动 |
| 模板继承 | 手动 | 内置 | 内置 | 内置 |
| 编译缓存 | 无 | 内置 | 内置 | 内置 |
| 沙箱模式 | 无 | 内置 | 无 | 内置 |
| 独立性 | 原生 | 独立 | 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 模板(零依赖)
- 维护旧项目 -> 保持现有模板引擎下一节
继续学习:模板最佳实践