Move some logic from Router to Route for easier subclassing and better OOP principles. Add comments and PHPDoc blocks.
This commit is contained in:
parent
37b75d69d1
commit
5af80d8b1d
|
|
@ -8,6 +8,8 @@ require_once(dirname(__FILE__) . '/Response.inc.php');
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Handler
|
* Handler
|
||||||
*
|
*
|
||||||
|
* A Handler issues a response for a given resource.
|
||||||
|
*
|
||||||
* @package WellRESTed
|
* @package WellRESTed
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
@ -33,22 +35,22 @@ class Handler {
|
||||||
* Matches array from the preg_match() call used to find this Handler.
|
* Matches array from the preg_match() call used to find this Handler.
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $matches;
|
protected $args;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new Handler for a specific request.
|
* Create a new Handler for a specific request.
|
||||||
*
|
*
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param array $matches
|
* @param array $args
|
||||||
*/
|
*/
|
||||||
public function __construct($request, $matches=null) {
|
public function __construct($request, $args=null) {
|
||||||
|
|
||||||
$this->request = $request;
|
$this->request = $request;
|
||||||
|
|
||||||
if (is_null($matches)) {
|
if (is_null($args)) {
|
||||||
$matches = array();
|
$args = array();
|
||||||
}
|
}
|
||||||
$this->matches = $matches;
|
$this->args = $args;
|
||||||
|
|
||||||
$this->response = new Response();
|
$this->response = new Response();
|
||||||
$this->buildResponse();
|
$this->buildResponse();
|
||||||
|
|
@ -64,11 +66,14 @@ class Handler {
|
||||||
case 'response':
|
case 'response':
|
||||||
return $this->getResponse();
|
return $this->getResponse();
|
||||||
default:
|
default:
|
||||||
throw new Exception('Property ' . $name . ' does not exist.');
|
throw new \Exception('Property ' . $name . ' does not exist.');
|
||||||
}
|
}
|
||||||
|
|
||||||
} // __get()
|
} // __get()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
public function getResponse() {
|
public function getResponse() {
|
||||||
return $this->response;
|
return $this->response;
|
||||||
}
|
}
|
||||||
|
|
@ -160,6 +165,13 @@ class Handler {
|
||||||
$this->response->statusCode = 405;
|
$this->response->statusCode = 405;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method for handling HTTP DELETE requests.
|
||||||
|
*/
|
||||||
|
protected function delete() {
|
||||||
|
$this->response->statusCode = 405;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method for handling HTTP PATCH requests.
|
* Method for handling HTTP PATCH requests.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ namespace wellrested;
|
||||||
case 'query':
|
case 'query':
|
||||||
return $this->getQuery();
|
return $this->getQuery();
|
||||||
default:
|
default:
|
||||||
throw new Exception('Property ' . $name . ' does not exist.');
|
throw new \Exception('Property ' . $name . ' does not exist.');
|
||||||
}
|
}
|
||||||
|
|
||||||
} // __get()
|
} // __get()
|
||||||
|
|
@ -118,13 +118,12 @@ namespace wellrested;
|
||||||
return $this->query;
|
return $this->query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set instance members based on the HTTP request sent to the server.
|
* Set instance members based on the HTTP request sent to the server.
|
||||||
*/
|
*/
|
||||||
public function readHttpRequest() {
|
protected function readHttpRequest() {
|
||||||
|
|
||||||
$this->body = file_get_contents("php://input");
|
$this->body = file_get_contents("php://input");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ class Response {
|
||||||
case 'headers':
|
case 'headers':
|
||||||
return $this->getHeaders();
|
return $this->getHeaders();
|
||||||
default:
|
default:
|
||||||
throw new Exception('Property ' . $name . ' does not exist.');
|
throw new \Exception('Property ' . $name . ' does not exist.');
|
||||||
}
|
}
|
||||||
|
|
||||||
} // __get()
|
} // __get()
|
||||||
|
|
@ -71,9 +71,10 @@ class Response {
|
||||||
|
|
||||||
switch ($name) {
|
switch ($name) {
|
||||||
case 'body':
|
case 'body':
|
||||||
return $this->setBody($value);
|
$this->setBody($value);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception('Property ' . $name . ' does not exist or is read only.');
|
throw new \Exception('Property ' . $name . ' does not exist or is read only.');
|
||||||
}
|
}
|
||||||
|
|
||||||
} // __set()
|
} // __set()
|
||||||
|
|
@ -92,7 +93,7 @@ class Response {
|
||||||
* of the new body string.
|
* of the new body string.
|
||||||
*
|
*
|
||||||
* @param string $value
|
* @param string $value
|
||||||
* @param bool $setContentLenght Automatically add a Content-length header
|
* @param bool $setContentLength Automatically add a Content-length header
|
||||||
*/
|
*/
|
||||||
public function setBody($value, $setContentLength=true) {
|
public function setBody($value, $setContentLength=true) {
|
||||||
|
|
||||||
|
|
@ -118,7 +119,7 @@ class Response {
|
||||||
* Return if the response contains a header with the given key.
|
* Return if the response contains a header with the given key.
|
||||||
*
|
*
|
||||||
* @param string $header
|
* @param string $header
|
||||||
* @param bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function hasHeader($header) {
|
public function hasHeader($header) {
|
||||||
return isset($this->headers[$header]);
|
return isset($this->headers[$header]);
|
||||||
|
|
@ -128,7 +129,7 @@ class Response {
|
||||||
* Return the value of a given header, or false if it does not exist.
|
* Return the value of a given header, or false if it does not exist.
|
||||||
*
|
*
|
||||||
* @param string $header
|
* @param string $header
|
||||||
* @return string|false
|
* @return string|bool
|
||||||
*/
|
*/
|
||||||
public function getHeader($header) {
|
public function getHeader($header) {
|
||||||
|
|
||||||
|
|
@ -164,6 +165,7 @@ class Response {
|
||||||
public function respond($headersOnly = false) {
|
public function respond($headersOnly = false) {
|
||||||
|
|
||||||
// Output the HTTP status code.
|
// Output the HTTP status code.
|
||||||
|
// TODO: Available in 5.4+. PHP.net has a good alternative in the comments.
|
||||||
http_response_code($this->statusCode);
|
http_response_code($this->statusCode);
|
||||||
|
|
||||||
// Output each header.
|
// Output each header.
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,38 @@ class Route {
|
||||||
const RE_ALPHA = '[a-zA-Z]+';
|
const RE_ALPHA = '[a-zA-Z]+';
|
||||||
const RE_ALPHANUM = '[0-9a-zA-Z]+';
|
const RE_ALPHANUM = '[0-9a-zA-Z]+';
|
||||||
|
|
||||||
|
const URI_TEMPLATE_EXPRESSION_RE = '/{([a-zA-Z]+)}/';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regular Expression to use to validate a template variable.
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
static public $defaultVariablePattern = self::RE_SLUG;
|
static public $defaultVariablePattern = self::RE_SLUG;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regular expression used to match a Request URI path component
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
public $pattern;
|
public $pattern;
|
||||||
public $handler;
|
|
||||||
public $handlerPath;
|
|
||||||
public $uriTemplate;
|
|
||||||
|
|
||||||
public function __construct($pattern, $handler, $handlerPath=null) {
|
/**
|
||||||
|
* Name of the Handler class to use
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path to the source file defing the handler class.
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $handlerPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $pattern
|
||||||
|
* @param $handler
|
||||||
|
* @param $handlerPath
|
||||||
|
*/
|
||||||
|
public function __construct($pattern, $handler, $handlerPath) {
|
||||||
|
|
||||||
$this->pattern = $pattern;
|
$this->pattern = $pattern;
|
||||||
$this->handler = $handler;
|
$this->handler = $handler;
|
||||||
|
|
@ -31,42 +55,67 @@ class Route {
|
||||||
|
|
||||||
} // __construct
|
} // __construct
|
||||||
|
|
||||||
static public function newFromUriTemplate($uriTemplate, $handler, $handlerPath=null, $variables=null) {
|
/**
|
||||||
|
* Create a new Route using a URI template to generate the pattern.
|
||||||
|
*
|
||||||
|
* @param string $uriTemplate
|
||||||
|
* @param string $handler
|
||||||
|
* @param string $handlerPath
|
||||||
|
* @param array $variables
|
||||||
|
* @throws \Exception
|
||||||
|
* @return Route
|
||||||
|
*/
|
||||||
|
static public function newFromUriTemplate($uriTemplate, $handler,
|
||||||
|
$handlerPath=null,
|
||||||
|
$variables=null) {
|
||||||
|
|
||||||
$pattern = '';
|
$pattern = '';
|
||||||
|
|
||||||
|
// REGEX for idenifying Level 1 URI Templates, like /foo/{bar}
|
||||||
|
$expressionPattern = '/{([a-zA-Z]+)}/';
|
||||||
|
|
||||||
|
// Explode the template into an array of path segments.
|
||||||
if ($uriTemplate[0] === '/') {
|
if ($uriTemplate[0] === '/') {
|
||||||
$parts = explode('/', substr($uriTemplate, 1));
|
$parts = explode('/', substr($uriTemplate, 1));
|
||||||
} else {
|
} else {
|
||||||
$parts = explode('/', $uriTemplate);
|
$parts = explode('/', $uriTemplate);
|
||||||
}
|
}
|
||||||
|
|
||||||
$expressionPattern = '/{([a-zA-Z]+)}/';
|
|
||||||
|
|
||||||
foreach ($parts as $part) {
|
foreach ($parts as $part) {
|
||||||
|
|
||||||
$pattern .= '\/';
|
$pattern .= '\/';
|
||||||
|
|
||||||
if (preg_match($expressionPattern, $part, $matches)) {
|
// Is this part an expression or a literal?
|
||||||
|
if (preg_match(self::URI_TEMPLATE_EXPRESSION_RE,
|
||||||
|
$part, $matches)) {
|
||||||
|
|
||||||
$variablePattern = self::$defaultVariablePattern;
|
// This part of the path is an expresion.
|
||||||
|
|
||||||
if (count($matches) === 2) {
|
if (count($matches) === 2) {
|
||||||
|
|
||||||
|
// Locate the name for the variable from the template.
|
||||||
$variableName = $matches[1];
|
$variableName = $matches[1];
|
||||||
|
|
||||||
if (isset($groups[$variableName])) {
|
// If the caller passed an array with this variable name
|
||||||
$variablePattern = $groups[$variableName];
|
// as a key, use its value for the pattern here.
|
||||||
|
// Otherwise, use the class's current default.
|
||||||
|
if (isset($variables[$variableName])) {
|
||||||
|
$variablePattern = $variables[$variableName];
|
||||||
|
} else {
|
||||||
|
$variablePattern = self::$defaultVariablePattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$pattern .= sprintf('(?<%s>%s)', $variableName,
|
||||||
|
$variablePattern);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Not sure why this would happen.
|
||||||
|
throw new \Exception('Invalid URI Template.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$pattern .= sprintf('(?<%s>%s)', $variableName, $variablePattern);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
// This part is a literal.
|
||||||
$pattern .= $part;
|
$pattern .= $part;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -75,7 +124,6 @@ class Route {
|
||||||
|
|
||||||
$klass = __CLASS__;
|
$klass = __CLASS__;
|
||||||
$route = new $klass($pattern, $handler, $handlerPath);
|
$route = new $klass($pattern, $handler, $handlerPath);
|
||||||
$route->uriTemplate = $uriTemplate;
|
|
||||||
return $route;
|
return $route;
|
||||||
|
|
||||||
} // newFromUriTemplate()
|
} // newFromUriTemplate()
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@ require_once(dirname(__FILE__) . '/Route.inc.php');
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Router
|
* Router
|
||||||
*
|
*
|
||||||
|
* A Router uses a table of Routes to find the appropriate Handler for a
|
||||||
|
* request.
|
||||||
|
*
|
||||||
* @package WellRESTed
|
* @package WellRESTed
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
@ -16,53 +19,45 @@ class Router {
|
||||||
|
|
||||||
protected $routes;
|
protected $routes;
|
||||||
|
|
||||||
public $handlerPathPattern = '%s.inc.php';
|
/**
|
||||||
|
* Create a new Router.
|
||||||
|
*/
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
$this->routes = array();
|
$this->routes = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getHandlerPath($handler) {
|
/**
|
||||||
return sprintf($this->handlerPathPattern, $handler);
|
* Append a new Route instance to the Router's route table.
|
||||||
}
|
* @param $route
|
||||||
|
*/
|
||||||
public function addRoute($pattern, $handler, $handlerPath=null) {
|
public function addRoute(Route $route) {
|
||||||
|
$this->routes[] = $route;
|
||||||
if (is_null($handlerPath)) {
|
|
||||||
$handlerPath = $this->getHandlerPath($handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->routes[] = new Route($pattern, $handler, $handlerPath);
|
|
||||||
|
|
||||||
} // addRoute()
|
} // addRoute()
|
||||||
|
|
||||||
public function addUriTemplate($uriTemplate, $handler, $handlerPath=null, $variables=null) {
|
/**
|
||||||
|
* @param string $requestPath
|
||||||
|
* @return Handler
|
||||||
|
*/
|
||||||
|
public function getRequestHandler($requestPath=null) {
|
||||||
|
|
||||||
if (is_null($handlerPath)) {
|
if (is_null($requestPath)) {
|
||||||
$handlerPath = $this->getHandlerPath($handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->routes[] = Route::newFromUriTemplate($uriTemplate, $handler, $handlerPath, $variables);
|
|
||||||
|
|
||||||
} // addUriTemplate()
|
|
||||||
|
|
||||||
public function getRequestHandler($request=null) {
|
|
||||||
|
|
||||||
if (is_null($request)) {
|
|
||||||
$request = Request::getRequest();
|
$request = Request::getRequest();
|
||||||
|
$path = $request->path;
|
||||||
|
} else {
|
||||||
|
$path = $requestPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
$path = $request->path;
|
|
||||||
|
|
||||||
foreach ($this->routes as $route) {
|
foreach ($this->routes as $route) {
|
||||||
|
|
||||||
if (preg_match($route->pattern, $path, $matches)) {
|
if (preg_match($route->pattern, $path, $matches)) {
|
||||||
|
|
||||||
if (!class_exists($route->handler)) {
|
$klass = $route->handler;
|
||||||
|
|
||||||
|
if (!class_exists($klass)) {
|
||||||
require_once($route->handlerPath);
|
require_once($route->handlerPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $handler = new $route->handler($request, $matches);
|
return $handler = new $klass($request, $matches);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue