Gar*_*hby 374 php urlencode url-encoding
如果我想使用变量创建URL,我有两个选择来编码字符串.urlencode()
和rawurlencode()
.
究竟有什么区别,哪个是首选?
Jon*_*and 320
这取决于你的目的.如果与其他系统的互操作性很重要,那么看来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的注释.在php 5.3之前的rawurlencode ~
根据RFC 1738 编码了波形符().然而,从PHP 5.3开始,rawurlencode遵循RFC 3986,不需要编码波形符.
urlencode将空格编码为加号(不像%20
rawurlencode中那样)(参见http://us2.php.net/manual/en/function.urlencode.php)
返回一个字符串,其中除-_之外的所有非字母数字字符.已被替换为百分号(%),后跟两个十六进制数字和空格,编码为加号(+).它的编码方式与编码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中所示).
Inc*_*ito 211
证明是PHP的源代码.
我会带你快速了解如何在将来随时找到这类东西.忍受我,你可以浏览很多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);
}
Run Code Online (Sandbox Code Playgroud)
以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);
}
Run Code Online (Sandbox Code Playgroud)
好的,那么这里有什么不同?
它们本质上分别调用两个不同的内部函数: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);
}
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
在我向前迈进之前,有一点知识,EBCDIC是另一个字符集,类似于ASCII,但却是一个竞争对手.PHP试图解决这两个问题.但基本上,这意味着字节EBCDIC 0x4c字节不是L
ASCII,它实际上是一个<
.我相信你会在这看到混乱.
如果Web服务器已定义EBCDIC,则这两个函数都将管理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";
Run Code Online (Sandbox Code Playgroud)
除此之外,功能确实不同,我将用ASCII和EBCDIC解释它们.
URLEncode的:
+
在输出字符串中添加一个符号.isalnum(c)
),也不是和_
,-
或.
字符,那么我们输出一个%
符号到数组位置0,做一个数组查找hexchars
数组以查找os_toascii
数组(从阵列中的Apache该转化为关键炭到十六进制代码)c
(本字符),我们接着逐位右移4,分配该值与字符1和位置2我们分配相同的查找,除了我们瓶坯逻辑并查看值是否为15(0xF),并在这种情况下返回1,否则返回0.最后,你最终会得到编码的东西._-.
字符,它会输出它的确切含义.RAWURLENCODE:
注意:许多程序员可能从未见过这样的for循环迭代,它有点hackish而不是大多数for循环使用的标准约定,注意,它分配,x
并y
检查退出len
到达0,并递增x
和y
.我知道,这不是你所期望的,但它是有效的代码.
str
._-.
字符,如果不是,我们执行与URLENCODE几乎相同的分配,它会预先形成查找,但是,我们增加不同,使用y++
而不是to[1]
,这是因为字符串以不同的方式构建,但无论如何都达到了相同的目标.\0
字节.区别:
\0
为字符串分配一个字节,RawUrlEncode没有(这可能是一个有争议的问题)它们基本上以不同的方式迭代,一个在ASCII 20的情况下指定一个+号.
URLEncode的:
0
,用作为一个例外.
或-
,OR小于A
但不是char更大9
,OR大于Z
和小于a
但不是_
.或者大于z
(是的,EBCDIC有点搞砸).如果它匹配其中任何一个,请执行与ASCII版本中相似的查找(它不需要在os_toascii中查找).RAWURLENCODE:
z
,~
则从URL编码中排除.\0
在返回之前将字节附加到字符串.~
UrlEncode没有(这是一个报告的问题).值得注意的是,ASCII和EBCDIC 0x20都是空格.+
,RawUrlEncode为%20
通过数组查找创建了一个空格.免责声明:多年来我没有接触过C,而且我很长时间没有看过EBCDIC.如果我在某处错了,请告诉我.
基于所有这些,rawurlencode是大多数时间的方式.正如你在Jonathan Fingland的回答中看到的那样,在大多数情况下坚持使用它.它涉及URI组件的现代方案,其中urlencode以旧学校的方式处理事物,其中+表示"空间".
如果你想在老格式和新格式之间进行转换,请确保您的代码不偷懒并通过编码双不慎把东西是解码+登录到一个空间,或者解决这个类似"哎呀呀"情景空间/ 20%/ +问题.
如果你正在使用旧版软件的旧系统,不喜欢新的格式,坚持用urlencode,但在工作,我相信20%实际上是向后兼容的,因为旧标准20%在工作,只是没有首选.如果你想要玩,请试一试,让我们知道它是如何为你工作的.
基本上,除非你的EBCDIC系统真的讨厌你,否则你应该坚持使用raw.大多数程序员永远不会在2000年以后甚至1990年之后的任何系统上遇到EBCDIC(这是推动,但仍然可能在我看来).
jit*_*ter 36
echo rawurlencode('http://www.google.com/index.html?id=asd asd');
Run Code Online (Sandbox Code Playgroud)
产量
http%3A%2F%2Fwww.google.com%2Findex.html%3Fid%3Dasd%20asd
Run Code Online (Sandbox Code Playgroud)
而
echo urlencode('http://www.google.com/index.html?id=asd asd');
Run Code Online (Sandbox Code Playgroud)
产量
http%3A%2F%2Fwww.google.com%2Findex.html%3Fid%3Dasd+asd
Run Code Online (Sandbox Code Playgroud)
区别在于asd%20asd
vsasd+asd
urlencode与RFC 1738的不同之处在于编码空格+
而不是%20
小智 28
选择其中一个的一个实际原因是,如果您要在另一个环境中使用结果,例如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编码为plus("+")的空格("")将无法由JavaScript中的decodeURI正确解码.
在这种情况下,应该使用rawurlencode PHP函数.
Sal*_*n A 21
我认为空格必须编码为:
%20
在URL路径组件中使用时+
在URL查询字符串组件或表单数据中使用时(参见17.13.4表单内容类型)以下示例显示了正确使用rawurlencode
和urlencode
:
echo "http://example.com"
. "/category/" . rawurlencode("latest songs")
. "/search?q=" . urlencode("lady gaga");
Run Code Online (Sandbox Code Playgroud)
输出:
http://example.com/category/latest%20songs/search?q=lady+gaga
Run Code Online (Sandbox Code Playgroud)
如果您反过来编码路径和查询字符串组件会发生什么?对于以下示例:
http://example.com/category/latest+songs/search?q=lady%20gaga
Run Code Online (Sandbox Code Playgroud)
latest+songs
而不是latest songs
q
将包含lady gaga
%20
vs.+
我在大多数情况下看到使用的最大原因rawurlencode()
是因为urlencode
将文本空间编码为+
(加号),其中rawurlencode
将它们编码为常见的%20
:
echo urlencode("red shirt");
// red+shirt
echo rawurlencode("red shirt");
// red%20shirt
Run Code Online (Sandbox Code Playgroud)
我特别看到某些接受编码文本查询的 API 端点期望看到%20
空格,因此,如果使用加号来代替,则会失败。显然,这在 API 实现之间会有所不同,并且您的里程可能会有所不同。
区别在于返回值,即:
返回一个字符串,其中除-_之外的所有非字母数字字符.已被替换为百分号(%),后跟两个十六进制数字和空格,编码为加号(+).它的编码方式与编码WWW表单中的发布数据的方式相同,这与application/x-www-form-urlencoded媒体类型的方式相同.这与»RFC 1738编码(参见rawurlencode())的不同之处在于,由于历史原因,空格被编码为加号(+).
返回一个字符串,其中除-_之外的所有非字母数字字符.已被替换为百分号(%),后跟两个十六进制数字.这是»RFC 1738中描述的编码,用于保护文字字符不被解释为特殊的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">
Run Code Online (Sandbox Code Playgroud)
唯一的区别在于处理空间的方式:
urlencode - 基于遗留实现将空格转换为+
rawurlencode - 基于RFC 1738将空格转换为%20
区别的原因是因为+在URL中保留并且有效(未编码).
我真的很想看到选择其中一个的一些理由......我希望能够选择一个并且永远使用它而不用大惊小怪.
很公平,我做出这些决定时会遵循一个简单的策略,我将与您分享,希望它可以提供帮助.
我认为这是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
Run Code Online (Sandbox Code Playgroud)
看起来PHP确实考虑到了这一点,即使我从来没有遇到任何拒绝这两种格式的人,我想不出更好的策略来采用你的事实策略,对吗?
的nJoy!