首页 > 解决方案 > URL 中带有“И”和“Э”utf-16 符号的 PHP 7 fopen/file_get_contents 甚至无需调用服务器即可返回 HTTP 500(适用于 PHP8)

问题描述

当在 url 链接中使用两个大写西里尔字母“И”和“Э”之一时,会出现奇怪的行为:

file_get_contents("http://localhost/И")
fopen("http://localhost/И", "r")

两者都返回以下错误,但服务器甚至没有被调用:

failed to open stream: HTTP request failed! HTTP/1.1 500 Internal Server Error

有人知道这是已知问题吗?是否报告了错误?

似乎它在 PHP8 中已修复,但为什么会发生此错误?

PS。这与向请求添加标头无关(我尝试过)-甚至没有发生调用。

更新:检查了本地 nginx 日志,并且确实调用了服务器,这就是我对这两个符号所拥有的 - php 将 unicode 符号的第二部分视为“_”: 在此处输入图像描述

"GET /\xD0_ HTTP/1.0" 500    <----  php7-  
"GET /\xD0\x98 HTTP/1.0" 404 <----  php8

更新 2:我发现不仅这两个符号在 PHP 7 中有这样的问题,而且在 UTF-8 表的十六进制代码中以“98”或“ad”结尾的每个符号,这里是其他符号的示例相同的行为:

file_get_contents("http://localhost/ϭ"); // cf ad
file_get_contents("http://localhost/Θ"); // ce 98
file_get_contents("http://localhost/Ҙ"); // d2 98
file_get_contents("http://localhost/ј"); // d1 98
file_get_contents("http://localhost/ѭ"); // d1 ad
file_get_contents("http://localhost/Ә"); // d3 ad
file_get_contents("http://localhost/‘&quot;); // e2 80 98
file_get_contents("http://localhost/ĭ"); // c4 ad
file_get_contents("http://localhost/Ę"); // c4 98

标签: phpphp-7php-8

解决方案


因为http://localhost/И是格式错误的 URL,您需要对包含 127 以上代码点的路径组件进行 urlencode。您的浏览器以及可能的一些 HTTP 库透明地执行此操作,但在 PHP 中使用文件/流函数调用 URL 绝对不会。

// because this is what I copy/pasted off of SO, which is UTF8
$in_8  = 'И';
// your endianness may vary
$in_16 = mb_convert_encoding($in_8, 'UTF-16LE', 'UTF-8');

$url_8  = 'http://example.com/'.urlencode($in_8);
$url_16 = 'http://example.com/'.urlencode($in_16);

var_dump(
    bin2hex($in_8),
    $url_8,
    bin2hex($in_16),
    $url_16
);

输出:

string(4) "d098"
string(25) "http://example.com/%D0%98"
string(4) "1804"
string(25) "http://example.com/%18%04"

推荐阅读