diff --git a/Handler.inc.php b/Handler.inc.php index 1ea819f..f833213 100644 --- a/Handler.inc.php +++ b/Handler.inc.php @@ -8,6 +8,8 @@ require_once(dirname(__FILE__) . '/Response.inc.php'); /******************************************************************************* * Handler * + * A Handler issues a response for a given resource. + * * @package WellRESTed * ******************************************************************************/ @@ -33,22 +35,22 @@ class Handler { * Matches array from the preg_match() call used to find this Handler. * @var array */ - protected $matches; + protected $args; /** * Create a new Handler for a specific 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; - if (is_null($matches)) { - $matches = array(); + if (is_null($args)) { + $args = array(); } - $this->matches = $matches; + $this->args = $args; $this->response = new Response(); $this->buildResponse(); @@ -64,11 +66,14 @@ class Handler { case 'response': return $this->getResponse(); default: - throw new Exception('Property ' . $name . ' does not exist.'); + throw new \Exception('Property ' . $name . ' does not exist.'); } } // __get() + /** + * @return Response + */ public function getResponse() { return $this->response; } @@ -160,6 +165,13 @@ class Handler { $this->response->statusCode = 405; } + /** + * Method for handling HTTP DELETE requests. + */ + protected function delete() { + $this->response->statusCode = 405; + } + /** * Method for handling HTTP PATCH requests. */ diff --git a/Request.inc.php b/Request.inc.php index 9a6a26c..b5895ee 100644 --- a/Request.inc.php +++ b/Request.inc.php @@ -89,7 +89,7 @@ namespace wellrested; case 'query': return $this->getQuery(); default: - throw new Exception('Property ' . $name . ' does not exist.'); + throw new \Exception('Property ' . $name . ' does not exist.'); } } // __get() @@ -118,13 +118,12 @@ namespace wellrested; return $this->query; } - // ------------------------------------------------------------------------- /** * 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"); diff --git a/Response.inc.php b/Response.inc.php index d52964c..d76c02e 100644 --- a/Response.inc.php +++ b/Response.inc.php @@ -62,7 +62,7 @@ class Response { case 'headers': return $this->getHeaders(); default: - throw new Exception('Property ' . $name . ' does not exist.'); + throw new \Exception('Property ' . $name . ' does not exist.'); } } // __get() @@ -71,9 +71,10 @@ class Response { switch ($name) { case 'body': - return $this->setBody($value); + $this->setBody($value); + break; 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() @@ -92,7 +93,7 @@ class Response { * of the new body string. * * @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) { @@ -118,7 +119,7 @@ class Response { * Return if the response contains a header with the given key. * * @param string $header - * @param bool + * @return bool */ public function hasHeader($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. * * @param string $header - * @return string|false + * @return string|bool */ public function getHeader($header) { @@ -164,6 +165,7 @@ class Response { public function respond($headersOnly = false) { // Output the HTTP status code. + // TODO: Available in 5.4+. PHP.net has a good alternative in the comments. http_response_code($this->statusCode); // Output each header. diff --git a/Route.inc.php b/Route.inc.php index 70dca99..de6256a 100644 --- a/Route.inc.php +++ b/Route.inc.php @@ -16,14 +16,38 @@ class Route { const RE_ALPHA = '[a-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; + /** + * Regular expression used to match a Request URI path component + * @var string + */ 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->handler = $handler; @@ -31,42 +55,67 @@ class Route { } // __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 = ''; + // 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] === '/') { $parts = explode('/', substr($uriTemplate, 1)); } else { $parts = explode('/', $uriTemplate); } - $expressionPattern = '/{([a-zA-Z]+)}/'; - foreach ($parts as $part) { $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) { + // Locate the name for the variable from the template. $variableName = $matches[1]; - if (isset($groups[$variableName])) { - $variablePattern = $groups[$variableName]; + // If the caller passed an array with this variable name + // 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 { - + // This part is a literal. $pattern .= $part; - } } @@ -75,7 +124,6 @@ class Route { $klass = __CLASS__; $route = new $klass($pattern, $handler, $handlerPath); - $route->uriTemplate = $uriTemplate; return $route; } // newFromUriTemplate() diff --git a/Router.inc.php b/Router.inc.php index 83b7fd8..19d2b0f 100644 --- a/Router.inc.php +++ b/Router.inc.php @@ -8,6 +8,9 @@ require_once(dirname(__FILE__) . '/Route.inc.php'); /******************************************************************************* * Router * + * A Router uses a table of Routes to find the appropriate Handler for a + * request. + * * @package WellRESTed * ******************************************************************************/ @@ -16,53 +19,45 @@ class Router { protected $routes; - public $handlerPathPattern = '%s.inc.php'; - + /** + * Create a new Router. + */ public function __construct() { $this->routes = array(); } - protected function getHandlerPath($handler) { - return sprintf($this->handlerPathPattern, $handler); - } - - public function addRoute($pattern, $handler, $handlerPath=null) { - - if (is_null($handlerPath)) { - $handlerPath = $this->getHandlerPath($handler); - } - - $this->routes[] = new Route($pattern, $handler, $handlerPath); - + /** + * Append a new Route instance to the Router's route table. + * @param $route + */ + public function addRoute(Route $route) { + $this->routes[] = $route; } // addRoute() - public function addUriTemplate($uriTemplate, $handler, $handlerPath=null, $variables=null) { + /** + * @param string $requestPath + * @return Handler + */ + public function getRequestHandler($requestPath=null) { - if (is_null($handlerPath)) { - $handlerPath = $this->getHandlerPath($handler); - } - - $this->routes[] = Route::newFromUriTemplate($uriTemplate, $handler, $handlerPath, $variables); - - } // addUriTemplate() - - public function getRequestHandler($request=null) { - - if (is_null($request)) { + if (is_null($requestPath)) { $request = Request::getRequest(); + $path = $request->path; + } else { + $path = $requestPath; } - $path = $request->path; - foreach ($this->routes as $route) { if (preg_match($route->pattern, $path, $matches)) { - if (!class_exists($route->handler)) { + $klass = $route->handler; + + if (!class_exists($klass)) { require_once($route->handlerPath); } - return $handler = new $route->handler($request, $matches); + return $handler = new $klass($request, $matches); }