If you need to build a very simple REST API with PHP you are in right place! I needed some time ago to build a very simple REST API in PHP. I had to use PHP – this was the only back-end technology in my project and at that moment, my skills were poor in PHP.
I was looking for a very simple tutorial to have endpoint in normal form like http://my-domain.com/posts/12
just for a very basic functionality. Then I found on the internet big tutorials using some PHP frameworks to build great and fully proper REST APIs in PHP. But it wasn’t something what I was looking for.
But I didn’t want to spend a few days on learning object programming in PHP, learning frameworks etc. I needed to do a quick solution in good enough (not perfect) shape. So I started plying with PHP and in result, I created totally simple PHP REST API.
Read further to know how to do it.
First we will build the simplest possible example of php rest api with mapping URL path and invoking methods.
Second example will be still easy, but a little more sophisticated, and it will show how to build easy but fully working REST API with PHP.
Table of Contents
Example 1 – the simplest REST API in PHP
- Open code of this example on our Gitlab account HERE.
- The first, probably the most important thing, is that we want to use nice-looking path to call our API, like:
http://my-domain.com/customers/11
, not the url GET parameters instead of nice path.We need to create URL address mapping in.htaccess
file, its content is very short but works perfectly – it turns nice-looking URL path into GET parameters to make it easy to read by PHP (further below, in index.php file).
.htaccessRewriteEngine On RewriteRule ^(.+)(\?.+)?$ index.php?path=$1 [L,QSA]
- Next, we need to have some dummy data just to show it in browser for testing. In reality, in this place you will connect to data base to get some data or you will do some operations based on user input.
storage.php<?php function set_faked_data () { $orders_data_json = '[{"index":0,"price":135,"product":"motherboard","owner":"LOCAZONE"},{"index":1,"price":138,"product":"cpu","owner":"MULTRON"},{"index":2,"price":278,"product":"cpu","owner":"TRIBALOG"},{"index":3,"price":202,"product":"motherboard","owner":"ANDRYX"},{"index":4,"price":87,"product":"hid","owner":"CONFRENZY"},{"index":5,"price":355,"product":"gpu","owner":"COMVEX"},{"index":6,"price":348,"product":"gpu","owner":"GLEAMINK"},{"index":7,"price":360,"product":"hid","owner":"OHMNET"},{"index":8,"price":300,"product":"hid","owner":"CENTURIA"},{"index":9,"price":351,"product":"hid","owner":"JOVIOLD"},{"index":10,"price":40,"product":"cpu","owner":"ELECTONIC"},{"index":11,"price":251,"product":"cpu","owner":"SIGNIDYNE"},{"index":12,"price":272,"product":"audio","owner":"MANGLO"}]'; $orders_data = json_decode($orders_data_json, true); $_SESSION["orders_data"] = $orders_data; } ?>
- Next, we must have some function which will take all ORDERS data or single item from array and return it to client (browser) on request to our php REST API:
orders.php<?php /** * ORDERS */ function get_orders ($order_index = "") { if($order_index) { foreach($_SESSION["orders_data"] as $order) { if($order["index"] == $order_index) { return $order; } } return null; } else { return $_SESSION["orders_data"]; } } function create_new_order ($data) { array_push($_SESSION["orders_data"], json_decode($data, true)); return $_SESSION["orders_data"]; } ?>
First method
get_orders ($order_index = "")
is returning all orders – when argument$order_index
is empty, if order_index has some value, then it returns one single order element.Second method
create_new_order ($data)
adds new order to session variable “orders_data” based on JSON from user input. - Next, we need additional file with helpers functions. There will be only one function now:
_shared.php<?php function header_status($statusCode) { static $status_codes = null; if ($status_codes === null) { $status_codes = array ( 100 => 'Continue', 101 => 'Switching Protocols', 102 => 'Processing', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 207 => 'Multi-Status', 299 => 'Fake 4xx error from middleware API', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Timeout', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Long', 415 => 'Unsupported Media Type', 416 => 'Requested Range Not Satisfiable', 417 => 'Expectation Failed', 422 => 'Unprocessable Entity', 423 => 'Locked', 424 => 'Failed Dependency', 426 => 'Upgrade Required', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported', 506 => 'Variant Also Negotiates', 507 => 'Insufficient Storage', 509 => 'Bandwidth Limit Exceeded', 510 => 'Not Extended' ); } if ($status_codes[$statusCode] !== null) { $status_string = $statusCode . ' ' . $status_codes[$statusCode]; header($_SERVER['SERVER_PROTOCOL'] . ' ' . $status_string, true, $statusCode); } } ?>
So method
header_status($statusCode)
returns appropriate HTTP response header like “404 Not found”. - OK, so now let’s move to the most important file in our API, and this will be, of course, index.php file:
index.php<?php require_once '_shared.php'; require_once 'storage.php'; require_once 'orders.php'; /** * RUN API HERE */ set_faked_data(); runApi(); function decodePath() { if(!empty($_GET['path'])) { $path_parts = $_GET['path']; } else { $path_parts = false; } if(empty($path_parts)) { return false; } if(strrpos($path_parts, '/') === false) { $path_parts .= '/'; } $path_parts = explode('/', $path_parts); if(empty($path_parts[count($path_parts) - 1])) { array_pop($path_parts); } return $path_parts; } function runApi () { /* * 1st param: method * 2nd param: detailed action * 3rd param: props */ $data = file_get_contents("php://input"); $path = decodePath(); $http_method = $_SERVER["REQUEST_METHOD"]; $method = isset($path[0]) && !empty($path[0]) ? $path[0] : null; $prop = isset($path[1]) && !empty($path[1]) ? $path[1] : null; $data = sizeof($data) ? $data : null; if($method === "orders") { switch($http_method) { case "GET": send_results(get_orders($prop)); break; case "PUT": send_results(create_new_order($data)); break; } } else { send_results("", 404); } } ?>
Start reading from the top to the bottom. On the very beginning of the file we include 3 files created in steps above.
We set dummy/fake data from
storage.php
file and this function will place ORDERS data in session variable.runApi()
function triggers a function which makes us our REST API in PHP working.Below, we can find
decodePath()
function which is very important – it is transforming GET parameters (taken from nice-looking path likehttp://my-domain.com/customers/11
by .htaccess file – look at step 1 at very top of the page).Below, there is a declaration of
runApi()
function. Inside, there is decoded URL path, and all GET parameters are transformed here into variables:$method, $prop
and , we get the HTTP method used by client (browser) and sent data:$http_method, $data
.So now we have everything to trigger a function from ORDERS.PHP file. We know what end point user called (/orders) and if there are additional parameters, like order index (for getting single order only).
So we have if statement which checks the method name (/orders) and what was the HTTP method and sends parameters to right function fromorders.php
file. - When you type URL to your simple REST API in PHP in your client (browser) like:
http://localhost:80/php-api/orders
you will see JSON with all ORDERS from storage.php file:
- We can also use PUT method to insert data into storage ORDERS session variable (array). For this case we must use app like POSTMAN to make a PUT request to our simple php REST API. This method will return all ORDERS (with the new one). We can send JSON like that:
{"index":13,"price":111111,"product":"CAR","owner":"TEEEEEEST"}
with headerContent-Type: application/json
.
Example 2 – full simple REST API in PHP
In this example we will create almost normal REST API in PHP which allows to build bigger back-end API for your solution. We will still reuse files from example 1. In this example we extend our API to work with longer URL path: /method/end-point/parameter
.
Check code of this example on our Gitlab account HERE.
- In storage.php file we add new data set for CUSTOMERS:
storage.php
<?php function set_faked_data () { $customers_data_json = '[{"index":0,"age":20,"eyeColor":"blue","name":"Puckett Branch","gender":"male","company":"FIREWAX","email":"puckettbranch@firewax.com","phone":"+1 (968) 479-2314"},{"index":1,"age":31,"eyeColor":"green","name":"Shelia Boyd","gender":"female","company":"LOCAZONE","email":"sheliaboyd@locazone.com","phone":"+1 (995) 468-3653"},{"index":2,"age":37,"eyeColor":"green","name":"Tameka Pacheco","gender":"female","company":"JUNIPOOR","email":"tamekapacheco@junipoor.com","phone":"+1 (840) 412-2533"},{"index":3,"age":37,"eyeColor":"green","name":"Franks Holmes","gender":"male","company":"ZILLACTIC","email":"franksholmes@zillactic.com","phone":"+1 (968) 563-3318"},{"index":4,"age":23,"eyeColor":"blue","name":"Oneil Clayton","gender":"male","company":"AMTAS","email":"oneilclayton@amtas.com","phone":"+1 (998) 556-2196"},{"index":5,"age":28,"eyeColor":"blue","name":"Jenna Edwards","gender":"female","company":"CUIZINE","email":"jennaedwards@cuizine.com","phone":"+1 (805) 406-2471"},{"index":6,"age":24,"eyeColor":"blue","name":"Claire Strong","gender":"female","company":"TERASCAPE","email":"clairestrong@terascape.com","phone":"+1 (851) 561-3299"},{"index":7,"age":33,"eyeColor":"brown","name":"Spencer Reyes","gender":"male","company":"EXOTERIC","email":"spencerreyes@exoteric.com","phone":"+1 (897) 509-2920"},{"index":8,"age":26,"eyeColor":"blue","name":"Gretchen Farrell","gender":"female","company":"BLUPLANET","email":"gretchenfarrell@bluplanet.com","phone":"+1 (892) 579-3020"},{"index":9,"age":38,"eyeColor":"blue","name":"Lupe Campbell","gender":"female","company":"SNORUS","email":"lupecampbell@snorus.com","phone":"+1 (864) 521-2530"},{"index":10,"age":26,"eyeColor":"brown","name":"Boone Cortez","gender":"male","company":"BARKARAMA","email":"boonecortez@barkarama.com","phone":"+1 (978) 404-2534"},{"index":11,"age":31,"eyeColor":"green","name":"Leta Riley","gender":"female","company":"ATOMICA","email":"letariley@atomica.com","phone":"+1 (992) 434-2713"}]'; $customers_data = json_decode($customers_data_json, true); $orders_data_json = '[{"index":0,"price":135,"product":"motherboard","owner":"LOCAZONE"},{"index":1,"price":138,"product":"cpu","owner":"MULTRON"},{"index":2,"price":278,"product":"cpu","owner":"TRIBALOG"},{"index":3,"price":202,"product":"motherboard","owner":"ANDRYX"},{"index":4,"price":87,"product":"hid","owner":"CONFRENZY"},{"index":5,"price":355,"product":"gpu","owner":"COMVEX"},{"index":6,"price":348,"product":"gpu","owner":"GLEAMINK"},{"index":7,"price":360,"product":"hid","owner":"OHMNET"},{"index":8,"price":300,"product":"hid","owner":"CENTURIA"},{"index":9,"price":351,"product":"hid","owner":"JOVIOLD"},{"index":10,"price":40,"product":"cpu","owner":"ELECTONIC"},{"index":11,"price":251,"product":"cpu","owner":"SIGNIDYNE"},{"index":12,"price":272,"product":"audio","owner":"MANGLO"}]'; $orders_data = json_decode($orders_data_json, true); $_SESSION["customers_data"] = $customers_data; $_SESSION["orders_data"] = $orders_data; } ?>
- In
_shared.php
file with helpers we will add two methods responsible for returning the end data to client (browser) + we will create there a PHP Class (this is Object Oriented PHP) for handling end point’s methods (like fromorders.php
file from example 1). - File containing ORDERS and CUSTOMERS function will also change into Object Oriented, and will look like:
order.php<?php /** * ORDERS */ require_once '_shared.php'; class Orders extends Methods { public function get_orders () { return $_SESSION["orders_data"]; } public function create_new_order () { array_push($_SESSION["orders_data"], json_decode($this->data, true)); return $_SESSION["orders_data"]; } } ?>
customers.php
<?php /** * CUSTOMER ORDERS */ require_once '_shared.php'; class Customer extends Methods { public function get_all_names () { return array_map(function ($customer) { return $customer["name"]; }, $_SESSION["customers_data"]); } public function get_customers () { return $_SESSION["customers_data"]; } public function get_single_customer () { foreach($_SESSION["customers_data"] as $customer) { if($customer["index"] == $this->prop) { return $customer; } } return null; } public function get_all_emails () { return array_map(function ($customer) { return $customer["email"]; }, $_SESSION["customers_data"]); } public function create_new_customer () { array_push($_SESSION["customers_data"], json_decode($this->data, true)); return $_SESSION["customers_data"]; } } ?>
Customers.php file is a little more extended – it has more methods just to show you how easy you can now extend your simple PHP REST API.
- Now you must realize – if our API would grow and grow, it would be hard to maintain all cases inside index.php file just by if/switch statements. It would look very messy.
So we will build now important functionality – the JSON definition of all used methods (both GET/PUT/POST and internal names of methods and end points). This definition is a matrix where values are name of methods triggered for orders and customers. This functionality will be used inindex.php
file.For that purpose we createswitcher.php
file with following code:
switcher.php<?php class Switcher { public $methods_JSON = '{ "customers": { "GET": { "default": "get_customers", "customer": "get_single_customer", "emails": "get_all_emails", "names": "get_all_names" }, "PUT": { "default": "create_new_customer" } }, "orders": { "GET": { "default": "get_orders" }, "PUT": { "default": "create_new_order" } } }'; public $methods = []; public function __construct () { $this->methods = json_decode($this->methods_JSON, true); } public function get_function ($method, $http_method, $end_point) { if(!$end_point) { $end_point = "default"; } $method_item = $this->find_in_array_by_key($this->methods, $method); $http_method_item = $this->find_in_array_by_key($method_item, $http_method); $function = $this->find_in_array_by_key($http_method_item, $end_point); return $function; } private function find_in_array_by_key ($arr, $key) { $results = array_filter($arr, function ($item) use ($key) { return $item === $key; }, ARRAY_FILTER_USE_KEY); if(isset($results) && !empty($results) && count($results) === 1) { return $results[$key]; } else { return []; } } } ?>
- Let’s move to index.php of our simple REST API in PHP. It has changed a little, but I will put here whole code of that file:
index.php<?php require_once '_shared.php'; require_once 'storage.php'; require_once 'switcher.php'; require_once 'customers.php'; require_once 'orders.php'; /** * RUN API HERE */ set_faked_data(); runApi(); function decodePath() { if(!empty($_GET['path'])) { $path_parts = $_GET['path']; } else { $path_parts = false; } if(empty($path_parts)) { return false; } if(strrpos($path_parts, '/') === false) { $path_parts .= '/'; } $path_parts = explode('/', $path_parts); if(empty($path_parts[count($path_parts) - 1])) { array_pop($path_parts); } return $path_parts; } function runApi () { /* * 1st param: method * 2nd param: detailed action * 3rd param: props */ $data = file_get_contents("php://input"); $path = decodePath(); $http_method = $_SERVER["REQUEST_METHOD"]; $method = isset($path[0]) && !empty($path[0]) ? $path[0] : null; $end_point = isset($path[1]) && !empty($path[1]) ? $path[1] : null; $prop = isset($path[2]) && !empty($path[2]) ? $path[2] : null; $data = sizeof($data) ? $data : null; $sheduler = new Switcher(); $func_name = $sheduler->get_function($method, $http_method, $end_point); if(!$func_name) { send_results("Method not found", 404); return; } switch ($method) { case "customers": $customer_functions = new Customer($prop, $data); $func_res = call_user_func( array($customer_functions, $func_name) ); send_results($func_res); break; case "orders": $orders_functions = new Orders($prop, $data); $func_res = call_user_func( array($orders_functions, $func_name) ); send_results($func_res); break; default: send_results("", 404); } } ?>
The difference is on the very top of the file – there are included new files of course.
Next, in
runApi()
function there is created a new instance of a Switcher class – which will give us instant mapping of the end point’s function by used HTTP method, URL path method and end point.
It will map it and save in$func_name
variable.Next, we have switch method which is used here to identify the main URL path method and create instance of right ORDERS or CUSTOMERS class.
You can now extend your simple php REST API with a new methods very easy – just extend JSON API definition in
switcher.php
file and in index.php file in switch method – if you introduce a new main method.
- You can type now in your browser URLs like:
http://localhost:80/php-api-2/customers http://localhost:80/php-api-2/customers/emails http://localhost:80/php-api-2/customers/names http://localhost:80/php-api-2/customers/customer/1 http://localhost:80/php-api-2/orders
And you will get results like on screen shots below:
- If you want to test PUT request, do it here for customers via URL:
http://localhost:80/php-api-2/customers
:
PHP simple REST API – summary
I hope that by this examples I showed to you in a simple way how to build own REST API in a very basic form in PHP. I’m a JavaScript developer, but I like to work with PHP from time to time because this is quite simple language and in some parts, similar to JavaScript. And about 70% of the internet is running on apache servers with PHP, so there is always a high probability that even if you don’t code in PHP, that in the future you will need to do something in this. And by this simple REST API example I wanted to make things easier for you 🙂