本文摘譯整理自PHP最佳實踐——PHP與UTF8。
沒有一行式解決方案。小心、注意細節,以及一致性。
PHP 中的 UTF-8 糟透了。原諒我的用詞。
目前 PHP 在低層次上還不支持 Unicode。有幾種方式可以確保 UTF-8 字符串能夠被正確處理, 但並不容易,需要深入到 web 應用的所有層麵,從 HTML,到 SQL,到 PHP。我們旨在提供一個簡潔、 實用的概述。
PHP 層麵的 UTF-8
基本的字符串操作,如串接 兩個字符串、將字符串賦給變量,並不需要任何針對 UTF-8 的特殊東西。 然而,多數 字符串函數,如 strpos() 和 strlen,就需要特殊的考慮。 這些函數都有一個對應的 mb_* 函數:例如,mb_strpos() 和 mb_strlen()。 這些對應的函數統稱為多字節字符串函數。 這些多字節字符串函數是專門為操作 Unicode 字符串而設計的。
當你操作 Unicode 字符串時,必須使用 mb_* 函數。 例如,如果你使用 substr() 操作一個 UTF-8 字符串,其結果就很可能包含一些亂碼。 正確的函數應該是對應的多字節函數, mb_substr()。
難的是始終記得使用 mb_* 函數。即使你僅一次忘了,你的 Unicode 字符串在接下來的處理中就可能產生亂碼。
並不是所有的字符串函數都有一個對應的 mb_*。如果不存在你想要的那一個,那你就隻能自認倒黴了。
此外,在每個 PHP 腳本的頂部(或者在全局包含腳本的頂部)你都應使用 mb_internal_encoding 函數,如果你的腳本會輸出到瀏覽器,那麽還得緊跟其後加個mb_http_output() 函數。在每個腳本中顯式地定義字符串的編碼在以後能為你減少很多令人頭疼的事情。
最後,許多操作字符串的 PHP 函數都有一個可選參數讓你指定字符編碼。 若有該選項, 你應始終顯式地指明 UTF-8 編碼。 例如,htmlentities() 就有一個字符編碼方式選項,在處理這樣的字符串時應始終指定 UTF-8。
MySQL 層麵的 UTF-8
如果你的 PHP 腳本會訪問 MySQL,即使你遵從了前述的注意事項,你的字符串也有可能在數據庫中存儲為非 UTF-8 字符串。
確保從 PHP 到 MySQL 的字符串為 UTF-8 編碼的,確保你的數據庫以及數據表均設置為 utf8mb4 字符集, 並且在你的數據庫中執行任何其他查詢之前先執行 MySQL 查詢 `set names utf8mb4`。這是至關重要的。 示例請查看連接並查詢 MySQL 數據庫一節內容。
注意你必須使用 `utf8mb4` 字符集來獲得完整的 UTF-8 支持,而不是 `utf8` 字符集!原因請查看進一步閱讀。
瀏覽器層麵的 UTF-8
使用 mb_http_output() 函數 來確保你的 PHP 腳本輸出 UTF-8 字符串到瀏覽器。 並且在 HTML 頁麵的 <head> 標簽塊中包含 字符集 <meta> 標簽塊。
示例
<?php // Tell PHP that we're using UTF-8 strings until the end of the script mb_internal_encoding('UTF-8'); // Tell PHP that we'll be outputting UTF-8 to the browser mb_http_output('UTF-8'); // Our UTF-8 test string $string = 'Aš galiu valgyti stiklą ir jis manęs nežeidžia'; // Transform the string in some way with a multibyte function $string = mb_substr($string, 0, 10); // Connect to a database to store the transformed string // See the PDO example in this document for more information // Note the `set names utf8mb4` commmand! $link = new \PDO( 'mysql:host=your-hostname;dbname=your-db', 'your-username', 'your-password', array( \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, \PDO::ATTR_PERSISTENT => false, \PDO::MYSQL_ATTR_INIT_COMMAND => 'set names utf8mb4' ) ); // Store our transformed string as UTF-8 in our database // Assume our DB and tables are in the utf8mb4 character set and collation $handle = $link->prepare('insert into Sentences (Id, Body) values (?, ?)'); $handle->bindValue(1, 1, PDO::PARAM_INT); $handle->bindValue(2, $string); $handle->execute(); // Retrieve the string we just stored to prove it was stored correctly $handle = $link->prepare('select * from Sentences where Id = ?'); $handle->bindValue(1, 1, PDO::PARAM_INT); $handle->execute(); // Store the result into an object that we'll output later in our HTML $result = $handle->fetchAll(\PDO::FETCH_OBJ); ?><!doctype html> <html> <head> <meta http-equiv="Content-Type" content="text/html; 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>
進一步閱讀
- PHP 手冊:多字節字符串函數
- PHP UTF-8 備忘單
- Stack Overflow: 什麽因素致使 PHP 不兼容 Unicode?
- Stack Overflow: PHP 與 MySQL 之間國際化字符串的最佳實踐
- 怎樣在MySQL數據庫中完整支持Unicode