Urlencode vs rawurlencode?

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

到底有什么不同,哪个更可取?

209606 次浏览
urlencode:这与 »RFC 1738编码(见 Rawurlencode())用于历史 原因是,空格被编码为加号 (+)迹象。< / p >

这取决于你的目的。如果与其他系统的互操作性很重要,那么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。rawurlencode在php 5.3之前根据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 media类型的编码方式相同。这与»RFC 3986编码(参见rawurlencode())不同,因为历史原因,空格被编码为加号(+)。

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

更多阅读:

你可能也想在http://bytes.com/groups/php/5624-urlencode-vs-rawurlencode上看到讨论。

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

< p >查询组件,在一个字符<代码 >";", "/", "?", ":", "@",< br > “和 ;", "=", "+", ",", 和“$”> < /代码保留。< / p >

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

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

区别在于返回值,即:

urlencode ():

返回一个字符串 非字母数字字符,-_除外。 已替换为百分比(%) 符号后面跟着两个十六进制数字和 空格编码为加号(+)。它 的编码方式与 从WWW表单发布的数据是 编码,这和 应用程序/ x-www-form-urlencoded 媒体类型。这与»不同 RFC 1738编码(参见rawurlencode()) 因为历史原因,空间 编码为加号(+)

rawurlencode ():

返回一个字符串 非字母数字字符,-_除外。 已替换为百分比(%) 符号后面跟着两个十六进制数字。这 编码是否在»RFC中描述 保护文字字符 避免被解释为特殊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">

证明在PHP的源代码中。

我将带你快速了解如何在将来任何你想要的时候自己找到这类事情。请容忍我,有很多C源代码您可以略读(我会解释它)。如果你想温习一些C语言,一个很好的开始就是我们的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是另一个字符集,类似于ASCII,但完全是竞争对手。PHP尝试处理这两种情况。但基本上,这意味着字节EBCDIC 0x4c字节不是ASCII中的L,它实际上是<。我相信你看到这里的困惑了。

如果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翻译过来 char数组十六进制代码)的关键c(当前字符),然后逐位右移4,将值分配给角色,和位置2我们分配相同的查询,除非我们执行一个逻辑和,看看值是否为15 (0xF),在这种情况下返回1,否则返回0。最后,你会得到一些编码的东西。
  • 如果它不是空格,而是字母数字或_-.字符之一,则输出它是什么。

RAWURLENCODE:

  • 为字符串分配内存
  • 基于函数调用中提供的长度迭代它(不像URLENCODE那样在函数中计算)。

注意:许多程序员可能从未见过for循环以这种方式迭代,这有点粗糙,而且不是大多数for循环使用的标准约定,注意,它分配xy,在len达到0时检查退出,并同时增加xy。我知道,这不是您所期望的,但这是有效的代码。

  • 将当前字符分配到str中匹配的字符位置。
  • 它检查当前字符是否是字母数字,或者是_-.字符之一,如果不是,我们执行几乎与URLENCODE相同的赋值,在那里它执行查找,然而,我们使用不同的增量,使用y++而不是to[1],这是因为字符串以不同的方式构建,但无论如何在结束时达到相同的目标。
  • 当循环结束,长度结束时,它实际上终止了字符串,并分配了\0字节。
  • 它返回编码后的字符串。

差异:

  • UrlEncode检查空格,分配一个+号,RawURLEncode没有。
  • UrlEncode不会将\0字节分配给字符串,但RawUrlEncode会(这可能是一个有争议的问题)
  • 他们迭代不同,一个可能容易溢出畸形的字符串,我仅仅是建议这和我还没实际上调查。

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

EBCDIC的差异:

URLENCODE:

  • 与ASCII相同的迭代设置
  • 仍然将“空格”字符转换为+ 的迹象。注意——我认为这需要在EBCDIC中编译,否则您将以错误告终?有人能编辑确认一下吗?
  • 它检查当前字符是否是0之前的字符,除非是.-小于A但大于字符9大于Z且小于a但不是_大于z(是的,EBCDIC工作起来有点混乱)。如果它与其中任何一个匹配,就像在ASCII版本中一样进行查找(只是不需要在os_toascii中进行查找)。

RAWURLENCODE:

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

大总结

  • 两者都使用相同的六边形查找表
  • URIEncode不会用\0结束字符串,raw会。
  • 如果你在EBCDIC中工作,我建议使用RawUrlEncode,因为它管理UrlEncode没有的~ (这是一个报告问题)。值得注意的是,ASCII和EBCDIC 0x20都是空格。
  • 它们的迭代方式不同,一个可能更快,另一个可能更容易利用内存或字符串。
  • URIEncode将一个空格转化为+, RawUrlEncode通过数组查找将一个空格转化为%20

免责声明:我已经很多年没有碰过C了,我已经很久很久没有看过EBCDIC了。如果我哪里说错了,请告诉我。

建议实现

基于所有这些,rawurlencode是大多数时候的选择。正如你在乔纳森·芬格兰的回答中看到的,在大多数情况下坚持下去。它处理URI组件的现代方案,其中urlencode使用老式方法,其中+表示“空格”。

如果您试图在旧格式和新格式之间进行转换,请确保您的代码不会出错,不会因为意外的双重编码而将已解码的+符号转换为空格,或者围绕空格/20%/+问题出现类似的“哎呀”情况。

如果您在一个不喜欢新格式的旧系统和旧软件上工作,请坚持使用urlencode,但是,我相信%20实际上是向后兼容的,因为在旧标准下%20可以工作,只是不受欢迎。试试吧,如果你想玩的话,让我们知道你是如何成功的。

基本上,你应该坚持生的,除非你的EBCDIC系统真的讨厌你。大多数程序员永远不会在2000年之后的任何系统上遇到EBCDIC,甚至可能是1990年(这有点催促,但在我看来仍然有可能)。

选择其中一种而不是另一种的一个实际原因是,如果您将在另一种环境中使用结果,例如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编码到加号("+")的空格(" ")将不会被JavaScript中的decodeURI正确解码。

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

我认为空格必须被编码为:

下面的例子显示了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
  • web服务器将查找目录latest+songs而不是latest songs
  • 查询字符串参数q将包含lady gaga

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

PHP似乎就是这么想的,尽管我从来没有遇到过有人拒绝这两种格式中的任何一种,但我想不出更好的策略来作为您的实际策略,不是吗?

nJoy !

我相信urlencode用于查询参数,而rawurlencode用于路径段。这主要是由于%20用于路径段,而+用于查询参数。请看这个关于空格的答案:何时将空格编码为+(+)或%20?

然而,%20现在也适用于查询参数,这就是rawurlencode总是更安全的原因。然而,加号往往用于用户编辑经验和查询参数的可读性很重要的地方。

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

如果你希望输入和输出之间的编码和解码保持一致,并且你已经选择总是使用+而不是%20作为查询参数,那么urlencode对于查询参数(键和值)是合适的。

结论是:

路径段-总是使用rawurlencode/rawurldecode

查询参数-解码时总是使用urldecode(自动完成),编码时,rawurlencode或urlencode都可以,只是选择一个一致,特别是在比较url时。

空格编码为%20 vs. +

我所见过的在大多数情况下使用rawurlencode()的最大原因是因为urlencode将文本空格编码为+(加号),而rawurlencode将它们编码为常见的%20:

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


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

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

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