JS escape() 生成 %uXXXX 格式,PHP urldecode() 無法處理。本文詳細解析編碼原理,提供多種 PHP 反解方案,並補充編碼範圍與正則應用知識。
// 前端
window.open('product_list.php?p_sort=' + escape('PHP開發資源網'));
// 產生 URL
// https://127.0.0.1/shop/product_list.php?p_sort=PHP%u5F00%u53D1%u8D44%u6E90%u7F51
// 後端 PHP
urldecode($_GET['p_sort']); // 無效,返回原字串
base64_decode($_GET['p_sort']); // 無效
<?php
function js_unescape($str) {
$ret = '';
$len = strlen($str);
for ($i = 0; $i < $len; $i++) {
if ($str[$i] == '%' && $str[$i+1] == 'u') { // %uXXXX
$val = hexdec(substr($str, $i+2, 4));
if ($val < 0x7f) $ret .= chr($val);
elseif ($val < 0x800) $ret .= chr(0xc0|($val>>6)) . chr(0x80|($val&0x3f));
else $ret .= chr(0xe0|($val>>12)) . chr(0x80|(($val>>6)&0x3f)) . chr(0x80|($val&0x3f));
$i += 5;
} elseif ($str[$i] == '%') { // %XX
$ret .= urldecode(substr($str, $i, 3));
$i += 2;
} else {
$ret .= $str[$i];
}
}
return $ret;
}
// 使用 (注意編碼轉換)
$decoded = js_unescape($_REQUEST['p_sort']); // UTF-8
echo iconv('UTF-8', 'GB2312', $decoded); // 轉為 GB2312 輸出
function phpescape($str) {
$sublen = strlen($str);
$returnString = "";
for ($i = 0; $i < $sublen; $i++) {
if (ord($str[$i]) >= 127) { // 非 ASCII
$tmpString = bin2hex(iconv("GB2312", "UCS-2", substr($str, $i, 2)));
// $tmpString = substr($tmpString,2,2).substr($tmpString,0,2); // Windows 可能需字節序調整
$returnString .= "%u" . $tmpString;
$i++;
} else {
$returnString .= "%" . dechex(ord($str[$i]));
}
}
return $returnString;
}
function phpEscape($str) {
preg_match_all("/[\x80-\xff].|[\x01-\x7f]+/", $str, $r);
$ar = $r[0];
foreach ($ar as $k => $v) {
if (ord($v[0]) < 128) { // ASCII 字符串
$ar[$k] = rawurlencode($v);
} else { // 中文字符 (雙位元組)
$ar[$k] = "%u" . bin2hex(iconv("GB2312", "UCS-2", $v));
}
}
return join("", $ar);
}
[\x80-\xff].:匹配雙位元組字符 (首位元組 0x80-0xFF + 任意第二位元組) → 中文[\x01-\x7f]+:匹配連續單位元組字符 (0x01-0x7F) → ASCII 字串rawurlencode,中文轉 UCS-2 後 bin2hexfunction unescape($str) {
$str = rawurldecode($str);
preg_match_all("/%u.{4}|.{4};|\d+;|.+/U", $str, $r);
$ar = $r[0];
foreach ($ar as $k => $v) {
if (substr($v, 0, 2) == "%u") {
$ar[$k] = iconv("UCS-2", "GBK", pack("H4", substr($v, -4)));
} elseif (substr($v, 0, 3) == "") {
$ar[$k] = iconv("UCS-2", "GBK", pack("H4", substr($v, 3, -1)));
} elseif (substr($v, 0, 2) == "") {
$ar[$k] = iconv("UCS-2", "GBK", pack("n", substr($v, 2, -1)));
}
}
return join("", $ar);
}
| 編碼 | 範圍 | 說明 |
|---|---|---|
| GBK | \x00-\xff | 雙位元組完整範圍 |
\x20-\x7f | ASCII 可見字符 | |
\xa1-\xff | 中文區 | |
\x80-\xff | 中文區 (含擴展) | |
| UTF-8/Unicode | \u4e00-\u9fa5 | 中文 CJK 統一表意文字 |
\x3130-\x318F | 韓文字母 | |
\xAC00-\xD7A3 | 韓文音節 | |
\u0800-\u4e00 | 日文區塊 |
<?php
// 移除中文 (GBK)
$str = preg_replace("/([\x80-\xff])/", "", $str);
// 移除中文 (UTF-8)
$str = preg_replace("/([\x{4e00}-\x{9fa5}])/u", "", $str);
// 提取中文
preg_match_all("/[\x{4e00}-\x{9fa5}]+/u", $str, $match);
理解編碼本質與位元組結構,才能編寫正確、高效的編解碼邏輯。