ThinkPHP 8 中{$name}未转义并非框架失效,而是因模板缓存未更新、default_filter被清空或使用{:}语法绕过默认过滤;应优先用{$name|raw}显式控制,确保内容安全前提下输出。

ThinkPHP 8 首页模板中 {$name} 输出未转义,不是“框架失效”,而是你正在用的变量输出方式或配置绕过了默认过滤——{$var} 本该自动转义,但若模板被缓存、default_filter 被清空、或变量来自 {:} 语法,就完全跳过 htmlspecialchars()。
确认是否真没转义,还是被其他逻辑覆盖
先在首页模板里加两行对比:
dump($name);<br>{$name}<br>{:$name}
如果 dump() 显示原始 HTML(如 <p>Hello</p>),而 {$name} 渲染出段落、{:$name} 也渲染出段落,说明:
• 模板编译缓存未更新(删掉 runtime/view/ 下对应文件)
• config/template.php 中 'default_filter' => [] 或 '',全局关闭了转义
• 变量赋值时用了 $this->assign('name', $html),但后续又在模板里写了 {:$name} —— 这个写法天生不转义
修复:优先用 |raw 显式控制,而非依赖默认行为
|raw 是 ThinkPHP 8 推荐的可控方案,它明确表达“此处我确认内容安全”,且不受 default_filter 配置干扰:
立即学习“PHP免费学习笔记(深入)”;
-
{$name|raw}→ 跳过转义,直接输出(适用于后台富文本字段) -
{$name}→ 默认走htmlspecialchars($name, ENT_QUOTES, 'UTF-8')(适用于普通文本) -
{$name|htmlspecialchars}→ 手动再套一层,可能造成双重编码(慎用)
关键点:
• |raw 不等于“不过滤”,它只是跳过 htmlspecialchars,不代表内容本身安全
• 若 $name 来自用户输入(如搜索框、评论),绝不能用 |raw,必须前端或业务层做 XSS 过滤(如 HTMLPurifier)
修复 Core bailout 状态损坏安全问题,以及 BZ2、DOM、PDO、Opcache/JIT 内存与执行错误,改进 Unicode 字素处理、URL 解析、TLS 流状态、信号分发及会话错误处理。
检查控制器中是否误用了 input() 的过滤参数
首页数据常来自 input() 或模型查询。若你在控制器里这样写:
$title = input('title', '', 'htmlspecialchars');
那 $title 已是实体化字符串(如 <p>),再进模板用 {$title} 就会二次转义成 <p></p>。正确做法是:
- 入库前不做
htmlspecialchars()(那是输出层的事) - 从数据库读出后,直接
$this->assign('content', $row['content']) - 模板中统一用
{$content},由框架负责转义
如果内容确需保留 HTML(如文章正文),则控制器中应确保它已通过白名单过滤(非简单 htmlspecialchars_decode),再用 {$content|raw} 输出。
注意 runtime 编译文件的残留影响
ThinkPHP 8 的模板转义逻辑写死在 runtime/view/ 下的编译 PHP 文件里,比如:
echo htmlspecialchars($name, ENT_QUOTES, 'UTF-8');
但如果你改过 config/template.php,又没清缓存,旧编译文件仍会执行老逻辑。务必:
- 删掉
runtime/view/全部内容 - 确认
config/template.php中default_filter是['htmlspecialchars'](TP8.0+ 默认值) - 避免在模板中混用
{$x}和{:$x}处理同一变量
最易被忽略的是:你以为关掉了某个中间件或配置,其实只是缓存没清——模板引擎不会主动重编译,除非检测到源文件修改时间变化。


















