HiveBrain v1.2.0
Get Started
← Back to all entries
patternphpMinor

REST API using PHP

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
restapiusingphp

Problem

I am trying to write an REST API using php from scratch which I am gonna use to play with Angular.js. It is working fine now and I need your opinions on the things I have done in this code to improve myself.

NOTE: I am not going to use the below code in production and I am not recommending anyone. I am just trying to learn how to write REST API from scratch and I am an absolute beginner to REST.

index.php

view($parameters, $req_body);
        break;
    }
    case 'POST':
    {
        $controller->save($parameters, $req_body);
        break;
    }
    case 'DELETE':
    {
        $controller->delete($parameters, $req_body);
        break;
    }
    default:
    {
        header($_SERVER['SERVER_PROTOCOL'].' 405 Method Not allowed');
        break;
    }
}
?>


Controller

```
loadmodel('model_products');
}
public function view($id = "",$parameters)
{
$product_id = 0;
if(isset($id[0]))
{
$product_id = $id[0];
}
if(!is_numeric($product_id))
{
header($_SERVER['SERVER_PROTOCOL']." 400 Bad Request");
die();
}
$data = "";
if($product_id == 0)
{
$data = $this->model->getProducts();
}
else
{
$data = $this->model->getSingleProduct($product_id);
}
$this->set_header('json');
echo json_encode($data);
}
public function save($id = 0,$body)
{
$product_id = 0;
if(isset($id[0]))
{
$product_id = $id[0];
}
if(!is_numeric($product_id))
{
header($_SERVER["SERVER_PROTOCOL"]." 400 Bad Request");
die();
}
if(!is_array($body))
{
header($_SERVER["SERVER_PROTOCOL"]." 400 Bad Request");
die();
}
else
{
$response = "";
if($product_id == 0)
{
$response = $this->model->save

Solution

Instead of:

$_SERVER['REQUEST_URI']


REQUEST_URI: index.php/products/xxx/yyy

Use:

$_SERVER['PATH_INFO']


PATH_INFO: /products/xxx/yyy

PATH_INFO is relative to the root URL. REQUEST_URI is not.

This leaves flexibility for the ROOT directory using relative URIs.

You can use various URLs and sub-domains.

URL: http://api.yoursite.com/post/index.php/parameters/

REQUEST_URI: /post/index.php/xxx/yyy


PATH_INFO: /products/xxx/zzz

Would not be a bad idea to include a space in trim().


$called = explode('/', trim($_SERVER['PATH_INFO'],'\x20\x2f'));


preg_match_all() could provide error detection and correction (user typo) as will as doing the explode().

I would make the code function correctly within the definition of the rules. Although I would expand on it. I would NOT use the Request Method to determine any functionality.


The API should be architected where GET and POST Types can be used interchangeably. I would NEVER use them to select view, save, delete.

While on the subject of parameters I would suggest using integer parameters over string whenever possible.
Integer is compact keeping down the Request Header size, is the easiest to validate, and makes comparison functions faster.

Example: On a string true false parameter $param = intval($param);, no need to do if(isset()) it's not there it's zero. And it resolves typing between numeric 1 and string '1' when it is used as an array dimension.

Instead of this:

if(isset($id[0]))
{
    $product_id = $id[0];
}
if(!is_numeric($product_id))
{
    header($_SERVER['SERVER_PROTOCOL']." 400 Bad Request");
    die();
}
$data = "";
if($product_id == 0)
{
    $data = $this->model->getProducts();
}


Create a product with a product id of zero with a description of 'Invalid Product ID" and process it just like a valid product or process the same as an invalid product id number.

$product_id = $data = $this->model->getProducts(intval($id[0]));


If you want a product id to return a list of products use a non-zero id.


For delete, return an error status status on zero the same as if it were an invalid product id.

Consider using an extension other than .php. Even html is easier than php for users to remember.

You could use the extension for the user to select the response type.

For Example: In the Apache config or .htaccess


    SetHandler php5-script


This is easier on the memory allocation, memory bus I/O, and retyping a variable from string to array.

$req_body = json_decode($file_get_contents('php://input'),true);


Than:

$req_body = json_decode($file_get_contents('php://input');
$req_body = json_decode($req_body,true);


This is confusing when the first passed argument is $parameters
$controller->view($parameters, $req_body);

And the accepted 2nd argument is$parameters


function view($id = "",$parameters)


This is poor on more than one level.

if(count($called) <= 3){
    header($_SERVER['SERVER_PROTOCOL']." 400 Bad Request");
    die();
}


Check validity of all passed parameters. Then determine the best, most specific and user friendly response.
The best user response is to fix the error when possible.


If a parameter is missing, add a default.

If you had an HTML error on your web page, do you expect it to render? Would you like it if a single HTML error returned a 404 response? If a Browser was that rigid it may not get much use, same goes for your API.


404 is not an appropriate response when the document URL (index.php) is correct. Additionally this will confuse some users as to why. They may just think your server is down and they go somewhere else.

Consider using an alternate response such as 409. You want to make it as easily as possible for the user to understand what their mistake was. 404 should be reserved for an error in the URL

Also for diagnostic and support issues, use various response codes for different issues and document them for the user.

You could use a text header and a message identifying the specifics problem with an error message.

header('Content-Type: text/plain; charset=utf-8');
echo $error;


Or follow the HTTP 4xx header with an error code in an additional header key value with the error code.

header("x-error: $error");


For Example: The W3C HTML Markup Validator puts the number of HTML errors in the header.

There are much better HTTP Response Codes to use over 404.

Better diagnostics reduces support and increases user satisfaction.

400 Bad Request

The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications.

403 Forbidden

The server understood the request, but is refusing to fulfill it. Authorization will not help and the request SHOULD NOT be repeated. If the request method was not HEAD and the server wishes to make public why the request has not been fulfilled, it SHOULD describe the re

Code Snippets

$_SERVER['REQUEST_URI']
$_SERVER['PATH_INFO']
$called = explode('/', trim($_SERVER['PATH_INFO'],'\x20\x2f'));
if(isset($id[0]))
{
    $product_id = $id[0];
}
if(!is_numeric($product_id))
{
    header($_SERVER['SERVER_PROTOCOL']." 400 Bad Request");
    die();
}
$data = "";
if($product_id == 0)
{
    $data = $this->model->getProducts();
}
$product_id = $data = $this->model->getProducts(intval($id[0]));

Context

StackExchange Code Review Q#85586, answer score: 5

Revisions (0)

No revisions yet.