• 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),后两者为为非显示的控制字符。

  • 相关阅读:
    Object类入门这一篇就够了!
    什么是Java内部类?
    Spring Cloud Alibaba基础教程:支持的几种服务消费方式(RestTemplate、WebClient、Feign)
    Spring Cloud Alibaba基础教程:使用Nacos实现服务注册与发现
    Spring Cloud Stream同一通道根据消息内容分发不同的消费逻辑
    Spring Cloud Stream消费失败后的处理策略(四):重新入队(RabbitMQ)
    Spring Cloud Stream消费失败后的处理策略(三):使用DLQ队列(RabbitMQ)
    Spring Cloud Stream消费失败后的处理策略(二):自定义错误处理逻辑
    Spring Cloud Stream消费失败后的处理策略(一):自动重试
    Spring Cloud Stream如何消费自己生产的消息?
  • 原文地址:https://www.cnblogs.com/dreamyoung/p/php-urlencode.html
Copyright © 2020-2023  润新知