[转载][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>
了解更多
No comments yet.