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

PHP + MySQL UTF-8 編碼遷移全棧指南:從文件、數據庫到 HTTP 頭的零亂碼實踐

     編輯:PHP 相關知識

徹底解決 PHP + MySQL UTF-8 亂碼:全棧統一編碼實戰

「亂碼」本質是**編碼不一致**。實現「全棧 UTF-8」需覆蓋:文件存儲、PHP 代碼、數據庫、表結構、連接、HTTP 響應、前端頁面、文件下載。本文提供標準化遷移清單。

一、PHP 文件與代碼層

  1. 文件編碼: 所有 `.php` `.html` `.js` `.css` 文件 **統一保存為 UTF-8 無 BOM** (編輯器/IDE 右下角設置)。BOM 會導致 `session_start()` / `header()` 失效 (輸出在頭部前)。
  2. HTTP 響應頭: 入口文件最頂部 (或公共配置) 設置:
    header('Content-Type: text/html; charset=utf-8');
  3. 內部字符串處理: 多字節函數加 `mb_` 前綴:`mb_strlen`、`mb_substr`、`mb_strpos`,並在 `php.ini` 設置 `mbstring.internal_encoding = UTF-8`。

二、MySQL 數據庫層

1. 創建庫/表指定字符集

-- 庫級
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+ 新規範)。

2. 連接層設置 (極其關鍵,最常遺漏)

// 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` 三個會話變量,保證「客戶端發送 → 服務端處理 → 結果返回」全鏈路編碼一致。

3. 遷移存量數據 (已有 latin1/utf8 表)

-- 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 頁面

...

四、特殊場景處理

1. 文件下載中文文件名

$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}");
}

2. 截取 UTF-8 字符串不斷字

// 錯誤: 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 . '...';
}

3. cURL/對外 API 請求

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 無 BOM
  • [ ] `header('Content-Type: ... charset=utf-8')` 生效
  • [ ] 數據庫/表/列 全 `utf8mb4`
  • [ ] 連接代碼 `SET NAMES utf8mb4` / `charset=utf8mb4`
  • [ ] 存量數據 `CONVERT TO CHARACTER SET utf8mb4`
  • [ ] 前端 ``
  • [ ] 中文文件下載、截取、cURL 均正常
  • [ ] 測試 Emoji 😀🎉🚀 寫入/讀出/顯示正常

「全棧 UTF-8」不是一句口號,是每一層配置的精確對齊。建立項目編碼規範文檔,納入 Code Review 清單,從源頭杜絕亂碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved