【问题标题】:PHP 7 fopen/file_get_contents with "И" and "Э" utf-16 symbol in URL return HTTP 500 without even calling server (works on PHP8)URL 中带有“И”和“Э”utf-16 符号的 PHP 7 fopen/file_get_contents 甚至无需调用服务器即可返回 HTTP 500(适用于 PHP8)
【发布时间】:2021-07-22 06:51:27
【问题描述】:

在 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 中已修复,但为什么会出现此错误?

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

更新: 检查了本地 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/‘"); // e2 80 98
file_get_contents("http://localhost/ĭ"); // c4 ad
file_get_contents("http://localhost/Ę"); // c4 98

【问题讨论】:

  • 当您在浏览器中访问http://localhost/И 时会发生什么?
  • 你设置ini_set('display_errors', '1'); error_reporting(E_ALL);了吗?如果您这样做并且错误仍然是 500 - 您是否检查了网络服务器的日志以了解实际导致错误的原因? :-) 它也是 UTF-16,所以它不是错误,而是缺少支持:) 还要检查 URL 是否按照@ChrisHaas 的建议首先工作。
  • 出于好奇,您正在运行什么操作系统?如果将“r”更改为“rb”会发生什么?
  • 500 Internal Server Error 是服务器生成的消息,因此即使您认为不会调用,也会发生调用。查看服务器错误日志以获取更有意义的消息。
  • @ChrisHaas 在浏览器中工作正常 - 返回文件。正如我所说,这是 php 本身的问题,而不是网络或其他服务器端问题

标签: php php-7 php-8


【解决方案1】:

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

【讨论】:

  • 是的,接缝显式编码只是一种方式
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-04
  • 1970-01-01
相关资源
最近更新 更多