带有“И”和“Э”utf-16 符号的 PHP 7 fopen/file_get_contents 在 URL 中返回 HTTP 500,甚至无需调用服务器适用于 PHP8

问题描述

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

Image.asset(
    'images/yourimage.jpg'
    )

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

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

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

PHP8 好像已经修复了,但是为什么会出现这个错误

附注。这不是向请求添加标题(我尝试过) - 甚至不会发生调用

更新: 检查了本地 Nginx 日志,它确实调用了服务器,这就是我对这两个符号所拥有的 - PHP 将 unicode 符号的第二部分视为“_”:

enter image description here

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

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

"GET /\xD0_ HTTP/1.0" 500    <----  PHP7-  
"GET /\xD0\x98 HTTP/1.0" 404 <----  PHP8

解决方法

因为 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"