将字符串中非 ASCII 字符(中文、表情符、特殊符号)转为 HTML 数字实体引用(xxx;),可在不指定字符集的环境下安全传输、存储、显示。本文实现一个高性能、支持多编码源的静态工具类。
<?php
class HtmlEncode {
// 默认源编码
private static $_srcEncoding = 'UTF-8';
/**
* 将非 ASCII 字符转为 HTML 实体
* @param string $s 待转换字符串
* @param string $srcEncoding 源字符串编码 (UTF-8, GB2312, GBK 等)
* @return string HTML 实体字符串
* @example HtmlEncode::encode("我信了") → "我信了"
*/
public static function encode($s, $srcEncoding = 'UTF-8') {
self::$_srcEncoding = $srcEncoding;
// 正则匹配非 ASCII 字节序列 (连续非 0x00-0x7F)
return preg_replace_callback(
'|[^\x00-\x7F]+|',
[__CLASS__, '_convertToHtmlEntities'],
$s
);
}
/**
* 回调处理:将匹配的多字节序列转为实体
* @param array|string $data 匹配结果 (PREG_OFFSET_CAPTURE 时为数组)
* @return string 实体字符串
*/
private static function _convertToHtmlEntities($data) {
// 兼容 preg_replace_callback 传入数组或字符串
$str = is_array($data) ? $data[0] : $data;
// 1. 转为 UCS-2BE (大端序,每字符 2 字节)
$ucs2 = iconv(self::$_srcEncoding, "UCS-2BE", $str);
// 2. 按 2 字节分割
$chars = str_split($ucs2, 2);
// 3. 每字符转为十进制实体
$entities = array_map(function($char) {
// 高字节在前,低字节在后
$code = (ord($char[0]) << 8) | ord($char[1]);
return "{$code};";
}, $chars);
return implode('', $entities);
}
}
?>
<?php
require 'HtmlEncode.php';
// 1. UTF-8 中文转实体
echo HtmlEncode::encode("你好世界");
// 输出: 你好世界
// 2. GB2312 源编码转实体
$gbStr = iconv("UTF-8", "GB2312", "测试");
echo HtmlEncode::encode($gbStr, "GB2312");
// 输出: 测试
// 3. 混合内容
echo HtmlEncode::encode("Hello 世界! 😀");
// 输出: Hello 世界! 😀
// 4. 数据库存储场景
$safeTitle = HtmlEncode::encode($userInput); // 存入 latin1 表也能正确显示中文
?>
[^\x00-\x7F]+:匹配连续非 ASCII 字节,避免逐字符回调开销(ord($c[0]) << 8) | ord($c[1]):大端序合成 Unicode 码点code;:浏览器通用支持,无需实体名映射表<?php
class HtmlEncodeFast {
private static $pattern = '|[^\x00-\x7F]+|';
public static function encode($s, $srcEncoding = 'UTF-8') {
// 预转整个字符串为 UCS-2BE,避免回调中重复 iconv
$ucs2 = iconv($srcEncoding, "UCS-2BE", $s);
$len = strlen($ucs2);
$result = '';
$asciiStart = 0;
for ($i = 0; $i < $len; $i += 2) {
$high = ord($ucs2[$i]);
if ($high < 0x80) { // ASCII 字符 (高字节为 0)
continue;
}
// 非 ASCII:输出前面的 ASCII 部分
if ($i > $asciiStart) {
$result .= substr($s, $asciiStart, ($i - $asciiStart) / 2);
}
// 转换当前字符
$code = ($high << 8) | ord($ucs2[$i + 1]);
$result .= "{$code};";
$asciiStart = $i + 2;
}
// 尾部 ASCII
if ($asciiStart < strlen($s)) {
$result .= substr($s, $asciiStart);
}
return $result;
}
}
?>
| 特性 | htmlentities | HtmlEncode::encode |
|---|---|---|
| 转换范围 | 所有特殊字符 (& < > " ' 等) | 仅非 ASCII 字符 |
| 输出格式 | 命名实体 (©) + 数字实体 | 仅十进制数字实体 |
| 编码依赖 | 需指定 charset 参数 | 需指定源编码 |
| 数据库存储 | 增加体积 2-4 倍 | 仅非 ASCII 增加,体积较小 |
| 反向解码 | html_entity_decode | html_entity_decode 通用 |
<?php
function htmlEntitiesToString($html) {
return html_entity_decode($html, ENT_QUOTES | ENT_HTML5, 'UTF-8');
}
// 测试
$encoded = "你好世界";
echo htmlEntitiesToString($encoded); // 输出: 你好世界
?>