專案環境:PHP 檔案與網頁均為 UTF-8 編碼,資料庫為 SQL Server 2008 預設編碼(Code Page 936,即 GBK)。直接使用 json_encode() 輸出查詢結果時,中文欄位顯示為空或亂碼。
採用「查詢語句轉碼 + 資料陣列遞迴轉碼」雙重保障:
sqlsrv_query()。urlencode → json_encode → urldecode 三步走,繞過 json_encode 對中文 Unicode 轉義的限制。employeeGet.php<?php
header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-cache');
require '../conn.php';
require '../share/json_gbk2utf8.php';
// 日期欄位轉字串避免毫秒顯示問題
$query = 'SELECT seq, employeeID, employeeName, department, position, sex, birthday, entryTime, description,
CONVERT(varchar(20), createTime, 120) AS createTime,
CONVERT(varchar(20), updateTime, 120) AS updateTime
FROM employees';
// 查詢語句轉 GBK
$query = iconv('UTF-8', 'GBK//IGNORE', $query);
$arr = [];
if ($result = sqlsrv_query($conn, $query)) {
while ($row = sqlsrv_fetch_array($result, SQLSRV_FETCH_ASSOC)) {
$arr[] = $row;
}
}
// 使用自訂 JSON 函數輸出
echo JSON($arr);
?>
json_gbk2utf8.php<?php
/**
* 遞迴處理陣列,對每個值執行指定函數
* @param array &$array 目標陣列(引用)
* @param callable $function 處理函數
* @param bool $applyToKeys 是否同時處理鍵名
*/
function arrayRecursive(array &$array, callable $function, bool $applyToKeys = false): void {
static $depth = 0;
if (++$depth > 1000) {
die('Possible deep recursion attack');
}
foreach ($array as $key => &$value) {
if (is_array($value)) {
arrayRecursive($value, $function, $applyToKeys);
} else {
// GBK → UTF-8
$value = iconv('GBK//IGNORE', 'UTF-8', $value);
// 執行使用者函數(如 urlencode)
$value = $function($value);
$array[$key] = $value;
}
if ($applyToKeys && is_string($key)) {
$newKey = $function($key);
if ($newKey !== $key) {
$array[$newKey] = $array[$key];
unset($array[$key]);
}
}
}
$depth--;
}
/**
* 將陣列安全轉為 JSON 字串(支援中文)
* @param array $array
* @return string
*/
function JSON(array $array): string {
arrayRecursive($array, 'urlencode', true);
$json = json_encode($array);
return urldecode($json);
}
?>
新增/更新 SQL 語句同樣需轉碼:
$query = "INSERT INTO employees (employeeID, employeeName, department) VALUES (N'{$employeeID}', N'{$employeeName}', N'{$department}')";
// 或 UPDATE ...
$query = iconv('UTF-8', 'GBK//IGNORE', $query); // 關鍵步驟
sqlsrv_query($conn, $query);
N'字串':T-SQL 前綴 N 表示 Unicode 字串常值,配合 GBK 查詢語句可正確寫入中文。iconv('GBK//IGNORE', 'UTF-8', ...)://IGNORE 參數可跳過無法轉換的字元,避免中斷。urlencode → json_encode → urldecode:利用 URL 編碼將中文轉為 ASCII 安全字元,繞過 json_encode 的 Unicode 轉義,最後還原。若環境允許,建議直接使用 json_encode($data, JSON_UNESCAPED_UNICODE),並確保資料庫連線與 PHP 內部編碼皆為 UTF-8(sqlsrv_connect 設定 CharacterSet => 'UTF-8'),可徹底省去手動轉碼麻煩。