首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PDO 連接 MySQL 中文亂碼問題:根因分析與三種解決方案實測

     編輯:PHP 相關知識

問題背景

使用 PDO 連接 MySQL 時,中文數據讀寫出現亂碼、截斷、問號等問題,網上流傳多種解決方案但效果不一,本文深度分析根因並實測驗證。

PDO 基礎配置

 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 - 結果集返回編碼

當這三者與數據庫/表/列的實際存儲編碼不一致時,就會發生亂碼。

三種常見解決方案實測

方案一:構造函數傳遞 MYSQL_ATTR_INIT_COMMAND

 "SET NAMES 'utf8'"
]);

// 實測問題:某些環境下只顯示首字符,後續中文丟失
// 原因:SET NAMES utf8 在某些 MySQL 版本下不完整

方案二:連接後執行 exec()

exec("SET NAMES 'utf8'");
// 或
$pdo->exec("SET CHARACTER SET utf8");

方案三:查詢前執行 query()

query("SET NAMES utf8");

關鍵發現:utf8 vs utf8mb4 vs gbk

字符集MySQL 實際名稱最大字符長度支持 Emoji中文支持
utf8utf8mb33 字節❌✅ 基本
utf8mb4utf8mb44 字節✅✅ 完整
gbkgbk2 字節❌✅ 完整

實測結論:將方案中的 utf8 改為 gbk 可解決舊項目亂碼,但新項目強烈建議使用 utf8mb4。

終極解決方案:DSN 中指定 charset

 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");
?>

常見坑點避雷指南

1. 數據庫/表/列字符集不一致

-- 檢查各層級字符集
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;

2. PHP 文件編碼不是 UTF-8

  • 編輯器保存為 UTF-8 without BOM
  • PHP 文件頭聲明:header('Content-Type: text/html; charset=utf-8');

3. 終端/瀏覽器顯示編碼不匹配

  • HTML meta: <meta charset="utf-8">
  • HTTP Header: Content-Type: text/html; charset=utf-8

4. MySQL 版本過舊不支持 utf8mb4

  • MySQL 5.5.3+ 才支持 utf8mb4
  • 舊版本只能用 utf8 (即 utf8mb3) 或 gbk

最佳實踐總結

  1. 新項目:DSN 指定 charset=utf8mb4,數據庫/表/列統一 utf8mb4
  2. 舊項目維護:若已是 gbk 體系,DSN 指定 charset=gbk 即可
  3. 避免:手動 SET NAMES、SET CHARACTER SET、INIT_COMMAND(易出時序問題)
  4. 驗證:部署後必跑字符集驗證腳本
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved