PHP Serving RESTful APIs Exposing and Routing to a Resource - Web Development and Design | Tutorial for Java, PHP, HTML, Javascript PHP Serving RESTful APIs Exposing and Routing to a Resource - Web Development and Design | Tutorial for Java, PHP, HTML, Javascript

Breaking

Post Top Ad

Post Top Ad

Thursday, June 20, 2019

PHP Serving RESTful APIs Exposing and Routing to a Resource

PHP Serving RESTful APIs


Exposing and Routing to a Resource


Problem

You want to provide access to a resource and handle requests according to the HTTP method.

Solution

Use the $_SERVER['REQUEST_METHOD'] variable to route the request:

       $request = explode('/', $_SERVER['PATH_INFO']);
       
       $method = strtolower($_SERVER['REQUEST_METHOD']);
       switch($method) {
             case 'get':
                   // handle a GET request
                   break;
             case 'post':
                   // handle a POST request
                   break;
             case 'put':
                   // handle a PUT request
                   break;
             case 'delete':
                   // handle a DELETE request
                   break;
             default:
                   // unimplemented method
                   http_response_code(405);
             }

Discussion

When processing a request for a RESTful resource, you need to both know the requested resource and the action the client wants to take.

However, it’s rare to have a one-to-one mapping between resources and the PHP script that processes them. For example, a resource for books could use a book’s ISBN as the key. So, PHP Cookbook is at /v1/books.php/9781449363758, Learning PHP 5 is at /v1/ books.php/9780596005603, and so on.

But it’s not a good idea to have individual files at each of those locations. Instead, use a single books.php file, which uses the ISBN as a parameter. In many scripts, you’d pass the ISBN as a query parameter, such as /v1/books.php?isbn=9781449363758, and read this in your PHP code at $_GET['isbn'].

However, with REST, you use slashes to identify each resource. And you cannot use the standard PHP superglobals with a URL such as /v1/books.php/9781449363758. Instead, parse the path into its components by breaking the $_SERVER['PATH_INFO'] apart on “/”:

       $request = explode('/', $_SERVER['PATH_INFO']);

This breaks a request for /v1/books.php/9781449363758 into:

       Array
       (
             [0] =>
             [1] => 9781449363758
       )

Next, route the request based on the HTTP method, so you can handle GETs, PUTs, POSTs, and DELETEs in different functions. For this, use $_SERVER['REQUEST_METHOD']:

       $method = strtolower($_SERVER['REQUEST_METHOD']);
       switch($method) {
             case 'get':
                   // handle a GET request
                   get_book($request);
                   break;
             case 'post':
                   // handle a POST request
                   post_book($request);
                   break;
             case 'put':
                   // handle a PUT request
                   put_book($request);
                   break;
             case 'delete':
                   // handle a DELETE request
                   delete_book($request);
                   break;
             default:
                    // unimplemented method
                    http_response_code(405);
       }

Because you may not choose to implement all methods for a resource, a switch statement makes it easy to insert the methods you want, while also having a default behavior of returning HTTP status code 405 to signal “Method Not Allowed”.

You may find it convenient to map the RESTful resources you expose to PHP classes. Furthermore, those classes can have methods of get(), post(), and so forth. For example:

       class books {
             static public function get($request) {
                   // handle a GET request
             }

             static public function post($request) {
                   // handle a POST request
             }

             // other methods, too
       }

       class albums {
             static public function get($request) {
                   // handle a GET request
             }
       }

Then you can modify the router to be a single index.php to process all resources, instead of separate files for each resource:

       // break apart URL and extract the root resource
       $request = explode('/', $_SERVER['PATH_INFO']);
       $resource = array_shift($request);

       // only process valid resources
       $resources = array('books' => true, 'music' => true);
       if (! array_key_exists($resource, $resources)) {
             http_response_code(404);
             exit;
       }

       // route the request to the appropriate function based on method
       $method = strtolower($_SERVER["REQUEST_METHOD"]);
       switch($method) {
             case 'get':
             case 'post':
             case 'put':
             case 'delete':
             // any other methods you want to support, such as HEAD
                   if (method_exists($resource, $method)) {
                         call_user_func(array($resource, $method), $request);
                         break;
                   }
                   // fall through
             default:
                   http_response_code(405);
       }

First, you break apart the URL on /. Then you pop off the first element to extract the resource, such as books or albums.

Then you make sure that resource is a legitimate one to call. For instance, asking for /v1/movies/fletch generates a 404 error, because that resource doesn’t exist.

Finally, you check if the class with the same name as the resource has a class method that matches the HTTP method. If so, you use call_user_func() to invoke the method.

If not, you return a response code of 405 (Method Not Allowed). You also only handle the get(), post(), put(), and delete() methods, so people cannot invoke other class methods.



No comments:

Post a Comment

Post Top Ad