ThinkPHP模板默认对{$var}自动htmlspecialchars转义,属安全设计;需原样渲染HTML时应使用{:$var}跳过转义,或谨慎清空default_filter配置,但须确保富文本等内容已通过HtmlPurifier等白名单过滤。

ThinkPHP模板输出默认会对变量做 htmlspecialchars() 转义,这不是 bug,是安全默认行为;但如果你要原样渲染 HTML(比如富文本内容),必须显式绕过它——否则你存的 <p>Hello</p> 会变成页面上可见的源码文本。
模板中用 {:$var} 跳过自动转义
这是最直接、最常用的做法。ThinkPHP 的模板引擎对变量输出做了分层处理:
-
{$data.content}→ 自动调用htmlspecialchars(),适合纯文本或用户输入字段 -
{:$data.content}→ 跳过所有转义,原样输出,适用于已过滤/可信的 HTML 内容(如后台富文本编辑器保存的内容)
注意:{: 冒号必须紧贴左花括号,中间不能有空格;且仅对当前变量生效,不影响其他变量。
全局关闭模板默认转义(不推荐,慎用)
ThinkPHP 6.1+ 允许通过配置禁用模板层的默认过滤,但会削弱 XSS 防护能力,只应在全站内容可控、且已做服务端 HTML 过滤的前提下考虑:
立即学习“PHP免费学习笔记(深入)”;
- 修改
config/template.php中的'default_filter' => [],清空默认过滤器数组 - 或者设为
'default_filter' => ''(空字符串),效果相同 - 改完后,
{$data.content}也不再转义,所有变量输出都需手动加htmlspecialchars()
风险点:一旦漏掉某个变量(比如用户昵称、评论内容),就可能引入 XSS 漏洞。不建议在面向用户的项目中全局关闭。
需要解码已错误转义的内容怎么办
如果历史数据已在入库前被误调用了 htmlspecialchars(),导致数据库里存的是 <p>Hello</p>,那模板里光用 {:$data.content} 不行,得先还原:
- 在控制器或模型中用
htmlspecialchars_decode($content, ENT_QUOTES)解码一次 - 或模板中写
{:htmlspecialchars_decode($data.content, ENT_QUOTES)} - 注意:仅用于修复场景,不是常规流程;解码后仍需确保内容本身安全(比如不含
<script>)
别在模板里嵌套多层 decode/encode,容易逻辑混乱;优先从数据写入源头规范流程。
为什么不能只靠数据库或模型层“关转义”
有人试图在模型的 save() 或数据库查询时“阻止转义”,这是方向错误:
- PDO 预处理本身不碰 HTML 字符,
Db::name('post')->update(['content' => $html])中的$html原样进 SQL 参数绑定 - 真正转义发生在模板渲染阶段,和数据库无关
- 混淆这点会导致你在模型里加各种
stripslashes()或html_entity_decode(),反而污染数据层逻辑
关键分界要清楚:入库前确保是原始 HTML(没被 htmlspecialchars() 过),出库后按需在模板层决定是否转义——这个决策点在视图,不在模型或数据库。



















