为什么在 PHP 中的函数 header() 之后调用的函数 http_response_code() 表现得很奇怪?

Why is function http_response_code() acting strange that was called after function header() in PHP?

嗯。我对 http_response_code() 有疑问,我找不到解释。 如果我在 http_response_code() 之前使用 header(),PHP returns 由 header() 设置的 HTTP 状态并忽略任何 http_response_code().

比如我有一个文件:

<?php
header('HTTP/1.1 404 Not Found');
file_put_contents('./log',http_response_code(501).PHP_EOL,FILE_APPEND);
file_put_contents('./log',http_response_code(502).PHP_EOL,FILE_APPEND);
file_put_contents('./log',http_response_code(503).PHP_EOL,FILE_APPEND);

(我使用 file_put_contents() 来防止任何输出,因为有人会说,这就是这个问题的答案)

我使用默认的 php-server(但问题可以用 NGINX 重现):

php -S localhost:9985

有需求:

curl -D - http://localhost:9985

有回复:

HTTP/1.1 404 Not Found
Host: localhost:9958
Date: Wed, 15 Sep 2021 17:20:05 GMT
Connection: close
X-Powered-By: PHP/8.0.10
Content-type: text/html; charset=UTF-8

还有日志:

404
501
502

响应包括第一行:

HTTP/1.1 404 Not Found 

但我一直在等待 HTTP/1.1 503 Service Unavailable。 日志文件包含预期数据,它显示 http_response_code 正确返回状态。但它不影响 HTTP 响应代码。

我认为原因是一些数据可以发送到 OUTPUT,因为我使用了 header()。 但是如果我使用 header() 两次,它不会产生问题。 我更改了我的文件:

<?php
header('HTTP/1.1 404 Not Found');
header('HTTP/1.1 503 Service Unavailable');

并重复请求:

curl -I http://localhost:9958

有回复:

HTTP/1.1 503 Service Unavailable
Host: localhost:9958
Date: Wed, 15 Sep 2021 17:26:08 GMT
Connection: close
X-Powered-By: PHP/8.0.10
Content-type: text/html; charset=UTF-8

效果很好!

问题可以在 PHP 7.2、PHP 7.4、PHP 8 上重现。 我在它所在的位置找到了一条评论 https://www.php.net/manual/ru/function.http-response-code.php#125538。但是有阿帕奇。我应该说,如果我使用 Apache,这个问题对我来说是不可重现的。

请描述,为什么函数 (http_response_code) 无法正常工作。

请不要建议不要使用该函数,因为我想知道该函数行为的真正原因(神圣的含义)。


更新: 错误报告 https://bugs.php.net/bug.php?id=81451&edit=2

简短说明

这是因为在某些(全部?)PHP SAPI 实现中,传递给 header() 的状态行优先于 http_response_code()

技术说明(具体到PHP8.0.10)

PHP 在两个单独的变量中跟踪状态行和 HTTP 响应代码:SG(sapi_headers).http_status_lineSG(sapi_headers).http_response_code.

header('HTTP/1.1 404 Not Found')http_status_line 设置为“HTTP/1.1 404 未找到”here and updates SG(sapi_headers).http_response_code a few lines earlier, while http_response_code(503) only sets SG(sapi_headers).http_response_code to 503 here.

PHP 的内置服务器用于发送 header 的代码可以在 sapi_cli_server_send_headers 函数中找到(php_cli_server.c). In that function, we see that SG(sapi_headers).http_response_code is ignored when SG(sapi_headers).http_status_line is set. The sapi_cgi_send_headers function used by PHP-FPM shows a similar story. The apache2handler SAPI uses 两者http_status_line和http_response_code。理论上,它们可以指向不同的状态!

错误?

也许吧。但是 changing/fixing 这种行为在谁知道多少年后会破坏向后兼容性,所以它可能应该单独保留。最好坚持使用 header() 或 http_response_code().

来完全避免这种情况