urlencode vs rawurlencode?

Gar*_*hby 374 php urlencode url-encoding

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

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

Jon*_*and 320

这取决于你的目的.如果与其他系统的互操作性很重要,那么看来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将空格编码为加号(不像%20rawurlencode中那样)(参见http://us2.php.net/manual/en/function.urlencode.php)

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

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

补充阅读:

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

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

在查询组件中,保留字符.";", "/", "?", ":", "@",
"&", "=", "+", ",", and "$"

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

  • rawurlencode.在这种情况下符合标准.urlencode仅供传统使用 (78认同)
  • 哪个是优先的? (26认同)
  • 我认为它的rawurlencode不会将空格编码为加号,而是编码为%20s (3认同)
  • 非常感谢,这就是我的想法,我只想在开始更新大量代码之前获得第二意见. (2认同)
  • @Pindatjuh:你引用的部分*一个例外是遗留系统,它希望查询字符串遵循编码为+而不是%20的空格的形式编码样式(在这种情况下你需要urlencode)*意味着虽然rawurlencode适合于大多数情况下,一些系统希望空格被编码为+(加号).对于这样的系统,urlencode是更好的选择. (2认同)

Inc*_*ito 211

证明是PHP的源代码.

我会带你快速了解如何在将来随时找到这类东西.忍受我,你可以浏览很多C源代码(我解释一下).如果你想了解一些C,一个好的起点是我们的SO维基.

下载源代码(或使用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);
}
Run Code Online (Sandbox Code Playgroud)

以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);
}
Run Code Online (Sandbox Code Playgroud)

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

它们本质上分别调用两个不同的内部函数: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);
}
Run Code Online (Sandbox Code Playgroud)

当然,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;
}
Run Code Online (Sandbox Code Playgroud)

在我向前迈进之前,有一点知识,EBCDIC是另一个字符集,类似于ASCII,但却是一个竞争对手.PHP试图解决这两个问题.但基本上,这意味着字节EBCDIC 0x4c字节不是LASCII,它实际上是一个<.我相信你会在这看到混乱.

如果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";
Run Code Online (Sandbox Code Playgroud)

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

ASCII的差异:

URLEncode的:

  • 计算输入字符串的开始/结束长度,分配内存
  • 走一个while循环,递增直到我们到达字符串的末尾
  • 抓住现在的角色
  • 如果字符等于ASCII字符0x20(即"空格"),请+在输出字符串中添加一个符号.
  • 如果它不是空格,并且它也不是字母数字(isalnum(c)),也不是和_,-.字符,那么我们输出一个%符号到数组位置0,做一个数组查找hexchars数组以查找os_toascii数组(从阵列中的Apache该转化为关键炭到十六进制代码)c(本字符),我们接着逐位右移4,分配该值与字符1和位置2我们分配相同的查找,除了我们瓶坯逻辑并查看值是否为15(0xF),并在这种情况下返回1,否则返回0.最后,你最终会得到编码的东西.
  • 如果它最终不是一个空格,它是字母数字或其中一个_-.字符,它会输出它的确切含义.

RAWURLENCODE:

  • 为字符串分配内存
  • 根据函数调用中提供的长度对其进行迭代(不像URLENCODE那样计算).

注意:许多程序员可能从未见过这样的for循环迭代,它有点hackish而不是大多数for循环使用的标准约定,注意,它分配,xy检查退出len到达0,并递增xy.我知道,这不是你所期望的,但它是有效的代码.

  • 将当前字符分配给匹配的字符位置str.
  • 它会检查当前字符是否为字母数字或其中一个_-.字符,如果不是,我们执行与URLENCODE几乎相同的分配,它会预先形成查找,但是,我们增加不同,使用y++而不是to[1],这是因为字符串以不同的方式构建,但无论如何都达到了相同的目标.
  • 当循环完成并且长度消失时,它实际上终止了字符串,分配了\0字节.
  • 它返回编码的字符串.

区别:

  • UrlEncode检查空格,分配+号,RawURLEncode不分配.
  • UrlEncode没有\0为字符串分配一个字节,RawUrlEncode没有(这可能是一个有争议的问题)
  • 他们迭代不同,一个人可能容易溢出格式错误的字符串,我只是建议这个,我实际上没有调查过.

它们基本上以不同的方式迭代,一个在ASCII 20的情况下指定一个+号.

EBCDIC的差异:

URLEncode的:

  • 与ASCII相同的迭代设置
  • 仍然将"空格"字符翻译成+ 号.注意 - 我认为这需要在EBCDIC中编译,否则你最终会得到一个bug?有人可以编辑并确认吗?
  • 它检查如果当前字符是字符之前0,用作为一个例外.-,OR小于A但不是char更大9,OR大于Z和小于a但不是_.或者大于z(是的,EBCDIC有点搞砸).如果它匹配其中任何一个,请执行与ASCII版本中相似的查找(它不需要在os_toascii中查找).

RAWURLENCODE:

  • 与ASCII相同的迭代设置
  • 与URL Encode的EBCDIC版本中描述的相同,但如果它大于z,~则从URL编码中排除.
  • 与ASCII RawUrlEncode相同的赋值
  • 仍然\0在返回之前将字节附加到字符串.

总结

  • 两者都使用相同的hexchars查找表
  • URIEncode不会终止带有\ 0的字符串,原始的.
  • 如果您在EBCDIC工作,我建议使用RawUrlEncode,因为它管理~UrlEncode没有(这是一个报告的问题).值得注意的是,ASCII和EBCDIC 0x20都是空格.
  • 它们以不同的方式迭代,一个可能更快,一个可能倾向于内存或基于字符串的攻击.
  • URIEncode创建了一个空格+,RawUrlEncode为%20通过数组查找创建了一个空格.

免责声明:多年来我没有接触过C,而且我很长时间没有看过EBCDIC.如果我在某处错了,请告诉我.

建议的实施

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

如果你想在老格式和新格式之间进行转换,请确保您的代码不偷懒并通过编码双不慎把东西是解码+登录到一个空间,或者解决这个类似"哎呀呀"情景空间/ 20%/ +问题.

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

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

  • +1,对于这部分:"我相信%20实际上会向后兼容,因为根据旧标准%20工作,只是不是首选" (2认同)
  • 好的答案,但也许有点矫kill过正? (2认同)

jit*_*ter 36

echo rawurlencode('http://www.google.com/index.html?id=asd asd');
Run Code Online (Sandbox Code Playgroud)

产量

http%3A%2F%2Fwww.google.com%2Findex.html%3Fid%3Dasd%20asd
Run Code Online (Sandbox Code Playgroud)

echo urlencode('http://www.google.com/index.html?id=asd asd');
Run Code Online (Sandbox Code Playgroud)

产量

http%3A%2F%2Fwww.google.com%2Findex.html%3Fid%3Dasd+asd
Run Code Online (Sandbox Code Playgroud)

区别在于asd%20asdvsasd+asd

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


小智 28

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

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

但是如果你需要使用decodeURI()函数在JavaScript中"解码"这个,那么decodeURI("test+1")你会在给你的"test+1"同时decodeURI("test%201")给你"test 1"结果.

换句话说,PHP 中的urlencode编码为plus("+")的空格("")将无法由JavaScript中的decodeURI正确解码.

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

  • 这是迄今为止我见过的最佳答案.它通过一个真实的例子提供了一个使用建议.此外,它简洁. (6认同)

Sal*_*n A 21

我认为空格必须编码为:

  • %20 在URL路径组件中使用时
  • +在URL查询字符串组件或表单数据中使用时(参见17.13.4表单内容类型)

以下示例显示了正确使用rawurlencodeurlencode:

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

输出:

http://example.com/category/latest%20songs/search?q=lady+gaga
Run Code Online (Sandbox Code Playgroud)

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

http://example.com/category/latest+songs/search?q=lady%20gaga
Run Code Online (Sandbox Code Playgroud)
  • Web服务器将查找目录latest+songs而不是latest songs
  • 查询字符串参数q将包含lady gaga

  • "查询字符串参数`q`将包含`lady gaga`"否则它还包含什么?无论在PHP 5.2+中使用`rawurlencode`还是`urlencode`,查询参数`q`似乎都传递给`$ _GET`数组.虽然,`urlencode`以`application/x-www-form-urlencoded`格式编码,这是GET请求的默认格式,所以我采用你的方法.+1 (2认同)
  • 我想澄清一下,当在查询字符串中使用时,"+"和"%20"都被解码为空格. (2认同)

Jak*_*son 6

空格编码为%20vs.+

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

echo urlencode("red shirt");
// red+shirt

echo rawurlencode("red shirt");
// red%20shirt
Run Code Online (Sandbox Code Playgroud)

我特别看到某些接受编码文本查询的 API 端点期望看到%20空格,因此,如果使用加号来代替,则会失败。显然,这在 API 实现之间会有所不同,并且您的里程可能会有所不同。


kar*_*m79 5

区别在于返回值,即:

urlencode():

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

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">
Run Code Online (Sandbox Code Playgroud)

  • OP询问如何知道使用哪个以及何时使用.如果他不知道不同回报值的重要性,那么知道每个用空格做什么并不能帮助OP做出决定. (2认同)

nic*_*kl- 5

1.究竟有什么不同之处

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

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

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

区别的原因是因为+在URL中保留并且有效(未编码).

2.哪个更好?

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

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

我认为这是HTTP/1.1规范RFC 2616,它要求" 容忍的应用程序 "

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

当面对这样的问题时,最好的策略总是尽可能地消费并产生符合标准的东西.

因此,我的建议是使用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
Run Code Online (Sandbox Code Playgroud)

看起来PHP确实考虑到了这一点,即使我从来没有遇到任何拒绝这两种格式的人,我想不出更好的策略来采用你的事实策略,对吗?

的nJoy!