Best way to send HTTP response code in PHP - 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}");
// ...

Related

WP_REST_Response vs WP_Error

I'm a bit confused about how errors are handled in Wordpress's REST API. In their examples, they suggest using WP_Error to return errors, but WP_REST_Response has the HTTP status code as a second parameter, which makes it shorter and somewhat cleaner to my taste.
So I'm comparing this way of returning an error:
return new WP_REST_Response(array('error' => 'Error message.'), 400);
With this one:
return new WP_Error('rest_custom_error', 'Error message.', array('status' => 400));
With the first option, I can have just the error text in my response and it's enough for me. So the response would look like so:
{"error":"Error message."}
With the second one it's more detailed:
{"code":"rest_custom_error","message":"Error message.","data":{"status":403}}
But I also need to specify the error code (first parameter), which doesn't give any advantage to my front-end implementation. Other than the syntax, I'm curious about differences in performance, security and future-proof factors.
So is there any reason to prefer one over the other than personal preferences?
I do as follows:
WP_REST_Response // Used when endpoint code runs just fine but needs to
// tell the client about some other downstream error (e.g. 4xx)
WP_Error // Used when endpoint code has an issue and needs to tell you
// it didn't compute or couldn't find resources etc (e.g. 5xx)
As far as I can tell, WordPress changes the status codes of the responses in unpredictable ways. This is dumb. For instance, if you create a WP_REST_RESPONSE with status 400, it actually sends it as status 200 with "status:400" as data in the body.
What I do:
Just use PHP response functions:
http_response_code(400);
exit;
If you tell me I'm wrong, please explain the benefit of using these two function wrappers. I see the benefit if you work for Automattic and are creating regressions in core itself. If you're a plugin or theme dev, [and you're assuming core works] these functions should be avoided as having no use.

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.

PHP headers_list() is not showing all headers

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.

Error decoding JSON input via json_decode

I'm writing my first web service, and I have a problem related to JSON data passing. I have my web service divided in two files: controller.php, which contains the service handler, and service.php, which contains the classes and methods to be served on request.
This is the acquisition fragment from controller.php:
public function atender() {
// pre-procesamos la peticiĆ³n
if (!empty($_POST)) {
if (!empty($_POST["class"]) && !empty($_POST["action"]) && !empty($_POST["function"])) {
$clase = ucwords($_POST["class"]);
$metodo = "{$_POST["action"]}{$this->obtenerMetodo($_POST["function"])}";
$id = (!empty($_POST["datos"]) ? stripslashes($_POST["datos"]) : null);
I can attend requests on both GET and POST mode (I use GET for methods not requiring authentication, like getCategories, getCategoryById, getProducts and getProductById. These are methods to get the dish categories and dishes in a sushi restaurant.)
For any of the GET requests, everything works like expected. My problem comes when I handle POST requests. I need to get all URL parameters in JSON, as this is to serve an iOS app, and JSON is the way we handle data to/from.
This is the processing fragment from service.php:
public function putUser($datos) {
if (!empty($datos)) {
$usuario = json_decode($datos);
$this->log .= implode("/", $usuario) . "\r\n";
In this case, $datos is the JSON-encoded data from the request. It's received as $id in controller.php (the code above). As it's my first web service, it's very probable I'm doing something really bad here, but I'm a bit blinded.
I've tried different variations of the service handling code. Using json_decode($datos, true) doesn't work either. I get
'Unexpected token <' as a response and, in raw form (using the advanced REST client from Google Chrome) it says: ''Warning: implode() [<a href='function.implode'>function.implode</a>]: Invalid arguments passed in /home/refine/public_html/sushigo/palma/service.php on line 344'.
I know SOAP is, usually, a better alternative to writing custom code like this but, for now, I need to stick with this code and implement a better alternative for my next project. Could you tell me what am I doing wrong?
The error message says, basically, that it is NOT a valid JSON - and such error messages are usually right.
Your error is somewhere in the sending / receiving code. Probably you send the JSON in one form and try to access it in some other way. Since I have no way of looking at the requests sent from the phone, I would guess that:
you send the data as application/json and try to receive it as an url encoded form. If you don't understand the difference, here's your problem.
you use stripslashes on the JSON data, which is wrong. UNLESS you have magic_quotes turned on, which would also be wrong (that is: both magic_quotes and stripslashes have to go).

PHP lithium(li3) how to set up automatic response with JSON

I have a lithium app set up that way, so when
return($data)
is used it either can be used in the lithium view.html.php as
echo $data
or if request header "accept" equals "json/javacript" it would return something like
{
data: { a:'b' }
}
automatically.
Unfortunately in the new app that I made as a test app that flow is not happening (and only HTML is always returned).
After doing a little research it seems like that it is supposed to be done automatically if I uncomment
require __DIR__ . '/bootstrap/media.php';
inside bootstrap.php But that didn't work, I still have HTML returned. I downloaded a recent version of the lithium framework(I downloaded it may be 1 or 2 months ago)
Anybody knows if automatic response with JSON requires some set up or not?
taken from http://dev.lithify.me/lithium/tickets/view/353
which is then taken from the lithium docs
To enable automatic content-type negotiation (i.e. determining the content type of the response based on the value of the HTTP Accept header), set the 'negotiate' flag to true. Otherwise, the response will only be based on the type parameter of the request object (defaulting to 'html' if no type is present in the Request parameters)
http://li3.me/docs/lithium/action/Controller::$_render
If you need more help on how to implement leave a comment.
It is also possible to set type to $this->request->accepts() when calling render().
return $this->render(array('type' => $this->request->accepts()));

Categories