首页 文章

urlencode vs rawurlencode?

提问于
浏览
351

如果我想使用变量创建一个URL,我有两个选择来编码字符串 . urlencode()rawurlencode() .

究竟有什么区别,哪些是首选?

11 回答

  • 204

    这取决于你的目的 . 如果与其他系统的互操作性很重要,那么看来rawurlencode就是最佳选择 . 一个例外是遗留系统,它期望查询字符串遵循编码为空格式的编码样式,而不是%20(在这种情况下,您需要urlencode) .

    rawurlencode 之后是PHP 5.3.0之前的RFC 1738和之后的RFC 3986(参见http://us2.php.net/manual/en/function.rawurlencode.php

    返回一个字符串,其中除-_ . 〜之外的所有非字母数字字符都替换为百分号(%)后跟两个十六进制数字 . 这是»RFC 3986中描述的编码,用于保护文字字符不被解释为特殊的URL分隔符,以及保护URL不被具有字符转换的传输媒体(如某些电子邮件系统)破坏 .

    关于RFC 3986 vs 1738的注释 . 在php 5.3之前的rawurlencode根据RFC 1738编码了波形符( ~ ) . 但是,从PHP 5.3开始,rawurlencode遵循RFC 3986,不需要编码波形符 .

    urlencode 将空格编码为加号(而不是像rawurlencode中那样 %20 )(参见http://us2.php.net/manual/en/function.urlencode.php

    返回一个字符串,其中除-_之外的所有非字母数字字符 . 已被替换为百分号(%),后跟两个十六进制数字和编码为plus()符号的空格 . 它的编码方式与编码WWW表单中的发布数据的方式相同,这与application / x-www-form-urlencoded媒体类型的方式相同 . 这与»RFC 3986编码(参见rawurlencode())的不同之处在于,由于历史原因,空格被编码为plus()符号 .

    这对应于RFC 1866中application / x-www-form-urlencoded的定义 .

    Additional Reading:

    您可能还希望在http://bytes.com/groups/php/5624-urlencode-vs-rawurlencode看到讨论 .

    另外,RFC 2396值得一看 . RFC 2396定义了有效的URI语法 . 我们感兴趣的主要部分来自3.4查询组件:

    在查询组件中,保留字符“;”,“/”,“?”,“:”,“@”,“&”,“=”,“”,“,”和“$” .

    如您所见, + 是查询字符串中的保留字符,因此需要根据RFC 3986进行编码(如rawurlencode中所示) .

  • 1

    证明是PHP的源代码 .

    我'll take you through a quick process of how to find out this sort of thing on your own in the future any time you want. Bear with me, there'将会有很多你可以浏览的C源代码(我解释一下) . If you want to brush up on some C, a good place to start is our SO wiki .

    下载源代码(或使用http://lxr.php.net/在线浏览),grep函数名称的所有文件,你会发现如下内容:

    PHP 5.3.6(最近编写本文时)在文件url.c中描述了本机C代码中的两个函数 .

    RawUrlEncode()

    PHP_FUNCTION(rawurlencode)
    {
        char *in_str, *out_str;
        int in_str_len, out_str_len;
    
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &in_str,
                                  &in_str_len) == FAILURE) {
            return;
        }
    
        out_str = php_raw_url_encode(in_str, in_str_len, &out_str_len);
        RETURN_STRINGL(out_str, out_str_len, 0);
    }
    

    UrlEncode()

    PHP_FUNCTION(urlencode)
    {
        char *in_str, *out_str;
        int in_str_len, out_str_len;
    
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &in_str,
                                  &in_str_len) == FAILURE) {
            return;
        }
    
        out_str = php_url_encode(in_str, in_str_len, &out_str_len);
        RETURN_STRINGL(out_str, out_str_len, 0);
    }
    

    好的,那么这里有什么不同?

    它们本质上分别调用两个不同的内部函数: php_raw_url_encodephp_url_encode

    所以去寻找那些功能吧!

    让我们看看php_raw_url_encode

    PHPAPI char *php_raw_url_encode(char const *s, int len, int *new_length)
    {
        register int x, y;
        unsigned char *str;
    
        str = (unsigned char *) safe_emalloc(3, len, 1);
        for (x = 0, y = 0; len--; x++, y++) {
            str[y] = (unsigned char) s[x];
    #ifndef CHARSET_EBCDIC
            if ((str[y] < '0' && str[y] != '-' && str[y] != '.') ||
                (str[y] < 'A' && str[y] > '9') ||
                (str[y] > 'Z' && str[y] < 'a' && str[y] != '_') ||
                (str[y] > 'z' && str[y] != '~')) {
                str[y++] = '%';
                str[y++] = hexchars[(unsigned char) s[x] >> 4];
                str[y] = hexchars[(unsigned char) s[x] & 15];
    #else /*CHARSET_EBCDIC*/
            if (!isalnum(str[y]) && strchr("_-.~", str[y]) != NULL) {
                str[y++] = '%';
                str[y++] = hexchars[os_toascii[(unsigned char) s[x]] >> 4];
                str[y] = hexchars[os_toascii[(unsigned char) s[x]] & 15];
    #endif /*CHARSET_EBCDIC*/
            }
        }
        str[y] = '\0';
        if (new_length) {
            *new_length = y;
        }
        return ((char *) str);
    }
    

    当然,php_url_encode:

    PHPAPI char *php_url_encode(char const *s, int len, int *new_length)
    {
        register unsigned char c;
        unsigned char *to, *start;
        unsigned char const *from, *end;
    
        from = (unsigned char *)s;
        end = (unsigned char *)s + len;
        start = to = (unsigned char *) safe_emalloc(3, len, 1);
    
        while (from < end) {
            c = *from++;
    
            if (c == ' ') {
                *to++ = '+';
    #ifndef CHARSET_EBCDIC
            } else if ((c < '0' && c != '-' && c != '.') ||
                       (c < 'A' && c > '9') ||
                       (c > 'Z' && c < 'a' && c != '_') ||
                       (c > 'z')) {
                to[0] = '%';
                to[1] = hexchars[c >> 4];
                to[2] = hexchars[c & 15];
                to += 3;
    #else /*CHARSET_EBCDIC*/
            } else if (!isalnum(c) && strchr("_-.", c) == NULL) {
                /* Allow only alphanumeric chars and '_', '-', '.'; escape the rest */
                to[0] = '%';
                to[1] = hexchars[os_toascii[c] >> 4];
                to[2] = hexchars[os_toascii[c] & 15];
                to += 3;
    #endif /*CHARSET_EBCDIC*/
            } else {
                *to++ = c;
            }
        }
        *to = 0;
        if (new_length) {
            *new_length = to - start;
        }
        return (char *) start;
    }
    

    我前进之前的一点知识,EBCDIC is another character set,类似于ASCII,但总是竞争对手 . PHP试图解决这两个问题 . 但基本上,这意味着字节EBCDIC 0x4c字节不是ASCII中的 L ,它实际上是 < . 我相信你会看到这里的混乱 .

    如果Web服务器已定义EBCDIC,则这两个函数都管理EBCDIC .

    另外,它们都使用字符数组(想想字符串类型) hexchars 查找来获取一些值,数组描述如下:

    /* rfc1738:
    
       ...The characters ";",
       "/", "?", ":", "@", "=" and "&" are the characters which may be
       reserved for special meaning within a scheme...
    
       ...Thus, only alphanumerics, the special characters "$-_.+!*'(),", and
       reserved characters used for their reserved purposes may be used
       unencoded within a URL...
    
       For added safety, we only leave -_. unencoded.
     */
    
    static unsigned char hexchars[] = "0123456789ABCDEF";
    

    除此之外,功能确实不同,我将用ASCII和EBCDIC解释它们 .

    ASCII的差异:

    URLENCODE:

    • 计算输入字符串的开始/结束长度,分配内存

    • 走一个while循环,递增直到我们到达字符串的末尾

    • grab 现在的角色

    • 如果字符等于ASCII字符0x20(即"space"),请在输出字符串中添加 + 符号 .

    • 如果它's not a space, and it'也不是字母数字( isalnum(c) ),也不是和 _-. 字符,那么我们输出一个 % 符号到数组位置0,做一个数组查找 hexchars 数组进行查找对于 os_toascii 数组(从Apache that translates字符到十六进制代码的数组),对于 c (当前字符)的键,我们然后按位向右移位4,将该值分配给字符1,到位置2我们分配相同的查找,除了我们预先形成一个逻辑并查看该值是否为15(0xF),并在这种情况下返回1,否则返回0 . 最后,你最终会得到编码的东西 .

    • 如果它最终是's not a space, it'的字母数字或其中一个 _-. 字符,它会输出它的确切含义 .

    RAWURLENCODE:

    • 为字符串分配内存

    • 根据函数调用中提供的长度对其进行迭代(不像URLENCODE那样计算) .

    Note: 许多程序员可能从未见过以这种方式进行for循环迭代,这有点像hackish而不是大多数for循环使用的标准约定,注意,它指定 xy ,检查退出 len 达到0,并递增 xy . 我知道,它是's not what you'期待,但它是有效的代码 .

    • 将当前字符分配给 str 中的匹配字符位置 .

    • 它检查当前字符是否为字母数字,或 _-. 字符之一,如果不是,我们执行与URLENCODE几乎相同的分配,它会预先形成查找,但是,我们使用 y++ 而不是 to[1] 进行不同的增量,这是因为琴弦以不同的方式构建,但无论如何都达到了相同的目标 .

    • 当循环's done and the length'消失时,它实际上终止了字符串,分配 \0 字节 .

    • 它返回编码的字符串 .

    Differences:

    • UrlEncode检查空间,分配符号,RawURLEncode没有 .

    • UrlEncode没有为字符串分配 \0 字节,RawUrlEncode确实如此(这可能是一个有争议的问题)

    • 他们迭代不同,一个人可能容易出现格式错误的字符串溢出,我是 merely suggesting 这个,我实际调查了 haven't .

    它们基本上以不同的方式迭代,一个在ASCII 20事件中指定一个符号 .

    EBCDIC的差异:

    URLENCODE:

    • 与ASCII相同的迭代设置

    • 仍然将"space"字符翻译成符号 . 注意 - 我认为这需要在EBCDIC中编译,否则你最终会遇到错误?有人可以编辑并确认吗?

    • 它检查当前char是否是 0 之前的char,除了 .-OR 小于 A 但大于char 9OR 大于 Z 且小于 a 但不是 _ . OR 大于 z (是的,EBCDIC有点搞砸了) . 如果它匹配其中任何一个,请执行与ASCII版本中相似的查找(它不需要在os_toascii中查找) .

    RAWURLENCODE:

    • 与ASCII相同的迭代设置

    • 与URL编码的EBCDIC版本中描述的相同,但如果它大于 z ,则从URL编码中排除 ~ .

    • 与ASCII RawUrlEncode相同的赋值

    • 仍然在返回之前将 \0 字节附加到字符串 .

    总结

    • 两者都使用相同的十六进制查找表

    • URIEncode不会终止带有\ 0的字符串,raw会终止 .

    • 如果你建议使用RawUrlEncode,因为它管理的是UrlEncode没有的 ~this is a reported issue) . 值得注意的是,ASCII和EBCDIC 0x20都是空格 .

    • 他们以不同的方式迭代,一个可能更快,一个可能倾向于内存或基于字符串的攻击 .

    • URIEncode在 + 中创建一个空格,RawUrlEncode通过数组查找在 %20 中创建一个空格 .

    Disclaimer: 在很长一段时间里我没有看过EBCDIC . 如果我在某处错了,请告诉我 .

    建议的实施

    基于所有这些,rawurlencode是大多数时间的方式 . 正如你在Jonathan Fingland的回答中看到的那样,在大多数情况下坚持使用它 . 它涉及URI组件的现代方案,其中urlencode以旧学校的方式处理事物,其中意味着“空间” .

    如果您尝试在旧格式和新格式之间进行转换,请确保您的代码没有搞砸,并通过意外双重编码或类似的“oops”场景将这些解码符号转换为空格/ 20%/问题 .

    如果您正在使用较旧的软件而不喜欢新格式的旧系统,请坚持使用urlencode,但是,我认为%20实际上是向后兼容的,因为根据旧标准%20工作,只是不是首选 . 如果你想要玩,请试一试,让我们知道它是如何为你工作的 .

    基本上,你应该坚持使用raw,除非你的EBCDIC系统真的很讨厌你 . 大多数程序员永远不会在2000年以后甚至1990年之后的任何系统上遇到EBCDIC(这是推动,但仍然可能在我看来) .

  • 4
    echo rawurlencode('http://www.google.com/index.html?id=asd asd');
    

    产量

    http%3A%2F%2Fwww.google.com%2Findex.html%3Fid%3Dasd%20asd
    

    echo urlencode('http://www.google.com/index.html?id=asd asd');
    

    产量

    http%3A%2F%2Fwww.google.com%2Findex.html%3Fid%3Dasd+asd
    

    不同的是 asd%20asdasd+asd

    urlencode与RFC 1738的不同之处在于将空格编码为 + 而不是 %20

  • 32

    选择其中一个的一个实际原因是,如果您要在另一个环境中使用结果,例如JavaScript .

    在PHP中 urlencode('test 1') 返回 'test+1'rawurlencode('test 1') 返回 'test%201' 作为结果 .

    但是如果你需要"decode"这在JavaScript中使用 decodeURI() 函数,那么 decodeURI("test+1") 会给你 "test+1"decodeURI("test%201") 会给你 "test 1" 作为结果 .

    换句话说,PHP中由 urlencode 编码的空间(" ")到_(#600554_)将无法由 decodeURI 正确解码 .

    在这种情况下,应使用 rawurlencode PHP函数 .

  • 5

    我相信空间必须编码如:

    在URL路径组件中使用

    以下示例显示正确使用rawurlencodeurlencode

    echo "http://example.com"
        . "/category/" . rawurlencode("latest songs")
        . "/search?q=" . urlencode("lady gaga");
    

    输出:

    http://example.com/category/latest%20songs/search?q=lady+gaga
    

    如果您以相反的方式编码路径和查询字符串组件会发生什么?对于以下示例:

    http://example.com/category/latest+songs/search?q=lady%20gaga
    
    • 网络服务器将查找目录 latest+songs 而不是 latest songs

    • 查询字符串参数 q 将包含 lady gaga

  • 1

    区别在于返回值,即:

    urlencode()

    返回一个字符串,其中除-_之外的所有非字母数字字符 . 已被替换为百分号(%),后跟两个十六进制数字和编码为plus()符号的空格 . 它的编码方式与编码WWW表单中的发布数据的方式相同,这与application / x-www-form-urlencoded媒体类型的方式相同 . 这与»RFC 1738编码(参见rawurlencode())的不同之处在于,由于历史原因,空格被编码为plus()符号 .

    rawurlencode()

    返回一个字符串,其中除-_之外的所有非字母数字字符 . 已被替换为百分号(%),后跟两个十六进制数字 . 这是»RFC 1738中描述的编码,用于保护文字字符不被解释为特殊的URL分隔符,以及保护URL免受带有字符转换的传输媒体(如某些电子邮件系统)的破坏 .

    两者非常相似,但后者(rawurlencode)将用'%'和两个十六进制数字替换空格,这适用于编码密码等,其中''不是例如:

    echo '<a href="ftp://user:', rawurlencode('foo @+%/'),
         '@ftp.example.com/x.txt">';
    //Outputs <a href="ftp://user:foo%20%40%2B%25%2F@ftp.example.com/x.txt">
    
  • 0

    1.究竟有什么区别和

    唯一的区别在于处理空间的方式:

    urlencode - 基于遗留实现将空格转换为

    rawurlencode - 基于RFC 1738将空格转换为%20

    造成这种差异的原因是因为在网址中保留并且有效(未编码) .

    2.哪个更受欢迎?

    我真的很想看到选择其中一个的一些理由......我希望能够选择一个并永远使用它而不用大惊小怪 .

    很公平,我做出这些决定时会遵循一个简单的策略,我将与您分享,希望它可以提供帮助 .

    我认为是HTTP / 1.1规范RFC 2616,它要求 "Tolerant applications"

    解析请求行时,客户端应该容忍解析状态行和服务器容忍度 .

    When faced with questions like these the best strategy is always to consume as much as possible and produce what is standards compliant.

    所以我的建议是使用 rawurlencode 生成符合标准的RFC 1738编码字符串,并使用 urldecode 向后兼容并容纳您可能遇到的任何内容 .

    现在你可以接受我的话,但我们要证明它......

    php > $url = <<<'EOD'
    <<< > "Which, % of Alice's tasks saw $s @ earnings?"
    <<< > EOD;
    php > echo $url, PHP_EOL;
    "Which, % of Alice's tasks saw $s @ earnings?"
    php > echo urlencode($url), PHP_EOL;
    %22Which%2C+%25+of+Alice%27s+tasks+saw+%24s+%40+earnings%3F%22
    php > echo rawurlencode($url), PHP_EOL;
    %22Which%2C%20%25%20of%20Alice%27s%20tasks%20saw%20%24s%20%40%20earnings%3F%22
    php > echo rawurldecode(urlencode($url)), PHP_EOL;
    "Which,+%+of+Alice's+tasks+saw+$s+@+earnings?"
    php > // oops that's not right???
    php > echo urldecode(rawurlencode($url)), PHP_EOL;
    "Which, % of Alice's tasks saw $s @ earnings?"
    php > // now that's more like it
    

    虽然我从来没有遇到任何人拒绝这两种格式中的任何一种,但我认为没有人会想到采用更好的策略来作为你的事实策略,你呢?

    的nJoy!

  • 26

    urlencode:这与»RFC 1738编码(参见rawurlencode())的不同之处在于,由于历史原因,空格被编码为plus()符号 .

  • 298

    我相信urlencode用于查询参数,而rawurlencode用于路径段 . 这主要是由于路径段的 %20 与查询参数的 + . 看到这个答案,讨论空格:When to encode space to plus (+) or %20?

    但是 %20 现在也适用于查询参数,这就是rawurlencode总是更安全的原因 . 但是,在编辑的用户体验和查询参数的可读性很重要的情况下,往往会使用加号 .

    请注意,这意味着 rawurldecode 不会将 + 解码为空格(http://au2.php.net/manual/en/function.rawurldecode.php) . 这就是为什么$ _GET总是自动通过 urldecode ,这意味着 +%20 都被解码为空格 .

    如果您希望编码和解码在输入和输出之间保持一致,并且您已选择始终使用 + 而不是 %20 作为查询参数,则 urlencode 适用于查询参数(键和值) .

    结论是:

    路径段 - 始终使用rawurlencode / rawurldecode

    查询参数 - 用于解码总是使用urldecode(自动完成),对于编码,rawurlencode或urlencode都很好,只需选择一个是一致的,特别是在比较URL时 .

  • 5

    空格编码为%20 vs.

    在大多数情况下,我看到使用 rawurlencode() 的最大原因是因为 urlencode 将文本空间编码为 + (加号),其中 rawurlencode 将它们编码为常见的 %20

    echo urlencode("red shirt");
    // red+shirt
    
    echo rawurlencode("red shirt");
    // red%20shirt
    

    我已经特别看到某些接受编码文本查询的API endpoints 期望查看空间的 %20 ,因此如果使用加号则会失败 . 显然,这与API实现有所不同,您的里程可能会有所不同 .

  • 19

    简单* rawurlencode路径 - 路径是“?”之前的部分 - 空格必须编码为%20 *urlencode查询字符串 - 查询字符串是“?”之后的部分 - 空格被更好地编码为“”= rawurlencode通常更兼容

相关问题