strip_tags() 的本质是提取标签间可见文本,而非删除标签;它跳过所有标签(含 script/style/注释),保留其中字符,不解析DOM、不校验属性、不保留排版空白。

它不会保留标签内容——但会保留标签之间的文本,这是设计行为,不是 bug
strip_tags() 的本质是“提取可见文本”,不是“删除标签”
很多人误以为 strip_tags() 是在做“字符串替换”,其实它底层模拟的是浏览器渲染前的文本提取逻辑:跳过所有标签(包括 <script>、<style>、注释),只把标签之间的纯字符序列拼起来。
所以这个输入:
<div>hello</div><script>alert(1)</script>world
输出是 helloworld,不是空或报错——alert(1) 被当作了 <script> 标签的“内容”,而 strip_tags() 默认会保留标签内容(除非你用白名单且该标签被允许)。
立即学习“PHP免费学习笔记(深入)”;
- 它不解析 DOM 结构,不关心
<script>是否合法,只按“<开头、>结尾”粗略匹配并跳过整段 - 遇到破损标签如
<div>abc<span>def(缺闭合),它可能从第一个<一路吃到末尾或下一个>,导致abc<span>def全被吞掉 -
<!-- comment -->和<?php ?>同样被无条件剥离,且无法通过$allowed_tags恢复
为什么 <script>xxx</script> 的 xxx 还在?
因为 strip_tags() 的规则是:“移除标签本身,保留标签包裹的文本”。它把 <script> 当作一个“不可见容器”,就像对待 <p> 一样——除非你显式禁止 <script>,否则它的起始和结束标签被删,中间文本留下。
这正是它和 wp_strip_all_tags() 的关键区别:
-
strip_tags('<script>xxx</script>')→xxx -
wp_strip_all_tags('<script>xxx</script>')→''(空字符串)
后者先用正则干掉整个 <script>...</script> 块(含内容),再调 strip_tags()。如果你需要真正“清空脚本/样式内容”,别绕弯,直接用 wp_strip_all_tags() 或自己加一层 preg_replace()。
白名单参数 $allowed_tags 不是你想的那样安全
传 ['<p>', '<strong>'] 并不等于“只留这些标签,其他全杀”,而是“这些标签不删,但它们的属性照单全收”:
<p onclick="javascript:alert(1)">click me</p>
经 strip_tags($html, ['p']) 后变成:
<p onclick="javascript:alert(1)">click me</p>
标签还在,属性毫发无损——这根本不是 XSS 防护,只是格式保留。官方文档明确警告:不能用于防御 XSS。
- 属性不校验、不过滤、不转义
- 自闭合标签如
<br>在白名单中无效(PHP 7.4+ 数组写法也不支持) - PHP 8.0+ 支持
null作为第二个参数,等价于不传,但别指望它比默认行为更严格
文本换行和空格消失,是因为它真没打算做排版
strip_tags() 的输出是“纯文本流”,不是“可读文案”。原始 HTML 中的 <p>A</p><p>B</p> 经处理后是 AB,中间没有空格、没有换行。
这不是缺陷,是定位决定的——它对标的是 fgetss()(从文件读取并过滤标签),场景是快速提取字段值,不是生成用户可见摘要。
- 若需保留段落分隔,先用
str_replace(['<p>', '</p>', '<br>'], ["\n", "\n", "\n"], $html)把语义换行转成真实换行符,再strip_tags() - 若需压缩空白,用
preg_replace('/\s+/', ' ', $text)+trim(),但注意\s包含\n和\t,会把换行也压成空格 - 别对结果用
nl2br()——那是在已有文本上加 HTML 换行,你已经没标签了
真正棘手的地方从来不是“怎么删标签”,而是“删完之后,文本是否还能被人眼识别、是否还满足下游系统(比如搜索索引、摘要生成)的空白敏感要求”。这点容易被忽略,直到上线后发现文章摘要变成一整块密文。



















