ThinkPHP 的模板引擎(默认是 Think\Template)设计初衷是为了分离 PHP 逻辑和 HTML 视图,让前端开发者能够更容易地修改页面,而无需接触复杂的 PHP 代码,它鼓励使用模板标签来代替原生 PHP 代码。

ThinkPHP 也提供了在模板中直接嵌入 PHP 代码的能力,以应对一些复杂或特殊的逻辑需求。
下面我将从推荐做法到不推荐做法,再到核心原理,为你详细解析。
推荐做法:使用模板标签(最佳实践)
这是 ThinkPHP 模板设计的核心,也是你应该优先使用的方式,模板标签语法简单,可读性强,并且经过了模板引擎的安全处理。
输出变量
这是最常用的功能。

// 在 PHP 控制器中分配变量
$this->assign('name', 'ThinkPHP');
$this->assign('user', ['id' => 1, 'username' => 'zhangsan']);
在模板文件 index.html 中:
<!DOCTYPE html>
<html>
<head>ThinkPHP 模板示例</title>
</head>
<body>
<h1>Hello, {$name}!</h1>
<p>用户ID: {$user.id}</p>
<p>用户名: {$user.username}</p>
<!-- 使用默认值 -->
<p>年龄: {$user.age|default:18}</p>
</body>
</html>
{$name}: 输出普通变量。{$user.id}: 输出关联数组(或对象)的元素。{$user.age|default:18}:user.age不存在或为空,则输出默认值18。
使用函数
可以对变量进行简单的函数处理。
// 控制器
$this->assign('content', 'hello world');
<p>原文: {$content}</p>
<p>转为大写: {:strtoupper($content)}</p>
<p>首字母大写: {:ucwords($content)}</p>
{:function($var)}: 表示执行函数并输出结果。
条件判断 if / elseif / else
// 控制器
$this->assign('status', 1); // 1: 已发布, 0: 草稿
$this->assign('score', 85);
{if $status eq 1}
<p style="color: green;">文章已发布</p>
{elseif $status eq 0}
<p style="color: orange;">文章为草稿</p>
{else /}
<p>状态未知</p>
{/if}
<br>
{if $score >= 90}
<p>优秀!</p>
{else /}
<p>继续努力!</p>
{/if}
eq: 等于neq: 不等于gt: 大于lt: 小于egt: 大于等于elt: 小于等于mod: 取模
循环输出 volist / for
volist 用于遍历数组:
// 控制器
$this->assign('list', [
['id' => 1, 'name' => '苹果'],
['id' => 2, 'name' => '香蕉'],
['id' => 3, 'name' => '橙子'],
]);
<ul>
{volist name="list" id="vo" key="k"}
<li>
索引: {$k} - ID: {$vo.id} - 名称: {$vo.name}
</li>
{/volist}
</ul>
name: 要遍历的变量名。id: 循环中每个元素的临时变量名。key: 循环的索引(键名),可选。
for 用于数字循环:

{for start="1" end="10" step="2" name="i"}
<p>当前数字: {$i}</p>
{/for}
模板包含 include
用于引入其他模板文件,实现模块化。
假设有一个公共头部模板 public/header.html:
<!-- public/header.html -->
<header>
<h1>网站头部</h1>
</header>
在主模板 index.html 中:
{include file="public/header" /}
<main>
<!-- 页面主要内容 -->
</main>
{include file="public/footer" /}
不推荐做法:直接使用 PHP 代码
虽然 ThinkPHP 允许在模板中写原生 PHP,但这会破坏模板的初衷,导致视图层逻辑混乱,不利于维护,请谨慎使用。
输出 PHP 代码
这个标签在上面已经提过,用于执行单行 PHP 代码并输出结果。
// 控制器
$this->assign('time', time());
<p>当前时间戳: {:time()}</p>
<p>格式化时间: {:date('Y-m-d H:i:s', $time)}</p>
执行 PHP 代码 {php}
用于执行一段 PHP 代码,但不直接输出结果。
{php}
$a = 10;
$b = 20;
$sum = $a + $b;
{/php}
<p>计算结果: {$sum}</p>
<!-- 注意:$sum 变量需要在 {php} 块外部定义才能被模板识别 -->
更规范的做法(不推荐但可行):
{php}
$a = 10;
$b = 20;
$sum = $a + $b;
// 将结果赋值给一个模板变量
$this->assign('sum', $sum);
{/php}
<p>计算结果: {$sum}</p>
使用原生 PHP 标签 <?php ?>
ThinkPHP 默认是开启原生 PHP 标签支持的,但强烈建议关闭,因为它完全失去了模板引擎的意义。
如果你确实需要,并且配置文件 config/template.php 中的 'php_support' => true,那么你可以这样写:
<?php
$userList = [ ... ]; // 复杂的数组构建逻辑
foreach ($userList as $user) {
// ... 循环处理
}
?>
<div>
<?php foreach ($userList as $u): ?>
<p><?php echo $u['name']; ?></p>
<?php endforeach; ?>
</div>
为什么强烈不推荐?
- 逻辑耦合:视图层包含了大量业务逻辑,违反了 MVC 分离原则。
- 可读性差:对于前端开发者不友好。
- 安全性:如果模板文件可以被用户上传,原生 PHP 标签会带来巨大的安全风险(如代码注入)。
核心原理:模板如何解析 PHP 代码?
当你使用模板标签(如 {$name})时,ThinkPHP 的模板引擎在后台做了以下事情:
- 编译:当模板文件第一次被请求时,模板引擎会读取
index.html文件。 - 替换:引擎会将模板标签替换成原生的 PHP 代码。
{$name}会被编译成<?php echo $name; ?>{if $status eq 1}会被编译成<?php if ($status == 1): ?>{volist name="list" id="vo"}会被编译成<?php foreach($list as $vo): ?>
- 生成缓存:编译后的 PHP 代码会被保存到一个缓存文件(
Runtime/cache/编译后文件名.php)中。 - 执行:后续请求该模板时,如果模板源文件没有修改,ThinkPHP 会直接执行这个缓存文件,从而大大提高了运行效率。
当你使用 {php} 或 <?php ?> 标签时,模板引擎会跳过编译和替换步骤,直接将你写的 PHP 代码原样复制到最终的缓存文件中执行。
总结与最佳实践
| 方法 | 语法 | 推荐度 | 说明 |
|---|---|---|---|
| 变量输出 | {$var} |
⭐⭐⭐⭐⭐ | 首选,简洁安全 |
| 函数调用 | {:function($var)} |
⭐⭐⭐⭐⭐ | 首选,用于简单逻辑处理 |
| 条件判断 | {if}...{elseif}...{else}{/if} |
⭐⭐⭐⭐⭐ | 首选,逻辑清晰 |
| 循环输出 | {volist}, {for} |
⭐⭐⭐⭐⭐ | 首选,专门为遍历设计 |
| 模板包含 | {include file="..."} |
⭐⭐⭐⭐⭐ | 首选,代码复用 |
| PHP 代码块 | {php}...{/php} |
⭐ | 谨慎使用,用于无法用标签实现的复杂逻辑 |
| 原生 PHP | <?php ... ?> |
☆ | 强烈不推荐,破坏 MVC 结构,带来安全隐患 |
核心思想:将尽可能多的逻辑放在控制器或模型中处理,只将最简单的数据展示和循环逻辑放在模板中,如果发现模板中的逻辑越来越复杂,就应该反思是否将过多的业务逻辑放到了视图层。
