PHP 中的 UTF-8 糟透了。原諒我的用詞。
目前 PHP 在低層次上還不支持 Unicode。有幾種方式可以確保 UTF-8 字符串能夠被正確處理, 但并不容易,需要深入到 web 應(yīng)用的所有層面,從 HTML,到 SQL,到 PHP。我們旨在提供一個簡潔、 實(shí)用的概述。
基本的字符串操作,如串接 兩個字符串、將字符串賦給變量,并不需要任何針對 UTF-8 的特殊東西。 然而,多數(shù) 字符串函數(shù),如 strpos() 和 strlen,就需要特殊的考慮。 這些函數(shù)都有一個對應(yīng)的 mb_*
函數(shù):例如,mb_strpos() 和 mb_strlen()。 這些對應(yīng)的函數(shù)統(tǒng)稱為多字節(jié)字符串函數(shù)。 這些多字節(jié)字符串函數(shù)是專門為操作 Unicode 字符串而設(shè)計(jì)的。
當(dāng)你操作 Unicode 字符串時(shí),必須使用 mb_*
函數(shù)。 例如,如果你使用 substr() 操作一個 UTF-8 字符串,其結(jié)果就很可能包含一些亂碼。 正確的函數(shù)應(yīng)該是對應(yīng)的多字節(jié)函數(shù), mb_substr()。
難的是始終記得使用 mb_*
函數(shù)。即使你僅一次忘了,你的 Unicode 字符串在接下來的處理中就可能產(chǎn)生亂碼。
并不是所有的字符串函數(shù)都有一個對應(yīng)的 mb_*。如果不存在你想要的那一個,那你就只能自認(rèn)倒霉了。
此外,在每個 PHP 腳本的頂部(或者在全局包含腳本的頂部)你都應(yīng)使用 mb_internal_encoding 函數(shù),如果你的腳本會輸出到瀏覽器,那么還得緊跟其后加個mb_http_output() 函數(shù)。在每個腳本中顯式地定義字符串的編碼在以后能為你減少很多令人頭疼的事情。
最后,許多操作字符串的 PHP 函數(shù)都有一個可選參數(shù)讓你指定字符編碼。 若有該選項(xiàng), 你應(yīng)始終顯式地指明 UTF-8 編碼。 例如,htmlentities() 就有一個字符編碼方式選項(xiàng),在處理這樣的字符串時(shí)應(yīng)始終指定 UTF-8。
如果你的 PHP 腳本會訪問 MySQL,即使你遵從了前述的注意事項(xiàng),你的字符串也有可能在數(shù)據(jù)庫中存儲為非 UTF-8 字符串。
確保從 PHP 到 MySQL 的字符串為 UTF-8 編碼的,確保你的數(shù)據(jù)庫以及數(shù)據(jù)表均設(shè)置為 utf8mb4 字符集, 并且在你的數(shù)據(jù)庫中執(zhí)行任何其他查詢之前先執(zhí)行 MySQL 查詢 set names utf8mb4
。這是至關(guān)重要的。 示例請查看連接并查詢 MySQL 數(shù)據(jù)庫一節(jié)內(nèi)容。
注意你必須使用 utf8mb4
字符集來獲得完整的 UTF-8 支持,而不是 utf8
字符集!原因請查看進(jìn)一步閱讀。
使用 mb_http_output() 函數(shù) 來確保你的 PHP 腳本輸出 UTF-8 字符串到瀏覽器。 并且在 HTML 頁面的
標(biāo)簽塊中包含 字符集 標(biāo)簽塊。<?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>