Simple PHP Library for RESTful APIs
Go to file
PJ Dietz 6a642dfed2 Fix documentation and add tests for BaseRoute 2014-07-13 17:18:08 -04:00
.idea Remove @package tag from phpdoc throughout. 2014-07-13 09:00:38 -04:00
src/pjdietz/WellRESTed Fix documentation and add tests for BaseRoute 2014-07-13 17:18:08 -04:00
test Fix documentation and add tests for BaseRoute 2014-07-13 17:18:08 -04:00
.gitignore 100% on RouteBuilder 2014-07-13 14:50:23 -04:00
CHANGES.md Add safeguard against infinite routing loops 2013-08-16 17:49:37 -04:00
README.md Update RouteBuilder docs and add test 2014-07-13 14:29:37 -04:00
composer.json Update RouteBuilder docs and add test 2014-07-13 14:29:37 -04:00
composer.lock Update RouteBuilder docs and add test 2014-07-13 14:29:37 -04:00
phpdoc.dist.xml Fix phpdoc for interfaces, Handler. 2014-07-13 10:12:12 -04:00
phpunit.xml Update RouteBuilder docs and add test 2014-07-13 14:29:37 -04:00

README.md

WellRESTed

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

Requirements

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 autoload.php file generated by Composer. vendor/autoload.php

Examples

Routing

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. Each Route is simply a mapping of a URI pattern to a class name. The class name represents the Handler (any class implementing HandlerInterface) which the router will dispatch at the time it receives a request for the given URI. The handlers are never loaded unless they are needed.

Here's an example of a Router that will handle two URIs:

// Build the router.
$myRouter = new Router();
$myRouter->addRoutes(array(
    new StaticRoute("/", "\\myapi\\Handlers\\RootHandler")),
    new StaticRoute("/cats/", "\\myapi\\Handlers\\CatCollectionHandler")),
    new TemplateRoute("/cats/{id}/", "\\myapi\\Handlers\\CatItemHandler"))
);
$myRouter->respond();

Building Routes with JSON

WellRESTed also provides a class to construct routes for you based on a JSON description. Here's an example.

$json = <<<'JSON'
{
    "handlerNamespace": "\\myapi\\Handlers",
    "routes": [
        {
            "path": "/",
            "handler": "RootHandler"
        },
        {
            "path": "/cats/",
            "handler": "CatCollectionHandler"
        },
        {
            "tempalte": "/cats/{id}",
            "handler": "CatItemHandler"
        }
    ]
}
JSON;

$builder = new RouteBuilder();
$routes = $builder->buildRoutes($json);

$router = new Router();
$router->addRoutes($routes);
$router->respond();

Notice that when you build routes through JSON, you can provide a handlerNamespace to be affixed to the front of every handler.

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 another Router.

For most cases, you'll want to use a subclass of the Handler class, which 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.

If your endpoint should reject particular verbs, no worries. The Handler base class defines the default verb-handling methods to respond with a 405 Method Not Allowed status.

Here's a simple Handler that matches the first endpoint, /things/.

class CatsCollectionHandler extends \pjdietz\WellRESTed\Handler
{
    protected function get()
    {
        // Read some things from the database, cache, whatever.
        // ...read this into the variable $cat

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

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

        // Build a response to send to the client.
        $this->response->setStatusCode(201);
        $this->response->setBody('You added a new cat!');
    }
}

This Handler works with the endpoint, /cats/{id}. The template for this endpoint has the variable {id} in it. The Handler can access path variables through its args member, which is an associative array of variables from the URI.

class ThingItemHandler extends \pjdietz\WellRESTed\Handler
{
    protected function get()
    {
        // Lookup a cat ($cat) based on $this->args['id']
        // ...do lookup here...

        if ($cat) {
            // The cat exists! Let's output a representation.
            $this->response->setStatusCode(200);
            $this->response->setHeader('Content-Type', 'application/json');
            $this->response->setBody(json_encode($cat));
        } else {
            // The ID did not match anything.
            $this->response->setStatusCode(404);
            $this->response->setHeader('Content-Type', 'text/plain');
            $this->response->setBody('No cat with id ' . $this->args['id']);
        }
    }
}

Requests and Responses

You've already seen a Response in use in the examples above. You can also use Responses outside of Handlers. 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;

The Request class goes hand-in-hand with the Response class. Again, this is used in the Handler class to read the information from the request being handled. From outside the context of a Handler, you can also use the Request class to read info for the request sent to the server.

// Call the static method Request::getRequest() to get a reference to the Request
// singleton that represents 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.
    // ...
}

The Request class can also make a request to another server and provide the response as a Response object. (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));

// Make the request.
$resp = $rqst->request();

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

More Examples

For more examples, see the project pjdietz/wellrested-samples. Not yet updated for version 2.0

Copyright © 2014 by PJ Dietz Licensed under the MIT license