UTF-8 亂碼是 PHP 開發者最常遇到的坑。根因在於「檔案編碼、HTML 輸出 Header、PHP 內部處理、MySQL 連線字符集、資料庫/表/欄位字符集」任一環節不一致。本文提供系統性排查清單與標準化配置模板,從源頭杜絕亂碼。
第 1 層:輸出端 (HTTP 響應)
PHP 檔案編碼 → HTML meta charset → HTTP Header Content-Type
第 2 層:處理端 (PHP 內部)
字串操作函數 → 正則匹配 → json_encode → 模板引擎
第 3 層:存儲端 (MySQL)
連線字符集 → 資料庫預設字符集 → 表字符集 → 欄位字符集
header()、session_start() 提前輸出錯誤find . -name "*.php" -exec iconv -f GBK -t UTF-8 {} -o {}.utf8 \;header() > <meta charset> > 伺服器預設charset utf-8; 作為兜底
$pdo = new PDO(
"mysql:host=localhost;dbname=test;charset=utf8mb4",
'user', 'pass',
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
// 或 mysqli
$mysqli = new mysqli('localhost', 'user', 'pass', 'test');
$mysqli->set_charset('utf8mb4'); // 必須在查詢前呼叫
// 老式 mysql_* (已棄用)
mysql_query("SET NAMES utf8mb4");
utf8 僅支援 3 字節 (BMP 平面)SET NAMES utf8mb4 等同於同時設定 character_set_client、connection、results
-- 建庫
CREATE DATABASE `myapp` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 建表
CREATE TABLE `users` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`bio` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 修改現有庫/表/欄位
ALTER DATABASE `myapp` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
ALTER TABLE `users` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE `users` MODIFY `name` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
| 排序規則 | 特點 | 適用場景 | |
|---|---|---|---|
utf8mb4_unicode_ci | UCA 標準、多語言支援佳、準確排序 | 通用推薦 | |
utf8mb4_general_ci | 簡化規則、速度略快、德語/俄語等排序不準 | 舊專案相容、極高效能場景 | |
utf8mb4_bin | 二進制比較、區分大小寫、可作唯一索引去重 | 大小寫敏感、Base64/Hash 存儲 |
| 現象 | 可能原因 | 排查步驟 | 修復 |
|---|---|---|---|
| 頁面顯示 | 輸出 Header 非 UTF-8 | 檢查 header()、meta、Nginx charset | 統一設定 UTF-8 Header |
| 中文變 ??? | MySQL 連線字符集錯誤 | 檢查 SET NAMES、PDO charset 參數 | 連線加 charset=utf8mb4 |
| 存入資料庫變亂碼 | 表/欄位字符集非 utf8mb4 | SHOW CREATE TABLE 檢查 | ALTER TABLE CONVERT TO utf8mb4 |
| Emoji 存入截斷/報錯 | 字符集僅 utf8 (3 字節) | 檢查資料庫/表/欄位/連線全鏈路 | 全鏈路升級 utf8mb4 |
| json_encode 中文成 \uXXXX | 預設行為 | 非錯誤,前端自動解碼 | 加 JSON_UNESCAPED_UNICODE 參數 |
| substr/strlen 截斷亂碼 | 多字節字元按字節截取 | 用 mb_substr/mb_strlen | 啟用 mbstring、用 mb_* 函數 |
| 正則匹配失效 | 未加 u 修飾符 | preg_match('/漢字/u', $str) | 加 u 修飾符、用 mb_ereg |
// Laravel .env
DB_CONNECTION=mysql
DB_CHARSET=utf8mb4
DB_COLLATION=utf8mb4_unicode_ci
// config/database.php
'mysql' => [
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'options' => [
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci'
]
]
// ThinkPHP config/database.php
'charset' => 'utf8mb4',
// 自動執行 SET NAMES
// CodeIgniter config/database.php
'char_set' => 'utf8mb4',
'dbcollat' => 'utf8mb4_unicode_ci',
mysqldump --default-character-set=utf8mb4 導出數據utf8mb4 導入charset=utf8mb4grep -r "utf8" --include="*.php" . | grep -v "utf8mb4"strlen/substr/strpos 為 mb_strlen/mb_substr/mb_strpos/u 修飾符UTF-8 編碼問題本質是「契約不一致」。建立「全鏈路 utf8mb4」標準,配合代碼規範、靜態掃描、自動化測試,即可從根源消除亂碼困擾。新專案啟動日即落地此標準,舊專案按清單逐步遷移,是專業團隊的基本建設。
Copyright © 程式碼之家 All Rights Reserved