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_encode和php_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循环使用的标准惯例,注意,它分配x和y ,检查len到0的退出,并增加x和y 。 我知道,这不是你所期望的,但它是有效的代码。 
-  将当前字符分配给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 )
 以下示例显示了rawurlencode和urlencode的正确使用: 
 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通常更兼容