Simple PHP Library for RESTful APIs
Go to file
PJ Dietz bc966f5924 New README in progress 2015-02-21 18:11:54 -05:00
.idea Add RouteTable 2015-02-21 07:13:09 -05:00
src/pjdietz/WellRESTed Update copyright and rename a couple tests 2015-02-21 16:01:06 -05:00
test Update copyright and rename a couple tests 2015-02-21 16:01:06 -05:00
.gitignore 100% on RouteBuilder 2014-07-13 14:50:23 -04:00
.travis.yml Update README. Add PHP 5.6 to Travis. 2015-01-21 12:56:55 -05:00
README.md New README in progress 2015-02-21 18:11:54 -05:00
composer.json Allow Route target to be a callable, string, or instance 2015-02-18 20:17:09 -05:00
composer.lock Allow Route target to be a callable, string, or instance 2015-02-18 20:17:09 -05:00
phpdoc.dist.xml Fix phpdoc for interfaces, Handler. 2014-07-13 10:12:12 -04:00
phpunit.xml.dist Use random ports for Client test to reduce false errors on Travis 2015-01-21 13:49:17 -05:00

README.md

WellRESTed

Build Status

WellRESTed is a micro-framework for creating RESTful APIs in PHP. It provides a lightweight yet powerful routing system and classes to make working with HTTP requests and responses clean and easy.

Requirements

  • PHP 5.3
  • PHP cURL for making requests with the Client class (Optional)

Install

Add an entry for "pjdietz/wellrested" to your composer.json file's require property. If you are not already using Composer, create a file in your project called "composer.json" with the following content:

{
    "require": {
        "pjdietz/wellrested": "2.*"
    }
}

Use Composer to download and install WellRESTed. Run these commands from the directory containing the composer.json file.

$ curl -s https://getcomposer.org/installer | php
$ php composer.phar install

You can now use WellRESTed by including the vendor/autoload.php file generated by Composer.

Overview

Routing and Routes

WellRESTed's primary goal is to facilitate mapping of URIs to classes that will provide or accept representations. To do this, create a Router instance and load it up with some routes.

// Build the router.
$myRouter = new Router();
$myRouter->add(
    ["/exact/", $exactHandler],
    ["/prefix/*", $prefixHandler],
    ["/template/{id}/", $templateHandler],
    ["~/regex/([0-9+])~", $regexHandler],
);
$myRouter->respond();

Each route maps a path to a handler. The path may be:

  • An exact match to a path (fastest, least powerful)
  • A prefix (ending with *)
  • A URI template with variables indicated with curly braces (ex: {variable})
  • A regular expression (slowest, most powerful)

Template and Regex routes will forward variables or captures to their handlers. Template routes can also be customized to restrict variables to match specific patterns. (TODO LINK)

The handler may be any of the following:

  • An instance that implements HandlerInterface
  • A string containing the fully qualified name of a HandlerInterface concrete class
  • A callable that returns a HandlerInterface concrete class

The best choices are strings and callables because they delay instantiation until the last instant. If your class is autoloaded, the class never needs to be loaded or instantiated until it is needed.

To illustrate, image you have a namespace \MyApi with several classes inside that implement HandlerInterface.

// Build the router.
$myRouter = new Router();
$myRouter->add(
    ["/instance/", new \MyApi\InstanceHandler()],
    ["/string/",    '\MyApi\StringHandler'],
    ["/callable/", function () {
        return new \MyApi\CallableHandler();
        }]
);
$myRouter->respond();

While the callable approach may look a bit weird, it's very powerful, and makes dependency injection very easy. (TODO LINK)

Handlers

Any class that implements HandlerInterface may be the handler for a route. This could be a class that builds the actual response, or it could be another Router.

When a router dispatches a request to a handler, it calls the handler's getResponse() method, passing along the request and an array of extra arguments. These extra arguments may be captures from a regular expression route, variables from a URI template route, or any other arbitrary pieces of information you want to pass to the handler.

The handler builds and returns a ResponseInterface. Or, the handler may return null to indicate that it won't be handling the request after all.

Handler Class

WellRESTed provides the abstract class Handler which you may subclass for your handlers. This class provides methods for responding based on HTTP method. When you create your Handler subclass, you will implement a method for each HTTP verb you would like the endpoint to support. For example, if /cats/ should support GET, you would override the get() method. For POST, post(), etc.

Here's a simple Handler that allows GET and POST.

class CatsCollectionHandler extends \pjdietz\WellRESTed\Handler
{
    protected function get()
    {
        // Read some cats from the database, cache, whatever.
        // ...read these an array as the variable $cats.

        // Set the values for the instance's response member. This is what the
        // Router will eventually output to the client.
        $this->response->setStatusCode(200);
        $this->response->setHeader("Content-Type", "application/json");
        $this->response->setBody(json_encode($cats));
    }

    protected function post()
    {
        // Read from the instance's request member and store a new cat.
        $cat = json_decode($this->request->getBody());
        // ...store $cat to the database...

        // Build a response to send to the client.
        $this->response->setStatusCode(201);
        $this->response->setBody(json_encode($cat));
    }
}

See Handlers to learn about the subclassing the Handler class. See HandlerInteface to learn about more ways build completely custom classes.

Responses

You've already seen a Response used inside a Handler in the examples above. You can also create a Response outside of Handler. Let's take a look at creating a new Response, setting a header, supplying the body, and outputting.

$resp = new \pjdietz\WellRESTed\Response();
$resp->setStatusCode(200);
$resp->setHeader("Content-type", "text/plain");
$resp->setBody("Hello world!");
$resp->respond();
exit;

This will output nice response, complete with status code, headers, body.

Requests

From outside the context of a Handler, you can also use the Request class to read info for the request sent to the server by using the static method Request::getRequest().

// Call the static method Request::getRequest() to get the request made to the server.
$rqst = \pjdietz\WellRESTed\Request::getRequest();

if ($rqst->getMethod() === 'PUT') {
    $obj = json_decode($rqst->getBody());
    // Do something with the JSON sent as the message body.
    // ...
}

HTTP Client

The Client class allows you to make an HTTP request using cURL.

(This feature requires PHP cURL.)

// Prepare a request.
$rqst = new \pjdietz\WellRESTed\Request();
$rqst->setUri('http://my.api.local/resources/');
$rqst->setMethod('POST');
$rqst->setBody(json_encode($newResource));

// Use a Client to get a Response.
$client = new Client();
$resp = $client->request($rqst);

// Read the response.
if ($resp->getStatusCode() === 201) {
    // The new resource was created.
    $createdResource = json_decode($resp->getBody());
}

Copyright © 2015 by PJ Dietz Licensed under the MIT license