Define interfaces more clearly and clean up code.

This commit is contained in:
PJ Dietz 2013-05-26 14:55:48 -04:00
parent ac752bb446
commit 98e04ab63b
16 changed files with 389 additions and 369 deletions

View File

@ -10,7 +10,7 @@
namespace pjdietz\WellRESTed\Exceptions;
use \Exception;
use Exception;
/**
* Top level class for custom exceptions thrown by Well RESTed.

View File

@ -10,33 +10,26 @@
namespace pjdietz\WellRESTed;
use pjdietz\WellRESTed\Interfaces\HandlerInterface;
use pjdietz\WellRESTed\Interfaces\RequestInterface;
use pjdietz\WellRESTed\Interfaces\ResponseInterface;
use pjdietz\WellRESTed\Interfaces\RouterInterface;
/**
* A Handler issues a response for a given resource.
*
* @property-read Response response The Response to the request
* @property-read ResponseInterface response The Response to the request
*/
abstract class Handler implements HandlerInterface
{
/**
* Matches array from the preg_match() call used to find this Handler.
*
* @var array
*/
/** @var array Matches array from the preg_match() call used to find this Handler */
protected $args;
/**
* The HTTP request to respond to.
*
* @var Request
*/
/** @var RequestInterface The HTTP request to respond to. */
protected $request;
/**
* The HTTP response to send based on the request.
*
* @var Response
*/
/** @var ResponseInterface The HTTP response to send based on the request. */
protected $response;
/** @var RouterInterface The router that dispatched this handler */
protected $router;
// -------------------------------------------------------------------------
// Accessors
@ -56,43 +49,43 @@ abstract class Handler implements HandlerInterface
return null;
}
/**
* @param array $args
*/
/** @param array $args */
public function setArguments(array $args)
{
$this->args = $args;
}
/**
* @return array
*/
/** @return array */
public function getArguments()
{
return $this->args;
}
/**
* @param RequestInterface $request
*/
public function setRequest(RequestInterface $request)
{
$this->request = $request;
}
/**
* @return \pjdietz\WellRESTed\Request
*/
/** @return RequestInterface */
public function getRequest()
{
return $this->request;
}
/**
* Return the instance's Reponse
*
* @return ResponseInterface
*/
/** @param RequestInterface $request */
public function setRequest(RequestInterface $request)
{
$this->request = $request;
}
/** @return RouterInterface */
public function getRouter()
{
return $this->router;
}
/** @param RouterInterface $router */
public function setRouter(RouterInterface $router)
{
$this->router = $router;
}
/** @return ResponseInterface */
public function getResponse()
{
$this->response = new Response();
@ -107,7 +100,7 @@ abstract class Handler implements HandlerInterface
*/
protected function buildResponse()
{
switch ($this->request->method) {
switch ($this->request->getMethod()) {
case 'GET':
$this->get();
@ -159,6 +152,14 @@ abstract class Handler implements HandlerInterface
$this->respondWithMethodNotAllowed();
}
/**
* Provide a default response for unsupported methods.
*/
protected function respondWithMethodNotAllowed()
{
$this->response->setStatusCode(405);
}
/**
* Method for handling HTTP HEAD requests.
*
@ -171,8 +172,8 @@ abstract class Handler implements HandlerInterface
$this->get();
if ($this->response->statusCode == 200) {
$this->response->setBody('', false);
if ($this->response->getStatusCode() == 200) {
$this->response->setBody('');
}
}
@ -226,12 +227,4 @@ abstract class Handler implements HandlerInterface
$this->respondWithMethodNotAllowed();
}
/**
* Provide a default response for unsupported methods.
*/
protected function respondWithMethodNotAllowed()
{
$this->response->statusCode = 405;
}
}

View File

@ -1,16 +0,0 @@
<?php
namespace pjdietz\WellRESTed;
/**
* Interface for a creating a response in reaction to a respond or arguments.
* @package pjdietz\WellRESTed
*/
interface HandlerInterface
{
public function getRequest();
public function setRequest(RequestInterface $request);
public function getArguments();
public function setArguments(array $args);
public function getResponse();
}

View File

@ -0,0 +1,39 @@
<?php
/**
* pjdietz\WellRESTed\Interfaces\HandlerInterface
*
* @author PJ Dietz <pj@pjdietz.com>
* @copyright Copyright 2013 by PJ Dietz
* @license MIT
*/
namespace pjdietz\WellRESTed\Interfaces;
/**
* Interface for a creating a response in reaction to a request or arguments.
* @package pjdietz\WellRESTed
*/
interface HandlerInterface
{
/** @return array Associative array used to obtain a response */
public function getArguments();
/** @param array $args Associative array used to obtain a response */
public function setArguments(array $args);
/** @return RequestInterface Request used to obtain a response */
public function getRequest();
/** @param RequestInterface $request Request used to obtain a response */
public function setRequest(RequestInterface $request);
/** @return RouterInterface Reference to the router used to dispatch this handler */
public function getRouter();
/** @param RouterInterface $router Request used to obtain a response */
public function setRouter(RouterInterface $router);
/** @return ResponseInterface Response obtained based on the args and request */
public function getResponse();
}

View File

@ -0,0 +1,39 @@
<?php
/**
* pjdietz\WellRESTed\Interfaces\RequestInterface
*
* @author PJ Dietz <pj@pjdietz.com>
* @copyright Copyright 2013 by PJ Dietz
* @license MIT
*/
namespace pjdietz\WellRESTed\Interfaces;
/**
* Interface for representing an HTTP request.
* @package pjdietz\WellRESTed
*/
interface RequestInterface
{
/** @return string HTTP request method (e.g., GET, POST, PUT). */
public function getMethod();
/** @return string Path component of the request URI */
public function getPath();
/** @return array Query paramters as key-value pairs */
public function getQuery();
/**
* Return the value for this header name
*
* @param $headerName
* @return string $headerName
*/
public function getHeader($headerName);
/** @return string Requst body */
public function getBody();
}

View File

@ -0,0 +1,47 @@
<?php
/**
* pjdietz\WellRESTed\Interfaces\ResponseInterface
*
* @author PJ Dietz <pj@pjdietz.com>
* @copyright Copyright 2013 by PJ Dietz
* @license MIT
*/
namespace pjdietz\WellRESTed\Interfaces;
/**
* Interface for representing an HTTP response.
* @package pjdietz\WellRESTed
*/
interface ResponseInterface
{
/** @return int HTTP status code */
public function getStatusCode();
/** @param int $statusCode HTTP status code */
public function setStatusCode($statusCode);
/**
* Return the value for this header name
*
* @param $headerName
* @return string $headerName
*/
public function getHeader($headerName);
/**
* @param string $headerName
* @param string $headerValue
*/
public function setHeader($headerName, $headerValue);
/** @return string */
public function getBody();
/** @param string $body */
public function setBody($body);
/** Issue the reponse to the client. */
public function respond();
}

View File

@ -0,0 +1,30 @@
<?php
/**
* pjdietz\WellRESTed\Interfaces\RouteInterface
*
* @author PJ Dietz <pj@pjdietz.com>
* @copyright Copyright 2013 by PJ Dietz
* @license MIT
*/
namespace pjdietz\WellRESTed\Interfaces;
/**
* Interface for a route to relate a pattern for matching a URI to a handler class.
* @package pjdietz\WellRESTed
*/
interface RouteInterface
{
/** @return string Regex pattern used to match the URI */
public function getPattern();
/** @para string $pattern Regex pattern used to match the URI */
public function setPattern($pattern);
/** @return string Fully qualified name of the class the route will dispatch. */
public function getHandler();
/** @param string $className Fully qualified name of the class the route will dispatch. */
public function setHandler($className);
}

View File

@ -0,0 +1,26 @@
<?php
/**
* pjdietz\WellRESTed\Interfaces\RouterInterface
*
* @author PJ Dietz <pj@pjdietz.com>
* @copyright Copyright 2013 by PJ Dietz
* @license MIT
*/
namespace pjdietz\WellRESTed\Interfaces;
/**
* The RouterInterface provides a mechanism for obtaining a response given a request.
* @package pjdietz\WellRESTed
*/
interface RouterInterface
{
/**
* Return the response for the given request.
*
* @param RequestInterface $request
* @return ResponseInterface
*/
public function getResponse(RequestInterface $request = null);
}

View File

@ -21,20 +21,10 @@ namespace pjdietz\WellRESTed;
*/
abstract class Message
{
/**
* Entity body of the message
*
* @var string
*/
/** @var string Entity body of the message */
protected $body;
/**
* Associative array of HTTP headers
*
* @var array
*/
/** @var array Associative array of HTTP headers */
protected $headers;
/**
* Associative array of lowercase header field names as keys with
* corresponding case sensitive field names from the $headers array as
@ -43,19 +33,9 @@ abstract class Message
* @var array
*/
protected $headerLookup;
/**
* Name of the protocol to use.
*
* @var string
*/
/** @var string Name of the protocol to use. */
protected $protocol = 'HTTP';
/**
* Version of the protocol to use.
*
* @var string
*/
/** @var string Version of the protocol to use. */
protected $protocolVersion = '1.1';
// -------------------------------------------------------------------------
@ -156,9 +136,7 @@ abstract class Message
return isset($this->body);
}
/**
* Unset the body property
*/
/** Unset the body property */
public function unsetBody()
{
unset($this->body);
@ -315,9 +293,7 @@ abstract class Message
return isset($this->protocol);
}
/**
* Unset the protocol property.
*/
/** Unset the protocol property. */
public function unsetProtocol()
{
unset($this->protocol);
@ -353,9 +329,7 @@ abstract class Message
return isset($this->protocolVersion);
}
/**
* Unset the version portion of the protocol.
*/
/** Unset the version portion of the protocol. */
public function unsetProtocolVersion()
{
unset($this->protocolVersion);

View File

@ -10,7 +10,7 @@
namespace pjdietz\WellRESTed;
// TODO: Include port in the URI
use pjdietz\WellRESTed\Interfaces\RequestInterface;
/**
* A Request instance represents an HTTP request. This class has two main uses:
@ -31,40 +31,7 @@ namespace pjdietz\WellRESTed;
*/
class Request extends Message implements RequestInterface
{
/**
* The Hostname for the request (e.g., www.google.com)
*
* @var string
*/
private $hostname;
/**
* HTTP method or verb for the request
*
* @var string
*/
private $method = 'GET';
/**
* Path component of the URI for the request
*
* @var string
*/
private $path = '/';
/**
* Array of fragments of the path, delimited by slashes
*
* @var array
*/
private $pathParts;
/**
* Associative array of query parameters
*
* @var array
*/
private $query;
// TODO: Include port in the URI
/**
* Singleton instance derived from reading info from Apache.
@ -73,6 +40,16 @@ class Request extends Message implements RequestInterface
* @static
*/
static protected $theRequest;
/** @var string The Hostname for the request (e.g., www.google.com) */
private $hostname;
/** @var string HTTP method or verb for the request */
private $method = 'GET';
/** @var string Path component of the URI for the request */
private $path = '/';
/** @var array Array of fragments of the path, delimited by slashes */
private $pathParts;
/**@var array Associative array of query parameters */
private $query;
// -------------------------------------------------------------------------
@ -96,6 +73,62 @@ class Request extends Message implements RequestInterface
// -------------------------------------------------------------------------
// Accessors
/**
* Set the URI for the Request. This sets the other members, such as path,
* hostname, etc.
*
* @param string $uri
*/
public function setUri($uri)
{
$parsed = parse_url($uri);
$host = isset($parsed['host']) ? $parsed['host'] : '';
$this->setHostname($host);
$path = isset($parsed['path']) ? $parsed['path'] : '';
$this->setPath($path);
$query = isset($parsed['query']) ? $parsed['query'] : '';
$this->setQuery($query);
}
/**
* Return a reference to the singleton instance of the Request derived
* from the server's information about the request sent to the script.
*
* @return Request
* @static
*/
public static function getRequest()
{
if (!isset(self::$theRequest)) {
$request = new Request();
$request->readHttpRequest();
self::$theRequest = $request;
}
return self::$theRequest;
}
/**
* Set instance members based on the HTTP request sent to the server.
*/
public function readHttpRequest()
{
$this->setBody(file_get_contents("php://input"), false);
$this->headers = apache_request_headers();
// Add case insensitive headers to the lookup table.
foreach ($this->headers as $key => $value) {
$this->headerLookup[strtolower($key)] = $key;
}
$this->method = $_SERVER['REQUEST_METHOD'];
$this->uri = $_SERVER['REQUEST_URI'];
$this->hostname = $_SERVER['HTTP_HOST'];
}
/**
* Return the hostname portion of the URI
*
@ -195,6 +228,8 @@ class Request extends Message implements RequestInterface
return $this->query;
}
// -------------------------------------------------------------------------
/**
* Set the query. The value passed can be a query string of key-value pairs
* joined by ampersands or it can be an associative array.
@ -232,28 +267,6 @@ class Request extends Message implements RequestInterface
return $uri;
}
/**
* Set the URI for the Request. This sets the other members, such as path,
* hostname, etc.
*
* @param string $uri
*/
public function setUri($uri)
{
$parsed = parse_url($uri);
$host = isset($parsed['host']) ? $parsed['host'] : '';
$this->setHostname($host);
$path = isset($parsed['path']) ? $parsed['path'] : '';
$this->setPath($path);
$query = isset($parsed['query']) ? $parsed['query'] : '';
$this->setQuery($query);
}
// -------------------------------------------------------------------------
/**
* Make a cURL request out of the instance and return a Response.
*
@ -335,40 +348,4 @@ class Request extends Message implements RequestInterface
return $resp;
}
/**
* Set instance members based on the HTTP request sent to the server.
*/
public function readHttpRequest()
{
$this->setBody(file_get_contents("php://input"), false);
$this->headers = apache_request_headers();
// Add case insensitive headers to the lookup table.
foreach ($this->headers as $key => $value) {
$this->headerLookup[strtolower($key)] = $key;
}
$this->method = $_SERVER['REQUEST_METHOD'];
$this->uri = $_SERVER['REQUEST_URI'];
$this->hostname = $_SERVER['HTTP_HOST'];
}
/**
* Return a reference to the singleton instance of the Request derived
* from the server's information about the request sent to the script.
*
* @return Request
* @static
*/
public static function getRequest()
{
if (!isset(self::$theRequest)) {
$request = new Request();
$request->readHttpRequest();
self::$theRequest = $request;
}
return self::$theRequest;
}
}

View File

@ -1,15 +0,0 @@
<?php
namespace pjdietz\WellRESTed;
/**
* Interface for representing an HTTP request.
* @package pjdietz\WellRESTed
*/
interface RequestInterface
{
public function getMethod();
public function setMethod($method);
public function getUri();
public function setUri($uri);
}

View File

@ -10,7 +10,8 @@
namespace pjdietz\WellRESTed;
use \InvalidArgumentException;
use InvalidArgumentException;
use pjdietz\WellRESTed\Interfaces\ResponseInterface;
/**
* A Response instance allows you to build an HTTP response and send it when
@ -31,12 +32,7 @@ class Response extends Message implements ResponseInterface
* @var string
*/
private $reasonPhrase;
/**
* HTTP status code
*
* @var int
*/
/** @var int HTTP status code */
private $statusCode;
// -------------------------------------------------------------------------
@ -84,26 +80,12 @@ class Response extends Message implements ResponseInterface
}
}
/**
* Return the portion of the status line explaining the status.
*
* @return string
*/
/** @return string Portion of the status line explaining the status. */
public function getReasonPhrase()
{
return $this->reasonPhrase;
}
/**
* Return true if the status code is in the 2xx range.
*
* @return bool
*/
public function getSuccess()
{
return $this->statusCode >= 200 && $this->statusCode < 300;
}
/**
* Assign an explaination for the status code. Not normally needed.
*
@ -114,11 +96,13 @@ class Response extends Message implements ResponseInterface
$this->reasonPhrase = $statusCodeMessage;
}
/**
* Return the status code.
*
* @return int
*/
/** @return bool True if the status code is in the 2xx range. */
public function getSuccess()
{
return $this->statusCode >= 200 && $this->statusCode < 300;
}
/** @return int */
public function getStatusCode()
{
return $this->statusCode;
@ -268,24 +252,6 @@ class Response extends Message implements ResponseInterface
}
/**
* Return HTTP status line, e.g. HTTP/1.1 200 OK.
*
* @return string
*/
protected function getStatusLine()
{
return sprintf(
'%s/%s %s %s',
strtoupper($this->protocol),
$this->protocolVersion,
$this->statusCode,
$this->reasonPhrase
);
}
// -------------------------------------------------------------------------
/**
* Output the response to the client.
*
@ -307,4 +273,18 @@ class Response extends Message implements ResponseInterface
}
}
// -------------------------------------------------------------------------
/** @return string HTTP status line, e.g. HTTP/1.1 200 OK. */
protected function getStatusLine()
{
return sprintf(
'%s/%s %s %s',
strtoupper($this->protocol),
$this->protocolVersion,
$this->statusCode,
$this->reasonPhrase
);
}
}

View File

@ -1,12 +0,0 @@
<?php
namespace pjdietz\WellRESTed;
/**
* Interface for representing an HTTP response.
* @package pjdietz\WellRESTed
*/
interface ResponseInterface
{
public function respond();
}

View File

@ -10,7 +10,8 @@
namespace pjdietz\WellRESTed;
use \Exception;
use pjdietz\WellRESTed\Exceptions\WellRESTedException;
use pjdietz\WellRESTed\Interfaces\RouteInterface;
/**
* A Route connects a URI pattern to a Handler.
@ -22,33 +23,26 @@ class Route implements RouteInterface
* digits, hyphen and underscore)
*/
const RE_SLUG = '[0-9a-zA-Z\-_]+';
/** Regular expression matching digitis */
const RE_NUM = '[0-9]+';
/** Regular expression matching letters */
const RE_ALPHA = '[a-zA-Z]+';
/** Regular expression matching letters and digits */
const RE_ALPHANUM = '[0-9a-zA-Z]+';
/** Regular expression matching a URI template variable (e.g., {id}) */
const URI_TEMPLATE_EXPRESSION_RE = '/{([a-zA-Z]+)}/';
/**
* Default regular expression used to match template variable
*
* @property string
*/
static public $defaultVariablePattern = self::RE_SLUG;
/**
* Regular expression used to match a Request URI path component
*
* @var string
*/
private $pattern;
/**
* Name of the Handler class to use
*
@ -68,45 +62,13 @@ class Route implements RouteInterface
$this->handler = $handler;
}
/**
* @param string $handler
*/
public function setHandler($handler)
{
$this->handler = $handler;
}
/**
* @return string
*/
public function getHandler()
{
return $this->handler;
}
/**
* @param string $pattern
*/
public function setPattern($pattern)
{
$this->pattern = $pattern;
}
/**
* @return string
*/
public function getPattern()
{
return $this->pattern;
}
/**
* Create a new Route using a URI template to generate the pattern.
*
* @param string $uriTemplate
* @param string $handler
* @param array $variables
* @throws Exception
* @throws WellRESTedException
* @return Route
*/
static public function newFromUriTemplate(
@ -155,7 +117,7 @@ class Route implements RouteInterface
} else {
// Not sure why this would happen.
throw new Exception('Invalid URI Template.');
throw new WellRESTedException('Invalid URI Template.');
}
} else {
@ -173,4 +135,36 @@ class Route implements RouteInterface
}
/**
* @return string
*/
public function getHandler()
{
return $this->handler;
}
/**
* @param string $handler
*/
public function setHandler($handler)
{
$this->handler = $handler;
}
/**
* @return string
*/
public function getPattern()
{
return $this->pattern;
}
/**
* @param string $pattern
*/
public function setPattern($pattern)
{
$this->pattern = $pattern;
}
}

View File

@ -1,38 +0,0 @@
<?php
namespace pjdietz\WellRESTed;
/**
* Interface for a route to relate a pattern for matching a URI to a handler class.
* @package pjdietz\WellRESTed
*/
interface RouteInterface
{
/**
* Return the regex pattern used to match the URI
*
* @return string
*/
public function getPattern();
/**
* Provide a regex pattern that matches the URI
*
* @para string $pattern
*/
public function setPattern($pattern);
/**
* Return the name of the class the route will dispatch.
*
* @return string
*/
public function getHandler();
/**
* Provide the classname to instantiate to handle the route.
*
* @param string $className
*/
public function setHandler($className);
}

View File

@ -10,24 +10,25 @@
namespace pjdietz\WellRESTed;
use pjdietz\WellRESTed\Interfaces\HandlerInterface;
use pjdietz\WellRESTed\Interfaces\RequestInterface;
use pjdietz\WellRESTed\Interfaces\ResponseInterface;
use pjdietz\WellRESTed\Interfaces\RouteInterface;
use pjdietz\WellRESTed\Interfaces\RouterInterface;
/**
* Router
*
* A Router uses a table of Routes to find the appropriate Handler for a
* request.
* A Router uses a table of Routes to find the appropriate Handler for a request.
*/
class Router
class Router implements RouterInterface
{
/**
* Array of Route objects
*
* @var array
*/
protected $routes;
/** @var string Fully qualified name for the interface for handlers */
const HANDLER_INTERFACE = '\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface';
/** @var array Array of Route objects */
private $routes;
/**
* Create a new Router.
*/
/** Create a new Router. */
public function __construct()
{
$this->routes = array();
@ -44,28 +45,29 @@ class Router
}
/**
* Return the Response built by the Handler based on the Request
* Return the response built by the handler based on the request
*
* @param Request $request
* @return Response
* @param RequestInterface $request
* @return ResponseInterface
*/
public function getResponse($request = null)
public function getResponse(RequestInterface $request = null)
{
if (is_null($request)) {
$request = Request::getRequest();
}
$path = $request->path;
$path = $request->getPath();
foreach ($this->routes as $route) {
/** @var RouteInterface $route */
if (preg_match($route->getPattern(), $path, $matches)) {
$handlerClassName = $route->getHandler();
if (is_subclass_of($handlerClassName, '\pjdietz\WellRESTed\HandlerInterface')) {
if (is_subclass_of($handlerClassName, self::HANDLER_INTERFACE)) {
/** @var HandlerInterface $handler */
$handler = new $handlerClassName();
$handler->setRequest($request);
$handler->setArguments($matches);
$handler->setRouter($this);
return $handler->getResponse();
} else {
return $this->getNoRouteResponse($request);
@ -77,15 +79,15 @@ class Router
}
/**
* Prepare a Resonse indicating a 404 Not Found error
* Prepare a resonse indicating a 404 Not Found error
*
* @param Request $request
* @return Response
* @param RequestInterface $request
* @return ResponseInterface
*/
protected function getNoRouteResponse(Request $request)
protected function getNoRouteResponse(RequestInterface $request)
{
$response = new Response(404);
$response->body = 'No resource at ' . $request->uri;
$response->body = 'No resource at ' . $request->getPath();
return $response;
}