PHP UTF编码的URL字符串

5
当我在Firefox中(在地址栏中)输入像http://www.example.com/?query=Траливали这样的URL时,它会自动编码为http://www.example.com/?query=%D2%F0%E0%EB%E8%E2%E0%EB%E8
但是像http://www.example.com/#ajax_call?query=Траливали这样的URL没有被转换。
其他浏览器(如IE8)根本不转换查询。
问题是:如何检测(在PHP中)查询是否已编码?如何解码?
我尝试过:
  1. $str = iconv('cp1251', 'utf-8', urldecode($str));

  2. $str = utf8_decode(urldecode($str));

  3. $str = (urldecode($str));

  4. 许多来自http://php.net/manual/en/function.urldecode.php的函数。什么都不起作用。

测试:

$str = $_GET['str'];

d('%D2%F0%E0%EB%E8%E2%E0%EB%E8' == urldecode('%D2%F0%E0%EB%E8%E2%E0%EB%E8'));

d('%D2%F0%E0%EB%E8%E2%E0%EB%E8' == $str);

d('Траливали' == $str);

d(urldecode($str));

d(utf8_decode(urldecode($str)));

!!! d('%D2%F0%E0%EB%E8%E2%E0%EB%E8' == urlencode($str)); !!!

返回:

[false] [false] [false] ��������� ???? [true]

一种解决方案:http://www.example.com/Траливали/ - 将查询作为url的一部分发送并使用mod_rewrite进行解析。


请注意,在编程中涉及两个步骤:从浏览器到脚本,从脚本到浏览器。如果您想要看到所需数据的正确输出,这两个步骤都需要正确执行。因此,这取决于您的脚本需要执行什么操作。请查看我的更新答案以获取一些建议。 - mvds
我添加了一些测试,rawurlencode 给出的结果与 urlencode 相同。 - topright gamedev
1
你需要放慢速度,逐步评估。看看你的示例URL:你确定"Траливали"在UTF-8中是"%D2%F0%E0%EB%E8%E2%E0%EB%E8"吗?这里显示为"%D0%A2%D1%80%D0%B0%D0%BB%D0%B8%D0%B2%D0%B0%D0%BB%D0%B8"。这可能是问题所在吗? - mvds
%D2%F0%E0%EB%E8%E2%E0%EB%E8 - 这是由Firefox自动生成的字符串。 - topright gamedev
是的。它是1251编码字符串,而不是utf8。 - Your Common Sense
显示剩余4条评论
7个回答

7

如果在片段后面没有查询部分,那么它不会被转换为有效的URL。

RFC 3986定义URI由以下部分组成:

     foo://example.com:8042/over/there?name=ferret#nose
     \_/   \______________/\_________/ \_________/ \__/
      |           |            |            |        |
   scheme     authority       path        query   fragment

订单无法更改。因此,
URL1: http://www.example.com/?query=Траливали#ajax_call

将被正确处理

URL2: http://www.example.com/#ajax_call?query=Траливали

不会。如果我们看一下URL2,IE实际上通过检测片段作为#ajax_call?query=Траливали正确处理URL,没有查询。片段总是最后一个并且从不发送到服务器
IE将正确编码URL1的查询组件,因为它将其检测为查询。
至于在PHP中解码,%D2和类似的内容将自动解码为$_GET ['query']变量。 $_GET变量未正确填充的原因是在URL2中,根据标准,没有查询。
还有最后一件事...当执行'Траливали' == $_GET ['query']时,仅当您的PHP脚本本身以UTF-8编码时才为真。您的文本编辑器应该能够告诉您文件的编码。

2
@topright #ajax_call?query=Траливали 表示该片段由文本 ajax_call?query=Траливали 组成。该片段不会发送到服务器。换句话说,在 URL 中以 # 开头的任何内容都永远不会发送到服务器 - deceze
@topright:片段非常适合用于 AJAX,因为它们存储在历史记录中,而不会通过向服务器发送无用数据来浪费带宽。这就是为什么它们在需要在客户端解析的 AJAX 场景中使用的原因。您正在尝试做的事情将无法使用片段实现(它们从未发送到 PHP),这就是为什么我们告诉您要改用查询。但您选择忽略了那个建议。 - Andrew Moore
片段通过Ajax调用发送到服务器。服务器以那种方式接收到Траливали。 - topright gamedev
2
不相信我?试一试吧... echo $_SERVER['REQUEST_URI']; 将会给你与 Apache 看到的请求完全相同的结果。你很快就会注意到片段丢失了。同时检查你的日志... 那里也没有片段。 - Andrew Moore
1
@topright:现在问题很清楚了,我敢打赌问题出在你的JavaScript片段转查询代码上... 你能把那一部分代码发出来吗? - Andrew Moore
显示剩余15条评论

4
rawurldecode($_GET['query']);

但这实际上应该已经由PHP完成了 ;-)

编辑 如果你说“什么都不起作用” - 你在尝试什么?如果文本在屏幕上显示不如你所愿,例如当你使用echo $_GET ['query'];时,你的问题可能是你指定的编码与发送回浏览器的页面不一致。

包括一行

header("Content-Type: text/html; charset=utf-8");

看看是否有帮助。


请展示整个脚本并告诉我们哪里出了问题。 - mvds
我在这篇文章中添加了一些测试。 - topright gamedev

2

不幸的是,片段如何编码取决于浏览器

是否通过应用RFC规定的URL转义规则来编码片段ID(哈希)?
MSIE:否
Firefox:部分
Safari:是
Opera:否
Chrome:否
Android:是

至于浏览器在将国际(即非ASCII)字符转换为%nn转义序列之前使用的编码方式,“大多数浏览器都通过在URL栏中手动输入的任何文本上默认发送UTF-8数据,并在所有跟随的链接上使用页面编码来处理这个问题。”(同一来源)。


并不重要代码片段的编码方式,因为它只在客户端进行处理。 - Andrew Moore
@And 这是怎么回事?对于 JavaScript 来说,"á" != "%C3%A1"。 - Artefacto

1

0

URLs的字符限制为ASCII字符。不适合URL的字符应该进行URL编码(即您看到的%hh编码)。一些浏览器可能会自动对出现在地址栏上的URL进行编码。


-1:在查询中传递UTF-8没有问题。多字节字符将被编码为两个字节,然后会被正确解码。 - Andrew Moore
但是浏览器仍然在幕后对URL进行编码。服务器应该看到一个格式良好的URL,Web应用程序将能够解码它。 - seand
1
浏览器不需要了解URL编码的字符集。它只需读取8个字节并将其转换为十六进制值。任何不被视为“可打印ASCII”字符的内容都将按照RFC3986由用户代理进行编码。 - Andrew Moore

0
答案很简单:字符串被编码总是如HTTP标准所述。
而Firefox 显示什么并不重要。
此外,由于PHP自动解码查询字符串,因此也不需要解码。
请注意,'%D2%F0%E0%EB%E8%E2%E0%EB%E8' 是单字节编码,因此您的页面可能在1251中。至少HTTP标头向浏览器表示如此。
而AJAX始终使用utf-8。
因此,您只需对您的页面使用单个编码(utf-8),或区分ajax调用和常规调用即可。
至于片段 - 不要使用片段值将其发送到服务器。有一个JS变量,然后使用它两次 - 设置片段和使用JSON发送到服务器。

0

RFC 1738规定URL中只有字母数字、特殊字符$-_.+!*'(),"和保留字符;/?:@=&不需要进行编码。其他所有字符都需要由HTTP客户端(即Web浏览器)进行编码。您可以使用rawurldecode()函数对查询字符串进行解码,无论PHP是否自动解码。双重解码没有任何危险。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接