首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

PHP + MySQL 中文亂碼終極解決方案:全鏈路 UTF-8 統一實踐

     編輯:MySQL 資料庫相關知識

亂碼本質:編碼鏈路任一環節不一致

瀏覽器 → HTTP Header → HTML Meta → PHP 源文件 → PHP-MBstring → MySQL 連接 → MySQL 表/列字符集 → 存儲引擎

任一節點編碼不匹配,即產生 、???、亂碼。

黃金法則:全鏈路 UTF-8 (utf8mb4)

層級配置項標準值驗證命令
瀏覽器/HTTPContent-Type Headertext/html; charset=utf-8curl -I url | grep -i content-type
HTML<meta charset="utf-8">UTF-8View Source 檢查
PHP 源文件編輯器保存編碼UTF-8 無 BOMfile -bi *.php | grep utf-8
PHP-MBstringphp.iniinternal_encoding=utf-8
default_charset=utf-8
php -i | grep mbstring
MySQL 連接PDO/mysqli 連接參數charset=utf8mb4SHOW VARIABLES LIKE 'character_set_%'
數據庫/表/列字符集/校對utf8mb4 / utf8mb4_unicode_ciSHOW CREATE TABLE t

PHP 連接配置最佳實踐

PDO (推薦)

$dsn = "mysql:host=127.0.0.1;dbname=app_db;charset=utf8mb4";
$options = [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES => false,  // 真實預處理
    PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"
];
$pdo = new PDO($dsn, 'user', 'pass', $options);

mysqli (面向對象)

$mysqli = new mysqli('127.0.0.1', 'user', 'pass', 'app_db', 3306);
if ($mysqli->connect_errno) {
    throw new Exception("Connect failed: " . $mysqli->connect_error);
}
$mysqli->set_charset('utf8mb4');  // 必須在查詢前調用
// 或
$mysqli->query("SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci");

舊項目 mysql_* 函數 (已棄用,僅維護)

$link = mysql_connect('127.0.0.1', 'user', 'pass');
mysql_select_db('app_db', $link);
mysql_query("SET NAMES utf8", $link);  // 老版本無 utf8mb4
mysql_query("SET CHARACTER SET utf8", $link);

MySQL 端配置檢查與修正

1. 服務器級字符集 (my.cnf)

[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
init_connect = 'SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci'
skip-character-set-client-handshake  # 強制忽略客戶端請求字符集,以服務器為準

2. 庫/表/列級統一

-- 創建庫
CREATE DATABASE app_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 修改現有庫
ALTER DATABASE app_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 修改表 (含所有列)
ALTER TABLE `user` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 僅修改表默認 (不影響現有列)
ALTER TABLE `user` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

3. 校對集選擇

  • utf8mb4_unicode_ci:基於 Unicode 9.0/14.0 排序規則,多語言準確,略慢
  • utf8mb4_0900_ai_ci:MySQL 8.0 默認,基於 Unicode 9.0,性能更好,支援音標/重音不敏感
  • utf8mb4_general_ci:舊版簡易校對,不準確 (如 ß=s, Œ=oe),不推薦新項目

三大經典亂碼場景速解

場景一:PHPMyAdmin 正常,網頁顯示 ???

原因:PHP 連接未設置字符集,MySQL 默認 latin1 發送數據。
解決:連接後立即執行 SET NAMES utf8mb4 或 PDO DSN 加 charset=utf8mb4。

場景一:插入中文報錯 Incorrect string value

原因:表/列字符集為 latin1/utf8,無法存儲 Emoji/生僻字 (需 utf8mb4)。
解決:ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4。

場景三:導出 SQL 導入新庫變亂碼

原因:導出文件編碼與導入會話字符集不符。
解決:
• 導出:mysqldump --default-character-set=utf8mb4 ... > backup.sql
• 導入:mysql --default-character-set=utf8mb4 db < backup.sql
• 文件編碼確認為 UTF-8 (Notepad++/VS Code 右下角可見)

排查工具箱

-- 1. 查看當前會話字符集
SHOW VARIABLES LIKE 'character_set_%';
SHOW VARIABLES LIKE 'collation_%';

-- 2. 檢查表實際字符集
SHOW CREATE TABLE `user`;

-- 3. 檢查列字符集
SHOW FULL COLUMNS FROM `user` WHERE Collation IS NOT NULL;

-- 4. 十六進制查看存儲內容
SELECT HEX(column_name), column_name FROM `user` WHERE id=1;
-- 中文 '測試' 在 utf8mb4 下為 E6B58BE8AF95 (6 字節)
-- 在 latin1 下通常為 2 字節亂碼

遷移舊項目 (latin1/utf8 → utf8mb4) 操作手冊

  1. 備份:mysqldump --default-character-set=latin1 --skip-set-charset db > backup_latin1.sql
  2. 修改文件:將 CHARSET=latin1 替換為 CHARSET=utf8mb4,COLLATE=latin1_swedish_ci 替換為 COLLATE=utf8mb4_unicode_ci
  3. 創建新庫:CREATE DATABASE new_db CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;
  4. 導入:mysql --default-character-set=utf8mb4 new_db < backup_latin1.sql
  5. 驗證:抽樣查詢 HEX(col) 確認中文 3-4 字節/字符,Emoji 4 字節
  6. 應用端同步修改連接字符集,灰度發布

堅持「全鏈路 utf8mb4」,亂碼問題將成為歷史。新項目從第一行代碼開始強制統一,舊項目按遷移手冊分批修正,技術債一次清零。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved