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通常更兼容