[转载]PHP 处理 UTF-8

转载自: PHP 最佳实践方法


此章节由 Alex Cabal 撰写,节录自 PHP Best Practices 并作为我们 UTF-8 建议的基础。

这不是开玩笑的,请小心与细心并前后一致地处理它。

PHP 至今在底层仍未支援 Unicode。而有许多方式可以确认 UTF-8 字串的处理是正确的,但通常不容易,还需要从上而下翻遍程序所有阶层,从 HTML、SQL 到 PHP。我们将会聚焦在简短的实践总结。

UTF-8 在 PHP 阶层

基本的字串操作,像是连接两个字串、赋值给变量,并不需要特别作 UTF-8 处理。然而,大部分字串函数,像是 strpos()strlen() 则确实需要考虑。这些函数通常以 mb_* 作为开头:像是 mb_strpos()mb_strlen()。这类函数借由 Multibyte String Extension 设计用来专门用来处理 Unicode 字串。

每次操作 Unicode 字串,都必须使用 mb_* 这类函数。例如,若你对 UTF-8 字串使用 substr() 有很大的机会结果会是包含半字符的乱码,正确的方式是使用 mb_substr()

困难的是,要记得每次都使用 mb_* 函数,万一有个地方没有用到,那么你的 Unicode 字串就有可能在接下来的程序中被转变成乱码。并不是所有的字串函数都有对应的 mb_* 函数,就算只有一处没有正确转换,都有可能会出现乱码。

你应该在 PHP 程式的开头使用 mb_internal_encoding() (或是在最上层的全域加载的地方使用),若是有输出讯息到浏览器,则要再加上 mb_http_output() 函数。明确定义你的字串编码可以帮你省下许多头痛的时间。

此外,许多 PHP 字串函数都允许设定字符编码,你应该总是设定成 UTF-8,例如,htmlentities() 函数。而在 PHP 5.4.0 之后已经将 UTF-8 作为 htmlentities()htmlspecialchars() 默认编码。

最后,如果你建立的是分布式架构程式,且不能确定 mbstring 函数是启用状态,那么则建议使用 patchwork/utf8 这个 Composer 套件。它将会自动判断该环境能否使用 mbstring 函数。

UTF-8 在数据库阶层

如果你是使用 PHP 来存取 MySQL,有可能会发生使用非 UTF-8 编码来储存资料,即使你有遵照上述的注意事项。

为了确保字串从 PHP 到 MySQL 都是 UTF-8 编码,请确认数据库和资料表都是设定为 utf8mb4 字符集,且使用 utf8mb4 作为 PDO 连线字串,请见以下范例,这将是非常重要的。

请注意,你必须使用 utf8mb4 字符集来作为完整的支援 UTF-8,而非使用 utf8 字符集!原因请参见了解更多。

UTF-8 在浏览器阶层

使用 mb_http_output() 函数来确保 PHP 输出 UTF-8 字串给浏览器。

浏览器需要借由 HTTP 回应来得知此页面要被解析成 UTF-8 编码,借由历史的方法来达到需要包含字符集 <meta> 标签在页面的 <head> 标签内。这种方式是非常有效的,但是设定这个字符集到 Content-Type 标头实际上会更快

<?php
// 告诉 PHP 此页面从头到尾使用 UTF-8 字串
mb_internal_encoding('UTF-8');

// 告诉 PHP 此页面输出 UTF-8 字串到浏览器
mb_http_output('UTF-8');

// 我们的 UTF-8 测试字串
$string = 'Êl síla erin lû e-govaned vîn.';

// 使用多位元字串函数来进行转换
// 注意我们如何裁剪非 Ascii 字符来作为演示
$string = mb_substr($string, 0, 15);

// 连接数据库来储存转换字串
// 参见文件的 PDO 范例,以获得更多的资讯
// 注意 `set names utf8mb4` 指令!
$link = new \PDO(
    'mysql:host=your-hostname;dbname=your-db;charset=utf8mb4',
    'your-username',
    'your-password',
    array(
        \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
        \PDO::ATTR_PERSISTENT => false
    )
);

// 数据库以 UTF-8 编码储存我们转换的字串
// 你的数据库和资料表是设定成 utf8mb4 字符集,对吧?
$handle = $link->prepare('insert into ElvishSentences (Id, Body) values (?, ?)');
$handle->bindValue(1, 1, PDO::PARAM_INT);
$handle->bindValue(2, $string);
$handle->execute();

// 取得我们刚才存入的字串,来验证储存正确。
$handle = $link->prepare('select * from ElvishSentences where Id = ?');
$handle->bindValue(1, 1, PDO::PARAM_INT);
$handle->execute();

// 将结果存入物件,待会要输出到 HTML
$result = $handle->fetchAll(\PDO::FETCH_OBJ);

header('Content-Type: text/html; charset=UTF-8');
?><!doctype html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>UTF-8 test page</title>
    </head>
    <body>
        <?php
        foreach($result as $row){
            print($row->Body);  // This should correctly output our transformed UTF-8 string to the browser
        }
        ?>
    </body>
</html>

了解更多

  1. No comments yet.

  1. No trackbacks yet.

return top

%d 位部落客按了赞: