問題背景
- 專案編碼:UTF-8(PHP、HTML、資料庫連線)
- 資料庫:SQL Server 2008,預設編碼 Chinese_PRC_CI_AS (代碼頁 936 / GBK)
- 現象:
json_encode() 輸出中文顯示為 null 或亂碼
根因分析
json_encode() 僅接受 UTF-8 編碼字串;若輸入含非 UTF-8 字元,會回傳 false (PHP 5.5+) 或將該字元編碼為 \u0000 / null。
- SQL Server 回傳資料為 GBK 編碼,PHP 未轉碼直接送入
json_encode。
解決方案:雙向編碼轉換
1. 讀取資料:GBK → UTF-8
header('Content-Type: application/json; charset=utf-8');
$conn = sqlsrv_connect($server, $connOpts);
$sql = "SELECT * FROM products WHERE id = ?";
$stmt = sqlsrv_query($conn, $sql, [$id]);
$row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
// 關鍵:將每個欄位由 GBK 轉 UTF-8
foreach ($row as $key => $val) {
if (is_string($val)) {
$row[$key] = iconv('GBK', 'UTF-8//IGNORE', $val);
}
}
echo json_encode($row, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
2. 寫入資料:UTF-8 → GBK
$name = $_POST['name']; // UTF-8
$nameGbk = iconv('UTF-8', 'GBK//IGNORE', $name);
$sql = "INSERT INTO products (name) VALUES (?)";
sqlsrv_query($conn, $sql, [$nameGbk]);
完整範例:員工資料增刪改查
<?php
header('Content-Type: application/json; charset=utf-8');
require '../conn.php'; // 返回 $conn (sqlsrv 連線)
$action = $_POST['action'] ?? 'list';
switch ($action) {
case 'list':
$stmt = sqlsrv_query($conn, "SELECT seq, employeeID, employeeName, department FROM employees");
$data = [];
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
foreach ($row as $k => $v) {
$row[$k] = is_string($v) ? iconv('GBK', 'UTF-8//IGNORE', $v) : $v;
}
$data[] = $row;
}
echo json_encode(['code' => 0, 'data' => $data], JSON_UNESCAPED_UNICODE);
break;
case 'save':
$seq = $_POST['seq'] ?? 0;
$id = iconv('UTF-8', 'GBK//IGNORE', $_POST['employeeID']);
$name = iconv('UTF-8', 'GBK//IGNORE', $_POST['employeeName']);
$dept = iconv('UTF-8', 'GBK//IGNORE', $_POST['department']);
if ($seq) {
$sql = "UPDATE employees SET employeeID=?, employeeName=?, department=?, updateTime=GETDATE() WHERE seq=?";
$params = [$id, $name, $dept, $seq];
} else {
$sql = "INSERT INTO employees (employeeID, employeeName, department, createTime, updateTime) VALUES (?, ?, ?, GETDATE(), GETDATE())";
$params = [$id, $name, $dept];
}
$ok = sqlsrv_query($conn, $sql, $params);
echo json_encode(['code' => $ok ? 0 : 1, 'msg' => $ok ? 'success' : 'fail']);
break;
}
?>
進階優化
- PDO + sqlsrv 驅動:使用
PDO::SQLSRV_ATTR_ENCODING => PDO::SQLSRV_ENCODING_UTF8 (驅動 5.8+ 支援),自動處理編碼轉換,無需手動 iconv。
- 資料庫層面:欄位改用
NVARCHAR 存儲 Unicode,並設定資料庫/欄位 Collation 為 Latin1_General_100_CI_AS_SC,配合 N'字串' 寫入,徹底避免編碼問題。
- 錯誤處理:
iconv 加 //IGNORE 忽略無法轉換字元,或用 //TRANSLIT 近似替換;生產環境建議記錄轉換失敗日誌。
驗證清單
- PHP 檔案儲存為 UTF-8 無 BOM。
- HTML 頁面
<meta charset="utf-8">。
- 資料庫連線字串包含
CharacterSet=UTF-8 (PDO) 或驅動層設定 UTF-8。
- 輸出 JSON 前
header('Content-Type: application/json; charset=utf-8')。
- 使用
JSON_UNESCAPED_UNICODE 讓中文直接顯示而非 \uXXXX。