From c689e9df1bde06395d9a82bc0819a3d566790640 Mon Sep 17 00:00:00 2001 From: Jared King Date: Fri, 4 Mar 2016 16:15:07 -0600 Subject: [PATCH] remove route resolution logic from router + added dispatch() method that gets passed on to the FastRoute dispatcher --- src/Router.php | 110 +------------- tests/RouterTest.php | 344 +++++++++---------------------------------- 2 files changed, 79 insertions(+), 375 deletions(-) diff --git a/src/Router.php b/src/Router.php index 910546d..0374e5d 100644 --- a/src/Router.php +++ b/src/Router.php @@ -12,7 +12,6 @@ use FastRoute\Dispatcher; use FastRoute\RouteCollector; -use Pimple\Container; class Router { @@ -33,9 +32,6 @@ class Router public function __construct(array $routes = [], array $settings = []) { $this->settings = array_replace([ - 'namespace' => '', - 'defaultController' => '', - 'defaultAction' => 'index', 'cacheFile' => null, ], $settings); @@ -191,109 +187,15 @@ public function buildRoutes(RouteCollector $r) } /** - * Routes a request and resopnse to the appropriate controller. + * Dispatches a request using the routing table. * - * @param \Pimple\Container $app DI container - * @param Request $req - * @param Response $res + * @param string $method + * @param string $path * - * @return bool was a route match made? + * @return array route [result, handler, parameters] */ - public function route(Container $app, Request $req, Response $res) + public function dispatch($method, $path) { - $dispatcher = $this->getDispatcher(); - - // the dispatcher returns an array in the format: - // [result, handler, parameters] - $routeInfo = $dispatcher->dispatch($req->method(), $req->path()); - - // 404 Not Found - if ($routeInfo[0] === Dispatcher::NOT_FOUND) { - $res->setCode(404); - - return false; - } - - // 405 Method Not Allowed - if ($routeInfo[0] === Dispatcher::METHOD_NOT_ALLOWED) { - // $allowedMethods = $routeInfo[1]; - $res->setCode(405); - - return false; - } - - // the route was found - // set any parameters that come from matching the route - $req->setParams($routeInfo[2]); - - return $this->performRoute($routeInfo[1], $app, $req, $res); - } - - ////////////////////////// - // PRIVATE METHODS - ////////////////////////// - - /** - * Executes a route handler. - * - * @param array|string $route array('controller','method') or array('controller') - * or 'method' - * @param \Pimple\Container DI container - * @param Request $req - * @param Response $res - * - * @return bool - */ - private function performRoute($route, Container $app, $req, $res) - { - $result = false; - - if (is_array($route) || is_string($route)) { - // method name and controller supplied - if (is_string($route) && $req->params('controller')) { - $route = [$req->params('controller'), $route]; - } - // method name supplied - elseif (is_string($route)) { - $route = [$this->settings['defaultController'], $route]; - } - // no method name? fallback to the index() method - elseif (count($route) == 1) { - $route[] = $this->settings['defaultAction']; - } - - list($controller, $method) = $route; - - $controller = $this->settings['namespace'].'\\'.$controller; - - if (!class_exists($controller)) { - $res->setCode(404); - - return false; - } - - $controllerObj = new $controller(); - - // give the controller access to the DI container - if (method_exists($controllerObj, 'injectApp')) { - $controllerObj->injectApp($app); - } - - // collect any preset route parameters - if (isset($route[2])) { - $params = $route[2]; - $req->setParams($params); - } - - $result = $controllerObj->$method($req, $res); - } elseif (is_callable($route)) { - $result = call_user_func($route, $req, $res); - } - - if ($result instanceof View) { - $res->render($result); - } - - return true; + return $this->getDispatcher()->dispatch($method, $path); } } diff --git a/tests/RouterTest.php b/tests/RouterTest.php index 4626fa8..2d5445a 100644 --- a/tests/RouterTest.php +++ b/tests/RouterTest.php @@ -8,21 +8,13 @@ * @copyright 2015 Jared King * @license MIT */ -use Infuse\Request; -use Infuse\Response; use Infuse\Router; -use Infuse\View; use Pimple\Container; class RouterTest extends PHPUnit_Framework_TestCase { public static $app; - public static $config = [ - 'namespace' => '', - 'defaultController' => 'MockController', - ]; - public static function setUpBeforeClass() { self::$app = new Container(); @@ -30,246 +22,6 @@ public static function setUpBeforeClass() @unlink(__DIR__.'/routes.cache'); } - public function setUp() - { - MockController::$staticRouteCalled = false; - MockController::$dynamicRouteCalled = false; - MockController::$dynamicRouteParams = []; - MockController::$indexRouteCalled = false; - MockController::$appInjected = false; - } - - public function testStaticDispatcher() - { - $routes = ['get /test' => 'index']; - $settings = []; - - $router = new Router($routes, $settings); - $this->assertInstanceOf('FastRoute\Dispatcher\GroupCountBased', $router->getDispatcher()); - } - - public function testCachedDispatcher() - { - $routes = ['get /test' => 'index']; - $settings = ['cacheFile' => __DIR__.'/routes.cache']; - - $router = new Router($routes, $settings); - $this->assertInstanceOf('FastRoute\Dispatcher\GroupCountBased', $router->getDispatcher()); - $this->assertTrue(file_exists(__DIR__.'/routes.cache')); - } - - public function testStaticRoute() - { - $routes = [ - 'get /this/is/a' => ['MockController', 'fail'], - 'get /this/is/a/test/route' => ['MockController', 'fail'], - 'post /this/is/a/test/route/{test}' => ['MockController', 'fail'], - 'post /this/is/a/test/route' => ['MockController', 'staticRoute'], - 'delete /this/is/a/test/route' => ['MockController', 'fail'], - 'get /this/is/a/test/route/' => ['MockController', 'fail'], - ]; - - $router = new Router($routes, self::$config); - - $req = Request::create('/this/is/a/test/route', 'POST'); - - $res = new Response(); - - $this->assertTrue($router->route(self::$app, $req, $res)); - - $this->assertTrue(MockController::$staticRouteCalled); - $this->assertTrue(MockController::$appInjected); - } - - public function testDynamicRoute() - { - $routes = [ - 'get /this/is/a' => 'fail', - 'get /this/is/a/test/route' => 'fail', - 'post /{a1}/{a2}/{a3}/{a4}/{a5}' => 'fail', - 'put /dynamic/{a1}/{a2}/{a3}/{a4}' => 'dynamicRoute', - 'delete /this/is/a/test/route' => 'fail', - 'get /this/is/a/test/route/' => 'fail', - ]; - - $router = new Router($routes, self::$config); - - $req = Request::create('/dynamic/1/2/3/4', 'PUT'); - - $res = new Response(); - - $this->assertTrue($router->route(self::$app, $req, $res)); - - $this->assertTrue(MockController::$dynamicRouteCalled); - $this->assertTrue(MockController::$appInjected); - - // test route params - $expected = ['a1' => 1, 'a2' => 2, 'a3' => 3, 'a4' => 4]; - $this->assertEquals(MockController::$dynamicRouteParams, $expected); - } - - public function testSingleAction() - { - $routes = [ - 'get /this/is/a/test/route' => 'fail', - 'post /this/is/a/test/route/{test}' => 'fail', - 'post /this/is/a/test/route' => 'staticRoute', - 'delete /this/is/a/test/route' => 'fail', - 'post /this/is/a/test/route/' => 'fail', - ]; - - $router = new Router($routes, self::$config); - - $req = Request::create('/this/is/a/test/route', 'POST'); - - $res = new Response(); - - $this->assertTrue($router->route(self::$app, $req, $res)); - - $this->assertTrue(MockController::$staticRouteCalled); - $this->assertTrue(MockController::$appInjected); - } - - public function testIndex() - { - // testing to see if index is appended when a method is not specified - $routes = [ - 'get /this/is/a' => ['MockController', 'fail'], - 'get /this/is/a/test/route' => ['MockController', 'fail'], - 'post /this/is/a/test/route/{test}' => ['MockController', 'fail'], - 'post /this/is/a/test/route' => ['MockController'], - 'delete /this/is/a/test/route' => ['MockController', 'fail'], - 'post /this/is/a/test/route/' => ['MockController', 'fail'], - ]; - - $router = new Router($routes, self::$config); - - $req = Request::create('/this/is/a/test/route', 'POST'); - - $res = new Response(); - - $this->assertTrue($router->route(self::$app, $req, $res)); - - $this->assertTrue(MockController::$indexRouteCalled); - $this->assertTrue(MockController::$appInjected); - } - - public function testView() - { - $router = new Router([], self::$config); - $router->map('GET', '/view', ['MockController', 'view']); - - $view = new View('test'); - MockController::$view = $view; - - $req = Request::create('/view'); - - $res = Mockery::mock('Infuse\Response'); - $res->shouldReceive('render') - ->withArgs([$view]) - ->once(); - - $this->assertTrue($router->route(self::$app, $req, $res)); - } - - public function testNotFound() - { - $router = new Router([], self::$config); - - $req = Request::create('/this/is/a/test/route', 'POST'); - - $res = new Response(); - - $this->assertFalse($router->route(self::$app, $req, $res)); - $this->assertEquals(404, $res->getCode()); - } - - public function testNonExistentController() - { - $router = new Router([], self::$config); - $router->map('POST', '/this/is/a/test/route', ['BogusController', 'who_cares']); - - $req = Request::create('/this/is/a/test/route', 'POST'); - - $res = new Response(); - - $this->assertFalse($router->route(self::$app, $req, $res)); - $this->assertEquals(404, $res->getCode()); - } - - public function testWrongMethod() - { - $router = new Router([], self::$config); - $router->map('GET', '/this/is/a/test/route', 'handler'); - - $req = Request::create('/this/is/a/test/route', 'POST'); - - $res = new Response(); - - $this->assertFalse($router->route(self::$app, $req, $res)); - $this->assertEquals(405, $res->getCode()); - } - - public function testRouterControllerParam() - { - $routes = [ - 'post /this/is/a/test/route' => 'staticRoute', - 'get /not/it' => 'fail', - ]; - - $router = new Router($routes, ['defaultController' => 'BogusController']); - - $req = Request::create('/this/is/a/test/route', 'POST'); - $req->setParams(['controller' => 'MockController']); - - $res = new Response(); - - $this->assertTrue($router->route(self::$app, $req, $res)); - - $this->assertTrue(MockController::$staticRouteCalled); - $this->assertTrue(MockController::$appInjected); - } - - public function testClosure() - { - $test = false; - $handler = function ($req, $res) use (&$test) { - $test = true; - }; - - $router = new Router([], self::$config); - $router->map('GET', '/test', $handler); - - $req = Request::create('/test'); - - $res = new Response(); - - $this->assertTrue($router->route(self::$app, $req, $res)); - - $this->assertTrue($test); - } - - public function testPresetParameters() - { - $extraParams = [ - 'test' => true, - 'hello' => 'world', - ]; - - $router = new Router([], self::$config); - $router->map('GET', '/test', ['MockController', 'staticRoute', $extraParams]); - - $req = Request::create('/test'); - - $res = new Response(); - - $this->assertTrue($router->route(self::$app, $req, $res)); - - $this->assertTrue(MockController::$staticRouteCalled); - $this->assertTrue(MockController::$appInjected); - $this->assertEquals($extraParams, $req->params()); - } - public function testGet() { $router = new Router(); @@ -339,45 +91,95 @@ public function testMap() $this->assertEquals([['GET', '/users/{id}', $handler]], $router->getRoutes()); } -} -class MockController -{ - public static $staticRouteCalled = false; - public static $dynamicRouteCalled = false; - public static $dynamicRouteParams = []; - public static $indexRouteCalled = false; - public static $appInjected = false; - public static $view; - - public function injectApp($app) + public function testStaticDispatcher() { - self::$appInjected = true; + $routes = ['get /test' => 'index']; + $settings = []; + + $router = new Router($routes, $settings); + $this->assertInstanceOf('FastRoute\Dispatcher\GroupCountBased', $router->getDispatcher()); } - public function staticRoute($req, $res) + public function testCachedDispatcher() { - self::$staticRouteCalled = true; + $routes = ['get /test' => 'index']; + $settings = ['cacheFile' => __DIR__.'/routes.cache']; + + $router = new Router($routes, $settings); + $this->assertInstanceOf('FastRoute\Dispatcher\GroupCountBased', $router->getDispatcher()); + $this->assertTrue(file_exists(__DIR__.'/routes.cache')); } - public function dynamicRoute($req, $res) + public function testDispatchDynamicRoute() { - self::$dynamicRouteCalled = true; - self::$dynamicRouteParams = $req->params(); + $routes = [ + 'get /this/is/a' => 'fail', + 'get /this/is/a/test/route' => 'fail', + 'post /{a1}/{a2}/{a3}/{a4}/{a5}' => 'fail', + 'put /dynamic/{a1}/{a2}/{a3}/{a4}' => 'dynamicRoute', + 'delete /this/is/a/test/route' => 'fail', + 'get /this/is/a/test/route/' => 'fail', + ]; + + $router = new Router($routes); + + $expected = [ + 1, + 'dynamicRoute', + [ + 'a1' => '1', + 'a2' => '2', + 'a3' => '3', + 'a4' => '4', + ], + ]; + + $this->assertEquals($expected, $router->dispatch('PUT', '/dynamic/1/2/3/4')); } - public function index($req, $res) + public function testDispatchStaticRoute() { - self::$indexRouteCalled = true; + $routes = [ + 'get /this/is/a/test/route' => 'fail', + 'post /this/is/a/test/route/{test}' => 'fail', + 'post /this/is/a/test/route' => 'staticRoute', + 'delete /this/is/a/test/route' => 'fail', + 'post /this/is/a/test/route/' => 'fail', + ]; + + $router = new Router($routes); + + $expected = [ + 1, + 'staticRoute', + [], + ]; + + $this->assertEquals($expected, $router->dispatch('POST', '/this/is/a/test/route')); } - public function view($req, $res) + public function testDispatchNotFound() { - return self::$view; + $router = new Router([]); + + $expected = [ + 0, + ]; + + $this->assertEquals($expected, $router->dispatch('POST', '/this/is/a/test/route')); } - public function fail($req, $res) + public function testDispatchMethodNotAllowed() { - // FAIL + $router = new Router([]); + $router->map('GET', '/this/is/a/test/route', 'handler'); + + $expected = [ + 2, + ['GET'], + ]; + + $this->assertEquals($expected, $router->dispatch('POST', '/this/is/a/test/route')); } }