zoukankan      html  css  js  c++  java
  • PHP的URL编码解码与原理、自定义实现

    PHP实现URL编码与解码时,参考的是RFC 1738与RFC 3986:

    RFC 1738: http://www.faqs.org/rfcs/rfc1738.html

    RFC 3986: http://www.faqs.org/rfcs/rfc3986.html

    其中rawurlencode()方法在PHP5.3.0之前,会依据RFC 1738,将波浪线~视为不安全字符,编码成%7E。

    在PHP5.3.0之后则符合RFC 3986标准,将~号作为无限制字符,根据URL所处部位,可以不进行编码(通常也如此)。在PHP5.3.4之后,~则完全不编码。

    rawurlencode主要的依据还是RFC 3986,其中不进行编码的字符包括:

    数字,大小写字母,点号,下划线,减号,波浪号            (即: 0-9A-Za-z._-~)

    其它都采用8位(一个字节)的%XX形式来编码

    urlencode()方法,则采用非标准的技术,其中不进行编码的字符包括:

    数字,大小写字母,点号,下划线,减号  (即: 0-9A-Za-z._-)

    空格编码成+号

    其它都采用8位(一个字节)的%XX形式来编码

    可以看到,与urlencode()与rawurlencode()不同在于对波浪线空格的处理。相同点在于对数字大小写字母减号 - 点号 . 下划线 _ 都不进行编码。

    PHP文档中对urlencode()rawurlencode()的说明:

    urlencode ( string $str ) : string    — 编码 URL 字符串 

    返回值

    返回字符串,此字符串中除了 -_. 之外的所有非字母数字字符都将被替换成百分号(%)后跟两位十六进制数,空格则编码为加号(+)。

    此编码与 WWW 表单 POST 数据的编码方式是一样的,

    同时与 application/x-www-form-urlencoded 的媒体类型编码方式一样。

    由于历史原因,此编码在将空格编码为加号(+)方面与  RFC3986 编码(参见 rawurlencode())不同。 

     

    rawurlencode ( string $str ) : string     — 按照 RFC 3986 对 URL 进行编码

    返回值

    返回字符串,此字符串中除了 -_. 之外的所有非字母数字字符都将被替换成百分号(%)后跟两位十六进制数。

    这是在 » RFC 3986 中描述的编码,是为了保护原义字符以免其被解释为特殊的 URL 定界符,同时保护 URL 格式以免其被传输媒体(像一些邮件系统)使用字符转换时弄乱。

     

    实例:

    echo urlencode("中文(zh_CN) : 2020.11~2020.12--%20+Days[!$*',%#?]") . nl2br(PHP_EOL);

     在PHP文件编码为GBK时,显示结果:

     %D6%D0%CE%C4%28zh_CN%29+%3A+2020.11%7E2020.12--%2520%2BDays%5B%21%24%2A%27%2C%25%23%3F%5D

    urlencode()在GBK编码下,可以看到:

          中文两字被编码成 %D6%D0%CE%C4  这是它的GBK编码,每个字节(8位)前都加了%

          空格被编码成   

          ~被编码成  %7E 

          数字、字母、. _ - 号都不被编码

          其它的特殊字符都编码为%XX形式

     在PHP文件编码为UTF-8时,显示结果:

      %E4%B8%AD%E6%96%87%28zh_CN%29+%3A+2020.11%7E2020.12--%2520%2BDays%5B%21%24%2A%27%2C%25%23%3F%5D

    urlencode()的在UTF-8编码下,可以看到:

          中文两字被编码成  %E4%B8%AD%E6%96%87  这是它的UTF-8编码,每个字节(8位)前都加了%

          其它的与GBK编码结果一致。

          

    echo rawurlencode("中文(zh_CN) : 2020.11~2020.12--%20+Days[!$*',%#?]") . nl2br(PHP_EOL);

      在PHP文件编码为GBK时,显示结果:

      %D6%D0%CE%C4%28zh_CN%29%20%3A%202020.11~2020.12--%2520%2BDays%5B%21%24%2A%27%2C%25%23%3F%5D

    rawurlencode()在GBK编码下,可以看到:

          中文两字被编码成 %D6%D0%CE%C4 这是它的GBK编码,每个字节(8位)前都加了%

          空格被编码成  %20 

          数字、字母、. _ - ~ 号都不被编码

          其它的特殊字符都编码为%XX形式

     在PHP文件编码为UTF-8时,显示结果:

      %E4%B8%AD%E6%96%87%28zh_CN%29%20%3A%202020.11~2020.12--%2520%2BDays%5B%21%24%2A%27%2C%25%23%3F%5D

    rawurlencode()的在UTF-8编码下,可以看到:

          中文两字被编码成  %E4%B8%AD%E6%96%87  这是它的UTF-8编码,每个字节(8位)前都加了%

          其它的与GBK编码结果一致。

     

     

    通过以上的例子,我们可以发现,urlencode()与rawurlencode()在PHP下自己也可以很方便实现,其原理就是:

    不管文件编码是GBK还是UTF-8, 除了数字、大小写字母、._-号,以及特殊处理空格及波浪线,其它字符(单字节的ASCII码特殊字符,双字节的GBK或不定长字节的UTF-8)都是各字节的十六进制(两位)大写形式前加%(即%XX)

    自定义实现urlencode()及rawurlencode()----兼容RFC 3986:

    function my_urlencode($str,$raw=false,$ebcdic=true){
        $url = '';
        for($i=0;$i<strlen($str);$i++){
            $ascii = ord(substr($str,$i,1));
            
            if($ascii==0x2D || $ascii==0x2E || $ascii==0x5F || ($ascii>=0x30 && $ascii<=0x39) || ($ascii>=0x41 && $ascii<=0x5A) || ($ascii>=0x61 && $ascii<=0x7A) ){//_.-以及数字、字母不转化
                $url .= chr($ascii);
            }else{
                if($ascii==0x20 && !$raw){//空格在非raw情况下,转化为+
                    $url .= '+';
                }else if($ascii==0x7e && $raw && $ebcdic){//波浪线在raw情况下不转化
                    $url .= chr($ascii);
                }else{
                    $url .= '%' . str_pad(strtoupper(dechex($ascii)),2,"0",STR_PAD_LEFT);
                }
            }
        }
        
        return $url;
    }
    echo my_urlencode("中文+_zh .%- 2020~") . nl2br(PHP_EOL);

    PHP文件编码为GBK时下显示:%D6%D0%CE%C4%2B_zh+.%25-+2020%7E

    PHP文件编码为UTF-8时下显示:%E4%B8%AD%E6%96%87%2B_zh+.%25-+2020%7E

    echo my_urlencode("中文+_zh .%- 2020~",true) . nl2br(PHP_EOL);

    PHP文件编码为GBK时下显示:%D6%D0%CE%C4%2B_zh%20.%25-%202020~

    PHP文件编码为UTF-8时下显示:%E4%B8%AD%E6%96%87%2B_zh%20.%25-%202020~

    echo my_urlencode("中文+_zh .%- 2020~",true,false) . nl2br(PHP_EOL);

    PHP文件编码为GBK时下显示:%D6%D0%CE%C4%2B_zh%20.%25-%202020%7E

    PHP文件编码为UTF-8时下显示:%E4%B8%AD%E6%96%87%2B_zh%20.%25-%202020%7E

    自定义实现urldecode()及rawurldecode()

    function my_urldecode($str,$raw=false){
        !$raw && $str = str_replace('+','%20',$str);//非raw的话需解码+号为空格
        
        $url = '';
        for($i=0;$i<strlen($str);$i++){
            $c = substr($str,$i,1);
            if($c=='%'){
                $url .= hex2bin(substr($str,$i+1,2));
                $i = $i+2;
                if($i>=strlen($str)) break;
            }else{//_.-~以及数字、字母
                $url .= $c;
            }
        }
        
        return $url;
    }
    echo my_urldecode('%D6%D0%20%7E+~') . nl2br(PHP_EOL);
    echo my_urldecode('%D6%D0%20%7E+~',true) . nl2br(PHP_EOL);

    PHP文件为GBK下显示:

    中 ~  ~
    中 ~+~

    另外还可以对不是符合URL编码的字符串进行解码而不出错的情况,修改下:

    function my_urldecode_ext($str,$raw=false){
        !$raw && $str = str_replace('+','%20',$str);//非raw的话需解码+号为空格
        
        $url = '';
        $len = strlen($str);
        for($i=0;$i<$len;$i++){
            
            $c = substr($str,$i,1);
            if($c=='%'){
                if($i+1<=$len){
                    $c2 = substr($str,$i+1,1);
                    if($i+2<=$len){
                        $c3 = substr($str,$i+2,1);
                        if((ord($c2)>=0x30 && ord($c2)<=0x39) || (ord($c2)>=0x41 && ord($c2)<=0x46) || (ord($c2)>=0x61 && ord($c2)<=0x66)){
                            if((ord($c3)>=0x30 && ord($c3)<=0x39) || (ord($c3)>=0x41 && ord($c3)<=0x46) || (ord($c3)>=0x61 && ord($c3)<=0x66)){
                                $url .= hex2bin($c2.$c3);
                                $i = $i+2;
                            }else{
                                $url .= hex2bin("0".$c2);
                                $i = $i+1;
                            }
                        }else{
                            $url .= '%';
                        }
                    }else{
                        if((ord($c2)>=0x30 && ord($c2)<=0x39) || (ord($c2)>=0x41 && ord($c2)<=0x46) || (ord($c2)>=0x61 && ord($c2)<=0x66)){
                            $url .= hex2bin("0".$c2);
                            $i = $i+1;
                        }else{
                            $url .= '%';
                        }
                    }
                }else{
                    $url .= '%';
                }
                
                if($i>=strlen($str)) break;
            }else{//_.-~以及数字、字母
                $url .= $c;
            }
        }
        
        return $url;
    }
    echo my_urldecode_ext('%D6%D0%20%7E+~%x%%%7%E') . nl2br(PHP_EOL);
    echo my_urldecode_ext('%D6%D0%20%7E+~%x%%%7%E',true) . nl2br(PHP_EOL);

    PHP文件为GBK下显示:

    中 ~ ~%x%%
    中 ~+~%x%%

    其中%x、%%、%7、%E 由于不符合URL编码后的规则,被解码为:%x、%%、chr(0x7)、chr(0xE),后两者为为非显示的控制字符。

  • 相关阅读:
    CentOS虚拟机和物理机共享文件夹实现
    集训第六周 数学概念与方法 概率 数论 最大公约数 G题
    集训第六周 数学概念与方法 概率 F题
    集训第六周 E题
    集训第六周 古典概型 期望 D题 Discovering Gold 期望
    集训第六周 古典概型 期望 C题
    集训第六周 数学概念与方法 UVA 11181 条件概率
    集训第六周 数学概念与方法 UVA 11722 几何概型
    DAG模型(矩形嵌套)
    集训第五周 动态规划 K题 背包
  • 原文地址:https://www.cnblogs.com/dreamyoung/p/php-urlencode.html
Copyright © 2011-2022 走看看