使用 PDO 連接 MySQL 時,中文數據讀寫出現亂碼、截斷、問號等問題,網上流傳多種解決方案但效果不一,本文深度分析根因並實測驗證。
true]
);
// 3. 常用屬性設置
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); // 使用真實預處理
MySQL 連接涉及三個字符集變量:
character_set_client - 客戶端發送數據的編碼character_set_connection - 連接層轉換編碼character_set_results - 結果集返回編碼當這三者與數據庫/表/列的實際存儲編碼不一致時,就會發生亂碼。
"SET NAMES 'utf8'"
]);
// 實測問題:某些環境下只顯示首字符,後續中文丟失
// 原因:SET NAMES utf8 在某些 MySQL 版本下不完整
exec("SET NAMES 'utf8'");
// 或
$pdo->exec("SET CHARACTER SET utf8");
query("SET NAMES utf8");
| 字符集 | MySQL 實際名稱 | 最大字符長度 | 支持 Emoji | 中文支持 |
|---|---|---|---|---|
| utf8 | utf8mb3 | 3 字節 | ❌ | ✅ 基本 |
| utf8mb4 | utf8mb4 | 4 字節 | ✅ | ✅ 完整 |
| gbk | gbk | 2 字節 | ❌ | ✅ 完整 |
實測結論:將方案中的 utf8 改為 gbk 可解決舊項目亂碼,但新項目強烈建議使用 utf8mb4。
PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
// 驗證字符集設置
$stmt = $pdo->query("SHOW VARIABLES LIKE 'character_set_%'");
$charsets = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
print_r($charsets);
/*
預期輸出:
character_set_client => utf8mb4
character_set_connection => utf8mb4
character_set_results => utf8mb4
character_set_database => utf8mb4
character_set_server => utf8mb4
*/
?>
exec("CREATE TEMPORARY TABLE t_charset_test (c VARCHAR(100) CHARSET utf8mb4)");
$stmt = $pdo->prepare("INSERT INTO t_charset_test (c) VALUES (?)");
$stmt->execute([$testStr]);
// 2. 讀取驗證
$stmt = $pdo->query("SELECT c FROM t_charset_test");
$result = $stmt->fetchColumn();
echo "原始: $testStr\n";
echo "讀取: $result\n";
echo "一致: " . ($testStr === $result ? "✅ 是" : "❌ 否") . "\n";
echo "長度: " . mb_strlen($result) . " / " . mb_strlen($testStr) . "\n\n";
$pdo->exec("DROP TEMPORARY TABLE t_charset_test");
}
// 測試不同連接方式
$dsnBase = "mysql:host=localhost;dbname=test";
// 方式1: DSN charset (推薦)
$pdo1 = new PDO("$dsnBase;charset=utf8mb4", "root", "");
testCharset($pdo1, "DSN charset=utf8mb4");
// 方式2: SET NAMES utf8mb4
$pdo2 = new PDO($dsnBase, "root", "");
$pdo2->exec("SET NAMES utf8mb4");
testCharset($pdo2, "exec SET NAMES utf8mb4");
// 方式3: 老項目 gbk
$pdo3 = new PDO("$dsnBase;charset=gbk", "root", "");
testCharset($pdo3, "DSN charset=gbk");
?>
-- 檢查各層級字符集
SHOW CREATE DATABASE db_name;
SHOW CREATE TABLE table_name;
SHOW FULL COLUMNS FROM table_name;
-- 統一修改為 utf8mb4
ALTER DATABASE db_name CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
ALTER TABLE table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
header('Content-Type: text/html; charset=utf-8');<meta charset="utf-8">Content-Type: text/html; charset=utf-8charset=utf8mb4,數據庫/表/列統一 utf8mb4charset=gbk 即可