PHP 頁面亂碼 90% 來自三個編碼環節不一致:檔案儲存編碼、頁面宣告編碼、資料庫連線編碼。本文系統拆解各環節機制、常見錯誤組合與對應解決方案,並說明 Apache 預設編碼干擾問題。
header('Content-Type: text/html; charset=utf-8'); (必須在任何輸出前呼叫)<meta charset="utf-8"> (HTML5 簡寫) 或 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">SET NAMES utf8mb4 (等同設定 `character_set_client`、`connection`、`results` 三者)。| 場景 | 現象 | 原因 | 解決 |
|---|---|---|---|
| 資料庫 utf8mb4,頁面宣告 GB2312 | 中文亂碼 | 連線編碼未設定,MySQL 用預設 latin1 傳輸,頁面以 GB2312 解碼 UTF-8 位元組 | 查詢前執行 $pdo->exec("SET NAMES gbk"); 或統一改用 UTF-8 |
| 檔案存 UTF-8,頁面宣告 GB2312 | 中文亂碼 | 檔案位元組為 UTF-8,瀏覽器用 GB2312 解碼 | 統一編碼:檔案存 GB2312 (ANSI) 或頁面宣告 UTF-8 |
| 虛擬主機 Apache 強制 AddDefaultCharset UTF-8 | 頁面宣告 GB2312 但瀏覽器仍用 UTF-8 | HTTP Header 優先級 > Meta,Apache 發送 Content-Type: text/html; charset=utf-8 | .htaccess 加 AddDefaultCharset GB2312 或聯繫管理員修改 vhost 設定 |
| 修改檔案後亂碼 (ZendStudio 等) | 編輯器以錯誤編碼開啟並儲存 | IDE 不自動偵測編碼,依預設開啟 | 設定 IDE 專案編碼為 UTF-8,或用支援自動偵測編碼的編輯器 |
<?php
// PDO (推薦)
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'user', 'pass');
// MySQLi
$mysqli = new mysqli('localhost', 'user', 'pass', 'test');
$mysqli->set_charset('utf8mb4'); // 必須在查詢前呼叫
// 手動執行 (所有介面通用)
$pdo->exec("SET NAMES utf8mb4");
// 等同於
$pdo->exec("SET character_set_client = utf8mb4");
$pdo->exec("SET character_set_connection = utf8mb4");
$pdo->exec("SET character_set_results = utf8mb4");
注意:SET NAMES utf8 (無 mb4) 僅支援 3 位元組字元,**無法儲存 Emoji (4 位元組)**,生產環境請用 utf8mb4。
# .htaccess (放於網站根目錄)
AddDefaultCharset GB2312
# 或
AddDefaultCharset UTF-8
# 若無 .htaccess 權限,修改虛擬主機設定
#
# AddDefaultCharset UTF-8
#
原理:Apache `AddDefaultCharset` 發送 HTTP Header,優先級高於頁面 Meta。虛擬主機用戶只能用 `.htaccess` 覆蓋。
header('Content-Type: text/html; charset=utf-8');。SET NAMES utf8mb4。亂碼本質是編碼不一致。建立「全站 UTF-8 無 BOM、資料庫 utf8mb4、連線 SET NAMES utf8mb4、HTTP Header 宣告 UTF-8」四位一體規範,並納入專案規範文件與 Code Review 清單,可徹底根治亂碼問題。