5

Well. I have a problem with http_response_code(), and I can't find an explanations. If I use header() before http_response_code(), PHP returns HTTP-status which was set by header() and ignores any http_response_code().

For example, I have a file:

<?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);

(I used file_put_contents() for preventing any output, because somebody can say, that it is the answer to this question)

I used default php-server (but the problem can be reproduced with NGINX):

php -S localhost:9985

There is request:

curl -D - http://localhost:9985

There is response:

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

And there is log:

404
501
502

The response includes first line:

HTTP/1.1 404 Not Found 

but I was waiting for HTTP/1.1 503 Service Unavailable. The log file includes an expected data and it shows that http_response_code returned statuses correctly. But it didn't affect for HTTP response code.

I thought that a reason is some data which could have been sent to OUTPUT because I used header(). But if I use header() twice, it doesn't create problems. I changed my file:

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

And repeated request:

curl -I http://localhost:9958

There was response:

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

This works great!

The problem can be reproduced on PHP 7.2, PHP 7.4, PHP 8. I found a comment https://www.php.net/manual/ru/function.http-response-code.php#125538 where it is. But there is apache. I should say that with if I use Apache, the problem is not reproducible for me.

Please, describe, why the function (http_response_code) doesn't work how expect.

Please, don't advise not to use the function because I would want to know true reason for the behavior of the function (the sacred meaning).


Update: Bug report https://bugs.php.net/bug.php?id=81451&edit=2

  • Yes, I know it. That's not the problem. – Alexander Karpov Sep 15 '21 at 18:00
  • `I used file_put_contents() for preventing any output, because somebody can say, that it is the answer to this question` But why? Why not do ONLY the http_response_code? It should not output anything – aynber Sep 15 '21 at 18:02
  • @aynber Because I wanted to show that `http_response_code` works. My log proves this. But it doesn't affect for HTTP Response Code ) – Alexander Karpov Sep 15 '21 at 18:05
  • Alright... So the issue is that `http_response_code()` does not override the status code generated by `header()` in certain SAPIs. For me, it works in mod_php, it fails in builtin server. So, honestly, it feels like a bug. – Álvaro González Sep 15 '21 at 18:10
  • @aynber I made some edits to the question for making the problem clearer. Thank you for your attention! – Alexander Karpov Sep 15 '21 at 18:11
  • 1
    @ÁlvaroGonzález yes! It feels like a bug, but if it is true, it is very strange because it can be reproduced in PHP 7.2, PHP 7.4, PHP 8. May be it has some other explanation? – Alexander Karpov Sep 15 '21 at 18:14
  • Like [#76082](https://bugs.php.net/bug.php?id=76082), although this one in particular is fixed and related to NTS builds. – Álvaro González Sep 15 '21 at 18:16
  • @ÁlvaroGonzález Maybe I should create a bug report on https://bugs.php.net/ ? – Alexander Karpov Sep 15 '21 at 18:23
  • 1
    I took a quick look at the current source and couldn't make out a reason, why this shouldn't work. Yeah seems like you found a nice bug there. I'd go ahead and file a bug report. – stui Sep 15 '21 at 18:29

1 Answers1

4

Short explanation

It's because the status line passed to header() is prioritized over http_response_code() in some (all?) PHP SAPI implementations.

Technical explanation (specific to PHP 8.0.10)

PHP tracks the status line and HTTP response code in two separate variables: SG(sapi_headers).http_status_line and SG(sapi_headers).http_response_code.

header('HTTP/1.1 404 Not Found') sets http_status_line to "HTTP/1.1 404 Not Found" 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.

The code that PHP's builtin server uses for sending the headers can be found in the sapi_cli_server_send_headers function (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 both http_status_line and http_response_code. Theoretically, they could point to different statuses!

Bug?

Maybe. But changing/fixing this behaviour after who knows how many years breaks backwards compatibility, so it should probably be left alone. It's probably best to avoid this situation altogether by sticking to either header() or http_response_code().

Pieter van den Ham
  • 4,381
  • 3
  • 26
  • 41
  • Thank you for the explanation! May be you now, why the problem missed in the official documentation ? I think that it would be so helpful. – Alexander Karpov Sep 17 '21 at 02:57
  • Certainly, it doesn't affect all SAPIs and Apache module behaves differently. And if it's missing from documentation it's only because nobody has cared to do this research and push changes to the manual. – Álvaro González Sep 17 '21 at 06:56