PHP + MySQL 中文亂碼徹底解決:Server/Connection/Page 三層編碼統一實戰
PHP 讀取 MySQL 中文出現亂碼,本質是**編碼不一致**:資料庫儲存編碼、連線傳輸編碼、頁面輸出編碼三者任一不匹配即會亂碼。本文提供標準化配置清單,一勞永逸解決。
三層編碼必須統一
| 層級 | 配置位置 | 建議值 |
| 資料庫儲存 | 建表/建庫 CHARSET=utf8mb4 | utf8mb4 (支援 Emoji) |
| 連線傳輸 | PHP 連線後執行 SET NAMES utf8mb4 | utf8mb4 |
| 頁面輸出 | HTTP Header / HTML Meta | UTF-8 |
MySQL 端配置 (my.cnf)
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
init_connect = 'SET NAMES utf8mb4'
[client]
default-character-set = utf8mb4
[mysql]
default-character-set = utf8mb4
修改後 **重啟 MySQL** 生效:systemctl restart mysqld
PHP 端配置 (三種方式任一)
方式一:PDO 連線參數 (推薦,現代、安全)
$dsn = "mysql:host=localhost;dbname=test;charset=utf8mb4";
$pdo = new PDO($dsn, 'user', 'pass', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
方式二:mysqli 設定字元集
$mysqli = new mysqli('localhost', 'user', 'pass', 'test');
$mysqli->set_charset('utf8mb4'); // 必須在查詢前呼叫
方式三:舊版 mysql_connect (已棄用,僅維護專案)
$conn = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('test', $conn);
mysql_query("SET NAMES 'utf8mb4'", $conn);
頁面輸出配置
<?php
// 最早輸出 Header
header('Content-Type: text/html; charset=UTF-8');
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>中文測試</title>
</head>
驗證步驟
- 資料庫建表:
CREATE TABLE t (c VARCHAR(20)) CHARSET=utf8mb4;
- 插入測試:
INSERT INTO t VALUES('中文測試😀');
- PHP 讀取輸出,瀏覽器檢視原始碼確認無亂碼
- 開發者工具 Network → Response Headers 確認
Content-Type: text/html; charset=UTF-8
常見殘留問題排查
| 現象 | 可能原因 | 檢查 |
| 存入正常、讀出亂碼 | 連線未 SET NAMES | PHP 連線代碼是否設定字元集 |
| 存入即亂碼 (如 ???) | 資料表 CHARSET 非 utf8mb4 | SHOW CREATE TABLE t; |
| 瀏覽器顯示 | 頁面無 charset meta 或 Header 錯誤 | HTML meta / PHP header |
| Emoji 變 ???? | 使用 utf8 (3 bytes) 非 utf8mb4 | 全鏈路升級 utf8mb4 |
黃金法則: **全鏈路 utf8mb4**。資料庫、連線、頁面、檔案編碼 (PHP 檔案存為 UTF-8 無 BOM) 五位一體,亂碼零發生。