前端傳遞資料常將中文編碼為 Unicode (\u4e2d\u6587) 或 Escape (%u4e2d%u6587) 格式。PHP 需解碼還原為 UTF-8 字串。本文提供完整解碼函式、編碼對照表與效能優化版。
<?php
/**
* 解碼 Unicode (\uXXXX) 與 HTML 實體 (〹) 為 UTF-8
* @param string $str 含編碼字串
* @return string 解碼後 UTF-8 字串
*/
function uni_decode($str) {
// 1. 匹配 HTML 實體:數字;
preg_match_all('/(\d{2,5});/', $str, $htmlMatches);
// 2. 匹配 JS Unicode:\uXXXX 或 %uXXXX
preg_match_all('/[\\\\%]u([0-9a-fA-F]{4})/', $str, $jsMatches);
// 合併所有需替換的原始片段
$sources = array_merge($htmlMatches[0], $jsMatches[0]);
// 轉換為 Unicode 碼點
$codepoints = [];
foreach ($htmlMatches[1] as $cp) $codepoints[] = (int)$cp;
foreach ($jsMatches[1] as $cp) $codepoints[] = hexdec($cp);
// 逐一替換
foreach ($codepoints as $i => $cp) {
$utf8 = codepointToUtf8($cp);
$str = str_replace($sources[$i], $utf8, $str);
}
return $str;
}
/**
* Unicode 碼點轉 UTF-8 字串
*/
function codepointToUtf8($cp) {
if ($cp < 0x80) {
return chr($cp);
} elseif ($cp < 0x800) {
return chr(0xC0 | ($cp >> 6)) . chr(0x80 | ($cp & 0x3F));
} elseif ($cp < 0x10000) {
return chr(0xE0 | ($cp >> 12)) . chr(0x80 | (($cp >> 6) & 0x3F)) . chr(0x80 | ($cp & 0x3F));
} elseif ($cp < 0x200000) {
return chr(0xF0 | ($cp >> 18)) . chr(0x80 | (($cp >> 12) & 0x3F)) . chr(0x80 | (($cp >> 6) & 0x3F)) . chr(0x80 | ($cp & 0x3F));
}
return ''; // 超出範圍
}
// 測試
$str = '%u5927%u5BB6%u597D%uFF0C%E6%88%91%E6%98%AF%E5%AD%A4%E9%AD%82%EF%BC%1
\u8FD9\u662F\u6D4B\u8BD5\u6587\u672C\uFF01';
echo uni_decode($str);
// 輸出:大家好,我是孤魂!這是測試文字!
?>
<?php
function uniDecodeFast($str) {
return preg_replace_callback(
'/(?:(\d{2,5});|[\\\\%]u([0-9a-fA-F]{4}))/',
function($m) {
$cp = $m[1] ? (int)$m[1] : hexdec($m[2]);
return codepointToUtf8($cp);
},
$str
);
}
?>
<?php
/**
* PHP 實作 JavaScript escape()
* @param string $str UTF-8 字串
* @return string Escape 編碼字串
*/
function php_escape($str) {
$result = '';
$len = strlen($str);
for ($i = 0; $i < $len; $i++) {
$c = $str[$i];
$ord = ord($c);
// ASCII 字母數字及 * @ - _ + . / 保持不變
if (($ord >= 48 && $ord <= 57) || // 0-9
($ord >= 65 && $ord <= 90) || // A-Z
($ord >= 97 && $ord <= 122) || // a-z
strpos('*@-_+./', $c) !== false) {
$result .= $c;
} elseif ($ord < 128) {
// 其他 ASCII:%XX
$result .= '%' . strtoupper(dechex($ord));
} else {
// 多位元組字元:%uXXXX (需取得 Unicode 碼點)
// 簡易版:假設 UTF-8,轉換為 Unicode
$utf8Char = $c;
$bytes = 1;
if (($ord & 0xE0) === 0xC0) $bytes = 2;
elseif (($ord & 0xF0) === 0xE0) $bytes = 3;
elseif (($ord & 0xF8) === 0xF0) $bytes = 4;
for ($j = 1; $j < $bytes; $j++) {
$utf8Char .= $str[++$i];
}
// UTF-8 轉 Unicode 碼點
$cp = 0;
if ($bytes === 1) $cp = ord($utf8Char);
elseif ($bytes === 2) $cp = ((ord($utf8Char[0]) & 0x1F) << 6) | (ord($utf8Char[1]) & 0x3F);
elseif ($bytes === 3) $cp = ((ord($utf8Char[0]) & 0x0F) << 12) | ((ord($utf8Char[1]) & 0x3F) << 6) | (ord($utf8Char[2]) & 0x3F);
elseif ($bytes === 4) $cp = ((ord($utf8Char[0]) & 0x07) << 18) | ((ord($utf8Char[1]) & 0x3F) << 12) | ((ord($utf8Char[2]) & 0x3F) << 6) | (ord($utf8Char[3]) & 0x3F);
$result .= '%u' . strtoupper(str_pad(dechex($cp), 4, '0', STR_PAD_LEFT));
}
}
return $result;
}
// 測試
echo php_escape('中文測試123');
// 輸出:%u4E2D%u6587%u6E2C%u8A66123
?>
| 格式 | 範例 | 來源 | PHP 解碼關鍵 |
|---|---|---|---|
| HTML 十進制實體 | 中文 | 網頁顯示 | preg_match('/(\d+);/') |
| HTML 十六進制實體 | 中文 | 網頁顯示 | preg_match('/([0-9A-F]+);/i') |
| JS Unicode | \u4e2d\u6587 | JavaScript 字串 | preg_match('/\\\\u([0-9A-F]{4})/i') |
| Escape | %u4E2D%u6587 | JS escape()、舊版 AJAX | preg_match('/%u([0-9A-F]{4})/i') |
| URL 編碼 | %E4%B8%AD%E6%96%87 | HTTP 參數傳遞 | urldecode() / rawurldecode() |
<?php
function decodeMixedInput($input) {
// 1. 先 URL 解碼(處理 %20, %E4%B8%AD...)
$decoded = urldecode($input);
// 2. 再解碼 Unicode/Escape/HTML 實體
$decoded = uniDecodeFast($decoded);
// 3. 移除可能殘留的控制字元
$decoded = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/', '', $decoded);
return $decoded;
}
// 範例:AJAX 接收
$raw = $_POST['content'] ?? '';
$clean = decodeMixedInput($raw);
// 存入資料庫、全文檢索等
?>
escape(escape(str)) 導致 %25u4E2D,解決:多次解碼直到不變化。😀 (U+1F600) 需 4 bytes UTF-8,Escape 為 %uD83D%uDE00 (UTF-16 代理對),codepointToUtf8 需支援 >0xFFFF。preg_replace_callback 單次通過,避免多次 preg_match_all + 迴圈替換。PHP 解碼 Unicode/Escape 核心:正則提取碼點 → 碼點轉 UTF-8 位元組序列。通用函式 uni_decode() 處理 HTML 實體與 JS Unicode,php_escape() 反向編碼。生產環境建議統一使用 UTF-8 + JSON 傳輸,避免多重編碼帶來的解碼複雜度與安全風險。