Updates to Router

- Add Router::add method
- Refactor Router to contain one RouteTable
This commit is contained in:
PJ Dietz 2015-02-21 14:11:53 -05:00
parent b350693aca
commit 5dacb232ec
3 changed files with 98 additions and 96 deletions

View File

@ -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();
@ -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])) {

View File

@ -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);
}
}

View File

@ -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,80 +27,63 @@ 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());
$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));
}
$genericRoute->getResponse(Argument::cetera())->shouldNotHaveBeenCalled();
$specificRoute->getResponse(Argument::cetera())->shouldHaveBeenCalled();
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()