php get meta tags
UTF-8一路通過 (10)
PHP中的Unicode支持仍然是一個巨大的混亂。 雖然它能夠將ISO8859字符串(它在內部使用)轉換為utf8,但它本身沒有使用unicode字符串的能力,這意味著所有的字符串處理函數都會破壞和破壞字符串。 所以你必須使用單獨的庫來獲得適當的utf8支持,或者自己重寫所有的字符串處理函數。
簡單的部分就是在HTTP頭文件和數據庫中指定charset等,但如果您的PHP代碼不輸出有效的UTF8,那麼這些都不重要。 這是一個很難的部分,而PHP在這方面幾乎沒有任何幫助。 (我認為PHP6應該可以解決最糟糕的問題,但這還有一段時間了)
我正在設置一個新的服務器,並且希望在我的Web應用程序中完全支持UTF-8。 過去我在現有的服務器上嘗試過,並且似乎最終不得不回退到ISO-8859-1。
我在哪裡需要設置編碼/字符集? 我知道我需要配置Apache,MySQL和PHP來做到這一點 - 是否有一些我可以遵循的標準清單,或者可能需要排除發生不匹配的問題?
這是一個新的Linux服務器,運行MySQL 5,PHP 5和Apache 2。
數據存儲 :
在數據庫中的所有表和文本列上指定
utf8mb4
字符集。 這使MySQL實際存儲和檢索以UTF-8本地編碼的值。 請注意,如果指定了utf8mb4_*
排序規則(沒有任何明確的字符集),MySQL將隱式使用utf8mb4
編碼。在舊版本的MySQL(<5.5.3)中,不幸的是你不得不僅僅使用
utf8
,它只支持一部分Unicode字符。 我希望我在開玩笑。
數據訪問 :
在您的應用程序代碼(例如PHP)中,無論您使用
utf8mb4
數據庫訪問方法,都需要將連接字符集設置為utf8mb4
。 這樣,MySQL在將數據傳遞到應用程序時不會從其本機UTF-8進行轉換,反之亦然。一些驅動程序提供了自己的配置連接字符集的機制,它們都會更新自己的內部狀態並通知MySQL將在連接上使用的編碼 - 這通常是首選方法。 在PHP中:
如果您使用PDO抽象層,則可以在DSN指定
charset
:$dbh = new PDO('mysql:charset=utf8mb4');
如果你使用mysqli ,你可以調用
set_charset()
:$mysqli->set_charset('utf8mb4'); // object oriented style mysqli_set_charset($link, 'utf8mb4'); // procedural style
如果你遇到了普通的mysql但碰巧運行的是PHP≥5.2.3,則可以調用
mysql_set_charset
。
如果驅動程序沒有提供自己的設置連接字符集的機制,那麼您可能必鬚髮出一個查詢來告訴MySQL您的應用程序期望連接上的數據是如何編碼的:
SET NAMES 'utf8mb4'
。關於
utf8mb4
/utf8
的相同考慮utf8mb4
適用。
輸出 :
如果您的應用程序將文本傳輸到其他系統,則還需要通知其字符編碼。 使用Web應用程序時,必須通知瀏覽器發送數據的編碼(通過HTTP響應標頭或HTML元數據 )。
在PHP中,您可以使用
default_charset
php.ini選項,或手動發布Content-Type
MIME頭,這只是更多的工作,但具有相同的效果。
輸入 :
不幸的是,在嘗試存儲或在任何地方使用它之前,您應該驗證每個收到的字符串是否為有效的UTF-8。 PHP的
mb_check_encoding()
有訣竅,但你必須虔誠地使用它。 真的沒有辦法解決這個問題,因為惡意客戶可以用他們想要的任何編碼提交數據,而且我還沒有發現讓PHP可靠地為您做這件事的竅門。從我閱讀當前的HTML規範 ,下面的子項目不再是現代HTML的必要或者甚至是有效的。 我的理解是,瀏覽器將使用為文檔指定的字符集並提交數據。 但是,如果您的目標是舊版HTML(XHTML,HTML4等),這些點可能仍然有用:
- 對於僅HTML5之前的HTML :您希望瀏覽器發送給您的所有數據都是UTF-8。 不幸的是,如果你唯一能夠可靠地做到這一點的方法是將
accept-charset
屬性添加到所有的<form>
標籤中:<form ... accept-charset="UTF-8">
。 - 對於僅HTML5之前的HTML :請注意,W3C HTML規範指出客戶端“應該”默認將表單發送回服務器,無論服務器使用什麼字符集,但這顯然只是一個建議,因此需要在每一個單獨的
<form>
標籤。
- 對於僅HTML5之前的HTML :您希望瀏覽器發送給您的所有數據都是UTF-8。 不幸的是,如果你唯一能夠可靠地做到這一點的方法是將
其他代碼注意事項 :
在我的情況下,我使用mb_split
,它使用正則表達式。 因此,我還必須通過執行mb_regex_encoding('UTF-8');
手動確保正則表達式編碼是utf-8 mb_regex_encoding('UTF-8');
作為一個方面說明,我還通過運行mb_internal_encoding()
發現內部編碼不是utf-8,並通過運行mb_internal_encoding("UTF-8");
改變它mb_internal_encoding("UTF-8");
。
如果你希望MySQL服務器決定字符集,而不是PHP作為客戶端(舊的行為;在我看來,首選),嘗試在你的my.cnf
添加skip-character-set-client-handshake
,在[mysqld]
,重啟mysql
。
如果您使用的不是UTF8,可能會造成麻煩。
我剛剛經歷了相同的問題,並在PHP手冊中找到了一個很好的解決方案。
我將所有文件編碼都改為UTF8,然後將我的連接上的默認編碼。 這解決了所有問題。
if (!$mysqli->set_charset("utf8")) {
printf("Error loading character set utf8: %s\n", $mysqli->error);
} else {
printf("Current character set: %s\n", $mysqli->character_set_name());
}
我唯一要添加到這些驚人的答案是強調保存您的文件在utf8編碼,我已經註意到瀏覽器通過設置utf8作為您的代碼編碼接受此屬性。 任何像樣的文本編輯器都會向你顯示這個,例如Notepad ++有一個文件添加菜單選項,它向你顯示當前的編碼,並允許你改變它。 對於我所有的php文件,我使用utf8沒有BOM。
有時候我有人問我為其他人設計的php / mysql應用程序添加utf8支持,我注意到所有文件都是用ANSI編碼的,所以我不得不使用ICONV來轉換所有文件,更改數據庫表以使用utf8 charset和utf8_general_ci collate,在連接後向數據庫抽象層添加'SET NAMES utf8'(如果使用5.3.6或更早版本,否則必須在連接字符串中使用charset = utf8),並更改字符串函數以使用php多字節字符串函數等效。
我最近發現使用strtolower()
可能會導致數據在特殊字符後被截斷的問題。
解決方案是使用
mb_strtolower($string, 'UTF-8');
mb_使用MultiByte。 它支持更多的字符,但總體來說會慢一點。
最好的答案非常好。 這是我必須在常規的debian / php / mysql設置上:
// storage
// debian. apparently already utf-8
// retrieval
// the mysql database was stored in utf-8,
// but apparently php was requesting iso. this worked:
// ***notice "utf8", without dash, this is a mysql encoding***
mysql_set_charset('utf8');
// delivery
// php.ini did not have a default charset,
// (it was commented out, shared host) and
// no http encoding was specified in the apache headers.
// this made apache send out a utf-8 header
// (and perhaps made php actually send out utf-8)
// ***notice "utf-8", with dash, this is a php encoding***
ini_set('default_charset','utf-8');
// submission
// this worked in all major browsers once apache
// was sending out the utf-8 header. i didnt add
// the accept-charset attribute.
// processing
// changed a few commands in php, like substr,
// to mb_substr
就這些 !
除了在php.ini中設置default_charset
,您還可以在任何輸出之前使用代碼中的header()
發送正確的字符集:
header('Content-Type: text/html; charset=utf-8');
在PHP中使用Unicode很容易,只要您意識到大多數字符串函數不適用於Unicode,並且有些可能會徹底破壞字符串 。 PHP認為“字符”長度為1個字節。 有時候這是可以的(例如, explode()
只查找字節序列並將其用作分隔符 - 因此,查找的實際字符無關緊要)。 但是有些時候,當函數實際上是為了處理字符而設計的時候,PHP並不知道您的文本是否具有Unicode字符。
一個好的圖書館是phputf8 。 這將重寫所有“壞”函數,以便您可以安全地使用UTF8字符串。 有像mbstring擴展那樣的擴展,也嘗試為你做這件事,但我更喜歡使用該庫,因為它更便攜(但我寫了大眾市場產品,所以這對我很重要)。 但是無論如何,phputf8可以在幕後使用mbstring來提高性能。
首先,如果你在<5.3PHP然後沒有。 你需要解決很多問題。
我很驚訝沒有人提到intl庫, 它對 unicode , 字符 , 字符串操作 , 本地化等有很好的支持,見下文。
我將在PHPBenelux'14上引用關於伊麗莎白史密斯 slides中關於unicode支持的一些信息
INTL
好:
- 包裝在ICU圖書館周圍
- 標準化的區域設置,設置每個腳本的區域設置
- 數字格式
- 貨幣格式
- 消息格式(替換gettext)
- 日曆,日期,時區和時間
- Transliterator
- Spoofchecker
- 資源包
- 轉換器
- 國際化域名支持
- 字形
- 整理
- 迭代器
壞:
- 不支持zend_multibite
- 不支持HTTP輸入輸出轉換
- 不支持函數重載
mb_string
- 啟用zend_multibyte支持
- 支持透明的HTTP輸入/輸出編碼
- 為函數提供了一些包裝,如strtoupper
ICONV
- 主要用於字符集轉換
- 輸出緩衝區處理器
- MIME編碼功能
- 轉變
- 一些字符串助手(len,substr,strpos,strrpos)
- Stream Filter
stream_filter_append($fp, 'convert.iconv.ISO-2022-JP/EUC-JP')
DATABASES
- mysql:表和連接上的字符集和整理(不是整理)。 也不要使用mysql-msqli或PDO
- postgresql:pg_set_client_encoding
- sqlite(3):確保它是用unicode和intl支持編譯的
其他一些問題
- 除非使用第三部分擴展名,否則不能在PHP和Windows中使用unicode文件名。
- 如果使用exec,proc_open和其他命令行調用,則以ASCII格式發送所有內容
- 純文本不是純文本,文件有編碼
- 您可以使用iconv過濾器即時轉換文件
我會更新這個答案,以防事情改變添加的功能等等。