{$name} 本质区别在执行时机和安全边界:前者由模板引擎解析并自动转义,后者跳过防护直接输出;{:function()} 在编译期执行, 在运行期执行;禁用全局转义极危险,应改用 {:$var|raw} 单点控制。

模板里写 {$name} 和 <?php echo $name; ?> 有啥区别
本质区别不在“能不能输出”,而在“执行时机”和“安全边界”。{$name} 是 ThinkPHP 模板引擎解析后转成 PHP 代码再执行的,中间会自动调用 htmlspecialchars() 做转义;而原生 <?php echo $name; ?> 完全跳过这层防护,直接输出原始内容。
常见错误现象:页面显示 <script>alert(1)</script> 被执行了,或者用户提交的 " 变成乱码 —— 很可能混用了两种写法,又没手动转义。
- 使用场景:展示用户输入内容(如评论、昵称)优先用
{$name};需要输出已信任的 HTML 片段(如后台富文本)才考虑<?php echo $name; ?>,且必须确保来源可控 - 参数差异:
{$name|default='-'|md5}这种管道语法只在模板标签里生效,原生 PHP 写不了 - 性能影响:模板标签有轻微解析开销,但对普通项目可忽略;滥用
<?php ?>块反而容易破坏模板结构,增加维护成本
{:function()} 和 <?php function(); ?> 执行顺序不一致
ThinkPHP 模板中 {:function()} 是“模板编译期”执行的,也就是在生成缓存文件时就运行了一次;而 <?php function(); ?> 是“运行期”执行,每次请求都跑。这点极易被忽略,尤其涉及数据库查询或时间函数时。
常见错误现象:用 {:date('Y-m-d')} 在模板里输出日期,结果所有缓存页都显示同一天;或者 {:Db::name('user')->count()} 导致缓存失效、查询重复执行。
立即学习“PHP免费学习笔记(深入)”;
- 使用场景:
{:...}适合无副作用、纯计算类操作(如{:config('app.debug')});带 IO 或状态变更的逻辑一律改用控制器赋值或<?php ?>块(并确认是否真需要每次执行) - 兼容性影响:开启模板缓存后,
{:...}的结果会被固化进缓存文件,重启服务或修改配置都不刷新 - 替代建议:把动态数据提前在控制器里查好,用
assign()传入,模板里只做{$count}这类纯展示
变量自动转义开关 default_return_type 怎么关才安全
默认开启的自动转义是防 XSS 的第一道防线,但有人为输出 HTML 把 default_return_type 改成 raw,结果整个模板都失去保护 —— 这是最危险的操作。
常见错误现象:改了全局配置后,连 {$title} 都开始执行 JS;或者只对某字段关转义,却忘了其他字段仍需手动处理。
- 正确做法:不要动全局配置,改用
{:$content|raw}对单个变量解除转义,且仅限明确信任的内容 - 更稳妥方式:用
{:htmlspecialchars_decode($content)}替代|raw,至少保留部分过滤能力 - 注意点:
|raw后面不能再接其他过滤器(如|default),否则无效;模板引擎会直接跳过后续处理
混合写法导致的解析失败:比如 {if $a}{<?php echo 'x'; ?>}{/if}
ThinkPHP 模板引擎不识别原生 PHP 标签嵌套在模板标签内部,{if} 和 {/if} 之间只能写模板语法,不能插 <?php ?>。一旦混用,轻则报错 Parse error: syntax error,重则生成非法 PHP 缓存文件。
常见错误现象:模板编译时报 unexpected ';或者页面空白,日志里出现 <code>PHP Parse error in Runtime/...。
- 根本原因:模板引擎先把
{if}...{/if}编译成 PHP 的if (...) {...},再把整块当字符串写入缓存文件;中间夹杂的<?php ?>会被当成普通文本输出,最终 PHP 解析时语法爆炸 - 解决方案:逻辑判断全部用模板标签(
{if}{elseif}{else}{/if});复杂逻辑提到控制器,模板只做分支渲染 - 调试技巧:临时关闭模板缓存(
'template' => ['cache' => false]),能更快看到真实报错位置
最麻烦的不是语法不会写,而是以为两种写法可以随意切换 —— 实际上它们活在不同生命周期里,强行拉到一起,崩得悄无声息。



















