首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 解析 JavaScript escape 編碼:原理、反解函數與編碼範圍詳解

     編輯:PHP 相關知識

前端用 escape('腳本之家') 得到 %u811A%u672C%u4E4B%u5BB6,PHP urldecode 無法識別 %uXXXX 格式。本文提供完整反解函數、編碼原理分析、常見編碼範圍對照表,解決前後端編碼不互通問題。

問題場景

<a href="" onclick="window.open('list.php?kw=' + escape('腳本之家'))">點擊</a>
// 實際請求:list.php?kw=%u811A%u672C%u4E4B%u5BB6
// PHP urldecode($_GET['kw']) → 仍為 %u811A...  無法解碼

PHP 版 unescape 反解函數(核心)

<?php
function js_unescape($str) {
    $ret = '';
    $len = strlen($str);
    for ($i = 0; $i < $len; $i++) {
        if ($str[$i] === '%' && isset($str[$i+1]) && $str[$i+1] === 'u') {
            // %uXXXX (Unicode)
            $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 (ASCII)
            $ret .= urldecode(substr($str, $i, 3));
            $i += 2;
        } else {
            $ret .= $str[$i];
        }
    }
    return $ret;
}

// 使用:注意編碼轉換
$kw = js_unescape($_REQUEST['kw']);        // 得到 UTF-8
$kw = iconv('UTF-8', 'GBK//IGNORE', $kw); // 若項目為 GBK,需轉碼
echo $kw; // 輸出:腳本之家
?>

PHP 實現 escape 編碼(反向生成給 JS 用)

<?php
function phpescape($str) {
    $len = strlen($str);
    $out = '';
    for ($i = 0; $i < $len; $i++) {
        if (ord($str[$i]) >= 127) { // 多字節字符(中文)
            // 取雙字節(GBK 編碼下一個漢字 2 字節)
            $char = substr($str, $i, 2);
            $ucs2 = bin2hex(iconv('GBK', 'UCS-2', $char));
            $out .= '%u' . $ucs2;
            $i++; // 跳過第二字節
        } else { // ASCII
            $out .= '%' . dechex(ord($str[$i]));
        }
    }
    return $out;
}

// 更通用的 UTF-8 版本
function escape_utf8($str) {
    return preg_replace_callback('/./u', function($m) {
        $cp = mb_ord($m[0], 'UTF-8');
        return $cp < 128 ? '%' . strtoupper(dechex($cp)) : '%u' . strtoupper(str_pad(dechex($cp), 4, '0', STR_PAD_LEFT));
    }, $str);
}
?>

原理解析:正則拆解字符串

常見開源實現:

<?php
function phpEscape($str) {
    // 正則:匹配「雙字節字符(含中文)」或「連續 ASCII」
    preg_match_all('/[\x80-\xff].|[\x01-\x7f]+/', $str, $r);
    $ar = $r[0];
    foreach ($ar as $k => $v) {
        if (ord($v[0]) < 128) {
            $ar[$k] = rawurlencode($v); // ASCII 直接 urlencode
        } else {
            $ar[$k] = '%u' . bin2hex(iconv('GB2312', 'UCS-2', $v)); // 中文轉 UCS-2
        }
    }
    return join('', $ar);
}
?>

正則詳解:

  • [\x80-\xff].:匹配首字節在 0x80-0xFF 範圍的雙字節字符(GBK 中文首字節範圍)。
  • [\x01-\x7f]+:匹配連續 ASCII 字符(0x01-0x7F)。
  • 兩者互斥且覆蓋所有字符,preg_match_all 將字符串完整切分。

常見字符編碼範圍對照表(正則寫法參考)

編碼/語言十六進制範圍正則寫法 (PCRE)
GBK 雙字節整體0x00 - 0xFF[\x00-\xff]
ASCII0x20 - 0x7F[\x20-\x7f]
GBK 中文區0xA1 - 0xFF[\xa1-\xff] 或 [\x80-\xff]
UTF-8 中文 (基本區)U+4E00 - U+9FA5[\x{4e00}-\x{9fa5}] (需 u 修飾符)
韓文U+3130-U+318F, U+AC00-U+D7A3[\x{3130}-\x{318f}\x{ac00}-\x{d7a3}]
日文U+0800-U+4E00[\x{0800}-\x{4e00}]

JS 端對應解碼

// JS 原生 unescape 已棄用,現代瀏覽器仍支持但建議遷移
// 推薦:前後端統一用 encodeURIComponent / decodeURIComponent (UTF-8)
// 或 JSON 傳輸,避免手動編碼

最佳實踐建議

  1. 新項目禁用 escape/unescape:改用 encodeURIComponent (JS) + urldecode (PHP),均基於 UTF-8,無兼容性問題。
  2. 存量系統:使用本文 js_unescape 反解,並統一項目編碼為 UTF-8,省去 iconv 麻煩。
  3. API 傳中文:直接 json_encode,Header 設置 Content-Type: application/json; charset=utf-8,最省心。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved