From b66c107cd2bc0705e5ecf86028772c6504d2df34 Mon Sep 17 00:00:00 2001 From: Phil Bennett Date: Wed, 21 Jul 2021 08:29:19 +0100 Subject: [PATCH] Fix to allow multiple routes with the same path across differing conditions. Fixes #298 --- src/Dispatcher.php | 25 ++--------------- src/RouteConditionHandlerTrait.php | 20 +++++++++++++ src/Router.php | 9 +++++- tests/DispatchIntegrationTest.php | 45 +++++++++++++++++++++++------- 4 files changed, 65 insertions(+), 34 deletions(-) diff --git a/src/Dispatcher.php b/src/Dispatcher.php index 82734d9..eb651c9 100644 --- a/src/Dispatcher.php +++ b/src/Dispatcher.php @@ -16,9 +16,11 @@ class Dispatcher extends GroupCountBasedDispatcher implements MiddlewareAwareInterface, RequestHandlerInterface, + RouteConditionHandlerInterface, StrategyAwareInterface { use MiddlewareAwareTrait; + use RouteConditionHandlerTrait; use StrategyAwareTrait; public function dispatchRequest(ServerRequestInterface $request): ResponseInterface @@ -66,29 +68,6 @@ protected function ensureHandlerIsRoute($matchingHandler, $httpMethod, $uri): Ro return new Route($httpMethod, $uri, $matchingHandler); } - protected function isExtraConditionMatch(Route $route, ServerRequestInterface $request): bool - { - // check for scheme condition - $scheme = $route->getScheme(); - if ($scheme !== null && $scheme !== $request->getUri()->getScheme()) { - return false; - } - - // check for domain condition - $host = $route->getHost(); - if ($host !== null && $host !== $request->getUri()->getHost()) { - return false; - } - - // check for port condition - $port = $route->getPort(); - if ($port !== null && $port !== $request->getUri()->getPort()) { - return false; - } - - return true; - } - protected function requestWithRouteAttributes(ServerRequestInterface $request, Route $route): ServerRequestInterface { $routerParams = $route->getVars(); diff --git a/src/RouteConditionHandlerTrait.php b/src/RouteConditionHandlerTrait.php index b92e42f..b12635b 100644 --- a/src/RouteConditionHandlerTrait.php +++ b/src/RouteConditionHandlerTrait.php @@ -4,6 +4,7 @@ namespace League\Route; +use Psr\Http\Message\ServerRequestInterface; use RuntimeException; trait RouteConditionHandlerTrait @@ -84,4 +85,23 @@ private function checkAndReturnSelf(): RouteConditionHandlerInterface RouteConditionHandlerInterface::class )); } + + protected function isExtraConditionMatch(Route $route, ServerRequestInterface $request): bool + { + // check for scheme condition + $scheme = $route->getScheme(); + if ($scheme !== null && $scheme !== $request->getUri()->getScheme()) { + return false; + } + + // check for domain condition + $host = $route->getHost(); + if ($host !== null && $host !== $request->getUri()->getHost()) { + return false; + } + + // check for port condition + $port = $route->getPort(); + return !($port !== null && $port !== $request->getUri()->getPort()); + } } diff --git a/src/Router.php b/src/Router.php index b979a22..0cabd8e 100644 --- a/src/Router.php +++ b/src/Router.php @@ -16,10 +16,12 @@ class Router implements MiddlewareAwareInterface, RouteCollectionInterface, StrategyAwareInterface, - RequestHandlerInterface + RequestHandlerInterface, + RouteConditionHandlerInterface { use MiddlewareAwareTrait; use RouteCollectionTrait; + use RouteConditionHandlerTrait; use StrategyAwareTrait; protected const IDENTIFIER_SEPARATOR = "\t"; @@ -153,6 +155,11 @@ public function prepareRoutes(ServerRequestInterface $request): void /** @var Route $route */ foreach ($routes as $route) { + // this allows for the same route to be mapped across different routes/hosts etc + if (false === $this->isExtraConditionMatch($route, $request)) { + continue; + } + if ($route->getStrategy() === null) { $route->setStrategy($this->getStrategy()); } diff --git a/tests/DispatchIntegrationTest.php b/tests/DispatchIntegrationTest.php index f7bea7a..e5eee56 100644 --- a/tests/DispatchIntegrationTest.php +++ b/tests/DispatchIntegrationTest.php @@ -917,15 +917,39 @@ public function process( public function testCanMapSameRoutePathOnDifferentConditions(): void { - $router = new Router(); + $routerOne = new Router(); + $routerTwo = new Router(); + + $responseOne = $this->getMockBuilder(ResponseInterface::class)->getMock(); + $responseTwo = $this->getMockBuilder(ResponseInterface::class)->getMock(); + $responseOne->expects(self::once())->method('withHeader')->willReturnSelf(); + $responseTwo->expects(self::once())->method('withHeader')->willReturnSelf(); + + $routerOne + ->get('/', static function (ServerRequestInterface $request) use ($responseOne): ResponseInterface { + return $responseOne->withHeader('test', 'test'); + }) + ->setHost('test1.com') + ; - $router - ->map('GET', '/', [Controller::class, 'action']) + $routerOne + ->get('/', static function (ServerRequestInterface $request) use ($responseOne): ResponseInterface { + return $responseOne->withHeader('test', 'test'); + }) + ->setHost('test2.com') + ; + + $routerTwo + ->get('/', static function (ServerRequestInterface $request) use ($responseTwo): ResponseInterface { + return $responseTwo->withHeader('test', 'test'); + }) ->setHost('test1.com') ; - $router - ->map('GET', '/', [Controller::class, 'action']) + $routerTwo + ->get('/', static function (ServerRequestInterface $request) use ($responseTwo): ResponseInterface { + return $responseTwo->withHeader('test', 'test'); + }) ->setHost('test2.com') ; @@ -937,14 +961,15 @@ public function testCanMapSameRoutePathOnDifferentConditions(): void $uriOne->method('getHost')->willReturn('test1.com'); $uriTwo->method('getHost')->willReturn('test2.com'); + $uriOne->method('getPath')->willReturn('/'); + $uriTwo->method('getPath')->willReturn('/'); $requestOne->method('getUri')->willReturn($uriOne); $requestTwo->method('getUri')->willReturn($uriTwo); + $requestOne->method('getMethod')->willReturn('GET'); + $requestTwo->method('getMethod')->willReturn('GET'); - $responseOne = $router->dispatch($requestOne); - self::assertSame($responseOne->getHeader('action'), 'true'); - - $responseTwo = $router->dispatch($requestTwo); - self::assertSame($responseTwo->getHeader('action'), 'true'); + $routerOne->dispatch($requestOne); + $routerTwo->dispatch($requestTwo); } }