【问题标题】:urlencode vs rawurlencode?urlencode vs rawurlencode?
【发布时间】:2010-11-03 00:38:37
【问题描述】:

如果我想使用变量创建 URL,我有两种选择来对字符串进行编码。 urlencode()rawurlencode()

究竟有什么区别,哪个更受欢迎?

【问题讨论】:

  • 我真的很想看看选择其中一个的一些原因(例如,一个或另一个可能遇到的问题),我(我希望其他人)希望能够选择一个并以最少的麻烦永远使用它,所以我已经开始在这个问题上悬赏。
  • @Tchalvak:如果您只想选择一个,请选择rawurlencode。您很少会遇到在给定编码为%20 的空格时阻塞的系统,而在编码为+ 的空格上阻塞的系统更为常见。

标签: php urlencode url-encoding


【解决方案1】:

urlencode: 这不同于 » RFC 1738 编码(参见 rawurlencode()) 用于历史 原因,空格被编码为加号 (+) 符号。

【讨论】:

    【解决方案2】:

    这取决于你的目的。如果与其他系统的互操作性很重要,那么似乎 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 与 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

    返回一个字符串,其中包含除 -_ 之外的所有非字母数字字符。已替换为百分号 (%) 后跟两个十六进制数字和编码为加号 (+) 的空格。它的编码方式与 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 Query Component:

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

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

    【讨论】:

    • rawurlencode。在这种情况下遵循标准。 urlencode 仅供旧式使用
    • 非常感谢,我就是这么想的,我只是想在开始更新大量代码之前获得第二意见。
    • 我认为它是 rawurlencode,它不会将空格编码为加号,而是编码为 %20s
    • @Jonathan Fingland 嗨乔纳森,我刚刚注意到这个答案在谷歌上搜索 urlencode 时很高。当然它在技术上是正确的,但它有点难以阅读,你认为你是否愿意为了清晰起见重新格式化它,使它成为对来到这个页面的 php 程序员更有用的资源?如果您允许我,我也愿意自己重新格式化以清楚起见。
    • @Pindatjuh:您引用的部分 一个例外是遗留系统,它期望查询字符串遵循编码为 + 而不是 %20 的空格的形式编码样式(在这种情况下,您需要urlencode) 表示虽然 rawurlencode 适用于大多数情况,但某些系统希望将空格编码为 +(加号)。对于这样的系统,urlencode 是更好的选择。
    【解决方案3】:
    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

    【讨论】:

      【解决方案4】:

      区别在于返回值,即:

      urlencode():

      返回一个字符串,其中所有 非字母数字字符, -_ 除外。 已替换为百分比 (%) 符号后跟两个十六进制数字和 空格编码为加号 (+)。它 被编码的方式相同 来自 WWW 表单的发布数据是 编码,这与中的方式相同 应用程序/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">
      

      【讨论】:

      • OP 询问如何知道使用哪个以及何时使用。如果 OP 不知道不同返回值的重要性,那么了解每个空间对空格的作用并不能帮助 OP 做出决定。
      【解决方案5】:

      证明在 PHP 的源代码中。

      我将带您快速了解如何在将来随时自行找出此类问题。请耐心等待,您可以浏览很多 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,它实际上是&lt;。我相信您会看到这里的困惑。

      如果 Web 服务器定义了 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循环,递增直到我们到达字符串的末尾
      • 抓取当前角色
      • 如果字符等于 ASCII Char 0x20(即“空格”),则在输出字符串中添加 + 符号。
      • 如果不是空格,也不是字母数字(isalnum(c)),也不是_-,或.字符,那么我们,输出一个%符号到数组位置 0,查找hexchars 数组以查找os_toascii 数组(从Apache that translates 字符到十六进制代码的数组)查找c 的键(当前字符),然后我们按位右移 4,将该值分配给字符 1,并将相同的查找分配给位置 2,除了我们执行逻辑并查看该值是否为 15 (0xF),并在这种情况下返回 1,或者否则为 0。最后,你会得到一些编码的东西。
      • 如果它最终不是空格,而是字母数字或_-. 字符之一,它会准确输出它的内容。

      RAWURLENCODE:

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

      注意:许多程序员可能从未见过这样迭代的 for 循环,这有点 hackish 并且不是大多数 for 循环使用的标准约定,请注意,它分配了 x 和 @ 987654345@,检查len 是否达到0,并增加xy。我知道,这不是您所期望的,但它是有效的代码。

      • 将当前字符分配给str 中的匹配字符位置。
      • 它检查当前字符是字母数字还是_-. 字符之一,如果不是,我们执行与执行查找的URLENCODE 几乎相同的分配,但是,我们使用@ 递增不同987654351@ 而不是to[1],这是因为字符串的构建方式不同,但最终还是达到了相同的目标。
      • 当循环完成并且长度消失时,它实际上终止了字符串,并分配了\0 字节。
      • 它返回编码后的字符串。

      区别:

      • UrlEncode 检查空格,分配一个 + 号,RawURLEncode 没有。
      • UrlEncode 不会将 \0 字节分配给字符串,RawUrlEncode 会(这可能是一个争论点)
      • 它们的迭代方式不同,其中一个可能容易溢出格式错误的字符串,我只是建议这一点,而我没有实际调查过。

      它们基本上迭代不同,一个在 ASCII 20 的情况下分配一个 + 号。

      EBCDIC 的区别:

      URLENCODE:

      • 与 ASCII 相同的迭代设置
      • 仍在将“空格”字符转换为 + 符号。注意——我认为这需要在 EBCDIC 中编译,否则最终会出现错误?有人可以编辑并确认吗?
      • 它检查当前字符是否是0之前的字符,除了.-OR小于A但大于字符@987654359 @, OR 大于 Z 且小于 a 但不是 _OR 大于 z(是的,EBCDIC 有点搞砸了)。如果它与其中任何一个匹配,请执行与 ASCII 版本中相似的查找(它只是不需要在 os_toascii 中查找)。

      RAWURLENCODE:

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

      大总结

      • 两者都使用相同的 hexchars 查找表
      • URIEncode 不会以 \0 终止字符串,而 raw 会。
      • 如果您在 EBCDIC 中工作,我建议您使用 RawUrlEncode,因为它可以管理 UrlEncode 没有的 ~ (this is a reported issue)。值得注意的是 ASCII 和 EBCDIC 0x20 都是空格。
      • 它们的迭代方式不同,一种可能更快,一种可能容易受到基于内存或字​​符串的攻击。
      • URIEncode 通过数组查找在+ 中创建一个空格,RawUrlEncode 在%20 中创建一个空格。

      免责声明:我已经很多年没有接触过 C 语言了,也很长时间没有看过 EBCDIC 了。如果我在某个地方错了,请告诉我。

      建议的实现

      基于所有这些,rawurlencode 是大多数时候要走的路。正如您在 Jonathan Fingland 的回答中看到的那样,在大多数情况下坚持下去。它处理 URI 组件的现代方案,其中 urlencode 以老式方式处理事情,其中​​ + 表示“空格”。

      如果您尝试在旧格式和新格式之间进行转换,请确保您的代码不会出错,并通过意外双重编码或类似的“哎呀”将解码后的 + 符号转换为空格围绕这个空间/20%/+ 问题的场景。

      如果您正在使用不喜欢新格式的旧软件在旧系统上工作,请坚持使用 urlencode,但是,我相信 %20 实际上会向后兼容,因为在旧标准下 %20 有效,只是不是首选。如果您愿意玩,请试一试,让我们知道它对您的效果。

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

      【讨论】:

      • 我从来不用担心双重编码,毕竟我应该知道我编码了什么,因为我认为是我在做编码。由于我使用知道如何处理 + 空间的兼容模式对收到的所有内容进行解码,因此我同样从未遇到过您试图在此处警告的问题。如果我们不知道某些东西是做什么的,我可以理解查看源代码,但是我们在这里学到了什么,而我们通过简单地执行这两个函数并不知道。我知道我有偏见,但我不禁认为这太过分了。虽然努力的荣誉! =)
      • +1,对于这部分:“我相信 %20 实际上会向后兼容,因为在旧标准下 %20 有效,只是不是首选”
      • “UrlEncode 没有为字符串分配 \0 字节”这是不正确的。它只是做不同的事情。见*to = 0;。这可以理解为将值零分配给to 指向的位置。而那个时候to就是指向空字节应该在的地方。另外,0'\0' 是相等的,只是说同一件事的方式不同。
      【解决方案6】:

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

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

      但是,如果您需要在 JavaScript 中使用 decodeURI() 函数“解码”它,那么decodeURI("test+1") 将为您提供"test+1",而decodeURI("test%201") 将为您提供"test 1" 作为结果。

      换句话说,在 PHP 中由 urlencode 编码为加号(“+”)的空格(“”)将不会被 JavaScript 中的 decodeURI 正确解码。 p>

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

      【讨论】:

      • 这是一个很好的例子,尽管我更喜欢 json_encodeJSON.parse
      【解决方案7】:

      我认为空格必须编码为:

      • %20 在 URL 路径组件中使用时
      • + 在 URL 查询字符串组件或表单数据中使用时(参见 17.13.4 Form content types

      下面的例子展示了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

      【讨论】:

      • "查询字符串参数q 将包含lady gaga"否则它还会包含什么?无论在 PHP 5.2+ 中使用 rawurlencodeurlencode,查询参数 q 似乎都具有相同的值传递给 $_GET 数组。不过,urlencodeapplication/x-www-form-urlencoded 格式编码,这是 GET 请求的默认格式,所以我将采用您的方法。 +1
      • 我想澄清一下,+%20 在查询字符串中使用时都被解码为空格。
      【解决方案8】:

      1。究竟有什么区别和

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

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

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

      差异的原因是因为 + 在 urls 中是保留且有效(未编码)的。

      2。哪个是首选?

      我真的很想看看选择其中一个而不是另一个的一些原因......我希望能够选择一个并永远使用它,而不必大惊小怪。

      公平地说,我在做出这些决定时遵循了一个简单的策略,我将与您分享,希望对您有所帮助。

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

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

      面对此类问题时,最佳策略始终是尽可能多地消费并生产符合标准的产品。

      所以我的建议是使用 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
      

      看起来 PHP 正是考虑到了这一点,尽管我从未遇到过任何人拒绝这两种格式中的任何一种,但我想不出更好的策略来作为您的实际策略,对吗?

      快乐!

      【讨论】:

        【解决方案9】:

        我相信 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 时。

        【讨论】:

          【解决方案10】:

          编码为%20+ 的空格

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

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

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

          【讨论】:

            【解决方案11】:

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

            【讨论】:

              猜你喜欢
              • 2012-09-15
              • 1970-01-01
              • 1970-01-01
              • 2012-04-27
              • 2014-01-15
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-06-19
              相关资源
              最近更新 更多