diff --git a/src/Routing/Router.php b/src/Routing/Router.php new file mode 100644 index 0000000..5f9900d --- /dev/null +++ b/src/Routing/Router.php @@ -0,0 +1,69 @@ + error handler */ + private $statusHandlers; + /** @var RouteTable Collection of routes */ + private $routeTable; + /** @var RouteFactory */ + private $routeFactory; + + public function __construct() + { + $this->routeTable = new RouteTable(); + $this->routeFactory = new RouteFactory($this->routeTable); + $this->statusHandlers = []; + } + + /** + * Create and return a route given a string path, a handler, and optional + * extra arguments. + * + * The method will determine the most appropriate route subclass to use + * and will forward the arguments on to the subclass's constructor. + * + * - Paths with no special characters will generate StaticRoutes + * - Paths ending with * will generate PrefixRoutes + * - Paths containing URI variables (e.g., {id}) will generate TemplateRoutes + * - Regular exressions will generate RegexRoutes + * + * @param string $target Path, prefix, or pattern to match + * @param mixed $middleware Middleware to dispatch + * @param $defaultPattern @see TemplateRoute + * @param $variablePatterns @see TemplateRoute + */ + public function add($target, $middleware, $defaultPattern = null, $variablePatterns = null) + { + $this->routeFactory->registerRoute($target, $middleware, $defaultPattern, $variablePatterns); + } + + public function setStatusHandler($statusCode, $middleware) + { + $this->statusHandlers[$statusCode] = $middleware; + } + + public function dispatch(ServerRequestInterface $request, ResponseInterface &$response) + { + try { + $this->routeTable->dispatch($request, $response); + } catch (HttpException $e) { + $response = $response->withStatus($e->getCode()); + $response = $response->withBody(new StringStream($e->getMessage())); + } + $statusCode = $response->getStatusCode(); + if (isset($this->statusHandlers[$statusCode])) { + $middleware = $this->statusHandlers[$statusCode]; + $dispatcher = new Dispatcher(); + $dispatcher->dispatch($middleware, $request, $response); + } + } +} diff --git a/test/tests/unit/Routing/RouterTest.php b/test/tests/unit/Routing/RouterTest.php new file mode 100644 index 0000000..4b146bd --- /dev/null +++ b/test/tests/unit/Routing/RouterTest.php @@ -0,0 +1,76 @@ +request = $this->prophesize("\\Psr\\Http\\Message\\ServerRequestInterface"); + $this->response = $this->prophesize("\\Psr\\Http\\Message\\ResponseInterface"); + $this->response->withStatus(Argument::any())->willReturn($this->response->reveal()); + $this->response->withBody(Argument::any())->willReturn($this->response->reveal()); + $this->response->getStatusCode()->willReturn(200); + $this->middleware = $this->prophesize("\\WellRESTed\\Routing\\MiddlewareInterface"); + $this->middleware->dispatch(Argument::cetera())->willReturn(); + } + + public function testDispatchedRoute() + { + $this->request->getRequestTarget()->willReturn("/cats/"); + + $router = new Router(); + $router->add("/cats/", $this->middleware->reveal()); + $router->dispatch($this->request->reveal(), $this->response->reveal()); + + $this->middleware->dispatch(Argument::cetera())->shouldHaveBeenCalled(); + } + + public function testRespondsWithErrorResponseForHttpException() + { + $this->request->getRequestTarget()->willReturn("/cats/"); + $this->middleware->dispatch(Argument::cetera())->willThrow(new NotFoundException()); + + $router = new Router(); + $router->add("/cats/", $this->middleware->reveal()); + $router->dispatch($this->request->reveal(), $this->response->reveal()); + + $this->response->withStatus(404)->shouldHaveBeenCalled(); + } + + public function testDispatchesErrorHandlerForStatusCode() + { + $this->response->getStatusCode()->willReturn(403); + + $statusMiddleware = $this->prophesize("\\WellRESTed\\Routing\\MiddlewareInterface"); + $statusMiddleware->dispatch(Argument::cetera())->willReturn(); + + $router = new Router(); + $router->add("/cats/", $this->middleware->reveal()); + $router->setStatusHandler(403, $statusMiddleware->reveal()); + $router->dispatch($this->request->reveal(), $this->response->reveal()); + + $statusMiddleware->dispatch(Argument::cetera())->shouldHaveBeenCalled(); + } +}