Android Tutorial Create a REST API in php the easy way

There are numerous cases where you need to access data for your app and you need the simplest and fastest way of acquiring them. In those cases you definitely have to create an API which can be a chore.
In this tutorial/example I will show you how to create your REST API using Luracast's Restler one of the fullest and easier REST API Frameworks for PHP.

Functionality of the provided API:
  • Public IP
  • Store Private IP as a pair for your Public IP
  • Retrieve Private IP
The Restler API features covered in the example:
  • Call routing
  • Versioning
  • Passing Variables in HTTP GET calls
  • Use of Default Route
The example for simplicity uses a csv file to store the Public/Private pairs since its a lot more readable but all calls can be substituted for your SQL flavor and will be even shorter.

The API is made of 4 php files
  • index.php : Used for setting up the API config as well as add the PHP classes for the API calls
  • publicip : Used for providing 2 calls to get the Clients Public IP Address [realipaddr(), remoteaddr()]
  • localip : Provides [push('LocalIP'), pullmyip(), pull('PublicIP'), publicip()]
  • defaultCall : Provides the default api call when the client just polls the API URL

everything else included is part of the Restler framework.

You can test all the calls live @ http://android.eservices-greece.net/getip/

API Setup
======

File: index.php
PHP:
<?php
require_once 'vendor/restler.php';
use Luracast\Restler\Restler;
Defaults::$useUrlBasedVersioning = true;
//use Luracast\Restler\Defaults;
//set the defaults to match your requirements
Defaults::$throttle = 20; //time in milliseconds for bandwidth throttling
//setup restler
$r = new Restler();
$r->setAPIVersion(2);
$r->addAPIClass('defaultCall', '');
$r->addAPIClass('publicip');
$r->addAPIClass('localip');
$r->handle(); //serve the response

Here we setup the API so that it does simple versioning (URLBasedVersioning)
Set a default throttle so that the user cant kill our host easily (very basic)
Set the latest version of our API (setAPIVersion(2))
One important setting we pass here is the default route, ie what the API will send back if we do a blind call to the root URL ex: http://148.251.36.224/getip/ to do that we just add a null string to the defaultCall class like this: addAPIClass('defaultCall', '');
And finally we register 2 classes, those 2 are the prefixes for our API calls.

Public IP Class
======

File: publicip.php
PHP:
<?php
class publicip
{
    function realipaddr()
    {
        if (!empty($_SERVER['HTTP_CLIENT_IP'])) 
            {
            $ip = $_SERVER['HTTP_CLIENT_IP'];
        } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
            {
            $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
        } else {
            $ip = $_SERVER['REMOTE_ADDR'];
        }
        return array (
        'publicip' => $ip
        );
    }

    function remoteaddr()
    {
        return array (
        'publicip' => $_SERVER['REMOTE_ADDR']
        );
    }
}

In the public API class we have 3 functions, all of them return the same exact thing, the Public IP of the Client but using different methods/

Note that since this class doesn't have a version namespace it will by default be part our v1 API

Local IP Class
======

File: localip.php
PHP:
<?php
namespace v2;
class localip
{

    private $csv_file = "data/iplist.csv";
    public function get_csv_file()
    {
        return $this->csv_file;
    }

    private $remote_IP;
    public function get_remoteIP()
    {
        return $this->remote_IP;
    }

    public function set_remoteIP($remote_IP)
    {
        $this->remote_IP = $remote_IP;
    }

    function push($localip)
    {
        $this->set_remoteIP($_SERVER['REMOTE_ADDR']);
        if (file_exists($this->get_csv_file())) {
            $handle      = fopen($this->get_csv_file(), "r");
            $temp_handle = fopen("data/iplist_temp.csv", "a+");
            while (($data = fgetcsv($handle, 32, ",")) !== FALSE) {
                if ($data[0] != $this->get_remoteIP()) {
                    $ipPair = array(
                        $data[0],
                        $data[1]
                    );
                    fputcsv($temp_handle, $ipPair);
                }
            }
            $myIP  = $_SERVER['REMOTE_ADDR'];
            $ipPair = array(
                $myIP,
                $localip
            );
            fputcsv($temp_handle, $ipPair);

            fclose($handle);
            fclose($temp_handle);
            unlink($this->get_csv_file());
            rename("data/iplist_temp.csv", $this->get_csv_file());
            return array(
                'localip' => 'Updated'
            );
        } else {
            $handle = fopen($this->get_csv_file(), "a+");
            $myIP  = $this->get_remoteIP();
            $ipPair = array(
                $myIP,
                $localip
            );
            fputcsv($handle, $ipPair);
            fclose($handle);
            return array(
                'localip' => 'Added'
            );
        }

    }

    function pullmyip()
    {
        $this->set_remoteIP($_SERVER['REMOTE_ADDR']);
        if (($handle = fopen($this->get_csv_file(), "r")) !== FALSE) {
            while (($data = fgetcsv($handle, 32, ",")) !== FALSE) {
                if ($data[0] === $this->get_remoteIP()) {
                    return array(
                        'localip' => $data[1]
                    );
                } else {
                    return array(
                        'localip' => 'IP Not Found'
                    );
                }
            }
            fclose($handle);
        }
    }

    function pull($publicip)
    {
        if (($handle = fopen($this->get_csv_file(), "r")) !== FALSE) {
            while (($data = fgetcsv($handle, 32, ",")) !== FALSE) {
                if ($data[0] === $publicip) {
                    return array(
                        'localip' => $data[1]
                    );
                } else {
                    return array(
                        'localip' => 'IP Not Found'
                    );
                }
            }
            fclose($handle);
        }
    }

    function publicip()
    {
        return array (
        'publicip' => $_SERVER['REMOTE_ADDR']
        );
    }

}

This is part of our new and improved API it has a version namespace and its our version 2 (v2) API.
In this we have numerous new calls we needed to implement in our API to provide more functionality, the reason we are using versioning is that older consumers of our API would normally break since those API calls are deprecated, now we have the option of still using the v1 API and at the same time move our app to v2 without any service interruption.

Note that in this I am using getters and setters which is the proper way of passing along data inside a class, mind you it is always bad to use global variables in your code, get used to using this method ;) If you don't get what they do or you are getting confused you can switch to using strings directly.


defaultCall Class
======

File: defaultCall.php
PHP:
<?php
class defaultCall
{
    function index()
    {
        return array (
        'publicip' => $_SERVER['REMOTE_ADDR']
        );
    }
}

What the actual API described here does
======
  • It allows you to get the public IP of the device using one of these calls
http://android.eservices-greece.net/getip/
http://android.eservices-greece.net/getip/publicip/realipaddr
http://android.eservices-greece.net/getip/publicip/remoteaddr
http://android.eservices-greece.net/getip/v1/publicip/realipaddr
http://android.eservices-greece.net/getip/v1/publicip/remoteaddr

As you will notice in the URL's above, using v1 to do a call to our first version of the API is not necessary, the framework will default to that, it is always good to include it in your calls for consistency but not needed.

  • There are cases where you need to know the Private IP of a device in order to setup an internal service, ie an HTTP Server in one of your devices and allow other local (lan) clients to connect to it. You can use v2 of this API to do that like this:
Make a call from the server device to:
Instead of 192.168.1.38 in this example you should put your devices local IP.

Then make a simple call from your clients to receive the Private IP, like this:
In this case you dont need to send anything else, the API will get your own public IP and search for already saved addresses.

Other functions included:
Query the stored pairs for a specific public IP

http://android.eservices-greece.net/getip/v2/localip/pull?publicip=8.8.8.8

Since we are eventually going to deprecate our v1 of this API we have implemented the public IP call in this class.
http://android.eservices-greece.net/getip/v2/localip/publicip

Notice how in the calls above the URL contains v2 since we are specifically are using that version for our calls, keep in mind that the API Framework is smart enough and it will server calls without the v2 in the URL BUT only in case there arent any v1 calls with the same namespace. Thus always make sure you include the version of the API to make sure you are seeing the proper result.

Other things you need to know about
======

  • The rate limiting the framework provides is very basic, if you intent on setting this up you should handle this using a more advanced method (custom http headers etc)
  • If you plan on using this for any real work switch to SQL, even though it may seem more difficult at start its actually easier, just a bit more difficult to begin with.

Apache Settings

======
In order to provide the cool URL's instead of the boring index.php we use mod_rewrite which is part of Apache by default. The re-write conditions and rules are included in .htaccess.

Also note that you need to set AllowOverride All in your httpd.conf, other wise you will have to pass the arguments to index.php

Download
======
You can get the tar with all the files from here

The server this API is on is going to rate limit the calls to this API heavily (max 10k req/minute) since I use it to provide other API's for my own apps, if you need it for heavy use please consider setting up your own server. Also note that I may stop access at any time without prior notification.
 
Last edited:

barx

Well-Known Member
Licensed User
Longtime User
I am a neophyte, but I have a doubt: security.

What we know of the behavior of Restler?

Sorry, but I'm naturally suspicious
the above example appears to have no security. You would have to build a security layer on top of it (unless this restler has something built in that is available).
 

ggpanta

Member
Licensed User
Longtime User
I am a neophyte, but I have a doubt: security.

What we know of the behavior of Restler?

Sorry, but I'm naturally suspicious

Its OSS and you can do anything you want with the code, including go and break it apart to see what it does, other than that it doesnt do anything weird, normally your data would be stored in your own server anyway and the data the API in the example "hosts" is your public IP and the private IP you provide it :)
 
Top