diff --git a/config/common.php b/config/common.php index 1cd0eab8..9f9e4e4c 100644 --- a/config/common.php +++ b/config/common.php @@ -8,5 +8,10 @@ return [ RouteCollectorInterface::class => RouteCollector::class, - CurrentRoute::class => CurrentRoute::class, + CurrentRoute::class => [ + 'reset' => function () { + $this->route = null; + $this->uri = null; + }, + ], ]; diff --git a/src/CurrentRoute.php b/src/CurrentRoute.php index 7e459f21..c0d3df5f 100644 --- a/src/CurrentRoute.php +++ b/src/CurrentRoute.php @@ -5,12 +5,18 @@ namespace Yiisoft\Router; use Psr\Http\Message\UriInterface; +use RuntimeException; /** * Holds information about current route e.g. matched last. */ final class CurrentRoute { + /** + * Current Route + * + * @var RouteParametersInterface|null + */ private ?RouteParametersInterface $route = null; /** * Current URI @@ -39,16 +45,27 @@ public function getUri(): ?UriInterface return $this->uri; } + /** + * @param RouteParametersInterface $route + */ public function setRoute(RouteParametersInterface $route): void { - $this->route = $route; + if ($this->route === null) { + $this->route = $route; + return; + } + throw new RuntimeException('Can not set route since it was already set.'); } /** - * @param UriInterface|null $uri + * @param UriInterface $uri */ - public function setUri(?UriInterface $uri): void + public function setUri(UriInterface $uri): void { - $this->uri = $uri; + if ($this->uri === null) { + $this->uri = $uri; + return; + } + throw new RuntimeException('Can not set URI since it was already set.'); } } diff --git a/src/MatchingResult.php b/src/MatchingResult.php index 822dedca..97710fdc 100644 --- a/src/MatchingResult.php +++ b/src/MatchingResult.php @@ -67,6 +67,11 @@ public function methods(): array return $this->methods; } + public function route(): Route + { + return $this->route; + } + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { if (!$this->isSuccess()) { diff --git a/src/Middleware/Router.php b/src/Middleware/Router.php index 1a69c09e..34a508a0 100644 --- a/src/Middleware/Router.php +++ b/src/Middleware/Router.php @@ -11,6 +11,7 @@ use Psr\Http\Server\RequestHandlerInterface; use Yiisoft\Http\Status; use Yiisoft\Middleware\Dispatcher\MiddlewareDispatcher; +use Yiisoft\Router\CurrentRoute; use Yiisoft\Router\UrlMatcherInterface; final class Router implements MiddlewareInterface @@ -18,18 +19,22 @@ final class Router implements MiddlewareInterface private UrlMatcherInterface $matcher; private ResponseFactoryInterface $responseFactory; private MiddlewareDispatcher $dispatcher; + private CurrentRoute $currentRoute; - public function __construct(UrlMatcherInterface $matcher, ResponseFactoryInterface $responseFactory, MiddlewareDispatcher $dispatcher) + public function __construct(UrlMatcherInterface $matcher, ResponseFactoryInterface $responseFactory, MiddlewareDispatcher $dispatcher, CurrentRoute $currentRoute) { $this->matcher = $matcher; $this->responseFactory = $responseFactory; $this->dispatcher = $dispatcher; + $this->currentRoute = $currentRoute; } public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $result = $this->matcher->match($request); + $this->currentRoute->setUri($request->getUri()); + if ($result->isMethodFailure()) { return $this->responseFactory->createResponse(Status::METHOD_NOT_ALLOWED) ->withHeader('Allow', implode(', ', $result->methods())); @@ -39,6 +44,8 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface return $handler->handle($request); } + $this->currentRoute->setRoute($result->route()); + foreach ($result->parameters() as $parameter => $value) { $request = $request->withAttribute($parameter, $value); } diff --git a/tests/Middleware/RouterTest.php b/tests/Middleware/RouterTest.php index 49e6c3e3..d7dead0f 100644 --- a/tests/Middleware/RouterTest.php +++ b/tests/Middleware/RouterTest.php @@ -12,11 +12,11 @@ use Psr\EventDispatcher\EventDispatcherInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; -use Psr\Http\Message\UriInterface; use Psr\Http\Server\RequestHandlerInterface; use Yiisoft\Http\Method; use Yiisoft\Middleware\Dispatcher\MiddlewareDispatcher; use Yiisoft\Middleware\Dispatcher\MiddlewareFactory; +use Yiisoft\Router\CurrentRoute; use Yiisoft\Router\MatchingResult; use Yiisoft\Router\Middleware\Router; use Yiisoft\Router\Route; @@ -24,7 +24,7 @@ final class RouterTest extends TestCase { - private function createRouterMiddleware(): Router + private function createRouterMiddleware(?CurrentRoute $currentRoute = null): Router { $container = $this->createMock(ContainerInterface::class); $dispatcher = new MiddlewareDispatcher( @@ -32,12 +32,12 @@ private function createRouterMiddleware(): Router $this->createMock(EventDispatcherInterface::class) ); - return new Router($this->getMatcher(), new Psr17Factory(), $dispatcher); + return new Router($this->getMatcher(), new Psr17Factory(), $dispatcher, $currentRoute ?? new CurrentRoute()); } - private function processWithRouter(ServerRequestInterface $request): ResponseInterface + private function processWithRouter(ServerRequestInterface $request, ?CurrentRoute $currentRoute = null): ResponseInterface { - return $this->createRouterMiddleware()->process($request, $this->createRequestHandler()); + return $this->createRouterMiddleware($currentRoute)->process($request, $this->createRequestHandler()); } public function testProcessSuccess(): void @@ -62,6 +62,27 @@ public function testMethodMismatchRespondWith405(): void $this->assertSame('GET, HEAD', $response->getHeaderLine('Allow')); } + public function testGetCurrentRoute(): void + { + $currentRoute = new CurrentRoute(); + $request = new ServerRequest('GET', '/'); + + $this->processWithRouter($request, $currentRoute); + + $this->assertInstanceOf(Route::class, $currentRoute->getRoute()); + $this->assertEquals('GET /', $currentRoute->getRoute()->getName()); + } + + public function testGetCurrentUri(): void + { + $currentRoute = new CurrentRoute(); + $request = new ServerRequest('GET', '/'); + + $this->processWithRouter($request, $currentRoute); + + $this->assertSame($request->getUri(), $currentRoute->getUri()); + } + private function getMatcher(): UrlMatcherInterface { $middleware = $this->createRouteMiddleware(); @@ -74,14 +95,6 @@ public function __construct($middleware) $this->middleware = $middleware; } - public function getCurrentRoute(): ?Route - { - } - - public function getCurrentUri(): ?UriInterface - { - } - /** * Emulates router with a single `GET /` route * @@ -92,7 +105,7 @@ public function getCurrentUri(): ?UriInterface public function match(ServerRequestInterface $request): MatchingResult { if ($request->getUri()->getPath() !== '/') { - return MatchingResult::fromFailure(Method::ANY); + return MatchingResult::fromFailure(Method::ALL); } if ($request->getMethod() === 'GET') {