「亂碼」本質是**編碼不一致**。實現「全棧 UTF-8」需覆蓋:文件存儲、PHP 代碼、數據庫、表結構、連接、HTTP 響應、前端頁面、文件下載。本文提供標準化遷移清單。
header('Content-Type: text/html; charset=utf-8');
-- 庫級
CREATE DATABASE `my_db` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 表級 (若庫已指定可繼承, 但建議顯式聲明)
CREATE TABLE `users` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`bio` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
關鍵: 必須用 **`utf8mb4`** 而非 `utf8` (MySQL 的 `utf8` 只有 3 字節,**不支援 Emoji/生僻字**),校對規則推薦 `utf8mb4_unicode_ci` (準確排序) 或 `utf8mb4_0900_ai_ci` (MySQL 8.0+ 新規範)。
// PDO (推薦)
$pdo = new PDO("mysql:host=localhost;dbname=my_db;charset=utf8mb4", $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"
]);
// mysqli (過程式)
$mysqli = new mysqli('localhost', $user, $pass, 'my_db');
$mysqli->set_charset('utf8mb4'); // 單行搞定, 等同於下面兩句
// $mysqli->query("SET NAMES utf8mb4");
// $mysqli->query("SET CHARACTER SET utf8mb4");
原理: `SET NAMES utf8mb4` 同時設置 `character_set_client`、`connection`、`results` 三個會話變量,保證「客戶端發送 → 服務端處理 → 結果返回」全鏈路編碼一致。
-- 1. 修改表默認字符集 (僅影響新列) ALTER TABLE `users` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 2. 轉換現有列數據 (關鍵步驟, 會重建表, 鎖表, 大表請用 pt-online-schema-change/gh-ost) ALTER TABLE `users` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 3. 驗證 SHOW CREATE TABLE `users`; -- 檢查 CHARSET=utf8mb4 SELECT HEX(`name`) FROM `users` WHERE `name` LIKE '%😀%'; -- 檢查 Emoji 存儲為 4 字節 (F0 9F 98 80)
UTF-8 頁面
...
$filename = '中文報表.xlsx';
// 瀏覽器兼容性處理
$encoded = rawurlencode($filename); // RFC 5987 標準
// 現代瀏覽器
header("Content-Disposition: attachment; filename*=UTF-8''{$encoded}");
// 兼容舊 IE (可選)
if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false) {
header("Content-Disposition: attachment; filename={$encoded}");
}
// 錯誤: substr($str, 0, 10) 會按字節截斷, 導致亂碼
// 正確: mb_substr (需 mbstring 擴展)
$brief = mb_substr($str, 0, 10, 'UTF-8') . '...';
// 無 mbstring 環境兜底 (按字節安全截取, 不產生無效字節序列)
function safe_substr($str, $len) {
if (strlen($str) <= $len) return $str;
$r = substr($str, 0, $len);
// 修正結尾不完整字節
while (strlen($r) > 0 && (ord($r[-1]) & 0xC0) === 0x80) { // 10xxxxxx 續字節
$r = substr($r, 0, -1);
}
return $r . '...';
}
curl_setopt($ch, CURLOPT_ENCODING, 'gzip, deflate'); // 自動解壓
// 發送 JSON 明確聲明編碼
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json; charset=utf-8',
'Accept: application/json; charset=utf-8'
]);
「全棧 UTF-8」不是一句口號,是每一層配置的精確對齊。建立項目編碼規範文檔,納入 Code Review 清單,從源頭杜絕亂碼。