必须先剥离HTML标签再截取,否则会导致标签断裂、乱码等问题;推荐在保存时生成摘要存入独立字段,而非每次读取都处理。

富文本字段直接截取会断开 HTML 标签、产生乱码,不能用 substr 硬切,必须先剥离标签再按字符截取,且摘要应尽量在保存时生成而非每次读取都处理。
为什么 getAttr 里直接 mb_substr 富文本会出问题
常见错误是这样写:
public function getContentAttr($value)
{
return mb_substr($value, 0, 120, 'UTF-8');
}
这会导致:
- 没判空 ——
$value为null或''时,mb_substr(null, ...)返回false,前端显示 “0” 或空白 - 没去 HTML —— 截到
<p><strong>...中间,闭合标签丢失,页面结构错乱 - 没指定编码 —— 中文环境若漏写
'UTF-8',mb_substr可能按字节截,导致乱码或异常终止 - 重复解码风险 —— 若内容入库前已用
htmlspecialchars转义,再调用html_entity_decode就可能暴露 XSS
strip_tags + mb_substr 是安全底线
必须按固定顺序执行:判空 → 去标签 → 截取。不推荐在获取器里做复杂解析(如提取首张图、识别 H1 标题),那属于业务逻辑,应前置到保存阶段。
立即学习“PHP免费学习笔记(深入)”;
实操建议:
- 先检查
is_string($value) && $value !== '',避免类型错误 - 用
strip_tags($value)剥离所有 HTML,不要加白名单参数(如strip_tags($value, '<p><br>')),否则仍可能残留样式或 script - 再用
mb_substr($cleaned, 0, 120, 'UTF-8'),长度 120 是经验起点,需结合前端展示宽度微调 - 结尾加省略号:拼接时用
mb_strlen($cleaned, 'UTF-8') > 120 ? '...' : '',别硬写进mb_substr
更优方案:保存时生成摘要存独立字段
每次读取都走 strip_tags + mb_substr,性能差、不可缓存、无法索引。真正稳定的方案是写入时就生成摘要,存在 summary 字段。
做法:
- 在模型的
setAttr方法中监听content字段,比如setContentAttr($value) - 内部调用同套清洗逻辑:
strip_tags→mb_substr→ 存入$this->summary = ... - 数据库加
summary字段(TEXT类型),并在getSummaryAttr中只做简单返回,不重复处理 - 查询时用
field('id,title,summary')显式指定,避免加载冗余的富文本大字段
append 动态加摘要字段为什么常返回 null
定义了 append = ['summary'],但 toArray() 里没有该键,或值为 null,原因通常是:
-
getSummaryAttr方法不存在,或拼写错误(如写成getsummaryAttr或getSummaryattr) - 方法体内用了未初始化变量,比如
return $this->xxx;,而$this->xxx从未赋值 - 方法里误调了关联查询,如
$this->user()->find(),触发新 SQL 且可能因数据缺失返回null -
summary同时是数据库真实字段名 —— ThinkPHP 会优先取库中值,忽略访问器;此时应改用其他名称如preview
调试时可临时加 dump($value); die; 到 getSummaryAttr 开头,确认是否被调用及输入值是否符合预期。富文本摘要真正的难点不在截取逻辑本身,而在清洗时机和字段生命周期管理——读时处理是权宜之计,写时固化才是生产级做法。



















