Thursday, February 7, 2019

Create a REST API in PHP

REST stands for REpresentational State Transfer. Which is only a stateless representation of information  among different resources. Where as API stands for Application Programming Interface which is an interface that allows different platforms to communicate.

Create a REST API in PHP
REST API in PHP is used to share information in a certain representation format using proper methods. Normally these interfaces use JSON format to exchange information. In this post we will be using our employees data which we have been using in previous posts. I will be using four basic and mostly used methods over http requests.
  • GET - To get a single or multiple records
  • POST - To add a new record
  • PUT - To update a record
  • DELELTE - To delete a single record
We will also implement a secure requests using an authorization token to authorize all requests. So lets start with our files structure we are going to need, define.php which will be containing all the constant values for database host, username, password and token etc. abstraction.php will be playing the role of an abstraction layer for performing action as get, add, update, delete employees.

define.php

<?php
define("DB_HOST", "DATABASE_HOST");  // Your database host
define("DB_NAME", "DATABASE_NAME");  // Your database name
define("DB_USER", "DATABASE_USERNAME");  // Username for database
define("DB_PASSWORD", "DATABASE_PASSWORD"); // Password for database
define("REST_TOKEN", "API_AUTHORIZATION_TOKEN"); // Authorization token
?>

abstraction.php

<?php
// Disable error reporting
error_reporting(0);
include("define.php");
global $dbcon;

// Open database connection
$dbcon = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME) OR die(mysqli_connect_error());

/**************************************
* Function to fetch records from table,
* $param = "SELECT * FROM TABLE_NAME"
**************************************/
function db_get($query){
    global $dbcon;

    $rs = mysqli_query($dbcon,$query);

    if($rs){
        $result = mysqli_fetch_all($rs,MYSQLI_ASSOC);
    }else{
        $result =  mysqli_error($dbcon);
    }
    return $result;
}

/**************************************
* Function to add record to database table,
* $table = table name
* $data = Associative array of key/value (record name / record value)
**************************************/
function db_insert($table,$data){
    global $dbcon;

    $keys = array_keys($data);

    // Escaping values before insert
    $values = array_map(function($value){
        global $dbcon;
        return mysqli_real_escape_string($dbcon,$value);
    },array_values($data));

    $query = "INSERT INTO $table (".implode(",",$keys).") VALUES ('".implode("','",$values)."')";
    $rs = mysqli_query($dbcon,$query);

    if($rs){
        $result = mysqli_insert_id($dbcon);
    }else{
        $result = mysqli_error($dbcon);
    }
    return $result;
}

/**************************************
* Function to update record in database table,
* $table = table name
* $data = Associative array of key/value (record name / record value)
* $where = String i.e employee_id = '1'
**************************************/
function db_update($table,$data,$where = ''){
    global $dbcon;

    $bind = [];
    foreach($data as $key => $value){
        $value = mysqli_real_escape_string($dbcon,$value);
        $bind[] = "$key = '$value'";
    }

    $query = "UPDATE $table SET ".implode(', ',$bind)." WHERE $where";
    $rs = mysqli_query($dbcon,$query);
    if($rs){
        $result = true;
    }else{
         $result = mysqli_error($dbcon);
    }
    return $result;
}

/**************************************
* Function to delete record from database table,
* $table = table name
* $where = String i.e employee_id = '1'
**************************************/
function db_delete($table,$where){
    global $dbcon;
    $query = "DELETE FROM $table WHERE $where";
    $rs = mysqli_query($dbcon,$query);
    if($rs){
        $result = true;
    }else{
        $result = mysqli_error($dbcon);
    }
    return $result;
}
?>

api.php

<?php
include("abstraction.php");

// SET application hearder, allowed methods & headers
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: GET,POST,PUT,DELETE");
header("Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With");
header("Cache-Control: no-cache,no-store,must-revalidate");
header("Pragma: no-cache");

// Get all request headers & and lower case header keys to handle case sensitivity
$request_headers = array_change_key_case(getallheaders(), CASE_LOWER);

// Set initial response code to current code
$reponse_code = http_response_code();

// Check if request contains authorization token
if(!isset($request_headers["authorization"]) || ($request_headers["authorization"] !== REST_TOKEN)){
    $res = ["success" => 0, "message" => "Unauthorized request"];
    http_response_code(401);
    die(json_encode($res));
}

// Check if request contains proper content type
if(!isset($request_headers["content-type"])
        || (($request_headers["content-type"] !== "application/x-www-form-urlencoded")
            && ($request_headers["content-type"] !== "application/json")))
{
    $res = ["success" => 0, "message" => "Set content-type to applicaton/x-www-form-urlencoded or applicaton/json"];
    http_response_code(400);
    die(json_encode($res));
}

// Get the method used to call API
$request_method = $_SERVER['REQUEST_METHOD'];

// Raw input received in request
$input_raw = file_get_contents("php://input");

// Prepare input for both json/x-www-form-urlencoded
$input_json = json_decode($input_raw,true); // Decode input to json
parse_str($input_raw,$input_arr); // Parse x-www-form-urlencoded to arrays

if(is_array($input_json)){
    // If input was json set this input to be used in script
    $input = $input_json;
}elseif(is_array($input_arr)){
    // If input was x-www-form-urlencoded set this input to be used in script
    $input = $input_arr;
}

switch($request_method){
    case "GET":
        $where = "WHERE 1=1";
        // If employee id was passed , Fetch record against that id only
        if(intval($_GET['employee_id'])){
            $where .= " AND employee_id = '".intval($_GET['employee_id'])."'";
        }
        $employees = db_get("SELECT employee_id,fullname,gender,email,designation FROM employees $where");
        $total = count($employees);

        // Set json response after getting employee record(s)
        $res = ["suceess" => 1, "data" => $employees, "total" => $total];
        $reponse_code = 400;
        break;
    case "POST":
        // Set json response if fullname & email was empty
        if(empty($input["fullname"]) && empty($input["email"])){
            $res = ["success" => 0, "message" => "Employee name and email can not be empty"];
            $reponse_code = 400;
            break;
        }
        $data = [];
        $data["fullname"]   = $input["fullname"];
        $data["email"]      = $input["email"];
        $data["gender"]     = $input["gender"];
        $data["designation"]= $input["designation"];

        db_insert("employees", $data);

        // Return json response after adding employee
        $res = ["success" => 1, "message" => "Employee has been added"];
        $reponse_code = 200;
        break;
    case "PUT":
        // Set json response if no employee id was provided to update against
        if(!isset($_GET["employee_id"])){
            $res = ["success" => 0, "message" => "Missing parameter employee_id"];
            $reponse_code = 400;
            break;
        }
        // Set json response if fullname or email was provided empty
        if(empty($input["fullname"]) || empty($input["email"]))
        {
            $res = ["success" => 0, "message" => "Employee name and email can not be empty"];
            $reponse_code = 400;
            break;
        }
        $where = "employee_id = '".intval($_GET["employee_id"])."'";
        $data["fullname"]   = $input["fullname"];
        $data["email"]      = $input["email"];
        $data["gender"]     = $input["gender"];
        $data["designation"]= $input["designation"];

        db_update("employees",$data,$where);

        // Set json response after updating employee
        $res = ["success" => 1, "message" => "Employee has been updated"];
        $reponse_code = 200;
        break;
    case "DELETE":
        // Set json response if no employee id was provided to delete record against
        if(!isset($input["employee_id"])){
            $res = ["success" => 0, "message" => "Missing parameter employee_id"];
            $reponse_code = 400;
            break;
        }
        $where = "employee_id = '".intval($input["employee_id"])."'";
        db_delete("employees",$where);

        // Set json response after deleting employee
        $res = ["success" => 1, "message" => "Employee has been deleted"];
        $reponse_code = 200;
        break;
    default:
        // If requested method was other than GET,POST,PUT,DELETE, return an error message
        $res = ["success" => 0, "message" => "Invalid request"];
        $reponse_code = 400;
        break;
}
// Set response status code
http_response_code($reponse_code);

// Return final json response
echo json_encode($res);
?>
Now that everything is ready to use our API lets play with .htaccess to write pretty URLs.
DirectoryIndex api.php
RewriteEngine On
## Replace Rewritebase to directory where you place downloaded files
RewriteBase /create-rest-api-in-php/
RewriteRule ^api/([\d]+) api.php?employee_id=$1 [L]
RewriteRule ^api/? api.php [L]
This will change our url as http://yourdomain.com/apifolder/api. Now open up postman and try all methods one by one. Responses should match as in screenshots below:
  • GET / All Records

    Create a REST API in PHP
  • GET / Single Record

    Create a REST API in PHP
  • POST / Add New Record

    Create a REST API in PHP