字元集轉換是跨系統整合、舊專案維護、多語言網站的常態痛點。本文分析 ThinkPHP 框架中的 auto_charset() 函式,解析其支援字串、陣列、物件遞迴轉換、多編碼別名正規化、雙引擎相容 (mbstring/iconv) 的設計智慧,並提供現代化改良版本。
function auto_charset($fContents, $from = '', $to = '') {
// 1. 無需轉換情況:編碼相同、空值、非字串標量
if (strtoupper($from) === strtoupper($to) || empty($fContents) || (is_scalar($fContents) && !is_string($fContents))) {
return $fContents;
}
// 2. 編碼別名正規化:UTF8 → utf-8
$from = strtoupper($from) === 'UTF8' ? 'utf-8' : $from;
$to = strtoupper($to) === 'UTF8' ? 'utf-8' : $to;
// 3. 字串轉換:優先 mb_convert_encoding,其次 iconv
if (is_string($fContents)) {
if (function_exists('mb_convert_encoding')) {
return mb_convert_encoding($fContents, $to, $from);
} elseif (function_exists('iconv')) {
return iconv($from, $to, $fContents);
} else {
exit('轉換失敗:缺少 mbstring 或 iconv 擴充');
}
}
// 4. 陣列遞迴轉換:鍵值皆轉換,鍵名變更時重建索引
elseif (is_array($fContents)) {
foreach ($fContents as $key => $val) {
$_key = auto_charset($key, $from, $to);
$fContents[$_key] = auto_charset($val, $from, $to);
if ($key !== $_key) unset($fContents[$key]);
}
return $fContents;
}
// 5. 物件屬性遞迴轉換
elseif (is_object($fContents)) {
$vars = get_object_vars($fContents);
foreach ($vars as $key => $val) {
$fContents->$key = auto_charset($val, $from, $to);
}
return $fContents;
}
return $fContents;
}
UTF8/utf-8 寫法,相容不同庫的慣例。mb_convert_encoding,無則退回 iconv,兩者皆無則明確報錯。function auto_charset(mixed $data, string $from = 'utf-8', string $to = 'utf-8'): mixed {
// 正規化編碼名稱
$from = str_ireplace('utf8', 'utf-8', $from);
$to = str_ireplace('utf8', 'utf-8', $to);
// 無需轉換
if (strcasecmp($from, $to) === 0 || $data === null || $data === false) {
return $data;
}
// 字串轉換
if (is_string($data)) {
if (extension_loaded('mbstring')) {
return mb_convert_encoding($data, $to, $from);
}
if (extension_loaded('iconv')) {
return @iconv($from, $to, $data) ?? $data; // 失敗回傳原值
}
trigger_error('缺少 mbstring 或 iconv 擴充,無法執行編碼轉換', E_USER_WARNING);
return $data;
}
// 陣列遞迴 (使用引用修改原陣列,效能更好)
if (is_array($data)) {
foreach ($data as $key => &$value) {
$newKey = auto_charset($key, $from, $to);
$value = auto_charset($value, $from, $to);
if ($newKey !== $key) {
unset($data[$key]);
$data[$newKey] = $value;
}
}
unset($value); // 解除引用
return $data;
}
// 物件屬性遞迴 (僅公開屬性)
if (is_object($data)) {
foreach ($data as $key => &$value) {
$value = auto_charset($value, $from, $to);
}
return $data;
}
return $data;
}
function_exists() 更語意化。trigger_error 替代 exit,不中斷程式流程。<?php
// 1. 字串轉換
$gbkStr = '中文測試';
$utf8Str = auto_charset($gbkStr, 'gbk', 'utf-8');
// 2. $_POST/$_GET 批次轉碼 (舊專案接收 GBK 資料)
$_POST = auto_charset($_POST, 'gbk', 'utf-8');
$_GET = auto_charset($_GET, 'gbk', 'utf-8');
// 3. 資料庫查詢結果物件轉碼
$stmt = $pdo->query('SELECT * FROM legacy_table');
while ($row = $stmt->fetch(PDO::FETCH_OBJ)) {
$row = auto_charset($row, 'big5', 'utf-8');
// 現在 $row->field 皆為 UTF-8
}
// 4. JSON 輸出前確保編碼
$response = ['data' => $rows, 'msg' => '成功'];
echo json_encode(auto_charset($response, 'utf-8', 'utf-8')); // 確保無雜訊
?>
$from 參數,減少判斷開銷。mbstring,效能與正確性優於 iconv。auto_charset 展示了處理編碼轉換的工程思維:防禦性輸入檢查、多引擎相容、結構保持遞迴、錯誤優雅降級。現代 PHP 專案建議統一 UTF-8 全鏈路,但維護舊系統、對接第三方 API 時,這類健壯的工具函式仍是救命稻草。