urlencode与rawurlencode?

如果我想使用variables创build一个URL我有两个select来编码string。 urlencode()rawurlencode()

究竟是什么差异,哪些是首选?

这将取决于你的目的。 如果与其他系统的互操作性非常重要,那么看起来rawurlencode就是要走的路。 一个例外是传统的系统,它希望查询string遵循编码为+而不是%20(在这种情况下,你需要urlencode)的空格编码风格。

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

返回一个string,其中除-_。〜之外的所有非字母数字字符已被replace为百分号(%)后跟两个hex数字。 这是»RFC 3986中描述的编码,用于保护文字字符不被解释为特殊的URL分隔符,并且保护URL不受字符转换的传输介质(如某些电子邮件系统)的影响。

关于RFC 3986和1738的说明。在5.3之前的rawurlencode根据RFC 1738编码波浪号( ~ )。然而,从PHP 5.3开始,rawurlencode遵循RFC 3986,它不需要编码波浪号字符。

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

返回除-_之外的所有非字母数字字符的string。 已经被百分号(%)符号replace,后面是两个hex数字和空格,编码为加号(+)。 它的编码方式与WWW表单的发布数据编码方式相同,与application / x-www-form-urlencoded媒体types的方式相同。 这不同于»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 "$"
";", "/", "?", ":", "@",
"&", "=", "+", ",", and "$"
";", "/", "?", ":", "@",
"&", "=", "+", ",", and "$"
被保留。

正如你所看到的那样, +是查询string中的保留字符,因此需要根据RFC 3986(如在rawurlencode中)进行编码。

certificate在PHP的源代码中。

我会带你通过一个快速的过程,在任何你想要的时候自己find这样的事情。 忍受我,会有很多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); } 

以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

所以去寻找这些function!

让我们看看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服务器已经定义了这两个函数,则pipe理EBCDIC。

此外,他们都使用一个字符数组(思考stringtypes) 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"; 

除此之外,function是非常不同的,我将用ASCII和EBCDIC来解释它们。

ASCII中的差异:

URLEncode的:

  • 计算inputstring的开始/结束长度,分配内存
  • 经过一个while循环,递增,直到到达string的末尾
  • 抓住现在的angular色
  • 如果字符等于ASCII字符0x20(即“空格”),则向输出string添加+符号。
  • 如果不是空格,也不是字母数字( isalnum(c) ),也不是和_-. 字符,那么我们输出一个%符号到数组位置0,做一个数组,查找hexchars数组,查找os_toascii数组(从Apache转换为hex代码的数组)的c (当前字符),然后我们按位向右移位4,将该值赋给字符1,然后到位置2,我们分配相同的查找,除了我们预先形成一个逻辑值,并看看值是15(0xF),并返回1这种情况下,或否则为0。 最后,你会得到一些编码的东西。
  • 如果它结束,它不是一个空格,它是字母数字或_-. 字符,它输出完全是什么。

RAWURLENCODE:

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

注意:许多程序员可能从来没有见过这样的for循环迭代,它有点骇人听闻,而不是大多数for循环使用的标准惯例,注意,它分配xy ,检查len到0的退出,并增加xy 。 我知道,这不是你所期望的,但它是有效的代码。

  • 将当前字符分配给str的匹配字符位置。
  • 它检查当前字符是字母数字还是_-. 字符,如果不是的话,我们做与使用y++而不是to[1] URLENCODE几乎相同的赋值,但是,我们增加了不同的值,这是因为string是以不同的方式构build的,但无论如何都要达到同样的目标。
  • 当循环完成并且长度消失时,它实际上终止string,分配\0字节。
  • 它返回编码的string。

区别:

  • UrlEncode检查空间,分配一个+号,RawURLEncode不。
  • UrlEncode不会为string分配一个\0字节,RawUrlEncode会(这可能是一个争议点)
  • 他们迭代不同,有人可能会倾向于错误的string溢出,我只是暗示这一点,我没有实际调查。

它们基本上是以不同的方式进行迭代,在ASCII 20的情况下分配一个+符号。

EBCDIC的差异:

URLEncode的:

  • 与ASCII相同的迭代设置
  • 仍然将“空格”字符翻译为+ 符号。 注意 – 我认为这需要在EBCDIC中进行编译,否则最终会出现一个错误? 有人可以编辑和确认吗?
  • 它检查当前的char是否是0之前的char,除了是a .- ,小于A但大于字符9或者大于Z ,小于a而不是_或者大于z (是的,EBCDIC有点乱了工作)。 如果它匹配其中的任何一个,则执行与ASCII版本类似的查找(它只是不需要在os_toascii中查找)。

RAWURLENCODE:

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

总结

  • 两者都使用相同的六进制查找表
  • URIEncode不会以\ 0结束一个string,原始的。
  • 如果你在EBCDIC工作,我会build议使用RawUrlEncode,因为它pipe理的UrlEncode没有( 这是一个报告的问题 )。 值得注意的是,ASCII和EBCDIC 0x20都是空格。
  • 它们以不同的方式迭代,其中一个可能会更快,一个可能容易出现内存或基于string的漏洞利用。
  • URIEncode将空格变成+ ,RawUrlEncode通过数组查找将空格变成%20

免责声明:我多年来没有碰过C,而且在很长一段时间里我还没有看过EBCDIC。 如果我在某个地方错了,请告诉我。

build议的实现

基于所有这些,rawurlencode是大部分时间去的方式。 正如你在乔纳森·芬兰的答案中看到的,在大多数情况下坚持下去。 它处理的是URI组件的现代化scheme,urlencode就像旧式的做法,其中+意味着“空间”。

如果您试图在旧格式和新格式之间进行转换,请确保您的代码不会混乱,并通过偶然的双重编码或类似的“oops”场景将解码+符号变成空格空间/ 20%/ +问题。

如果你正在使用旧版软件,而不喜欢新格式,那么坚持使用urlencode,但是,我相信%20实际上是向后兼容的,因为在旧的标准%20下工作,只是没有首选。 如果你喜欢玩耍,请给它一个镜头,让我们知道它是如何解决你的。

基本上,你应该坚持生,除非你的EBCDIC系统真的恨你。 大多数程序员在2000年之后,甚至1990年之前都不会遇到任何系统的EBCDIC(这是推动,但仍然可能在我看来)。

 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%20asd vs asd+asd

urlencode与RFC 1738的区别在于将空格编码为+而不是%20

select一个的一个实际的原因是如果你打算在另一个环境中使用结果,例如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函数。

我相信空间必须被编码为:

  • 在URLpath组件中使用时为%20
  • +在URL查询string组件或表单数据中使用时(请参阅17.13.4表单内容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 

如果你相反地编码path和查询string组件,会发生什么? 对于以下示例:

 http://example.com/category/latest+songs/search?q=lady%20gaga 
  • networking服务器将寻找目录latest+songs而不是latest songs
  • 查询string参数q将包含lady gaga

差异在于返回值,即:

urlencode() :

返回除-_之外的所有非字母数字字符的string。 已被百分号(%)符号replace,后接两个hex数字和空格,编码为加号(+)。 它的编码方式与WWW表单的发布数据编码方式相同,与application / x-www-form-urlencoded媒体types的方式相同。 这与RFC 1738编码不同(参见rawurlencode()),由于历史原因,空格被编码为加号(+)。

rawurlencode() :

返回除-_之外的所有非字母数字字符的string。 已被百分号(%)符号和两个hex数字replace。 这是RFC 1738中描述的编码,用于保护文字字符不被解释为特殊的URL分隔符,并且保护URL不受字符转换的传输介质(如某些电子邮件系统)的影响。

两者非常相似,但是后者(rawurlencode)将用'%'和两个hex数字replace空格,这适合于编码密码等,其中'+'不是例如:

 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"> 

1.什么是差异和

唯一的区别在于对待空间的方式:

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

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

不同的原因是因为+在URL中被保留和有效(未编码)。

2.哪个是首选?

我真的很希望看到一些select其中一个的原因…我希望能够select一个,并用最less的麻烦永远使用它。

很公平,我做了一个简单的策略,在做出这些决定时我会跟你们分享,希望它能帮上忙。

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

在parsing请求行时,客户端应该容忍parsing状态行和服务器容错。

面对这样的问题时,最好的策略是尽可能地消费,并生产符合标准的产品。

所以我的build议是使用rawurlencode生成符合RFC 1738标准的编码string,并使用urldecode向后兼容,并容纳任何可能遇到的消耗。

现在,你可以听取我的意见,但让我们certificate,我们…

 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正是这样想的,尽pipe我从来没有遇到过任何人拒绝这两种格式,但是我不能想到一个更好的策略来作为事实上的策略,对吗?

的nJoy!

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

我相信urlencode是查询参数,而rawurlencode是path段。 这主要是由于path段的%20和查询参数的+ 。 看到这个关于空格的答案: 什么时候把空格编码为加(+)或%20?

但是%20现在也可以在查询参数中工作,这就是为什么rawurlencode总是比较安全的原因。 然而,在用户对查询参数的编辑和可读性的体验很重要的情况下,加号往往被使用。

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

如果您希望编码和解码在input和输出之间保持一致,并且您已经select始终使用+而不是%20作为查询参数,那么对于查询参数(键和值), urlencode是很好的。

结论是:

path段 – 总是使用rawurlencode / rawurldecode

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

空格编码为%20+

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

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

我已经特别看到某些接受编码文本查询的API端点期望看到空间为%20 ,因此,如果使用加号代替失败。 很明显,API实现将会有所不同,你的里程可能会有所不同。

简单* rawurlencode的path – path是“?”之前的部分 – 空格必须编码为%20 * urlencode查询string – 查询string是“?”之后的部分。 – 空间编码更好,因为“+”= rawurlencode通常更兼容