No.
Error codes are an anachronism, they stem from ye olden days when output was really hard and expensive, and the only way to signal an error condition may have been through a bunch of front panel lights:
These days, we have mature exception handling built into pretty much every mainstream language. Use it. Give the user information they can work with; don't bother them with technical blah-blah, but rather tell them roughly what went wrong and what they can do about it. For logging, just give your exceptions descriptive names, and log the name. Easier to remember, and also easier to find using grep or similar search tools.
The exception is, of course, when you're programming for situations where output is still hard and expensive, such as embedded systems or network protocols. HTTP still uses numeric response codes because they are extremely easy to parse efficiently - in some situations, reading just the first digit can tell you enough already, and you can discard the rest of the packet.
In this situation, I always think of the interface first, then write PHP code to support it.
- It's a REST API, so meaningful HTTP status codes are a must.
- You want consistent, flexible data structures being sent to and from the client.
Let's think of all the things that could go wrong and their HTTP status codes:
- The server throws an error (500)
- Authentication failure (401)
- The resource requested was not found (404)
- The data you are modifying has been changed since you loaded it (409)
- Validation errors when saving data (422)
- The client has exceeded their request rate (429)
- Unsupported file type (415)
Note, there are others which you can research later.
For most of the failure conditions, there is only one error message to be returned. The 422 Unprocessable Entity
response, which I've used for "validation errors" could return more than one error --- One or more errors per form field.
We need a flexible data structure for error responses.
Take as an example, the 500 Internal Server Error
:
HTTP/1.1 500 Internal Server Error
Content-Type: text/json
Date: Fri, 16 Jan 2015 17:44:25 GMT
... other headers omitted ...
{
"errors": {
"general": [
"Something went catastrophically wrong on the server! BWOOP! BWOOP! BWOOP!"
]
}
}
Contrast that with simple validation errors when trying to POST something to the server:
HTTP/1.1 422 Unprocessable Entity
Content-Type: text/json
Date: Fri, 16 Jan 2015 17:44:25 GMT
... other headers omitted ...
{
"errors": {
"first_name": [
"is required"
],
"telephone": [
"should not exceed 12 characters",
"is not in the correct format"
]
}
}
They key here is the content type being text/json
. This tells client applications that they can decode the response body with a JSON decoder. If, say, an internal server error isn't caught and your generic "Something went wrong" web page gets delivered instead, the content type should be text/html; charset=utf-8
so client applications won't attempt to decode the response body as JSON.
This looks all find and dandy, until you need to support JSONP responses. You must return a 200 OK
response, even for failures. In this case you'll have to detect that the client is requesting a JSONP response (usually by detecting a URL request parameter called callback
) and change up the data structure a bit:
(GET /posts/123?callback=displayBlogPost)
<script type="text/javascript" src="/posts/123?callback=displayBlogPost"></script>
HTTP/1.1 200 OK
Content-Type: text/javascript
Date: Fri, 16 Jan 2015 17:44:25 GMT
... other headers omitted ...
displayBlogPost({
"status": 500,
"data": {
"errors": {
"general": [
"Something went catastrophically wrong on the server! BWOOP! BWOOP! BWOOP!"
]
}
}
});
Then the response handler on the client (in a web browser) should have a global JavaScript function called displayBlogPost
which accepts a single argument. This function would have to determine if the response was successful:
function displayBlogPost(response) {
if (response.status == 500) {
alert(response.data.errors.general[0]);
}
}
So we've taken care of the client. Now, let's take care of the server.
<?php
class ResponseError
{
const STATUS_INTERNAL_SERVER_ERROR = 500;
const STATUS_UNPROCESSABLE_ENTITY = 422;
private $status;
private $messages;
public function ResponseError($status, $message = null)
{
$this->status = $status;
if (isset($message)) {
$this->messages = array(
'general' => array($message)
);
} else {
$this->messages = array();
}
}
public function addMessage($key, $message)
{
if (!isset($message)) {
$message = $key;
$key = 'general';
}
if (!isset($this->messages[$key])) {
$this->messages[$key] = array();
}
$this->messages[$key][] = $message;
}
public function getMessages()
{
return $this->messages;
}
public function getStatus()
{
return $this->status;
}
}
And to use this in the case of a server error:
try {
// some code that throws an exception
}
catch (Exception $ex) {
return new ResponseError(ResponseError::STATUS_INTERNAL_SERVER_ERROR, $ex->message);
}
Or when validating user input:
// Validate some input from the user, and it is invalid:
$response = new ResponseError(ResponseError::STATUS_UNPROCESSABLE_ENTITY);
$response->addMessage('first_name', 'is required');
$response->addMessage('telephone', 'should not exceed 12 characters');
$response->addMessage('telephone', 'is not in the correct format');
return $response;
After that, you just need something that takes the returned response object and converts it into JSON and sends the response on its merry way.
Best Answer
I would go with text. It's OK to have a string where you only use numbers. As a rule of thumb: you should not use integers for things that are not mathematical in nature. That is, if you can do addition or multiplication on the values in a sensible way, then it's a number. Otherwise it's an identifier and you should stick with text. In practice you will probably be fine but using integer types to hold things that are not really numbers can end up being a problem. A classic example is using an integer to store zip-codes i.e. US postal codes.