最近我遇到了一个关于 urlencode 的奇怪问题,我的 url 的 pathinfo 和 querystring 部分恰好都有一个“+”。
例如:
http://example.com/A + B?s=C + D
我在 Firefox 中使用 tamperdata,并能确保 Firefox 已将 URL 编码为以下内容:
http://example.com/A%20+%20B?s=C%20+%20D
在服务器端,我使用以下指令启用了 apache url rewrite:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
然后,在 php 中,我得到以下内容:
$_REQUEST['q'] = 'A B';
$_REQUEST['s'] = 'C D';
$_SERVER['QUERY_STRING'] = 'q=A + B&s=C%20+%20D';
我们知道,php 会自动使用 urldecode 将查询字符串解析为 $_REQUEST 超级变量,这就解释了为什么 'A + B' 变成 'A B' 而 'C + D' 变成 'C D'。然后,我意识到 apache url rewrite 必须解码所有字符才能进行重写映射。旗B将有助于在映射之后重新编码它们。因此重写规则如下并应用了 B FLAG。
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteRule ^(.*)$ index.php?q=$1 [B,L,QSA]
那么结果就变成了:
$_REQUEST['q'] = 'A + B';
$_REQUEST['s'] = 'C D';
$_SERVER['QUERY_STRING'] = 'q=A+%2B+B&s=C%20+%20D';
我预期的是以下情况:
$_SERVER['QUERY_STRING'] = 'q=A%20+%20B&s=C%20+%20D';
然后,我可以使用 rawurldecode 手动解析查询字符串如下,这也是 Firefox 最初的做法
$_REQUEST['q'] = 'A + B';
$_REQUEST['s'] = 'C + D';
但是 apache mod-rewrite B glag make q 却变成了 'A+%2B+B',这与原来 Firefox 的编码 'A%20+%20B' 不一样。当然,apache 的编码与 php urlencode 函数兼容。
那么问题是为什么 Firefox 和 Apache 的行为如此不同?为什么 Firefox 不将 'A + B' 编码为通常使用的 'A+%2B+B' 而是 'A%20+%20',这会导致与 PHP 和服务器端存在如此多的不兼容?