亂碼是 PHP 開發者最常見的噩夢,根因通常在於「瀏覽器解碼方式」與「實際輸出編碼」不一致。本文系統整理 PHP 頁面、資料庫、檔案、前端傳輸四大層面的編碼設定,建立一套標準化流程,徹底解決亂碼問題。
PHP header() 發送的 HTTP 標頭優先於 HTML <meta> 標籤,**必須在任何輸出前呼叫** (連空白行都不行)。
<?php
// UTF-8 (推薦)
header("Content-Type: text/html; charset=UTF-8");
// Big5/GBK (舊專案)
header("Content-Type: text/html; charset=GBK");
// 或
header("Content-Type: text/html; charset=BIG5");
?>
常見錯誤:Warning: Cannot modify header information - headers already sent → 檢查檔案開頭是否有 BOM、空白、HTML 註解、include 檔案輸出。
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<!-- HTML5 簡寫 -->
<meta charset="UTF-8">
作用:若無 HTTP Header (如靜態 HTML、CDN 快取),瀏覽器依此解碼。編碼名稱**大寫** (UTF-8, GBK, BIG5)。
<?php
$pdo = new PDO("mysql:host=localhost;dbname=test;charset=utf8mb4", 'user', 'pass');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
關鍵:DSN 加 charset=utf8mb4 (支援 emoji),**不需**額外執行 SET NAMES。
舊版 mysqli:
$mysqli = new mysqli('localhost', 'user', 'pass', 'test');
$mysqli->set_charset('utf8mb4'); // 必須在查詢前呼叫
CREATE DATABASE `my_db` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE `users` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(100) NOT NULL
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
統一使用 utf8mb4 + utf8mb4_unicode_ci (或 _0900_ai_ci MySQL 8.0+)。
BOM 會導致 Header 失效、輸出異常,嚴禁使用 UTF-8 with BOM。
<form accept-charset="UTF-8" method="post" action="...">
瀏覽器會依此編碼送出表單資料。
JS 內部字串為 UTF-16,傳輸時自動編為 UTF-8。後端直接接收即可,**勿**使用 iconv 轉碼。
<?php
// 接收 Big5 編碼資料
$input = $_POST['data'];
$utf8 = mb_convert_encoding($input, 'UTF-8', 'BIG-5'); // 或 CP950
// 輸出給 Big5 系統
$output = mb_convert_encoding($utf8_data, 'BIG-5', 'UTF-8');
echo $output;
使用 mb_convert_encoding() 或 iconv(),**明確指定來源與目標編碼**。
header('Content-Type: application/json; charset=UTF-8');
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
header() 在最前面、正確設定 charset?<meta charset> 一致?charset=utf8mb4?utf8mb4_unicode_ci?accept-charset="UTF-8"?JSON_UNESCAPED_UNICODE?全站統一 UTF-8 (utf8mb4),檔案無 BOM,Header 最優先,資料庫連線指定 charset,拒絕手動 iconv 轉碼。
遵循此法則,新專案將零亂碼;維護舊專案時,僅在介接邊界做明確轉碼,核心保持 UTF-8 純淨。