Update documentation and README

This commit is contained in:
PJ Dietz 2014-07-26 22:39:26 -04:00
parent 2398fc6b77
commit e77c85f71b
4 changed files with 161 additions and 60 deletions

View File

@ -9,7 +9,7 @@ Requirements
------------ ------------
- PHP 5.3 - PHP 5.3
- [PHP cURL](http://php.net/manual/en/book.curl.php) for making requests with the `Client` class (Optional) - [PHP cURL](http://php.net/manual/en/book.curl.php) for making requests with the [`Client`](src/pjdietz/WellRESTed/Client.php) class (Optional)
Install Install
@ -40,7 +40,7 @@ Examples
### Routing ### 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 `Route`s. 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 when it receives a request for the given URI. **The handlers are never instantiated or loaded unless they are needed.** WellRESTed's primary goal is to facilitate mapping of URIs to classes that will provide or accept representations. To do this, create a [`Router`](src/pjdietz/WellRESTed/Router.php) 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 when it receives a request for the given URI. **The handlers are never instantiated or loaded unless they are needed.**
```php ```php
// Build the router. // Build the router.
@ -58,7 +58,7 @@ See [Routes](documentation/routes.md) to learn about the various route classes.
### Building Routes with JSON ### Building Routes with JSON
WellRESTed also provides a class to construct routes for you based on a JSON description. Here's an example. WellRESTed also provides a class to construct routes for you based on a JSON description. Here's an example:
```php ```php
$json = <<<'JSON' $json = <<<'JSON'
@ -89,15 +89,13 @@ $router->addRoutes($routes);
$router->respond(); $router->respond();
``` ```
Notice that when you build routes through JSON, you can provide a `handlerNamespace` to be affixed to the front of every `handler`. Notice that when you build routes through JSON, you can provide a `handlerNamespace` to be affixed to the front of every handler.
### Handlers ### 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`. Any class that implements [`HandlerInterface`](src/pjdietz/WellRESTed/Interface/HandlerInterface.php) may be the handler for a route. This could be a class that builds the actual response, or it could another [`Router`](src/pjdietz/WellRESTed/Router.php).
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. For most cases, you'll want to use a subclass of the [`Handler`](src/pjdietz/WellRESTed/Handler.php) class, which provides methods for responding based on HTTP method. When you create your [`Handler`](src/pjdietz/WellRESTed/Handler.php) 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 allows `GET` and `POST`. Here's a simple Handler that allows `GET` and `POST`.
@ -129,43 +127,11 @@ class CatsCollectionHandler extends \pjdietz\WellRESTed\Handler
} }
``` ```
#### Path Variables See [Handlers](documentation/handler.md) to learn about the various route classes.
When you use a `TemplateRoute` with variables (or a `RegexRoute` with capture groups), you can access the variables (or captures) through the `Handler` member variable `$args`.
Create this route...
```php
$route = TemplateRoute("/cats/{id}", "CatItemHandler");
```
...which dispatches a `CatItemHandler` instance.
```php
class CatItemHandler extends \pjdietz\WellRESTed\Handler
{
protected function get()
{
// Find a cat ($cat) based on $this->args["id"]
$id = $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"]);
}
}
}
```
### Responses ### Responses
You've already seen a `Response` in use in the examples above. You can also a `Response` outside of `Handler`. Let's take a look at creating a new `Response`, setting a header, supplying the body, and outputting. You've already seen a [`Response`](src/pjdietz/WellRESTed/Response.php) in use in the examples above. You can also a [`Response`](src/pjdietz/WellRESTed/Response.php) outside of [`Handler`](src/pjdietz/WellRESTed/Handler.php). Let's take a look at creating a new [`Response`](src/pjdietz/WellRESTed/Response.php), setting a header, supplying the body, and outputting.
```php ```php
$resp = new \pjdietz\WellRESTed\Response(); $resp = new \pjdietz\WellRESTed\Response();
@ -178,7 +144,7 @@ exit;
### Requests ### 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()`. From outside the context of a [`Handler`](src/pjdietz/WellRESTed/Handler.php), you can also use the [`Request`](src/pjdietz/WellRESTed/Request.php) class to read info for the request sent to the server by using the static method `Request::getRequest()`.
```php ```php
// Call the static method Request::getRequest() to get a reference to the Request // Call the static method Request::getRequest() to get a reference to the Request
@ -194,7 +160,7 @@ if ($rqst->getMethod() === 'PUT') {
### HTTP Client ### HTTP Client
The `Client` class allows you to make an HTTP request using cURL. The [`Client`](src/pjdietz/WellRESTed/Client.php) class allows you to make an HTTP request using cURL.
(This feature requires [PHP cURL](http://php.net/manual/en/book.curl.php).) (This feature requires [PHP cURL](http://php.net/manual/en/book.curl.php).)

135
documentation/handlers.md Normal file
View File

@ -0,0 +1,135 @@
# Handlers
[`Handler`](../src/pjdietz/WellRESTed/Handler.php) is an abstract base class for you to subclass to create controllers for generating responses given requests.
## Instance Members
Your [`Handler`](../src/pjdietz/WellRESTed/Handler.php) subclass has access to three protected members:
Member | Type | Description
---------- | ---- | -----------
`args` | `array` | Map of variables to supplement the request, usually path variables.
`request` | [`RequestInterface`](../src/pjdietz/WellRESTed/Interfaces/RequestInterface.php) | The HTTP request to respond to.
`response` | [`RequestInterface`](../src/pjdietz/WellRESTed/Interfaces/ResponseInterface.php) | The HTTP response to send based on the request.
## HTTP Verbs
Most of the action takes place inside the methods called in response to specific HTTP verbs. For example, to handle a `GET` request, implement the `get` method.
```php
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));
}
}
```
Implement the methods that you want to support. If you don't want to support `POST`, don't implement it. The default behavior is to respond with `405 Method Not Allowed` for most verbs.
The methods available to implement are:
HTTP Verb | Method | Default behavior
--------- | --------- | ----------------------
`GET` | `get` | 405 Method Not Allowed
`HEAD` | `head` | Call `get`, then clean the response body
`POST` | `post` | 405 Method Not Allowed
`PUT` | `put` | 405 Method Not Allowed
`DELETE` | `delete` | 405 Method Not Allowed
`PATCH` | `patch` | 405 Method Not Allowed
`OPTIONS` | `options` | Add `Allow` header, if able
### `OPTIONS` requests and `Allowed` headers
An `OPTIONS` request to your endpoint should result in the API responding with an `Allow` header listing the verbs the endpoint supports. For example:
```
HTTP/1.1 200 OK
Allow: GET, HEAD, POST, OPTIONS
Content-Length: 0
```
To support `OPTIONS` requests, implement `getAllowedMethods` and return an array of the methods you support. For a handler that supports the methods in the example response, do this:
```php
protected function getAllowedMethods()
{
return array("GET", "HEAD", "POST", "OPTIONS");
}
```
You do not need to implement `options`. `options` by default calls `getAllowedMethods`. If it gets a return value, it sets the status code to `200 OK` and adds the `Allow` header. Otherwise, it responds `405 Method Not Allowed`.
### Custom Verbs
To support custom verbs, redefine the `buildResponse`. To respond to the custom verb `SNIFF`, to this:
```php
protected function buildResponse()
{
switch ($this->request->getMethod()) {
case 'SNIFF':
// Assuming you also made a sniff() method...
$this->sniff();
break;
default:
// Let the parent's method do the rest.
self::buildResponse();
}
}
```
## HttpExceptions
Another useful feature of the [`Handler`](../src/pjdietz/WellRESTed/Handler.php) class is that it catches exceptions deriving from [`HttpException`](../src/pjdietz/WellRESTed/Exceptions/HttpExceptions) and turns them into responses.
[`HttpException`](../src/pjdietz/WellRESTed/Exceptions/HttpExceptions) and its subclasses provide the status code and description for simple error responses.
For example, you can throw a `NotFountException` if the resource the request indicates does not exist.
```php
use \pjdietz\WellRESTed\Handler;
use \pjdietz\WellRESTed\Exceptions\HttpExceptions\NotFoundException;
class CatsCollectionHandler extends Handler
{
protected function get()
{
// Lookup a cat by ID.
$cat = Cat::getById($this->args["id"]);
if (!$cat) {
throw new NotFoundException();
}
$this->response->setStatusCode(200);
$this->response->setHeader("Content-Type", "application/json");
$this->response->setBody(json_encode($cat));
}
}
```
Your [`Handler`](../src/pjdietz/WellRESTed/Handler.php) will automatically turn this into a `404 Not Found` response.
Here are the available `HttpException` classes:
Response Code | Class
--------------------------- | -----------------------
`400 Bad Request` | `BadRequestException`
`401 Unauthorized` | `UnauthorizedException`
`403 Forbidden` | `ForbiddenException`
`404 Not Found` | `NotFoundException`
`409 Conflict` | `ConflictException`
`500 Internal Server Error` | `HttpException`
You can also create your own by subclass `HttpException` and setting the exception's `$code` to the status code and `$messge` to a default message.

View File

@ -1,22 +1,22 @@
# Routes # Routes
WellRESTed comes with a few Route classes: WellRESTed comes with a few route classes:
- `StaticRoute`: Matches request paths exactly - [`StaticRoute`](../src/pjdietz/WellRESTed/Routes/StaticRoute.php): Matches request paths exactly
- `TemplateRoute`: Matches URI templates - [`TemplateRoute`](../src/pjdietz/WellRESTed/Routes/TemplateRoute.php): Matches URI templates
- `RegexRoute`: Matches a custom regular expression - [`RegexRoute`](../src/pjdietz/WellRESTed/Routes/RegexRoute.php): Matches a custom regular expression
Each works basically the same way: It first checks to see if it is a match for the request. If it's a match, it instantiates a specific class implementing the `HandlerInterface` (autoloading the class, if needed). Finally, it uses the handler class to provide a response. Each works basically the same way: It first checks to see if it is a match for the request. If it's a match, it instantiates a specific class implementing the [`HandlerInterface`](../src/pjdietz/WellRESTed/Interfaces/HandlerInterface.php) (autoloading the class, if needed). Finally, it uses the handler class to provide a response.
## StaticRoute ## StaticRoute
Use a `StaticRoute` when you know the exact path you want to handle. This route will match only requests to `/cats/`. Use a [`StaticRoute`](../src/pjdietz/WellRESTed/Routes/StaticRoute.php) when you know the exact path you want to handle. This route will match only requests to `/cats/`.
```php ```php
$route = new StaticRoute("/cats/", "CatHandler"); $route = new StaticRoute("/cats/", "CatHandler");
``` ```
You can also make a `StaticRoute` that matches multiple exact paths. For example, suppose you have a multi-use `AnimalHandler` that you want to invoke to handle requests to `/cats/`, `/dogs/`, and `/birds/`. You can make this by passing an array instead of a string as the first parameter. You can also make a [`StaticRoute`](../src/pjdietz/WellRESTed/Routes/StaticRoute.php) that matches multiple exact paths. For example, suppose you have a multi-use `AnimalHandler` that you want to invoke to handle requests to `/cats/`, `/dogs/`, and `/birds/`. You can make this by passing an array instead of a string as the first parameter.
```php ```php
$route = new StaticRoute(array("/cats/", "/dogs/", "/birds/"), "AnimalHandler"); $route = new StaticRoute(array("/cats/", "/dogs/", "/birds/"), "AnimalHandler");
@ -24,7 +24,7 @@ $route = new StaticRoute(array("/cats/", "/dogs/", "/birds/"), "AnimalHandler");
## TemplateRoute ## TemplateRoute
`StaticRoutes` are the best choice if you know the exact path up front. But, what if you want to handle a path that includes a variable? That's where the `TemplateRoute` comes in. [`StaticRoute`](../src/pjdietz/WellRESTed/Routes/StaticRoute.php) is the best choice if you know the exact path up front. But, what if you want to handle a path that includes a variable? That's where the [`TemplateRoute`](../src/pjdietz/WellRESTed/Routes/TemplateRoute.php) comes in.
Here's a route that will match a request to a specific cat by ID and send it to a `CatItemHandler`. Here's a route that will match a request to a specific cat by ID and send it to a `CatItemHandler`.
@ -32,7 +32,7 @@ Here's a route that will match a request to a specific cat by ID and send it to
$route = new TemplateRoute("/cats/{id}", "CatItemHandler"); $route = new TemplateRoute("/cats/{id}", "CatItemHandler");
``` ```
A `TemplateRoute` use a URI template to match a request. To include a variable in your template, enclose it in `{}`. The variable will be extracted and made available for the handler in the handler's `args` member. A [`TemplateRoute`](../src/pjdietz/WellRESTed/Routes/TemplateRoute.php) use a URI template to match a request. To include a variable in your template, enclose it in `{}`. The variable will be extracted and made available for the handler in the handler's `args` member.
```php ```php
class CatItemHandlder extends \pjdietz\WellRESTed\Handler class CatItemHandlder extends \pjdietz\WellRESTed\Handler
@ -48,7 +48,7 @@ class CatItemHandlder extends \pjdietz\WellRESTed\Handler
Your template may have multiple variables. Be sure to give each a unique name. Your template may have multiple variables. Be sure to give each a unique name.
With this `TemplateRoute`... With this [`TemplateRoute`](../src/pjdietz/WellRESTed/Routes/TemplateRoute.php)...
```php ```php
$route = new TemplateRoute("/cats/{catId}/{dogId}", "CatItemHandler"); $route = new TemplateRoute("/cats/{catId}/{dogId}", "CatItemHandler");
@ -59,13 +59,13 @@ $route = new TemplateRoute("/cats/{catId}/{dogId}", "CatItemHandler");
### Default Variable Pattern ### Default Variable Pattern
By default, the `TemplateRoute` will accept for a variable any value consisting of numbers, letters, underscores, and hyphens. You can change this behavior by passing a pattern to use as the third parameter of the constructor. Here we'll restrict the template to match only numeric values. By default, the [`TemplateRoute`](../src/pjdietz/WellRESTed/Routes/TemplateRoute.php) will accept for a variable any value consisting of numbers, letters, underscores, and hyphens. You can change this behavior by passing a pattern to use as the third parameter of the constructor. Here we'll restrict the template to match only numeric values.
```php ```php
$route = new TemplateRoute("/cats/{id}", "CatItemHandler", TemplateRoute::RE_NUM); $route = new TemplateRoute("/cats/{id}", "CatItemHandler", TemplateRoute::RE_NUM);
``` ```
The `TemplateRoute` includes constants for some common situations. The value of each constant is a partial regular expression. You can use one of the constants, or provide your own partial regular expression. The [`TemplateRoute`](../src/pjdietz/WellRESTed/Routes/TemplateRoute.php) includes constants for some common situations. The value of each constant is a partial regular expression. You can use one of the constants, or provide your own partial regular expression.
### Pattern Constants ### Pattern Constants
@ -78,7 +78,7 @@ The `TemplateRoute` includes constants for some common situations. The value of
### Variable Patterns Array ### Variable Patterns Array
You can also set a different pattern for each variable. To do this, pass an array to the `TemplateRoute` constructor as the fourth paramter. The array must have variable names as keys and patterns as values. You can also set a different pattern for each variable. To do this, pass an array to the [`TemplateRoute`](../src/pjdietz/WellRESTed/Routes/TemplateRoute.php) constructor as the fourth parameter. The array must have variable names as keys and patterns as values.
```php ```php
$patterns = array( $patterns = array(
@ -96,13 +96,13 @@ Here, `{id}` will need to match digits and `{name}` must be all letters. Since `
### RegexRoute ### RegexRoute
If `TemplateRoute` doesn't give you enough control, you can make a route that matches a regular expression. If [`TemplateRoute`](../src/pjdietz/WellRESTed/Routes/TemplateRoute.php) doesn't give you enough control, you can make a route that matches a regular expression using a [`RegexRoute`](../src/pjdietz/WellRESTed/Routes/RegexRoute.php).
```php ```php
$route = new RegexRoute("~/cat/[0-9]+~", "CatHandler") $route = new RegexRoute("~/cat/[0-9]+~", "CatHandler")
``` ```
This will match `/cat/102` or `/cat/999` or what have you. To make this more useful, we can add a capture group. The captures are made available to the `Handler` as the `$args` member, as with the URI template variables for the `TemplateRoute` This will match `/cat/102` or `/cat/999` or what have you. To make this more useful, we can add a capture group. The captures are made available to the handler as the `$args` member, as with the URI template variables for the [`TemplateRoute`](../src/pjdietz/WellRESTed/Routes/TemplateRoute.php)
Note that the entire matched path will always be the `0` item, and captured groups will begin at `1`. Note that the entire matched path will always be the `0` item, and captured groups will begin at `1`.

View File

@ -27,7 +27,7 @@ use pjdietz\WellRESTed\Interfaces\RequestInterface;
*/ */
abstract class Handler implements HandlerInterface abstract class Handler implements HandlerInterface
{ {
/** @var array Map of variables to suppliement the request, usually path variables. */ /** @var array Map of variables to supplement the request, usually path variables. */
protected $args; protected $args;
/** @var RequestInterface The HTTP request to respond to. */ /** @var RequestInterface The HTTP request to respond to. */
protected $request; protected $request;