urlencode vs rawurlencode?
如果我想使用变量创建URL,我有两种选择来编码字符串。 和。urlencode()
rawurlencode()
究竟有什么区别,哪个是首选的?
如果我想使用变量创建URL,我有两种选择来编码字符串。 和。urlencode()
rawurlencode()
究竟有什么区别,哪个是首选的?
这将取决于您的目的。如果与其他系统的互操作性很重要,那么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将空格编码为加号(不像在rawulencode中所做的那样)(请参阅 http://us2.php.net/manual/en/function.urlencode.php%20
)
返回一个字符串,其中除 -_ 之外的所有非字母数字字符。已替换为百分比 (%) 符号,后跟两个十六进制数字和编码为加号 (+) 的空格。它的编码方式与来自 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 查询组件:
在查询组件中,字符是保留的。
";", "/", "?", ":", "@",
"&", "=", "+", ",", and "$"
如您所见,它是查询字符串中的保留字符,因此需要按照 RFC 3986 进行编码(如在 rawurlencode 中)。+
证明在PHP的源代码中。
我将带您完成一个快速的过程,了解如何在将来的任何时间自己找到这种事情。请原谅我,有很多C源代码你可以略过(我解释一下)。如果你想复习一些C,一个很好的起点是我们的SO wiki。
下载源代码(或使用 http://lxr.php.net/ 在线浏览),删除函数名称的所有文件,你会发现这样的东西:
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
所以去寻找这些功能吧!
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);
}
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,则这两个函数都管理 EBCDIC。
此外,它们都使用字符数组(think字符串类型)查找来获取一些值,该数组描述如下: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来解释它们。
URLENCODE:
+
isalnum(c)
_
-
.
%
hexchars
os_toascii
c
_-.
RAWURLENCODE:
注意:许多程序员可能从未见过这种方式的for循环迭代,它有点黑客,而不是大多数for循环使用的标准约定,注意,它分配和,检查达到0时的退出,并同时递增和。我知道,这不是你所期望的,但它是有效的代码。x
y
len
x
y
str
_-.
y++
to[1]
\0
差异:
\0
它们基本上以不同的方式迭代,在ASCII 20的情况下分配一个+号。
URLENCODE:
0
.
-
A
9
Z
a
_
z
RAWURLENCODE:
z
~
\0
~
+
%20
免責聲明:我已经很多年没有接触过C,而且我已经很久没有看过EBCDIC了。如果我在某个地方错了,请告诉我。
基于所有这些,rawurlencode是大多数时候要走的路。正如你在乔纳森·芬格兰(Jonathan Fingland)的答案中看到的那样,在大多数情况下坚持下去。它涉及URI组件的现代方案,其中urlencode以旧学校的方式做事,其中+表示“空间”。
如果你试图在旧格式和新格式之间进行转换,请确保你的代码不会因为意外的双重编码而将解码的+号的东西变成空格,或者围绕这个space/20%/+问题出现类似的“哎呀”场景。
如果您使用的是较旧的系统,而较旧的软件不喜欢新格式,请坚持使用urlencode,但是,我相信%20实际上会向后兼容,因为在旧标准下,%20工作,只是不是首选。如果您愿意玩,请试一试,让我们知道它是如何为您解决的。
基本上,你应该坚持使用原始的,除非你的EBCDIC系统真的讨厌你。大多数程序员永远不会在2000年之后,甚至1990年之后制造的任何系统上遇到EBCDIC(这是推动的,但在我看来仍然可能)。