From 5dacb232ec775ed77c4ab9817df9335b4b1c94de Mon Sep 17 00:00:00 2001 From: PJ Dietz Date: Sat, 21 Feb 2015 14:11:53 -0500 Subject: [PATCH] Updates to Router - Add Router::add method - Refactor Router to contain one RouteTable --- src/pjdietz/WellRESTed/Router.php | 81 +++++++------ .../WellRESTed/Routes/RouteFactory.php | 5 +- test/RouterTest.php | 108 ++++++++---------- 3 files changed, 98 insertions(+), 96 deletions(-) diff --git a/src/pjdietz/WellRESTed/Router.php b/src/pjdietz/WellRESTed/Router.php index 6320930..0d0a9d9 100644 --- a/src/pjdietz/WellRESTed/Router.php +++ b/src/pjdietz/WellRESTed/Router.php @@ -15,6 +15,7 @@ use pjdietz\WellRESTed\Interfaces\HandlerInterface; use pjdietz\WellRESTed\Interfaces\RequestInterface; use pjdietz\WellRESTed\Interfaces\ResponseInterface; use pjdietz\WellRESTed\Routes\PrefixRoute; +use pjdietz\WellRESTed\Routes\RouteFactory; use pjdietz\WellRESTed\Routes\StaticRoute; /** @@ -26,26 +27,64 @@ class Router implements HandlerInterface { /** @var array Hash array of status code => error handler */ private $errorHandlers; - /** @var array Hash array HTTP verb => RouteTable */ - private $routeTables; + /** @var RouteTable */ + private $routeTable; /** Create a new Router. */ public function __construct() { $this->errorHandlers = array(); - $this->routeTables = array(); + $this->routeTable = new RouteTable(); + } + + /** + * Add a route or series of routes to the Router. + * + * When adding a single route, the first argument should be the path, path prefix, URI template, or regex pattern. + * The method will attempt to find the best type of route based on this argument and send the remainding arguments + * to that routes constructor. @see {RouteFactory::createRoute} + * + * To add multiple routes, pass arrays to add where each array contains an argument list. + */ + public function add() + { + $factory = new RouteFactory(); + + $args = func_get_args(); + if (count($args) > 1 && is_array($args[0])) { + foreach ($args as $argumentList) { + $route = call_user_func_array(array($factory, "createRoute"), $argumentList); + $this->addRoute($route); + } + return; + } + + $route = call_user_func_array(array($factory, "createRoute"), $args); + $this->addRoute($route); + } + + /** + * Append a series of routes. + * + * @param array $routes List array of routes + */ + public function addRoutes(array $routes) + { + foreach ($routes as $route) { + if ($route instanceof HandlerInterface) { + $this->addRoute($route); + } + } } /** * Append a new route to the route table. * * @param HandlerInterface $route - * @param string $method HTTP Method; * for any */ - public function addRoute(HandlerInterface $route, $method = "*") + public function addRoute(HandlerInterface $route) { - $table = $this->getRouteTable($method); - $table->addRoute($route); + $this->routeTable->addRoute($route); } /** @@ -96,7 +135,7 @@ class Router implements HandlerInterface */ public function getResponse(RequestInterface $request, array $args = null) { - $response = $this->getResponseFromRouteTables($request, $args); + $response = $this->tryResponse($this->routeTable, $request, $args); if ($response) { // Check if the router has an error handler for this status code. $status = $response->getStatusCode(); @@ -107,7 +146,7 @@ class Router implements HandlerInterface } return $response; } - + /** * Prepare a response indicating a 404 Not Found error * @@ -126,30 +165,6 @@ class Router implements HandlerInterface return $response; } - private function getRouteTable($method = "*") - { - if (!isset($this->routeTables[$method])) { - $this->routeTables[$method] = new RouteTable(); - } - return $this->routeTables[$method]; - } - - private function getResponseFromRouteTables(RequestInterface $request, array $args = null) - { - $method = $request->getMethod(); - if (isset($this->routeTables[$method])) { - $table = $this->routeTables[$method]; - return $this->tryResponse($table, $request, $args); - } - - if (isset($this->routeTables["*"])) { - $table = $this->routeTables["*"]; - return $this->tryResponse($table, $request, $args); - } - - return null; - } - private function getErrorResponse($status, $request, $args = null, $response = null) { if (isset($this->errorHandlers[$status])) { diff --git a/src/pjdietz/WellRESTed/Routes/RouteFactory.php b/src/pjdietz/WellRESTed/Routes/RouteFactory.php index 84c007b..222d949 100644 --- a/src/pjdietz/WellRESTed/Routes/RouteFactory.php +++ b/src/pjdietz/WellRESTed/Routes/RouteFactory.php @@ -10,6 +10,7 @@ namespace pjdietz\WellRESTed\Routes; +use pjdietz\WellRESTed\Interfaces\HandlerInterface; use ReflectionClass; /** @@ -17,6 +18,9 @@ use ReflectionClass; */ class RouteFactory { + /** + * @return HandlerInterface + */ public function createRoute() { $args = func_get_args(); @@ -51,6 +55,5 @@ class RouteFactory // Regex $reflector = new ReflectionClass("\\pjdietz\\WellRESTed\\Routes\\RegexRoute"); return $reflector->newInstanceArgs($args); - } } diff --git a/test/RouterTest.php b/test/RouterTest.php index 26dd624..8ccead6 100644 --- a/test/RouterTest.php +++ b/test/RouterTest.php @@ -4,6 +4,7 @@ namespace pjdietz\WellRESTed\Test; use pjdietz\WellRESTed\Exceptions\HttpExceptions\HttpException; use pjdietz\WellRESTed\Router; +use pjdietz\WellRESTed\Routes\TemplateRoute; use Prophecy\Argument; /** @@ -26,82 +27,65 @@ class RouterTest extends \PHPUnit_Framework_TestCase $this->handler = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface"); } - /** - * @dataProvider httpMethodProvider - */ - public function testAddsRouteToDefaultRouteTable($method) + public function testAddsSingleRoute() { - $this->request->getPath()->willReturn("/"); - $this->request->getMethod()->willReturn($method); - $this->route->getResponse(Argument::cetera())->willReturn($this->response->reveal()); + $this->request->getPath()->willReturn("/cats/"); + $this->handler->getResponse(Argument::cetera())->willReturn($this->response->reveal()); $router = new Router(); - $router->addRoute($this->route->reveal()); + $router->add("/cats/", $this->handler->reveal()); $response = $router->getResponse($this->request->reveal()); $this->assertNotNull($response); } - public function httpMethodProvider() - { - return [ - ["GET"], - ["POST"], - ["PUT"], - ["DELETE"], - ["HEAD"], - ["PATCH"], - ["OPTIONS"], - ["CUSTOM"] - ]; - } - /** - * @dataProvider httpMethodListProvider + * @dataProvider pathProvider */ - public function testAddsRouteToSpecificRouteTable($registerMethod, $requestMethod, $expectedResponse) + public function testAddsMultpleRoutes($path, $exptectedSuccess) { - $this->request->getPath()->willReturn("/"); - $this->request->getMethod()->willReturn($requestMethod); - $this->route->getResponse(Argument::cetera())->willReturn($this->response->reveal()); - - $router = new Router(); - $router->addRoute($this->route->reveal(), $registerMethod); - $response = $router->getResponse($this->request->reveal()); - - $this->assertEquals($expectedResponse, !is_null($response)); - } - - public function httpMethodListProvider() - { - return [ - ["GET", "GET", true], - ["POST", "GET", false] - ]; - } - - /** - * @dataProvider httpMethodProvider - */ - public function testMatchSpecificTableBeforeDefaultTable($method) - { - $this->request->getMethod()->willReturn("POST"); + $this->request->getPath()->willReturn($path); $this->handler->getResponse(Argument::cetera())->willReturn($this->response->reveal()); - $genericRoute = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface"); - $genericRoute->getResponse()->willReturn(null); - - $specificRoute = $this->prophesize("\\pjdietz\\WellRESTed\\Interfaces\\HandlerInterface"); - $specificRoute->getResponse(Argument::cetera())->willReturn(null); - $router = new Router(); - $router->addRoute($genericRoute->reveal()); - $router->addRoute($specificRoute->reveal(), "POST"); - $router->getResponse($this->request->reveal()); - - $genericRoute->getResponse(Argument::cetera())->shouldNotHaveBeenCalled(); - $specificRoute->getResponse(Argument::cetera())->shouldHaveBeenCalled(); + $router->add( + ["/cats/", $this->handler->reveal()], + ["/cats/*", $this->handler->reveal()], + ["/dogs/{name}", $this->handler->reveal(), TemplateRoute::RE_ALPHA], + ["~/hamsters/[a-z]+~", $this->handler->reveal()] + ); + $response = $router->getResponse($this->request->reveal()); + $this->assertEquals($exptectedSuccess, !is_null($response)); } - + + public function pathProvider() + { + return [ + ["/cats/", true], + ["/cats/molly", true], + ["/dogs/bear", true], + ["/hamsters/fizzgig", true], + ["/dogs/", false], + ["/birds/", false], + ["/hamsters/23", false] + ]; + } + + public function testAddsSingleRouteInstance() + { + $router = new Router(); + $router->addRoute($this->route->reveal()); + $router->getResponse($this->request->reveal()); + $this->route->getResponse(Argument::cetera())->shouldHaveBeenCalled(); + } + + public function testAddsMultipleRouteInstances() + { + $router = new Router(); + $router->addRoutes([$this->route->reveal()]); + $router->getResponse($this->request->reveal()); + $this->route->getResponse(Argument::cetera())->shouldHaveBeenCalled(); + } + public function testRespondsWithErrorResponseForHttpException() { $this->route->getResponse(Argument::cetera())->willThrow(new HttpException());