Simple PHP Library for RESTful APIs
Go to file
PJ Dietz 56cf56c6c5 Fix typo 2015-02-20 07:45:48 -05:00
.idea Allow Route target to be a callable, string, or instance 2015-02-18 20:17:09 -05:00
src/pjdietz/WellRESTed Fix typo 2015-02-20 07:45:48 -05:00
test Add RouteFactory 2015-02-20 07:45:39 -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 Update README. Add PHP 5.6 to Travis. 2015-01-21 12:56:55 -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.

Version 2

It's more RESTed than ever!

Version 2 brings a lot improvements over 1.x, but it is not backwards compatible. See Changes from Version 1 if you are migrating from a previous 1.x version of WellRESTed.

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.

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 path or path pattern to a class name. The class name represents the "handler" (any class implementing HandlerInterface ) which the router will dispatch when it receives a request for the given URI. The handlers are never instantiated or loaded unless they are needed.

// 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();

See Routes to learn about the various route classes.

Optimized Route Lookup

StaticRoute and PrefixRoute routes are optimized by providing a direct lookup from path to handler. This is different from convential lookups because a match must be found by iterating through the entire list of routes in the router. This reduces the lookup complexity from O(n)—linear—to O(1)—constant.

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.

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.

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());
}

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();

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

Copyright © 2015 by PJ Dietz Licensed under the MIT license