PHP htmlentities()函数用法讲解

发布时间:2026/10/9 0:02:38
PHP htmlentities()函数用法讲解 前言htmlentities()和htmlspecialchars()长得几乎一样同样的四个参数、同样的标志位常量、同样的返回类型。手册对它的定义也很干脆——「这个函数在所有方面都与htmlspecialchars()相同区别在于htmlentities()会把所有具有 HTML 字符实体对应物的字符都转换成实体」。正因为只有这一个区别很多教程把它描述成「htmlspecialchars()的增强版」「更安全的那个」。这个说法不准确而且会误导人做出错误的技术选择。事实是在防 XSS 这件事上两者是等价的。真正危险的字符是、、、、这五个htmlspecialchars()在设了对应标志位后全都会转换htmlentities()一个也不少。多转换的那些字符比如带重音字母本身并没有 HTML 语法含义。htmlentities()真正多做的事是把「本来可以原样输出」的字符也换成实体。Café会变成Cafeacute;这在某些场景是必要的在多数场景只是让输出变长、可读性变差。两者用的是同一份转换表。手册明确写着get_html_translation_table()返回的就是这两个函数内部使用的表可以用它亲眼确认某个字符在给定文档类型下会不会被转换。本文把「什么情况下用哪个」这个决策讲清楚并给出查看转换表、反向解码、生成数字实体的具体写法。一、签名与唯一的区别官方手册给出的签名是htmlentities(string $string,int $flags ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401,?string $encoding null,bool $double_encode true): string参数含义与htmlspecialchars()逐项对应参数说明与 htmlspecialchars 的差异$string待处理的字符串无$flags引号策略、非法序列策略、文档类型无同一批常量$encoding字符编码省略时取default_charset无$double_encode是否对已有实体再次编码无默认标志的版本差异同样存在ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401是PHP 8.1.0 起的默认值在那之前默认是ENT_COMPAT单引号不会被转换遇到非法编码序列还会返回空字符串。所以和htmlspecialchars()一样建议把标志显式写全不要依赖默认值。两者放在一起对比最直观?php // 适用于 PHP 8.0$s Café 版权 © 2026中文保持原样;echo htmlentities : , htmlentities($s, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, UTF-8), PHP_EOL;echo htmlspecialchars: , htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, UTF-8), PHP_EOL;输出对比htmlentities : Cafeacute; amp; 版权 copy; 2026中文保持原样htmlspecialchars: Café amp; 版权 © 2026中文保持原样可以看到htmlentities()把é变成eacute;、©变成copy;而htmlspecialchars()只动了。两者对的处理完全一致——这一点很关键它说明两者对 XSS 的防护能力没有差别。二、哪些字符会被转换取决于文档类型和编码「所有具有 HTML 字符实体对应物的字符」这句话里的「对应物」来自一张按文档类型组织的命名实体表。所以能不能被转换要看两件事字符本身有没有命名实体以及你选的文档类型里有没有收录它。文档类型标志命名实体规模典型效果ENT_HTML401HTML 4.01 实体集拉丁字母带重音符号如é、常用符号如©会被转换ENT_XML1/ENT_XHTMLXML 预定义实体 少量转换范围比 HTML 4.01 窄很多ENT_HTML5HTML5 命名引用表条目远多于 HTML 4.01能被转换的符号字符更多还有一点要特别说明汉字CJK 统一表意文字在 HTML 的命名实体表里没有对应项所以无论选哪个文档类型、用哪种编码中文都会被原样保留。上例里的「版权」「中文保持原样」都不变这不是漏掉了而是本来就没有某汉字;这种写法可用。另外要分清「命名实体」和「数字实体」这两件事htmlentities()只产出命名实体形如eacute;它不会产出数字实体形如#233;或#xE9;。需要把字符一律转成数字实体时要用mb_encode_numericentity()手册在htmlspecialchars()页面里也是这样指引的。它的签名是mb_encode_numericentity(string $string, array $map, ?string $encoding null, bool $hex false): string第二个参数指定要转换的码点区间。?php // 适用于 PHP 7.0需要 mbstring 扩展$s Café — 测试;// 0x80 到 0x10FFFF 全部转成十进制数字实体$map [0x80, 0x10FFFF, 0, 0x10FFFF];echo mb_encode_numericentity($s, $map, UTF-8), PHP_EOL;用 get_html_translation_table() 亲眼看转换表与其猜某个字符会不会被转换不如把表打印出来。get_html_translation_table()的签名是get_html_translation_table(int $table HTML_SPECIALCHARS,int $flags ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401,string $encoding UTF-8): array第一个参数取HTML_SPECIALCHARS时返回htmlspecialchars()用的表取HTML_ENTITIES时返回htmlentities()用的表。?php // 适用于 PHP 8.0$special get_html_translation_table(HTML_SPECIALCHARS, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, UTF-8);$entities get_html_translation_table(HTML_ENTITIES, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, UTF-8);printf(HTML_SPECIALCHARS 条目数: %d%s, count($special), PHP_EOL);printf(HTML_ENTITIES 条目数: %d%s, count($entities), PHP_EOL);// 看几个具体字符foreach ([, , , , , é, ©, 中] as $c) {printf(%-4s - special: %-10s entities: %-10s%s,$c,$special[$c] ?? (无),$entities[$c] ?? (无),PHP_EOL);}注意返回数组的键是被转换的原字符值是替换后的实体字符串。这个脚本跑一遍「哪些字符会被转换」这个问题就再也不用靠记忆了。三、什么时候用哪个结论很明确绝大多数场景用htmlspecialchars()。场景选择原因把用户输入输出到 HTML 页面htmlspecialchars()五个危险字符都处理了输出保持可读输出到 HTML 属性htmlspecialchars()带ENT_QUOTES即可页面本身声明为 UTF-8内容含中文 / emojihtmlspecialchars()汉字与大多数非拉丁字符本来就没有命名实体转不转都一样输出的页面是纯 ASCII 环境、终端/邮件设备不支持 UTF-8htmlentities()把带重音字符降级成实体能提高兼容性需要严格「全 ASCII」的 HTML 输出htmlentities()配合mb_encode_numericentity()后者能覆盖前者转不了的字符输出到 URL、JavaScript、SQL都不用分别用rawurlencode()/json_encode()/ 参数化查询选htmlentities()有一个可以量化的代价把字符换成实体必然让字符串变长。一个é在 UTF-8 里是 2 字节写成eacute;是 8 字节©在 UTF-8 里是 2 字节写成copy;是 6 字节。转换的字符种类越多、出现得越频繁输出体积增长就越明显而且转换过程本身也要多做查表工作。这不是「快几倍慢几倍」的问题而是一个可以自己算清楚的字节账。还需要提醒一点htmlentities()的转换范围依赖$encoding参数。如果输入数据的实际编码和传入的$encoding不一致转换表就会对不上结果是既没转对、还可能触发非法序列处理逻辑。所以无论用哪个函数都显式传UTF-8并确保数据真的是 UTF-8。四、实战一个统一的输出转义工具下面这段代码把前面几节的要点收拢成一个可直接运行的小示例给出两个转义入口HTML 文本 / 属性统一用h()并把转换表条目数打印出来做环境自检。?php // 适用于 PHP 8.0declare(strict_types1);const HTML_FLAGS ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401;const CHARSET UTF-8;/** 输出到 HTML 文本或属性时统一走这里 */function h(?string $s): string{return htmlspecialchars((string) $s, HTML_FLAGS, CHARSET);}/** 仅在需要「全 ASCII 兼容输出」时才用 */function h_all(?string $s): string{return htmlentities((string) $s, HTML_FLAGS, CHARSET, true);}$samples [plain Hello World,metachars a b c d e \f\,accented Café résumé,cjk 中文测试,];foreach ($samples as $name $value) {printf([%s]%s, $name, PHP_EOL);printf( 原文 : %s%s, $value, PHP_EOL);printf( htmlspecial… : %s%s, h($value), PHP_EOL);printf( htmlentities : %s%s, h_all($value), PHP_EOL);}// 反向把实体还原回字符$encoded h(a b c d);var_dump(htmlspecialchars_decode($encoded, ENT_QUOTES));var_dump(html_entity_decode($encoded, HTML_FLAGS, CHARSET));运行这个脚本一次就能看清两个函数的全部差别也能确认「汉字在这两张表里都没有对应项」这个事实。反向操作需要注意一点htmlspecialchars_decode()只还原htmlspecialchars()处理过的那几个实体而html_entity_decode()会还原所有命名实体。如果你用htmlentities()编码过数据解码时应该用html_entity_decode()并传入同样的文档类型标志和编码。常见坑点❌ 认为htmlentities()比htmlspecialchars()「更安全」于是全项目统一用它✅ 两者对 XSS 的防护是等价的危险字符集合相同。区别只在「是否顺带转换所有有命名实体的字符」。默认选htmlspecialchars()只有在明确需要 ASCII 兼容输出时才用htmlentities()。❌ 以为汉字会被转成实体✅ 汉字在 HTML 的命名实体表里没有对应项用任何文档类型都会原样保留。想要「全 ASCII 输出」还得配合mb_encode_numericentity()。❌ 以为htmlentities()会输出数字实体如#233;✅ 它只产出命名实体。数字实体要用mb_encode_numericentity()。❌ 只写htmlentities($s)依赖默认标志✅ PHP 8.1 之前默认是ENT_COMPAT单引号不转换非法序列还会返回空字符串。显式写ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401并传UTF-8。❌ 传入的$encoding和数据实际编码不一致✅ 转换表是按编码查的对不上就会转错或触发非法序列处理。统一在项目里用UTF-8并在数据入口做编码校验。❌ 在入库前用htmlentities()处理用户输入✅ 转义只在输出到 HTML 时做。入库前转义会让导出 CSV、调接口、做搜索时全是实体符号还会导致重复转义。❌ 用htmlentities()处理要放进 URL 或 JavaScript 的值✅ 它只管 HTML 上下文。URL 参数用rawurlencode()JavaScript 数据用json_encode()。❌ 用htmlentities($s) $s判断「有没有特殊字符」✅ 当字符串里本来就有实体文本时会误判而且松散比较容易出问题。直接比较用或者用htmlspecialchars($s, ...) ! $s判断是否需要转义。总结项目htmlspecialchars()htmlentities()转换范围5 个字符引号受标志控制所有具有命名实体对应物的字符防 XSS 能力足够与前者等价不多不少输出长度基本不变被转换的字符变长é从 2 字节变 8 字节汉字原样保留原样保留无对应命名实体数字实体不产出不产出要用mb_encode_numericentity()转换表查询get_html_translation_table(HTML_SPECIALCHARS, ...)get_html_translation_table(HTML_ENTITIES, ...)反向函数htmlspecialchars_decode()html_entity_decode()默认标志PHP 8.1 起为 ENT_QUOTES \ENT_SUBSTITUTE \把htmlentities()理解成「htmlspecialchars()加上把所有拉丁字母和符号也换成实体」就足够了。它不是一个更安全的版本只是一个转换范围更广的版本而转换范围广在中文为主的页面里既不会带来安全收益也不会碰到汉字唯一的实际影响是输出体积和可读性。默认用htmlspecialchars()把htmlentities()留给「必须输出纯 ASCII HTML」这一种明确需求是更省心的选择。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询