logo


Create your first API set in PHP

Downloads

Tutorial Details

  • Difficulty: Intermediate
  • Estimated Completion Time: ~25 Minutes

System Requirements

  • Apache with mod_rewrite
  • Mysql database access
  • PHP >= 5.2.0 suggested

Introduction to APIs

An API (Application Programming Interface) is a set of conventions and rules decided by a programmer to make his own application capable to communicate with external users.
APIs are particularly useful when the target of the programmer is to offer tools or information that can be reused by other developers to create 3rd party applications.

Our Example

To better understand the usefullness of an API system we’ll simulate a project, dealing with common
necessities, problems and casistics.

Our example refers to a movie database website, which commonly offers movie statistics.
For each movie our current website offers to its users the possibility to add themselves to the
number of people who actually saw that movie and obviously allows visitors to see each movie stats.

Now let’s imagine that we want our website to be easily usable from third party developer to create apps
based on our database
. For example they might like to create a widget for wordpress blogs with the
top seen movies, or maybe a desktop application wich automatically asks the user to submit to our directory
a movie they are watching.

Well for all these kinds of actions we need those developers to be able to directly interact with our databse,
making it possible for them to browse our lists or submit something new. The API system we’re going to build
will respond to this necessity, in other words, being a smart filter beetween their requests and our database.

Tutorial Introduction

As we previously declared, in this tutorial you will be guided to realize a simple set of API,
able to interact with an example database of movies.
The final target is to build a single PHP file that should be able to manage different types of requests
and perform simple operations
like inserting new movies or getting information about them.

First of all we’re going to brainstorm our set of API, learning how to organize them, which means to decide
what kind of output is shown to the user, which type of format this output needs to have and finally which
actions will our users be able to perform.

Then we’ll see how to connect those third party requests to our script and interrogate or update our database
basing our operations on GET parameters
, retrieved from the calls.

The latest paragraph will guide you through the process of beautify API calls urls, by using rewriting rules.
This obviously improve user experience and might be a plus point for your project to succeed.

The example database

In the Sources archive you will find a complete database dump which you can import via PhpMyAdmin or MySQL
command line tools.
Below there’s a quick review of the only table we’re going to use so that you can better understand
the next steps.

Table “Movies”

Field Name Field Description
id Primary key, incremental. Identifies the row.
movie_name Title of the movie. Can not be a duplicate.
movie_views Number of people who have seen this movie

Methods, params, outputs and formats

methods Create your first API set in PHP

Before starting developing we need to make a quick brainstorming to clarify
ourselves how to set up our API set.

Methods

First of all we have to think about how many and which kind of actions we want our users to be able to perform.
We’ll refer to these ‘actions’ as methods.

In this tutorial we’ll see how to insert a new row in our database, search through our records and update any of those.
Thus our API set will be configured to understand three methods: Create, Get and Update.

The Create method will allow external users to insert a new movie in the database.

By Updating a record the user will be able to add himself to the incremental number of viewers
of a certain movie.

Using the Get method it will be possible to search a movie by title and getting thus its own id and
the current number of viewers.

Parameters

Each of the above methods needs some parameters to work properly. For example when inserting a new movie, we need to specify the name.
Below is reported a list of required parameters for each method.

Method Name Required Parameters
Create movie name
Update movie id
Get movie name

Returned values

Now it’s time to define what the user will get back when using each method. For example when inserting a new movie, it’s better to return the inserted id for further usages.
Below is reported a list of returned values for each method.

Method Name Returned Values
Create movie id
Update number of views
Get movie id and number of views

Formats

As every developer has his own necessities and will use different methods and programming languages to interact with our APIs we should offer a range of output formats as wide as possible.
Looking at what the most famous websites are doing, this tutorial will guide you to output your response data using XML, JSON and PHP serialized arrays.

Calls example

Before starting developing we should be sure to have really understood how the user will call our script.
Below are some call examples, based on all the previous considerations.

Call Explanation
http://example.com/api.php?method=get&name=Memento&format=xml Asks for the id and views of the film titled ‘Memento’.
Output is asked to be in XML.
http://example.com/api.php?method=update&id=3&format=json Adds a ‘viewer’ to the film with id=3.
Output is asked to be in JSON.
http://example.com/api.php?method=create&name=Fight%20Club&format=php Creates a new entry in our database for the film ‘Fight Club’.
Output is asked to be in PHP serialized array.

Dealing with GET params

As we don’t need too many parameters to be passed to the script, we can manage everything (methods, formats and params) via GET requests.
With more complex projects, getting some data in POST and some other in GET might be the best choice.

Here, we are going to develop the first part of our code, which will take care of getting the GET parameters and creating the needed queries.
As you can see, the mysql_real_escape_string PHP native function must be used to avoid SQL Injection.

		<?php

			/* Using the *switch* construct to deal with different methods */
			switch ($_GET['method']) {
				case 'create' :
						$query = '
							INSERT INTO movies (
								`movie_name`,`movie_views`
							) VALUES (
								"'.mysql_real_escape_string($_GET['name']).'",
								1
							)
						';
						/* Will need to get the inserted id back */
					break;
				case 'update' :
						$query = '
							UPDATE `movies`
							SET movie_views=movie_views+1
							WHERE `id` = "'.mysql_real_escape_string($_GET['id']).'"
						';
						/* Will need to get the new movie_views value */
					break;
				case 'get' :
						$query = '
							SELECT `id`,`movie_views`
							FROM `movies`
							WHERE `movie_name`
								LIKE "%'.mysql_real_escape_string($_GET['name']).'%"
						';
						/* Will need to get query results */
					break;
			}

			/* Using the *switch* again, this time to deal with output formats */
			switch ($_GET['format']) {
				case 'xml' :
						// Need a class to transform the output in XML
					break;
				case 'json' :
						// Need a method to transform the output in JSON
					break;
				case 'php' :
						// Will serialize the output and then just echo it
					break;
			}

		?>

Connecting to database, get datas

As the queries are now ready to be executed, we can proceed with the development of our PHP script, putting a real MySQL connection and actually running the queries.
Please be sure to replace our fake datas with your server connection configuration.

		<?php

			/*
			 * You might want to make these connections externals.
			 * I put everything here to improve readability
			 */
			mysql_connect('localhost','your_username','your_password') or
				die ('Error while connection'.mysql_error());
			mysql_select_db('my_db') or
				die ('Error entering db'.mysql_error());

			/* Using the *switch* construct to deal with different methods */
			switch ($_GET['method']) {
				case 'create' :
						$query = '
							INSERT INTO movies (
								`movie_name`,`movie_views`
							) VALUES (
								"'.mysql_real_escape_string($_GET['name']).'",
								1
							)
						';
						mysql_query($query) or
							die ('Error while inserting new row'.mysql_error());
						$return = 'ID : '.mysql_insert_id($query);
					break;
				case 'update' :
						$query = '
							UPDATE `movies`
							SET movie_views=movie_views+1
							WHERE `id` = "'.mysql_real_escape_string($_GET['id']).'"
						';
						mysql_query($query) or
							die ('Error while updating the row'.mysql_error());
						$query = '
							SELECT `movie_views`
							FROM `movies`
							WHERE `id` = "'.mysql_real_escape_string($_GET['id']).'"
							LIMIT 1
						';
						$go = mysql_query($query) or
							die ('Error while getting data'.mysql_error());
						$fetch = mysql_fetch_row($go);
						$return = 'NEW VIEWS : '.$fetch[0];
					break;
				case 'get' :
						$query = '
							SELECT `id`,`movie_views`
							FROM `movies`
							WHERE `movie_name`
								LIKE "%'.mysql_real_escape_string($_GET['name']).'%"
						';
						$go = mysql_query($query) or
							die ('Error while looking for data'.mysql_error());
						$fetch = mysql_fetch_row($go);
						$return = 'ID : '.$fetch[0].' , VIEWS : '.$fetch[1];
					break;
			}

			mysql_close();

			/*
			 *  [ ... ]
			 */
		?>

Dealing with results

As we successfully managed to get our query results, we need to put them in a proper form.
The best practice to output something via JSON or XML from a PHP script is to place everything in a multidimensional associative array.

Our initial output data array will have the following form

		$results = Array(
			'body' => Array (
				'id' => $the_id,
				'views' => $the_views
			)
		);

Obviously not every kind of request will have both of the parameters.
Below are the needed code modifications to put our previous results in the array.

		<?php

			/*
			 * [...]
			 */

			switch ($_GET['method']) {
				case 'create' :
						/*
						 * [...]
						 */
						$return = mysql_insert_id($query);
						$results = Array(
							'body' => Array (
								'id' => $return
							)
						);
					break;
				case 'update' :
						/*
						 * [...]
						 */
						$return = $fetch[0];
						$results = Array(
							'body' => Array (
								'views' => $return
							)
						);
					break;
				case 'get' :
						/*
						 * [...]
						 */
						$return = Array($fetch[0],$fetch[1]);
						$results = Array(
							'body' => Array (
								'id'	=> $return[0],
								'views' => $return[1]
							)
						);
					break;
			}

			/*
			 *  [ ... ]
			 */
		?>

Output responses

With our results array done & ready, we can now proceed to output the responses, obviously in the format requested by the user.
Note that for each format, we are going to use a proper header() function to set the right file headers.
Headers are used to inform the browser, script, parser or any other kind of requester about which kind of file they are going to deal with.

XML

There are several classes that transform an associative array into an xml tree.
I’ve chosen the following method because it uses xmlwriter class, which is by default included in any php installation from version 5.1.2.
If you don’t own this class please refer to php.net manuals and install it.

			<?php

				/*
				*  [ ... ]
				*/

				switch ($_GET['format']) {
					case 'xml' :

							/* Setting XML header */
							@header ("content-type: text/xml charset=utf-8");

							/* Initializing the XML Object */
							$xml = new XmlWriter();
							$xml->openMemory();
							$xml->startDocument('1.0', 'UTF-8');
							$xml->startElement('callback');
							$xml->writeAttribute('xmlns:xsi','http://www.w3.org/2001/XMLSchema-instance');
							$xml->writeAttribute('xsi:noNamespaceSchemaLocation','schema.xsd');

							/* Function that converts each array element to an XML node */
							function write(XMLWriter $xml, $data){
									foreach($data as $key => $value){
											if(is_array($value)){
													$xml->startElement($key);
													write($xml, $value);
													$xml->endElement();
													continue;
											}
											$xml->writeElement($key, $value);
									}
							}

							/* Calls previously declared function, passing our results array as parameter */
							write($xml, $results);

							/* Closing last XML node */
							$xml->endElement();

							/* Printing the XML */
							echo $xml->outputMemory(true);
						break;
					case 'json' :
					/*
					*  [ ... ]
					*/
				}

			>?>

JSON

Luckily starting with PHP version 5.2.0 there is a built in function that translates any array into a JSON object.
If you have an older version of PHP you can always use PECL to install the required function json_encode().

			<?php

				/*
				*  [ ... ]
				*/

				switch ($_GET['format']) {
					case 'xml' :
							/*
							*  [ ... ]
							*/
						break;
					case 'json' :

							/* Setting up JSON headers */
							@header ("content-type: text/json charset=utf-8");

							/* Printing the JSON Object */
							echo json_encode($response);
						break;
					case 'php' :
					/*
					*  [ ... ]
					*/
				}

			>?>

PHP Serialized object

This is probably the easiest output method and even the most useful if users connecting to your API are using PHP.
The output is generated by PHP function serialize().

			<?php

				/*
				*  [ ... ]
				*/

				switch ($_GET['format']) {
					/*
					*  [ ... ]
					*/
					case 'json' :
							/*
							*  [ ... ]
							*/
						break;
					case 'php' :	

							/* Setting up PHP headers */
							header ("content-type: text/php charset=utf-8");

							/* Printing the PHP serialized Object*/
							echo serialize($response);
						break;
				}

			>?>

Dealing with Errors

At this point the script is ready to be used.
We are able to perform the user’s desired operations and to output results in the format we were asked to.
We now need to deal with possible errors. Errors may be everything : slow database, duplicate entries, long strings etc.
Giving the user a full error description will enhance a lot the usability of your API.

To standardize the output and make sure that any user can check and deal with errors, we are going to improve our results’ array structure.
This way we should affect any desired format.

So here is the new general structure of the array

			<?php

				$results = Array(
					'head' => Array(
						'status' => '0', // if the call was successful or not
						'error_number' => 'xxx', // reference number for the error
						'error_message' => 'Duplicate' // brief description of the problem
					),
					'body' => Array (
						'id' => $the_id,
						'views' => $the_views
					)
				);

			>?>

Obviously if there’s an error the body will be empty while if there are no problems, error_number and error_message won’t appear.
Now let’s explain a bit better what those new fields are.

  • head :
    this is the container of everything not related to our output.
    Exactly like in an HTML page this shows information, not data.
  • status :
    this indicates if the API system was able or not to process the user’s request.
    If it is 0, then an error occurred. It it is 1 everything is fine.
  • error_number :
    this is a reference code which the user can refer to in order to search our documentation.
    Usually I prefer to use error codes similar to the Apache ones (so 40x or 50x)
    but this is totally up to you.
  • error_message :
    a quick alert for the user to better understand what happened.
    May contain links to documentation.

To implement this kind of error handling we need to avoid PHP to output its own warnings and dies.
This means to not put ‘or die’ in mysql routines and write a lot of ‘@’ to disable warnings on header and query functions.

I’ll quickly review some error handling here, while if you want to see the complete system please refer to the source files.

Mysql connection errors

Alert the user in case we are not able to connect to our database.
This is usually caused by downtimes, so we are going to indicate it as a Temporary problem.

				<?php

					if (!@mysql_connect('localhost','your_username','your_password')) {
						$results = Array(
							'head' => Array(
								'status' 		=> '0',
								'error_number'	=> '500',
								'error_message' => 'Temporary Error.'.
									'Our server might be down, please try again later.'
							),
							'body' => Array ()
						);
						$errors=1;
					}
					if (!@mysql_select_db('my_db')) {
						$results = Array(
							'head' => Array(
								'status' 		=> '0',
								'error_number'	=> '500',
								'error_message' => 'Temporary Error.'.
									'Our server might be down, please try again later.'
							),
							'body' => Array ()
						);
						$errors=1;
					}

					if (!$errors)
						switch ($_GET['method']) {
							/*
							 *  [ ... ]
							 */
						}

					switch ($_GET['format']) {
						/*
						 *  [ ... ]
						 */
					}

				>?>

Duplicate movie error

As in the database structure we put an unique key for the movie_name field (we need to avoid duplicates), if an user tries to create a movie that already exists, the insert query will fail.

This example shows how to return the error to the user without stopping our script.

				<?php

					/*
					 *  [ ... ]
					 */

					switch ($_GET['method']) {
						case 'create' :
								$query = '
									INSERT INTO movies (
										`movie_name`,`movie_views`
									) VALUES (
										"'.mysql_real_escape_string($_GET['name']).'",
										1
									)
								';
								if (!$go=@mysql_query($query))
									$results = Array(
										'head' => Array(
											'status' 		=> '0',
											'error_number'	=> '601',
											'error_message' => 'Duplicate Entry.'.
									'			Movie already present.'
										),
										'body' => Array ()
									);
								else {
									$return = @mysql_insert_id();
									$results = Array(
										'head' => Array (
											'status' => 1
										),
										'body' => Array (
											'id' => $return
										)
									);
								}
							break;
						case 'update' :
							/*
							 *  [ ... ]
							 */
					}

				>?>

As stated before, for more error handling information, refer to source files, where more possible errors are handled.

Tidy up and Beautify API

So, we have data and error handling.
We can now proceed to put everything in full production.
Before that, we can use Apache2 mod_rewrite powerful tools to beautify our API calls, changing them from this

http://example.com/api.php?method=get&name=Memento&format=xml

to that

http://example.com/get.xml?name=Memento

Obviously the parameters are the same, but instead of having all of them after the question mark, we mask some of them inside the filename. Here’s the explaining pattern:

http://{DOMAIN}/{METHOD}.{FORMAT}?{PARAMETERS}

This kind of approach is more user friendly and might help developers dealing with your service
using a logical disposition of the parameters.

To achieve a similar result we are going to use the special Apache file .htacces which needs to be placed in the same directory as our api.php file.

After creating the .htaccess, that’s the content to put inside:

		RewriteEngine on

		RewriteCond %{REQUEST_FILENAME} !-d
		RewriteCond %{REQUEST_FILENAME} !-f
		RewriteCond %{QUERY_STRING} ^(.*)$
		RewriteRule ^([^.]+).([^?]+)$ api.php?method=$1&format=$2&%1

Below is a quick explanation for each row

  • RewriteEngine on :
    turns on mod_rewrite
  • RewriteCond %{REQUEST_FILENAME}!-d :
    checks that the requested file does not exist as directory in the server
  • RewriteCond %{REQUEST_FILENAME}!-f :
    checks that the requested url does not exist as file in the server
  • RewriteCond %{QUERY_STRING} ^(.*)$ :
    looks for the querystring and store it in the %1 variable
  • RewriteRule ^([^.]+).([^?]+)$ /api.php?method=$1&format=$2&%1 :
    rewrites the url creating the needed GET parameters

After saving your .htaccess file you should be able to call the PHP script with the new type of urls.
If you receive a 5** error or a 404 not found, your Apache installation might not correctly be configured to deal with .htaccess.

If your website runs on a shared hosting please contact your provider for more information.
If you manage your own server you probably just need to activate mod_rewrite.
In this case please refer to Apache documentation.

Final Notes

Now that you know how to realize a simple API system in a few minutes, you should concentrate on how to make it as useful as possible for any kind of user and request.

Documentation is probably the most important part of your brand new APIs.
Make your methods easy to understand by any user and be sure to create clear formats and clean information.
As we discussed before, be sure to handle all error possibilities and to document each of them.

Setting up a Wiki is probably the best way to create a better contact with your users.
Moreover by using people suggestions you can improve both APIs and the Documents themselves.

  • http://twitter.com/chrisvoo Christian Castelli

    This is not a post, but a book, interesting indeed :-D

  • loige

    Ottimo post. Estremamente completo ed accurato.
    Bravo ;)

  • Pingback: Create your first API set in PHP | Andrea Olivato's Blog | Coder Online

  • loige

    Ottimo post. Estremamente completo ed accurato.
    Bravo ;)

  • http://www.contussupport.com Php Programmer

    Thank you so much for sharing this source.

  • http://catgirlalexandria.wordpress.com Laura Garcia

    Wow this is an Awesome Tutorial best i have seen so far love it!

  • http://www.vibramshoesonline.com vibram fivefingers

    Mark S. is definitely on the right track. If you want to get a professional looking email address, Id recommend buying your name domain name, like or
    air jordan 16.5
    If its common it might be difficult to get, however, be creative and you can usually find something.

  • http://twitter.com/Anklebuster Mitchell Allen

    This is very detailed. I am currently reading a library book called Interface-Oriented Design. It's long on theory short on examples (so far) and shows C++ code. So your tutorial is very helpful in getting me to understand the theory in the book.

    My language is VB6, but I do understand database design, a bit :)

    I've bookmarked your site for further exploration.

    Cheers,

    Mitch

  • amitswba

    Great Tutorial :)

  • http://www.facebook.com/people/Ernest-Boabramah/100000921074630 Ernest Boabramah

    This is a good starting point to create my own api

  • http://twitter.com/pkanane Papa Kwame Anane

    The best tutorial on php API creation i’ve come across.. thanks :-)

  • Pingback: Create your first API set in PHP | Andrea Olivato’s Blog | cybernet

  • Asd

    aas

  • Xxsaxas

    Nice tutorial, but could you fix download links for source files. They are not working.

  • Anonymous

    Is there any way this can be customised to allow for multiple blog feeds?php simple xml  i dont expect it..

  • Aravind

    can u give api related to moblile app….

  • Emmanuel Owusu Addai

    Hi, i wanted to test this demo but the zipped file is not on your server. Can you please send it to my email, eoaddai@gmail.com

  • Hac

    perfect thansk

  • Hac

    download link not found ?

  • Josep M

    Great! Thanks for share!

  • web hosting

    Nice share. Was really looking for API implementation.

  • Steven Kleinert

    The source is not there any chanse to update the liks

  • http://twitter.com/si458 Simon Smith

    brilliant tutorial, any change of updating the download link as its still not working, from the looks of things years later!

  • http://twitter.com/si458 Simon Smith

    brilliant tutorial, any change of updating the download link as its still not working, from the looks of things years later! 

  • http://twitter.com/mtasuandi MTA Suandi

    Thank you for great tutorial. It’s old and It’s Gold. :)

  • Guest

    I cant download the files.

  • Mukunnda Panchal

    I can’t download the files. Can i get that from some other place?

  • http://www.hurricanesoftwares.com Ashish

    Very interesting article, i am developing API’s in PHP for long and this is exactly i was following. These days i am batting with scalability issues as in API you can’t cache anything especially if your application involves searching and adding data from thousands of users. Do you have some best practices for that too.

    I found that Apache, ningx both are bit heavy for API driven approach so i am trying Cherooke now. 

  • Mdoau

    Amazing tut!!! Thanks Your Highness!!!