PHP headers_list() is not showing all headers - php

According to the documentation: http://php.net/manual/en/function.headers-list.php, and this comment: http://php.net/manual/en/function.headers-list.php#110330, php code:
<?php var_dump(header_list()); ?>
Does not show the status headers.
This strange behavior is strange. So there are two questions:
Why? (I'm not sure if this question is opinion based, if it is, and there is no REAL explanation please omit it. I mean that sometimes opinion based questions aren't opinion based, and really have explanation, and this cannot be predict before they are asked).
I know that I can use my own function to set header, which will set header and additionally remember that this header was set. But this is kind of... workaround, as header_list() is quite sure HERE, FOR THAT. Additionally those headers are somewhere in the php engine memory so saving them second time inside script is not memory efficient. So... What is the back-door to get all headers, not as stupid as workaround below? This can be useful for example as a part of debug / developer class that is rendering all the "developer" data as html comments at the end of the page. Of course I'm omitting the content length header which is too soon to predict.
It looks like this function omit all the headers that don't have colon... Is it right?
To post more code, simple workaround to header function (linear not object, using globals and not static class just to show the idea). With the assumption that header function is omitting headers without colons (which may not be quite true...):
<?php
// Mechanism:
$headers = array();
function setHeader($header) {
header($header);
if (strpos($header, ':') === false) {
global $headers;
$headers[] = $header;
}
}
function getHeaders() {
global $headers;
return array_merge($headers, header_list());
}
// Example:
setHeader('HTTP/1.1 404 Not Found');
var_dump(getHeaders());
?>

Checking the engine source for headers_list and http_response_code, notice that the value for general headers and status code are separated:
// headers_list
SG(sapi_headers).headers
// http_response_code
SG(sapi_headers).http_response_code
But HTTP response code isn't the only header with dedicated storage: Content-Type does, too:
SG(sapi_headers).mimetype = NULL;
So what's going on here? The complete header() algorithm specifically checks for the following strings to adjust state:
HTTP/
Content-Type
Content-Length
Location
WWW-Authenticate
HTTP/ is checked specifically because that's how one set the status code explicitly before PHP 5.4: after that, http_response_code is available and is recommended for clarity. That header() was used is confusing, for the reason you're asking in this question and on general principle: the http header BNF clearly doesn't include status line:
header-field = field-name ":" OWS field-value OWS
PHP handles the others separately because they are single-value headers and/or their value matters for efficiency in later calculations.
TL;DR: HTTP/ set by header() isn't included in headers_list() because HTTP/ status lines are not headers in the strict RFC sense. But for the PHP < 5.4 limitation that header() was the only way to set HTTP/ status, it'd likely have never been a confusing issue.

It seems that only the status code is missing from the header_list.
You can get the current status code (they probably overwrite one another) using another function: http_response_code.

Related

Does http_response_code need to be called before any output is sent?

The http_response_code function seems like it should work like header in that it needs to be called before any output is sent. However, the PHP manual makes no mention of this. Does it work this way? For example, would the following program produce an error?
<!DOCTYPE html>
<html>
<?php
http_response_code(404);
?>
</html>
If it doesn't, why does header work that way but not http_response_code?
One of the comments on the PHP manual for this function sums it up nicely:
http_response_code is basically a shorthand way of writing a http
status header, with the added bonus that PHP will work out a suitable
Reason Phrase to provide by matching your response code to one of the
values in an enumeration it maintains within
php-src/main/http_status_codes.h. Note that this means your response
code must match a response code that PHP knows about. You can't create
your own response codes using this method, however you can using the
header method.
In summary - The differences between "http_response_code" and "header"
for setting response codes:
Using http_response_code will cause PHP to match and apply a Reason Phrase from a list of Reason Phrases that are hard-coded into the PHP
source code.
Because of point 1 above, if you use http_response_code you must set a code that PHP knows about. You can't set your own custom code,
however you can set a custom code (and Reason Phrase) if you use the
header method.

Symfony 2.7 set header from StreamedResponse's callback

I have to set dynamically my Content-Type into the StreamedResponse's callback.
I wrote a code like this that it works fine on Symfony 2.3 but not on 2.7. The returned Content-Type is text/html.
function indexAction()
{
$r = new \Symfony\Component\HttpFoundation\StreamedResponse();
$r->setCallback(function() use ( & $r)
{
// ...
$r->headers->set('Content-Type', 'application/json');
// ...
});
return $r;
}
I found this commit that it can be on but i don't really understand...
https://github.com/symfony/http-foundation/commit/6a0838a26d54eff153b825e1550c1f6fa05a0941
It looks like we can now only set header outside the callback.
It works if i send headers with the native php function into the closure but it's dirty...
header('Content-Type: application/json');
if someone has a clue...
Thanks!
Streamed response made for purposes when you need long-polling connections. And so you have to know what content-type it will serve before you send any data.
When you return your Response object in action Symfony already sends headers and starts to stream output from callback function. In this moment it is not right to send another header.
P.S. I have never seen case when you really need StreamedResponse and need to set Content-Type dynamically.
To be accurate, you can use hack with replacing headers up to version 2.7.19, it was merged in 2.7.20
As #MichaelSivolobov said in his answer, I also have never seen cases when you need it
I'm sure such commits are always made for enforce better application design. When I see such cases as in your example, it's a some kind of red flag to rethink code/data flow

Best way to send HTTP response code in PHP

From reading the php spec and other questions on Stack Overflow, I can see three ways of sending an HTTP response code from PHP:
header("HTTP/1.0 404 Not Found");
^ ^ ^
A B C
header(" ", false, 404);
^ ^ ^
C D B
http_response_code(404);
^
B
A: Defines HTTP header
B: Response code
C: Message
D: To replace previous header or not
What is the difference between these and which one is the best to use? Is my understanding of the parameters correct?
Thanks,
Tugzrida.
To answer your question about what is the difference, I found this comment in the PHP docs (thanks Steven):
http_response_code is basically a shorthand way of writing a http
status header, with the added bonus that PHP will work out a suitable
Reason Phrase to provide by matching your response code to one of the
values in an enumeration it maintains within
php-src/main/http_status_codes.h. Note that this means your response
code must match a response code that PHP knows about. You can't create
your own response codes using this method, however you can using the
header method.
In summary - The differences between http_response_code and header
for setting response codes:
Using http_response_code will cause PHP to match and apply a Reason Phrase from a list of Reason Phrases that are hard-coded into
the PHP source code.
Because of point 1 above, if you use http_response_code you must set a code that PHP knows about. You can't set your own custom code,
however you can set a custom code (and Reason Phrase) if you use the
header method.
I was curious about how some popular frameworks send the header in a standard response:
Symfony (and Laravel, by inheritance) sets the raw header:
// status
header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode);
Zend Framework 2 also sets the raw header:
public function renderStatusLine()
{
$status = sprintf(
'HTTP/%s %d %s',
$this->getVersion(),
$this->getStatusCode(),
$this->getReasonPhrase()
);
return trim($status);
}
And so does Yii
protected function sendHeaders()
{
if (headers_sent()) {
return;
}
$statusCode = $this->getStatusCode();
header("HTTP/{$this->version} $statusCode {$this->statusText}");
// ...

How does get_headers work in the background

I tryied searching for this and I belive I alredy know the answer but it's crusal that I'm not wrong, so here I go..
When calling get_headers, will I retrieve the whole file even though the function only returns the headers or will it retrieve, as expected, only the headers and nothing else?
I'm guessing the last but if I'm wrong this will cause some serious problems..
Also I noticed that there is a global setting I can change to send a HEAD request instead of the default GET request, witch is why I'm asking my self whats really going on.
Edit
Maybe this function is a better alternative? stream_get_meta_data or do they actually do the same thing?
You could also take a look at the source code, if you are familiar with C.
The function is defined here. I quickly looked over this, and it seems it is a header-only request, see line 715:
STREAM_ONLY_GET_HEADERS
GET
Requests a representation of the specified resource. Requests using
GET should only retrieve data and should have no other effect. (This
is also true of some other HTTP methods.) The W3C has published
guidance principles on this distinction, saying, "Web application
design should be informed by the above principles, but also by the
relevant limitations."
HEAD
Asks for the response identical to the one that would correspond to a
GET request, but without the response body. This is useful for
retrieving meta-information written in response headers, without
having to transport the entire content.
Wikipedia/Hypertext_Transfer_Protocol
The PHP-docs clearly states that normal get_headers() uses a GET-request, but you can force it to use HEAD instead, like this:
<?php
// By default get_headers uses a GET request to fetch the headers. If you
// want to send a HEAD request instead, you can do so using a stream context:
stream_context_set_default(
array(
'http' => array(
'method' => 'HEAD'
)
)
);
$headers = get_headers('http://example.com');
?>
Unfortunaley you're right, just read the PHP manual:
get_headers() returns an array with the headers sent by the server in response to a HTTP request.
Also take a look at the examples.
Okay, next time I should spend more attention to the question formulation.
Yeh, if the request type is set to GET (standard) you will get the whole content. You could change it to HEAD, but this is not what you want.

Can PHP detect the status code that it has or is preparing to send to the browser?

I am sending a status code via the header function, such as header('HTTP/1.1 403');, and would like to in another area of my code detect what status code is to be sent (in particular if I have sent an error code of some nature). As has been mentioned elsewhere, headers_list() does not return this particular item, and it's unclear to me if it actually counts as a header, as neither Firebug nor Fiddler2 treat it with the headers. So - how can a PHP script detect which status code is about to be sent to the browser?
I would rather not use a wrapper function (or object) around the header method, or otherwise set some global variable along with the sending of the status code. I'd also rather not call my code from itself using curl or the like, due to performance concerns. Please let me know what you think.
Consider setting a constant:
define('HTTP_STATUS', 403);
and using the defined function later on:
if(defined('HTTP_STATUS') && HTTP_STATUS == 403) // ...or whatever you're looking to do
Actually peeking back at the headers themselves is kind of a hack in itself as it's simply too slow: you're dealing with strings and arrays and all sorts of other messy data. Set for yourself a simple constant: it's blazing fast, it does the same thing, and it doesn't create any "true" global variables.
http_response_code() in PHP 5.4 does this now.
It's another call, but as a last resort you could use this rather than curl. If you have php 5.0, What about get_headers()?

Categories