PHP simple REST API

PHP simple REST API

Last Updated on Feb 15, 2023

Introduction

Last week rapid_api published a very good tutorial thread about creating a REST API with node.js and express. I want to help you develop the same simple REST API with PHP.

First of all if you don’t know about REST API make sure to check out this twitter thread

Goal

Before we start I’d like to mention that when I was writing this thread I wanted to make sure:

  1. I use pure PHP and no frameworks
  2. I use the simplest functions and structures so everyone can understand and follow 
  3. I separate the main parts

Now let’s get started

Preparation

On my local machine I have created a folder called api in xampp > htdocs and inside it there is a file called index.php

If you don’t have xampp or you don’t know how to get started with php make sure to check this post

Now if you go to localhost/api you will get an empty response because the index.php is empty.

Pretty URL

The very first thing that we need to take care of is the urls in our project

One of the key features of REST API is the way each url is responsible for one resource and one action

Problem

At the moment if I create a users.php then I have to go to 

localhost/api/users.php

Then for each id of a user I have to create a new file

localhost/api/users/1.php
localhost/api/users/2.php

And so on.

There are 2 issues with this approach.

  1.  it’s ridiculously boring and time consuming to create a new file for each user
  2. The routes are ugly. All of them have .php at the end

Solution

Let’s solve that.

As I mentioned I don’t want to use any framework and I want to use the simplest and most understandable approach

So let’s see how we can take care of that

In api folder create a file named .htaccess and copy the following text

RewriteEngine On
RewriteBase /api
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+)$ index.php [QSA,L]

We are telling our server to redirect all the request to /api and send them to index.php file

Now all the urls go to index.php for example all the following urls are going to index.php

api/users
api/users/10
api/users/5

Now we have solved both of the problems

  1. all the urls are being handled with one file
  2. urls are pretty and there is no .php at the end

URI

But how to check what uri user has requested?

Easy with $_SERVER superglobal variable Let’s see some examples

// url api/users
echo $_SERVER['REQUEST_URI'];
// /api/users

// url api/users/5
echo $_SERVER['REQUEST_URI'];
// /api/users/5

// url api
echo $_SERVER['REQUEST_URI'];
// /api

See it’s exactly what we need.

Now with a simple if or switch we can handle different paths

If you’ve never worked with conditionals check this post.

METHOD

The next thing we need to get from the request is the method of the request to see if it’s GET,POST,PUT,PATCH or DELETE

And you can get that from the $_SERVER superglobal as well

$_SERVER['REQUEST_METHOD']

Let’s store both of those values in variables

$uri = $_SERVER['REQUEST_URI'];
$method = $_SERVER['REQUEST_METHOD'];

We can use both of those variables in a simple switch statement to handle different requests

We need to check the following requests

  • GET request to api/users
  • GET request to api/users/{id}
  • POST request to api/users
  • PUT request to api/users/{id}
  • DELETE request api/users/{id}

So let’s write the switch statement of all of these requests

switch ($method | $uri) {
   /*
   * Path: GET /api/users
   * Task: show all the users
   */
   case ($method == 'GET' && $uri == '/api/users'):
       break;
   /*
   * Path: GET /api/users/{id}
   * Task: get one user
   */
   case ($method == 'GET' && preg_match('/\/api\/users\/[1-9]/', $uri)):
       break;
   /*
   * Path: POST /api/users
   * Task: store one user
   */
   case ($method == 'POST' && $uri == '/api/users'):
       break;
   /*
   * Path: PUT /api/users/{id}
   * Task: update one user
   */
   case ($method == 'PUT' && preg_match('/\/api\/users\/[1-9]/', $uri)):
       break;
   /*
   * Path: DELETE /api/users/{id}
   * Task: delete one user
   */
   case ($method == 'DELETE' && preg_match('/\/api\/users\/[1-9]/', $uri)):
       break;
   /*
   * Path: ?
   * Task: this path doesn't match any of the defined paths
   *      throw an error
   */
   default:
       break;
}

When we want to use 2 variables in switch we use them with | between them

To know how preg_match works check out this post

Database

Now about the data. The best way is to store the data in a database but for this tutorial I didn’t want to use a database. So instead I use a json file as my “database” to have consistency for our data

My json file looks like this:

{
   "1": "Pratham",
   "2": "Amir"
}

To learn how to work with json checkout this post

I load the json data and convert them to array and work with them in my file and if I wanted to change the data I will change the array to json and write the json to the file

To read the whole file as one and store it in a variable I use

file_get_contents($jsonFile);

And to write the json to file I use 

file_put_contents($jsonFile, $data);

Ok now that our database is taken care of let’s start with all the paths

I use postman to send the request and see the responses

GET ALL

Let's get all the users

case ($method == 'GET' && $uri == '/api/users'):
   header('Content-Type: application/json');
   echo json_encode($users, JSON_PRETTY_PRINT);
   break;

GET ONE

Let's get one of the users

case ($method == 'GET' && preg_match('/\/api\/users\/[1-9]/', $uri)):
   header('Content-Type: application/json');
   // get the id
   $id = basename($uri);
   if (!array_key_exists($id, $users)) {
       http_response_code(404);
       echo json_encode(['error' => 'user does not exist']);
       break;
   }
   $responseData = [$id => $users[$id]];
   echo json_encode($responseData, JSON_PRETTY_PRINT);
   break;

Basename($uri) gives me the last part of the uri. For example if uri is api/users/10 it returns 10.

Then with array_key_exists I check if there is a user with id 10

STORE

Let's add a new user

case ($method == 'POST' && $uri == '/api/users'):
   header('Content-Type: application/json');
   $requestBody = json_decode(file_get_contents('php://input'), true);
   $name = $requestBody['name'];
   if (empty($name)) {
       http_response_code(404);
       echo json_encode(['error' => 'Please add name of the user']);
   }
   $users[] = $name;
   $data = json_encode($users, JSON_PRETTY_PRINT);
   file_put_contents($jsonFile, $data);
   echo json_encode(['message' => 'user added successfully']);
   break;

With file_get_contents('php://input') I can get the body of the request and since in this case it’s json I will decode the json so I can get the name.

UPDATE

Let's update one of the users

case ($method == 'PUT' && preg_match('/\/api\/users\/[1-9]/', $uri)):
   header('Content-Type: application/json');
   // get the id
   $id = basename($uri);
   if (!array_key_exists($id, $users)) {
       http_response_code(404);
       echo json_encode(['error' => 'user does not exist']);
       break;
   }
   $requestBody = json_decode(file_get_contents('php://input'), true);
   $name = $requestBody['name'];
   if (empty($name)) {
       http_response_code(404);
       echo json_encode(['error' => 'Please add name of the user']);
   }
   $users[$id] = $name;
   $data = json_encode($users, JSON_PRETTY_PRINT);
   file_put_contents($jsonFile, $data);
   echo json_encode(['message' => 'user updated successfully']);
   break;

DELETE

Let's delete one of the users

case ($method == 'DELETE' && preg_match('/\/api\/users\/[1-9]/', $uri)):
   header('Content-Type: application/json');
   // get the id
   $id = basename($uri);
   if (empty($users[$id])) {
       http_response_code(404);
       echo json_encode(['error' => 'user does not exist']);
       break;
   }
   unset($users[$id]);
   $data = json_encode($users, JSON_PRETTY_PRINT);
   file_put_contents($jsonFile, $data);
   echo json_encode(['message' => 'user deleted successfully']);
   break;

Final File

Now our index.php file looks like this

In 70 lines of codes we could create a RESTful API in PHP. isn’t it amazing?!

<?php
$jsonFile = 'users.json';
$data = file_get_contents($jsonFile);
$users = json_decode($data, true);
$uri = $_SERVER['REQUEST_URI'];
$method = $_SERVER['REQUEST_METHOD'];
switch ($method | $uri) {
   case ($method == 'GET' && $uri == '/api/users'):
       header('Content-Type: application/json');
       echo json_encode($users, JSON_PRETTY_PRINT);
       break;
   case ($method == 'GET' && preg_match('/\/api\/users\/[1-9]/', $uri)):
       header('Content-Type: application/json');
       $id = basename($uri);
       if (!array_key_exists($id, $users)) {
           http_response_code(404);
           echo json_encode(['error' => 'user does not exist']);
           break;
       }
       $responseData = [$id => $users[$id]];
       echo json_encode($responseData, JSON_PRETTY_PRINT);
       break;
   case ($method == 'POST' && $uri == '/api/users'):
       header('Content-Type: application/json');
       $requestBody = json_decode(file_get_contents('php://input'), true);
       $name = $requestBody['name'];
       if (empty($name)) {
           http_response_code(404);
           echo json_encode(['error' => 'Please add name of the user']);
       }
       $users[] = $name;
       $data = json_encode($users, JSON_PRETTY_PRINT);
       file_put_contents($jsonFile, $data);
       echo json_encode(['message' => 'user added successfully']);
       break;
   case ($method == 'PUT' && preg_match('/\/api\/users\/[1-9]/', $uri)):
       header('Content-Type: application/json');
       $id = basename($uri);
       if (!array_key_exists($id, $users)) {
           http_response_code(404);
           echo json_encode(['error' => 'user does not exist']);
           break;
       }
       $requestBody = json_decode(file_get_contents('php://input'), true);
       $name = $requestBody['name'];
       if (empty($name)) {
           http_response_code(404);
           echo json_encode(['error' => 'Please add name of the user']);
       }
       $users[$id] = $name;
       $data = json_encode($users, JSON_PRETTY_PRINT);
       file_put_contents($jsonFile, $data);
       echo json_encode(['message' => 'user updated successfully']);
       break;
   case ($method == 'DELETE' && preg_match('/\/api\/users\/[1-9]/', $uri)):
       header('Content-Type: application/json');
       $id = basename($uri);
       if (empty($users[$id])) {
           http_response_code(404);
           echo json_encode(['error' => 'user does not exist']);
           break;
       }
       unset($users[$id]);
       $data = json_encode($users, JSON_PRETTY_PRINT);
       file_put_contents($jsonFile, $data);
       echo json_encode(['message' => 'user deleted successfully']);
       break;
   default:
       http_response_code(404);
       echo json_encode(['error' => "We cannot find what you're looking for."]);
       break;
}

Bonus

In this case I didn’t want all my users to be deleted so I added a new condition that if only one user is left don’t let it be deleted. Like this

if (sizeof($users) == 1){
   http_response_code(404);
   echo json_encode(['error' => 'there is only one user left. you cannot delete it!']);
   break;
}

Source Code

You can see the fully commented source code plus the post man collection on my github

Conclusion

Now you know how to create a simple RESTful API in PHP.

I recommend you to open a PHP files and go through all the steps we went and add another resource like posts.

If you have any suggestions, questions, or opinions, please contact me. I’m looking forward to hearing from you!

Key takeaways

  • Create a RESTful API in PHP with no framework
  • Pretty URL in PHP
  • Handle request's body
  • use Json file as your database
  • Switch with multiple variables

Category: programming

Tags: #php #RESTFul

Join the Newsletter

Subscribe to get my latest content by email.

I won't send you spam. Unsubscribe at any time.

Related Posts

Courses