diff --git a/src/Phaseolies/Support/Router.php b/src/Phaseolies/Support/Router.php index 7712f17a..75c58790 100644 --- a/src/Phaseolies/Support/Router.php +++ b/src/Phaseolies/Support/Router.php @@ -51,6 +51,44 @@ public function getGateway(): GatewayInterface return $this->gateway; } + /** + * Global middleware pushed at runtime by launchers (e.g. packages) + * + * @var array + */ + protected array $pushedGlobalMiddleware = []; + + /** + * Push a middleware onto the global chain from a launcher + * + * @param class-string $middleware + * @return void + */ + public function pushGlobalMiddleware(string $middleware): void + { + if (!in_array($middleware, $this->pushedGlobalMiddleware, true)) { + $this->pushedGlobalMiddleware[] = $middleware; + } + } + + /** + * Get the global middleware: the gateway's list followed by any pushed ones. + * + * @return array + */ + public function getGlobalMiddleware(): array + { + $global = $this->gateway->getGlobalMiddleware(); + + foreach ($this->pushedGlobalMiddleware as $middleware) { + if (!in_array($middleware, $global, true)) { + $global[] = $middleware; + } + } + + return $global; + } + /** * Holds the registered routes. * @@ -1060,7 +1098,7 @@ public function resolve(Application $app, Request $request): Response $chain->applyMiddleware($this->makeGatewayMiddleware($app, $middlewareClass)); } - foreach ($this->gateway->getGlobalMiddleware() as $middlewareClass) { + foreach ($this->getGlobalMiddleware() as $middlewareClass) { $chain->applyMiddleware($this->makeGatewayMiddleware($app, $middlewareClass)); } diff --git a/tests/Router/RouterTest.php b/tests/Router/RouterTest.php index e266d933..f2d73f57 100644 --- a/tests/Router/RouterTest.php +++ b/tests/Router/RouterTest.php @@ -887,6 +887,35 @@ public function testResolveActionWithClosure(): void $this->assertEquals('User 123: John', $result); } + public function testPushGlobalMiddlewareIsAppendedAfterGatewayList(): void + { + $this->router->pushGlobalMiddleware('Pushed\\Middleware'); + + $global = $this->router->getGlobalMiddleware(); + + $this->assertSame( + (new Gateway())->getGlobalMiddleware(), + array_slice($global, 0, count($global) - 1) + ); + $this->assertSame('Pushed\\Middleware', end($global)); + } + + public function testPushGlobalMiddlewareIsNotDuplicated(): void + { + $existing = (new Gateway())->getGlobalMiddleware()[0] ?? null; + + $this->router->pushGlobalMiddleware('Pushed\\Middleware'); + $this->router->pushGlobalMiddleware('Pushed\\Middleware'); + + if ($existing !== null) { + $this->router->pushGlobalMiddleware($existing); + } + + $global = $this->router->getGlobalMiddleware(); + + $this->assertSame($global, array_values(array_unique($global))); + } + public function testProcessRateLimitAnnotation(): void { $controller = new class {