diff --git a/Slim/App.php b/Slim/App.php index 3b366e1f1..692369b86 100644 --- a/Slim/App.php +++ b/Slim/App.php @@ -17,6 +17,7 @@ use Psr\Http\Server\RequestHandlerInterface; use Slim\Factory\ServerRequestCreatorFactory; use Slim\Interfaces\CallableResolverInterface; +use Slim\Interfaces\MiddlewareDispatcherInterface; use Slim\Interfaces\RouteCollectorInterface; use Slim\Interfaces\RouteResolverInterface; use Slim\Middleware\BodyParsingMiddleware; @@ -36,28 +37,30 @@ class App extends RouteCollectorProxy implements RequestHandlerInterface public const VERSION = '4.1.0'; /** - * @var MiddlewareDispatcher + * @var RouteResolverInterface */ - protected $middlewareDispatcher; + protected $routeResolver; /** - * @var RouteResolverInterface + * @var MiddlewareDispatcherInterface */ - protected $routeResolver; + protected $middlewareDispatcher; /** - * @param ResponseFactoryInterface $responseFactory - * @param ContainerInterface|null $container - * @param CallableResolverInterface|null $callableResolver - * @param RouteCollectorInterface|null $routeCollector - * @param RouteResolverInterface|null $routeResolver + * @param ResponseFactoryInterface $responseFactory + * @param ContainerInterface|null $container + * @param CallableResolverInterface|null $callableResolver + * @param RouteCollectorInterface|null $routeCollector + * @param RouteResolverInterface|null $routeResolver + * @param MiddlewareDispatcherInterface|null $middlewareDispatcher */ public function __construct( ResponseFactoryInterface $responseFactory, ?ContainerInterface $container = null, ?CallableResolverInterface $callableResolver = null, ?RouteCollectorInterface $routeCollector = null, - ?RouteResolverInterface $routeResolver = null + ?RouteResolverInterface $routeResolver = null, + ?MiddlewareDispatcherInterface $middlewareDispatcher = null ) { parent::__construct( $responseFactory, @@ -69,7 +72,13 @@ public function __construct( $this->routeResolver = $routeResolver ?? new RouteResolver($this->routeCollector); $routeRunner = new RouteRunner($this->routeResolver, $this->routeCollector->getRouteParser()); - $this->middlewareDispatcher = new MiddlewareDispatcher($routeRunner, $this->callableResolver, $container); + if (!$middlewareDispatcher) { + $middlewareDispatcher = new MiddlewareDispatcher($routeRunner, $this->callableResolver, $container); + } else { + $middlewareDispatcher->seedMiddlewareStack($routeRunner); + } + + $this->middlewareDispatcher = $middlewareDispatcher; } /** @@ -80,6 +89,14 @@ public function getRouteResolver(): RouteResolverInterface return $this->routeResolver; } + /** + * @return MiddlewareDispatcherInterface + */ + public function getMiddlewareDispatcher(): MiddlewareDispatcherInterface + { + return $this->middlewareDispatcher; + } + /** * @param MiddlewareInterface|string|callable $middleware * @return self diff --git a/Slim/Factory/AppFactory.php b/Slim/Factory/AppFactory.php index 331c6933e..7cb6357af 100644 --- a/Slim/Factory/AppFactory.php +++ b/Slim/Factory/AppFactory.php @@ -18,6 +18,7 @@ use Slim\Factory\Psr17\Psr17FactoryProvider; use Slim\Factory\Psr17\SlimHttpPsr17Factory; use Slim\Interfaces\CallableResolverInterface; +use Slim\Interfaces\MiddlewareDispatcherInterface; use Slim\Interfaces\Psr17FactoryProviderInterface; use Slim\Interfaces\RouteCollectorInterface; use Slim\Interfaces\RouteResolverInterface; @@ -59,17 +60,23 @@ class AppFactory */ protected static $routeResolver; + /** + * @var MiddlewareDispatcherInterface|null + */ + protected static $middlewareDispatcher; + /** * @var bool */ protected static $slimHttpDecoratorsAutomaticDetectionEnabled = true; /** - * @param ResponseFactoryInterface|null $responseFactory - * @param ContainerInterface|null $container - * @param CallableResolverInterface|null $callableResolver - * @param RouteCollectorInterface|null $routeCollector - * @param RouteResolverInterface|null $routeResolver + * @param ResponseFactoryInterface|null $responseFactory + * @param ContainerInterface|null $container + * @param CallableResolverInterface|null $callableResolver + * @param RouteCollectorInterface|null $routeCollector + * @param RouteResolverInterface|null $routeResolver + * @param MiddlewareDispatcherInterface|null $middlewareDispatcher * @return App */ public static function create( @@ -77,7 +84,8 @@ public static function create( ?ContainerInterface $container = null, ?CallableResolverInterface $callableResolver = null, ?RouteCollectorInterface $routeCollector = null, - ?RouteResolverInterface $routeResolver = null + ?RouteResolverInterface $routeResolver = null, + ?MiddlewareDispatcherInterface $middlewareDispatcher = null ): App { static::$responseFactory = $responseFactory ?? static::$responseFactory; return new App( @@ -85,7 +93,8 @@ public static function create( $container ?? static::$container, $callableResolver ?? static::$callableResolver, $routeCollector ?? static::$routeCollector, - $routeResolver ?? static::$routeResolver + $routeResolver ?? static::$routeResolver, + $middlewareDispatcher ?? static::$middlewareDispatcher ); } @@ -111,7 +120,18 @@ public static function createFromContainer(ContainerInterface $container): App ? $container->get(RouteResolverInterface::class) : null; - return new App($responseFactory, $container, $callableResolver, $routeCollector, $routeResolver); + $middlewareDispatcher = $container->has(MiddlewareDispatcherInterface::class) + ? $container->get(MiddlewareDispatcherInterface::class) + : null; + + return new App( + $responseFactory, + $container, + $callableResolver, + $routeCollector, + $routeResolver, + $middlewareDispatcher + ); } /** @@ -224,6 +244,14 @@ public static function setRouteResolver(RouteResolverInterface $routeResolver): static::$routeResolver = $routeResolver; } + /** + * @param MiddlewareDispatcherInterface $middlewareDispatcher + */ + public static function setMiddlewareDispatcher(MiddlewareDispatcherInterface $middlewareDispatcher): void + { + static::$middlewareDispatcher = $middlewareDispatcher; + } + /** * @param bool $enabled */ diff --git a/Slim/Interfaces/MiddlewareDispatcherInterface.php b/Slim/Interfaces/MiddlewareDispatcherInterface.php new file mode 100644 index 000000000..78eff1485 --- /dev/null +++ b/Slim/Interfaces/MiddlewareDispatcherInterface.php @@ -0,0 +1,48 @@ +seedMiddlewareStack($kernel); @@ -54,12 +55,9 @@ public function __construct( } /** - * Seed the middleware stack with the inner request handler - * - * @param RequestHandlerInterface $kernel - * @return void + * {@inheritdoc} */ - protected function seedMiddlewareStack(RequestHandlerInterface $kernel): void + public function seedMiddlewareStack(RequestHandlerInterface $kernel): void { $this->tip = $kernel; } @@ -83,9 +81,9 @@ public function handle(ServerRequestInterface $request): ResponseInterface * added one (last in, first out). * * @param MiddlewareInterface|string|callable $middleware - * @return self + * @return MiddlewareDispatcherInterface */ - public function add($middleware): self + public function add($middleware): MiddlewareDispatcherInterface { if ($middleware instanceof MiddlewareInterface) { return $this->addMiddleware($middleware); @@ -113,9 +111,9 @@ public function add($middleware): self * added one (last in, first out). * * @param MiddlewareInterface $middleware - * @return self + * @return MiddlewareDispatcherInterface */ - public function addMiddleware(MiddlewareInterface $middleware): self + public function addMiddleware(MiddlewareInterface $middleware): MiddlewareDispatcherInterface { $next = $this->tip; $this->tip = new class($middleware, $next) implements RequestHandlerInterface diff --git a/tests/AppTest.php b/tests/AppTest.php index a9c22cfcc..c0ec80461 100644 --- a/tests/AppTest.php +++ b/tests/AppTest.php @@ -25,6 +25,7 @@ use Slim\Exception\HttpNotFoundException; use Slim\Handlers\Strategies\RequestResponseArgs; use Slim\Interfaces\CallableResolverInterface; +use Slim\Interfaces\MiddlewareDispatcherInterface; use Slim\Interfaces\RouteCollectorInterface; use Slim\Interfaces\RouteCollectorProxyInterface; use Slim\Interfaces\RouteParserInterface; @@ -118,6 +119,27 @@ public function testCreatesRouteCollectorWhenNullWithInjectedContainer() $this->assertEquals($routeCollector, $app->getRouteCollector()); } + public function testGetMiddlewareDispatcherGetsSeededAndReturnsInjectedInstance() + { + $responseFactoryProphecy = $this->prophesize(ResponseFactoryInterface::class); + + $middlewareDispatcherProphecy = $this->prophesize(MiddlewareDispatcherInterface::class); + $middlewareDispatcherProphecy + ->seedMiddlewareStack(Argument::any()) + ->shouldBeCalledOnce(); + + $app = new App( + $responseFactoryProphecy->reveal(), + null, + null, + null, + null, + $middlewareDispatcherProphecy->reveal() + ); + + $this->assertSame($middlewareDispatcherProphecy->reveal(), $app->getMiddlewareDispatcher()); + } + public function lowerCaseRequestMethodsProvider() { return [ diff --git a/tests/Factory/AppFactoryTest.php b/tests/Factory/AppFactoryTest.php index d490ff720..29211e3f7 100644 --- a/tests/Factory/AppFactoryTest.php +++ b/tests/Factory/AppFactoryTest.php @@ -26,6 +26,7 @@ use Slim\Http\Factory\DecoratedResponseFactory; use Slim\Http\Response as DecoratedResponse; use Slim\Interfaces\CallableResolverInterface; +use Slim\Interfaces\MiddlewareDispatcherInterface; use Slim\Interfaces\RouteCollectorInterface; use Slim\Interfaces\RouteParserInterface; use Slim\Interfaces\RouteResolverInterface; @@ -117,6 +118,7 @@ public function testAppIsCreatedWithInstancesFromSetters() $routeCollectorProphecy = $this->prophesize(RouteCollectorInterface::class); $routeParserProphecy = $this->prophesize(RouteParserInterface::class); $routeResolverProphecy = $this->prophesize(RouteResolverInterface::class); + $middlewareDispatcherProphecy = $this->prophesize(MiddlewareDispatcherInterface::class); $routeCollectorProphecy->getRouteParser()->willReturn($routeParserProphecy); @@ -126,6 +128,7 @@ public function testAppIsCreatedWithInstancesFromSetters() AppFactory::setCallableResolver($callableResolverProphecy->reveal()); AppFactory::setRouteCollector($routeCollectorProphecy->reveal()); AppFactory::setRouteResolver($routeResolverProphecy->reveal()); + AppFactory::setMiddlewareDispatcher($middlewareDispatcherProphecy->reveal()); $app = AppFactory::create(); @@ -153,6 +156,11 @@ public function testAppIsCreatedWithInstancesFromSetters() $routeResolverProphecy->reveal(), $app->getRouteResolver() ); + + $this->assertSame( + $middlewareDispatcherProphecy->reveal(), + $app->getMiddlewareDispatcher() + ); } public function testAppIsCreatedWithInjectedInstancesFromFunctionArguments() @@ -242,6 +250,8 @@ public function testCreateAppWithContainerUsesContainerDependenciesWhenPresent() ->willReturn($routeParserProphecy->reveal()) ->shouldBeCalledOnce(); + $middlewareDispatcherProphecy = $this->prophesize(MiddlewareDispatcherInterface::class); + $containerProphecy = $this->prophesize(ContainerInterface::class); $containerProphecy @@ -284,6 +294,16 @@ public function testCreateAppWithContainerUsesContainerDependenciesWhenPresent() ->willReturn($routeResolverProphecy->reveal()) ->shouldBeCalledOnce(); + $containerProphecy + ->has(MiddlewareDispatcherInterface::class) + ->willReturn(true) + ->shouldBeCalledOnce(); + + $containerProphecy + ->get(MiddlewareDispatcherInterface::class) + ->willReturn($middlewareDispatcherProphecy->reveal()) + ->shouldBeCalledOnce(); + AppFactory::setSlimHttpDecoratorsAutomaticDetection(false); $app = AppFactory::createFromContainer($containerProphecy->reveal()); @@ -292,5 +312,6 @@ public function testCreateAppWithContainerUsesContainerDependenciesWhenPresent() $this->assertSame($app->getCallableResolver(), $callableResolverProphecy->reveal()); $this->assertSame($app->getRouteCollector(), $routeCollectorProphecy->reveal()); $this->assertSame($app->getRouteResolver(), $routeResolverProphecy->reveal()); + $this->assertSame($app->getMiddlewareDispatcher(), $middlewareDispatcherProphecy->reveal()); } }