PHP 8.2:弃用特性 — utf8_encode 及 utf8_decode 函数弃用
utf8_encode
与 utf8_decode
函数,用于将字符串在 ISO-8859-1 (也叫“Latin 1”) 和 UTF-8 之间互相转换。这些函数不会尝试检测给定的文本的实际符号,且总是在 ISO-8859-1 和 UTF-8 中相互转换,即使源文本并非使用 ISO-8859-1 进行编码。
虽然 PHP 在其标准库中包含了 utf8_encode
和 utf8_decode
函数,这些函数不能用于检测和转换其他编码方式的文本,比如将 Windows-1252、UTF-16 和 UTF-32 转码为 UTF-8。传入任意文本到 utf8_encode
函数,容易产生 bug,并且不会产生任何警告及错误,不过却带来了不需要的结果。
比较频繁的一些 Bug 的例子包括:
- 欧元标志 (€,字符序列 \xE2\x82\xAC),当其传入到
utf8_encode
函数,会导致输入杂乱文本(也叫乱码):â¬。 - 德文字符 (ß, 字符序列 \xDF),当使用
utf8_encode("ß")
进行编码时会输出 Ã。
上面的两个例子都不会出现任何警告或者错误,尽管它们输出的文本是错误的。
因为函数名误导、缺乏错误信息及警告、缺乏对 ISO-8859-1 之外的字符串的支持,utf8_encode
和 utf8_decode
函数在 PHP 8.2 中被弃用了,
在 PHP 8.2 中使用 utf8_encode
和 utf8_decode
函数会出现废弃通知,这些函数在 PHP 9.0 中会被移除。
utf8_encode('foo');
uft8_decode('foo');
Function utf8_encode() is deprecated in ... on line ...
Function uft8_decode() is deprecated in ... on line ...
替换弃用函数
utf8_encode
函数将 ISO-8859-1 编码的字符串转换成 UTF-8。在旧版 PHP 应用中大部分 utf8_encode
函数的调用将其作为传统的安全保障,以避免出现任何潜在畸形文本,不过如上述的例子,使用这一函数经常会带来不需要的输出。
相似地,调用 utf8_decode
函数用来解码字符串转码到 ISO-8859-1 字符编码。大部分的网站应用网站、文本格式实际上使用 UTF-8 编码而不是 ISO-8859-1。
在替代前优先重新评估是否需要调用 utf8_encode
和 utf8_decode
函数,因为更经常的情况是,这些函数的调用并不需要,而且只会带来不需要的输出。
PHP 在它的内核中没有捆绑多字节字符编码函数,不过 PHP 核心的 mbstring
、intl
和 iconv
扩展提供了健壮且准确的功能,用来检测和转换字符编码。mbstring
和 iconv
都是核心扩展,mbstring
广泛用于现代 PHP 应用,可以 polyfill 补丁更新。
替换 utf8_encode
如果实际的应用中存在将已知的 ISO-8859-1 编码字符转成 UTF-8,可以使用 iconv
、intl
或者 mbstring
扩展对其转码。另外,尽管有些性能损耗,也可以使用用户空间 PHP 直接将代码点转换成 UTF-8 字符。
当使用的 utf8_encode
自动检测字符编码并转成 UTF-8 的用例时,尽管该函数一开始并没有检测字符编码,替代的函数还是需要先检测编码再转换成 UTF-8。
ISO-8859-1 to UTF-8 | 其他编码转换成 UTF-8 | |
---|---|---|
PHP 标准函数 | 使用 标准 PHP 函数将 ISO-8859-1 转成 UTF-8 | N/A |
使用 mbstring | 使用 mbstring 将 ISO-8859-1 转成 UTF-8 | 使用 mbstring 将其他编码转换成 UTF-8 |
使用 intl | 使用 intl 将 ISO-8859-1 转成 UTF-8 | N/A |
使用 iconv | 使用 iconv 将 ISO-8859-1 转成 UTF-8 | N/A |
使用标准函数将 ISO-8859-1 转成 UTF-8
symfony/polyfill-php72
库提供了使用 PHP 标准函数模仿 utf8_encode
功能。为了让他更具可读性同时传达该函数的含义,将其重命名为 iso8859_1_to_utf8
。
function iso8859_1_to_utf8(string $string): string {
$s .= $string;
$len = \strlen($s);
for ($i = $len >> 1, $j = 0; $i < $len; ++$i, ++$j) {
switch (true) {
case $s[$i] < "\x80": $s[$j] = $s[$i]; break;
case $s[$i] < "\xC0": $s[$j] = "\xC2"; $s[++$j] = $s[$i]; break;
default: $s[$j] = "\xC3"; $s[++$j] = \chr(\ord($s[$i]) - 64); break;
}
}
return substr($s, 0, $j);
}
有了上述的函数,就可以将所有 utf8_encode
调用替换成新增的 iso8859_1_to_utf8
函数,以避免弃用通知:
- utf8_encode($string);
+ iso8859_1_to_utf8($string);
使用 mbstring 将 ISO-8859-1 转换成 UTF-8
mbstring 扩展,最广泛使用的 PHP 可选扩展,提供了更简洁和直接的方法将 ISO-8859-1 编码的字符串转成 UTF-8。这也能用来替换 PHP 8.2 中弃用的 utf8_encode
函数。
- utf8_encode($string);
+ mb_convert_encoding($string, 'UTF-8', 'ISO-8859-1');
使用 mbstring 将其他编码转换成 UTF-8
未实际了解输入文本的实际编码方式,可能再 PHP 强制检测字符编码时导致错误的结果。不过,可以合理猜测源文本编码并使用 mbstring 将其转换成 UTF-8。
- utf8_encode($string);
+ mb_convert_encoding($string, 'UTF-8', mb_list_encodings());
使用 intl 将 ISO-8859-1 转成 UTF-8
intl 扩展的 UConverter
类也提供了对字符编码进行转换的方法。它提供了与 mbstring 相似的函数签名。使用 UConverter::transcode
,可以替代 utf8_encode
功能:
- utf8_encode($string);
+ UConverter::transcode($latin1, 'UTF8', 'ISO-8859-1');
使用 iconv 将 ISO-8859-1 转成 UTF-8
使用 iconv 扩展的应用可以用 iconv
函数替换 utf8_encode
函数:
- utf8_encode($string);
+ iconv('ISO-8859-1', 'UTF-8', $string);
替换 utf8_decode
utf8_decode
函数将 UTF-8 编码的字符串解码转成 ISO-8859-1。随着 utf8_decode
函数弃用,可以使用 PHP 标准函数、mbstring 扩展、intl 扩展或 iconv 扩展替代该函数。
UTF-8 转成 ISO-8859-1 | |
---|---|
PHP 标准函数 | 使用标准PHP函数将UTF-8 转成 ISO-8859-1 |
使用 mbstring | 使用 mbsting 将UTF-8 转成 ISO-8859-1 |
使用 intl | 使用 intl 将UTF-8 转成 ISO-8859-1 |
使用 iconv | 使用 iconv 将UTF-8 转成 ISO-8859-1 |
使用标准 PHP 函数将 UTF-8 转成 ISO-8859-1
类似于 utf8_encode polyfill
,symfony/polyfill-php72
库也提供了一个模仿 utf8_decode
函数的 PHP 函数:
function utf8_to_iso8859_1(string $string): string {
$s = (string) $string;
$len = \strlen($s);
for ($i = 0, $j = 0; $i < $len; ++$i, ++$j) {
switch ($s[$i] & "\xF0") {
case "\xC0":
case "\xD0":
$c = (\ord($s[$i] & "\x1F") << 6) | \ord($s[++$i] & "\x3F");
$s[$j] = $c < 256 ? \chr($c) : '?';
break;
case "\xF0":
++$i;
// no break
case "\xE0":
$s[$j] = '?';
$i += 2;
break;
default:
$s[$j] = $s[$i];
}
}
return substr($s, 0, $j);
}
引入上述函数,可以用新增的 utf8_to_iso8859_1
替换掉 tf8_decode
的调用:
- utf8_decode($string);
+ utf8_to_iso8859_1($string);
使用 mbsting 将 UTF-8 转成 ISO-8859-1
下面是使用 mb_convert_encoding
函数代替 utf8_decode
的例子:
- utf8_decode($string);
+ mb_convert_encoding($string, 'ISO-8859-1', 'UTF-8');
使用 intl 将 UTF-8 转成 ISO-8859-1
使用 intl 扩展的 UConverter::transcode
,可以替换 utf8_decode
调用:
- utf8_encode($string);
+ UConverter::transcode($string, 'ISO-8859-1', 'UTF8', ['to_subst' => '?']);
使用 iconv 将 UTF-8 转成 ISO-8859-1
iconv 函数也能用于模仿和替换 utf8_decode
函数,以避免 PHP 8.2 中的弃用通知:
- utf8_encode($string);
+ iconv('UTF-8', 'ISO-8859-1', $string);
向后兼容性影响
utf8_encode
和 utf8_decode
函数在旧版 PHP 应用中有时候会用到,用来处理传入的使用各种编码的数据和文件。因为误导性的名字以及会带来预期之外的结果而不显式警告或报错,这些函数在 PHP 8.2 中会被弃用,在 PHP 9.0 中会被移除。
PHP 8.2 中,使用这两个函数每次调用都会导致弃用通知。
utf8_encode
和 utf8_decoden
函数在 PHP 9.0 中会被移除。
很多使用这两个函数的应用,并不清楚它们只能转换 ISO-8859-1 编码。因此先了解为什么使用这两个函数,再确定是否真的需要它们,可能是更好的修复方式。
可以根据应用所依赖的 PHP 扩展,决定如何替换 utf8_encode
和 utf8_decode
函数的调用。