From e819cde40ab0a3209fe265c45349b6431309fbb1 Mon Sep 17 00:00:00 2001 From: kafkiansky Date: Wed, 13 Dec 2023 10:36:53 +0300 Subject: [PATCH 1/6] upgrade dependencies --- .gitignore | 2 +- composer.json | 18 +++---- phpunit.xml | 49 +++++++++---------- psalm.xml | 2 + src/Attribute/Middleware.php | 13 +---- .../Reader/CacheAttributesReader.php | 13 ++--- .../Reader/ClassMethodAttributeReader.php | 7 +-- src/DependencyInjection/Configuration.php | 6 +-- .../SymiddlewareCompilerPass.php | 1 + .../SymiddlewareExtension.php | 2 +- src/Integration/ControllerListener.php | 13 ++--- src/Integration/ControllerReplacer.php | 13 ++--- src/Middleware/MiddlewareAction.php | 4 +- src/Middleware/MiddlewareGatherer.php | 19 ++++--- src/Middleware/MiddlewareRunner.php | 18 ++----- .../Registry/MiddlewareRegistry.php | 5 -- .../ServiceLocatorMiddlewareRegistry.php | 21 ++------ src/Middleware/StackMiddleware.php | 11 ++--- .../SymfonyActionRequestHandler.php | 12 ++--- ...HttpMessageBridgePsrRequestTransformer.php | 9 +--- ...ttpMessageBridgePsrResponseTransformer.php | 9 +--- 21 files changed, 84 insertions(+), 163 deletions(-) diff --git a/.gitignore b/.gitignore index 0d54553..c3c6ecd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ vendor/ composer.lock .idea/ -.phpunit.result.cache +.phpunit.cache/ diff --git a/composer.json b/composer.json index 9f65034..dd2570a 100644 --- a/composer.json +++ b/composer.json @@ -19,20 +19,20 @@ }, "require": { "php": "^8.2", - "symfony/dependency-injection": "^6.2", - "symfony/event-dispatcher": "^6.2", - "symfony/http-kernel": "^6.2", - "symfony/config": "^6.2", - "symfony/http-foundation": "^6.2", + "symfony/dependency-injection": "^7.0", + "symfony/event-dispatcher": "^7.0", + "symfony/http-kernel": "^7.0", + "symfony/config": "^7.0", + "symfony/http-foundation": "^7.0", "psr/http-server-middleware": "^1.0", "psr/http-server-handler": "^1.0", - "symfony/psr-http-message-bridge": "^2.1", + "symfony/psr-http-message-bridge": "^7.0", "nyholm/psr7": "^1.4", "symfony/cache": "^6.2" }, "require-dev": { - "phpunit/phpunit": "^9.5", - "vimeo/psalm": "^4.9" + "phpunit/phpunit": "^10.0", + "vimeo/psalm": "^5.0" }, "license": "MIT", "authors": [ @@ -44,6 +44,6 @@ "minimum-stability": "stable", "scripts": { "lint": "./vendor/bin/psalm --no-cache", - "test": "./vendor/bin/phpunit --do-not-cache-result" + "test": "./vendor/bin/phpunit --do-not-cache-result --no-coverage" } } diff --git a/phpunit.xml b/phpunit.xml index c7421e8..dbc45c2 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,29 +1,24 @@ - - - - ./tests - - - - - ./src - - ./src/Resources - ./src/DependencyInjection - ./src/SymiddlewareBundle.php - - - - - - + + + + + + + + + ./tests + + + + + + ./src + + + ./src/Resources + ./src/DependencyInjection + ./src/SymiddlewareBundle.php + + diff --git a/psalm.xml b/psalm.xml index 3240886..8822f66 100644 --- a/psalm.xml +++ b/psalm.xml @@ -5,6 +5,8 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://getpsalm.org/schema/config" xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" + findUnusedBaselineEntry="true" + findUnusedCode="false" > diff --git a/src/Attribute/Middleware.php b/src/Attribute/Middleware.php index 3f245a5..f3afe56 100644 --- a/src/Attribute/Middleware.php +++ b/src/Attribute/Middleware.php @@ -7,24 +7,13 @@ use Psr\Http\Server\MiddlewareInterface; #[\Attribute(\Attribute::TARGET_METHOD | \Attribute::TARGET_CLASS | \Attribute::IS_REPEATABLE)] -/** - * @psalm-immutable - */ final class Middleware { - /** - * @psalm-readonly - * - * @var class-string[]|string[] - */ - public array $list; - /** * @param class-string[]|string[] $list */ - public function __construct(array $list) + public function __construct(public readonly array $list) { - $this->list = $list; } /** diff --git a/src/Attribute/Reader/CacheAttributesReader.php b/src/Attribute/Reader/CacheAttributesReader.php index 028be6a..ef11836 100644 --- a/src/Attribute/Reader/CacheAttributesReader.php +++ b/src/Attribute/Reader/CacheAttributesReader.php @@ -10,13 +10,10 @@ final class CacheAttributesReader implements AttributeReader { - private CacheItemPoolInterface $cache; - private AttributeReader $delegate; - - public function __construct(CacheItemPoolInterface $cache, AttributeReader $delegate) - { - $this->cache = $cache; - $this->delegate = $delegate; + public function __construct( + private readonly CacheItemPoolInterface $cache, + private readonly AttributeReader $delegate, + ) { } /** @@ -85,7 +82,7 @@ private function normalizeToCache(array $middlewares): array private static function cacheKey(object $class, ?string $method = null): string { - $key = 'symfony.middleware.'.str_replace('\\', '', get_class($class)); + $key = 'symfony.middleware.'.str_replace('\\', '', $class::class); if ($method !== null) { $key .= '.'.$method; diff --git a/src/Attribute/Reader/ClassMethodAttributeReader.php b/src/Attribute/Reader/ClassMethodAttributeReader.php index 9ebbce4..d75a78d 100644 --- a/src/Attribute/Reader/ClassMethodAttributeReader.php +++ b/src/Attribute/Reader/ClassMethodAttributeReader.php @@ -9,12 +9,7 @@ final class ClassMethodAttributeReader implements AttributeReader { /** - * @param object $class - * @param string|null $method - * - * @throws \ReflectionException - * - * @return Middleware[] + * {@inheritdoc} */ public function read(object $class, ?string $method = null): array { diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 5e522f0..f980ed2 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -54,12 +54,12 @@ public function getConfigTreeBuilder(): TreeBuilder } /** - * @return \Closure + * @return \Closure(string): string */ private function createMiddlewareNormalizer(): callable { - return function (mixed $middlewareName): string { - if (!\is_string($middlewareName) || !\is_a($middlewareName, MiddlewareInterface::class, true)) { + return function (string $middlewareName): string { + if (!\is_a($middlewareName, MiddlewareInterface::class, true)) { throw new \RuntimeException( vsprintf('Each middleware must implements the "%s" interface, but "%s" doesn\'t.', [ MiddlewareInterface::class, diff --git a/src/DependencyInjection/SymiddlewareCompilerPass.php b/src/DependencyInjection/SymiddlewareCompilerPass.php index 4c3e3e7..ecf4d07 100644 --- a/src/DependencyInjection/SymiddlewareCompilerPass.php +++ b/src/DependencyInjection/SymiddlewareCompilerPass.php @@ -8,6 +8,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Psr\Http\Server\MiddlewareInterface; use Kafkiansky\SymfonyMiddleware\Middleware\Registry\MiddlewareRegistry; +use Symfony\Component\DependencyInjection\Loader\Configurator\ReferenceConfigurator; use function Symfony\Component\DependencyInjection\Loader\Configurator\service; use function Symfony\Component\DependencyInjection\Loader\Configurator\service_locator; diff --git a/src/DependencyInjection/SymiddlewareExtension.php b/src/DependencyInjection/SymiddlewareExtension.php index eaf0dcc..046e117 100644 --- a/src/DependencyInjection/SymiddlewareExtension.php +++ b/src/DependencyInjection/SymiddlewareExtension.php @@ -30,7 +30,7 @@ public function load(array $configs, ContainerBuilder $container): void return; } - /** @var array{groups: array} $config */ + /** @var array{groups: array, global?: string[]} $config */ $config = $this->processConfiguration($configuration, $configs); $config['groups']['global'] = [ diff --git a/src/Integration/ControllerListener.php b/src/Integration/ControllerListener.php index 9fd54ec..b01f80a 100644 --- a/src/Integration/ControllerListener.php +++ b/src/Integration/ControllerListener.php @@ -12,18 +12,11 @@ final class ControllerListener { - private MiddlewareGatherer $middlewareGatherer; - private AttributeReader $reader; - private ControllerReplacer $controllerReplacer; - public function __construct( - MiddlewareGatherer $middlewareGatherer, - AttributeReader $reader, - ControllerReplacer $controllerReplacer + private readonly MiddlewareGatherer $middlewareGatherer, + private readonly AttributeReader $reader, + private readonly ControllerReplacer $controllerReplacer, ) { - $this->middlewareGatherer = $middlewareGatherer; - $this->reader = $reader; - $this->controllerReplacer = $controllerReplacer; } /** diff --git a/src/Integration/ControllerReplacer.php b/src/Integration/ControllerReplacer.php index 216d99a..4470abe 100644 --- a/src/Integration/ControllerReplacer.php +++ b/src/Integration/ControllerReplacer.php @@ -16,18 +16,11 @@ final class ControllerReplacer { - private PsrRequestTransformer $psrRequestTransformer; - private PsrResponseTransformer $psrResponseTransformer; - private PsrRequestCloner $psrRequestCloner; - public function __construct( - PsrRequestTransformer $psrRequestTransformer, - PsrResponseTransformer $psrResponseTransformer, - PsrRequestCloner $psrRequestCloner, + private readonly PsrRequestTransformer $psrRequestTransformer, + private readonly PsrResponseTransformer $psrResponseTransformer, + private readonly PsrRequestCloner $psrRequestCloner, ) { - $this->psrRequestTransformer = $psrRequestTransformer; - $this->psrResponseTransformer = $psrResponseTransformer; - $this->psrRequestCloner = $psrRequestCloner; } /** diff --git a/src/Middleware/MiddlewareAction.php b/src/Middleware/MiddlewareAction.php index cc9582d..f5605f5 100644 --- a/src/Middleware/MiddlewareAction.php +++ b/src/Middleware/MiddlewareAction.php @@ -10,8 +10,8 @@ final class MiddlewareAction { public function __construct( - private MiddlewareRunner $middlewareRunner, - private ServerRequestInterface $serverRequest, + private readonly MiddlewareRunner $middlewareRunner, + private readonly ServerRequestInterface $serverRequest, ) { } diff --git a/src/Middleware/MiddlewareGatherer.php b/src/Middleware/MiddlewareGatherer.php index 676d32e..24704c1 100644 --- a/src/Middleware/MiddlewareGatherer.php +++ b/src/Middleware/MiddlewareGatherer.php @@ -10,11 +10,14 @@ final class MiddlewareGatherer { - private MiddlewareRegistry $middlewareRegistry; + /** + * Array-key where global middleware may be defined. + */ + private const GLOBAL_MIDDLEWARE_GROUP = 'global'; - public function __construct(MiddlewareRegistry $middlewareRegistry) - { - $this->middlewareRegistry = $middlewareRegistry; + public function __construct( + private readonly MiddlewareRegistry $middlewareRegistry, + ) { } /** @@ -26,11 +29,11 @@ public function __construct(MiddlewareRegistry $middlewareRegistry) */ public function gather(array $attributes): array { - $middlewaresOrGroups = array_unique(array_merge(...array_map(function (Middleware $middleware): array { - return $middleware->list; - }, $attributes))); + $middlewaresOrGroups = array_unique( + array_merge([], ...array_map(fn (Middleware $middleware): array => $middleware->list, $attributes)), + ); - $middlewares = $this->middlewareRegistry->byName(MiddlewareRegistry::GLOBAL_MIDDLEWARE_GROUP); + $middlewares = $this->middlewareRegistry->byName(self::GLOBAL_MIDDLEWARE_GROUP); foreach ($middlewaresOrGroups as $middlewareOrGroup) { $middlewares = array_merge($middlewares, $this->middlewareRegistry->byName($middlewareOrGroup)); diff --git a/src/Middleware/MiddlewareRunner.php b/src/Middleware/MiddlewareRunner.php index 0711820..0d7cee5 100644 --- a/src/Middleware/MiddlewareRunner.php +++ b/src/Middleware/MiddlewareRunner.php @@ -13,29 +13,19 @@ final class MiddlewareRunner { - /** - * @var MiddlewareInterface[] - */ - private array $middlewares; - private RequestHandlerInterface $requestHandler; - private PsrResponseTransformer $psrResponseTransformer; - /** * @param MiddlewareInterface[] $middlewares */ public function __construct( - array $middlewares, - RequestHandlerInterface $requestHandler, - PsrResponseTransformer $psrResponseTransformer, + private readonly array $middlewares, + private readonly RequestHandlerInterface $requestHandler, + private readonly PsrResponseTransformer $psrResponseTransformer, ) { - $this->middlewares = $middlewares; - $this->requestHandler = $requestHandler; - $this->psrResponseTransformer = $psrResponseTransformer; } public function run(ServerRequestInterface $serverRequest): Response { - /** @var \Closure(ServerRequestInterface): ResponseInterface */ + /** @psalm-var \Closure(ServerRequestInterface): ResponseInterface $processor */ $processor = array_reduce( array_reverse($this->middlewares), /** @param \Closure(ServerRequestInterface): ResponseInterface $stack */ diff --git a/src/Middleware/Registry/MiddlewareRegistry.php b/src/Middleware/Registry/MiddlewareRegistry.php index 2f7970c..1be9c82 100644 --- a/src/Middleware/Registry/MiddlewareRegistry.php +++ b/src/Middleware/Registry/MiddlewareRegistry.php @@ -9,11 +9,6 @@ interface MiddlewareRegistry { - /** - * Array-key where global middleware may be defined. - */ - public const GLOBAL_MIDDLEWARE_GROUP = 'global'; - /** * @param class-string|string $middlewareFqcnOrGroup * diff --git a/src/Middleware/Registry/ServiceLocatorMiddlewareRegistry.php b/src/Middleware/Registry/ServiceLocatorMiddlewareRegistry.php index 1ab4875..0ac133b 100644 --- a/src/Middleware/Registry/ServiceLocatorMiddlewareRegistry.php +++ b/src/Middleware/Registry/ServiceLocatorMiddlewareRegistry.php @@ -10,28 +10,17 @@ final class ServiceLocatorMiddlewareRegistry implements MiddlewareRegistry { - private ContainerInterface $container; - - /** - * @var array[]}> - */ - private array $groups; - /** * @psalm-param array[]}> $groups */ - public function __construct(ContainerInterface $container, array $groups) - { - $this->container = $container; - $this->groups = $groups; + public function __construct( + private readonly ContainerInterface $container, + private readonly array $groups, + ) { } /** - * @param class-string|string $middlewareFqcnOrGroup - * - * @throws MiddlewareNotConfigured - * - * @return MiddlewareInterface[] + * {@inheritdoc} */ public function byName(string $middlewareFqcnOrGroup): array { diff --git a/src/Middleware/StackMiddleware.php b/src/Middleware/StackMiddleware.php index 9df2fd9..4d04f99 100644 --- a/src/Middleware/StackMiddleware.php +++ b/src/Middleware/StackMiddleware.php @@ -10,17 +10,12 @@ final class StackMiddleware implements RequestHandlerInterface { - /** - * @var \Closure(ServerRequestInterface): ResponseInterface - */ - private \Closure $stack; - /** * @param \Closure(ServerRequestInterface): ResponseInterface $stack */ - public function __construct(\Closure $stack) - { - $this->stack = $stack; + public function __construct( + private readonly \Closure $stack, + ) { } public function handle(ServerRequestInterface $request): ResponseInterface diff --git a/src/Middleware/SymfonyActionRequestHandler.php b/src/Middleware/SymfonyActionRequestHandler.php index 63d2f2e..d9700f0 100644 --- a/src/Middleware/SymfonyActionRequestHandler.php +++ b/src/Middleware/SymfonyActionRequestHandler.php @@ -18,23 +18,17 @@ final class SymfonyActionRequestHandler implements RequestHandlerInterface * @var callable(SymfonyRequest): Response */ private $destination; - private PsrResponseTransformer $psrResponseTransformer; - private SymfonyRequest $symfonyRequest; - private PsrRequestCloner $psrRequestCloner; /** * @param callable(SymfonyRequest): Response $destination */ public function __construct( callable $destination, - SymfonyRequest $symfonyRequest, - PsrResponseTransformer $psrResponseTransformer, - PsrRequestCloner $psrRequestCloner, + private readonly SymfonyRequest $symfonyRequest, + private readonly PsrResponseTransformer $psrResponseTransformer, + private readonly PsrRequestCloner $psrRequestCloner, ) { $this->destination = $destination; - $this->psrResponseTransformer = $psrResponseTransformer; - $this->symfonyRequest = $symfonyRequest; - $this->psrRequestCloner = $psrRequestCloner; } public function handle(ServerRequestInterface $request): ResponseInterface diff --git a/src/Psr/Adapter/PsrHttpMessageBridgePsrRequestTransformer.php b/src/Psr/Adapter/PsrHttpMessageBridgePsrRequestTransformer.php index 2bc2194..1cb160e 100644 --- a/src/Psr/Adapter/PsrHttpMessageBridgePsrRequestTransformer.php +++ b/src/Psr/Adapter/PsrHttpMessageBridgePsrRequestTransformer.php @@ -12,15 +12,10 @@ final class PsrHttpMessageBridgePsrRequestTransformer implements PsrRequestTransformer { - private HttpMessageFactoryInterface $httpMessageFactory; - private HttpFoundationFactoryInterface $httpFoundationFactory; - public function __construct( - HttpMessageFactoryInterface $httpMessageFactory, - HttpFoundationFactoryInterface $httpFoundationFactory, + private readonly HttpMessageFactoryInterface $httpMessageFactory, + private readonly HttpFoundationFactoryInterface $httpFoundationFactory, ) { - $this->httpMessageFactory = $httpMessageFactory; - $this->httpFoundationFactory = $httpFoundationFactory; } public function toPsrRequest(SymfonyRequest $symfonyRequest): ServerRequestInterface diff --git a/src/Psr/Adapter/PsrHttpMessageBridgePsrResponseTransformer.php b/src/Psr/Adapter/PsrHttpMessageBridgePsrResponseTransformer.php index 1bcd338..35185b4 100644 --- a/src/Psr/Adapter/PsrHttpMessageBridgePsrResponseTransformer.php +++ b/src/Psr/Adapter/PsrHttpMessageBridgePsrResponseTransformer.php @@ -12,15 +12,10 @@ final class PsrHttpMessageBridgePsrResponseTransformer implements PsrResponseTransformer { - private HttpMessageFactoryInterface $httpMessageFactory; - private HttpFoundationFactoryInterface $httpFoundationFactory; - public function __construct( - HttpMessageFactoryInterface $httpMessageFactory, - HttpFoundationFactoryInterface $httpFoundationFactory + private readonly HttpMessageFactoryInterface $httpMessageFactory, + private readonly HttpFoundationFactoryInterface $httpFoundationFactory ) { - $this->httpMessageFactory = $httpMessageFactory; - $this->httpFoundationFactory = $httpFoundationFactory; } public function toPsrResponse(SymfonyResponse $symfonyResponse): ResponseInterface From db27d37436014cf103ce4f3cc597ae0e5b79fbaf Mon Sep 17 00:00:00 2001 From: kafkiansky Date: Wed, 13 Dec 2023 10:53:07 +0300 Subject: [PATCH 2/6] upgrade symfony/cache --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index dd2570a..0107c8f 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ "psr/http-server-handler": "^1.0", "symfony/psr-http-message-bridge": "^7.0", "nyholm/psr7": "^1.4", - "symfony/cache": "^6.2" + "symfony/cache": "^7.0" }, "require-dev": { "phpunit/phpunit": "^10.0", From 4f1bb40d2fe92baf6a2221c5b4c9fb960131fd31 Mon Sep 17 00:00:00 2001 From: kafkiansky Date: Wed, 13 Dec 2023 11:19:28 +0300 Subject: [PATCH 3/6] allow group names as global middleware --- src/DependencyInjection/Configuration.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index f980ed2..54a74aa 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -54,16 +54,16 @@ public function getConfigTreeBuilder(): TreeBuilder } /** - * @return \Closure(string): string + * @return \Closure(mixed): string */ private function createMiddlewareNormalizer(): callable { - return function (string $middlewareName): string { - if (!\is_a($middlewareName, MiddlewareInterface::class, true)) { + return function (mixed $middlewareName): string { + if (!\is_string($middlewareName) || !\is_a($middlewareName, MiddlewareInterface::class, true)) { throw new \RuntimeException( vsprintf('Each middleware must implements the "%s" interface, but "%s" doesn\'t.', [ MiddlewareInterface::class, - $middlewareName, + \is_string($middlewareName) ? $middlewareName : get_debug_type($middlewareName), ]) ); } From e407575d1f865d2ef6ee90c72b0a59eab93c60d5 Mon Sep 17 00:00:00 2001 From: kafkiansky Date: Wed, 13 Dec 2023 11:33:27 +0300 Subject: [PATCH 4/6] allow groups and classes that implements MiddlewareInterface --- src/DependencyInjection/Configuration.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 54a74aa..cdb9ad9 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -59,16 +59,16 @@ public function getConfigTreeBuilder(): TreeBuilder private function createMiddlewareNormalizer(): callable { return function (mixed $middlewareName): string { - if (!\is_string($middlewareName) || !\is_a($middlewareName, MiddlewareInterface::class, true)) { - throw new \RuntimeException( + return match (true) { + \is_string($middlewareName) && \class_exists($middlewareName) && \is_a($middlewareName, MiddlewareInterface::class, true) => $middlewareName, + \is_string($middlewareName) && !\class_exists($middlewareName) => $middlewareName, + default => throw new \RuntimeException( vsprintf('Each middleware must implements the "%s" interface, but "%s" doesn\'t.', [ MiddlewareInterface::class, \is_string($middlewareName) ? $middlewareName : get_debug_type($middlewareName), ]) - ); - } - - return $middlewareName; + ) + }; }; } } From acce9a845fe93fecd1164bb6a6bbd318b8e30049 Mon Sep 17 00:00:00 2001 From: kafkiansky Date: Wed, 13 Dec 2023 14:18:36 +0300 Subject: [PATCH 5/6] allow recursive groups --- .../ServiceLocatorMiddlewareRegistry.php | 72 +++++++++++++++---- .../ServiceLocatorMiddlewareRegistryTest.php | 8 +-- 2 files changed, 63 insertions(+), 17 deletions(-) diff --git a/src/Middleware/Registry/ServiceLocatorMiddlewareRegistry.php b/src/Middleware/Registry/ServiceLocatorMiddlewareRegistry.php index 0ac133b..ce7a3ea 100644 --- a/src/Middleware/Registry/ServiceLocatorMiddlewareRegistry.php +++ b/src/Middleware/Registry/ServiceLocatorMiddlewareRegistry.php @@ -8,15 +8,24 @@ use Psr\Http\Server\MiddlewareInterface; use Kafkiansky\SymfonyMiddleware\Middleware\MiddlewareNotConfigured; +/** + * @psalm-type MiddlewareGroup = array{if?: bool, middlewares?: list>} + */ final class ServiceLocatorMiddlewareRegistry implements MiddlewareRegistry { + /** @var array[]> */ + private readonly array $enabledMiddlewareGroups; + /** - * @psalm-param array[]}> $groups + * @psalm-param array $groups + * + * @throws MiddlewareNotConfigured */ public function __construct( private readonly ContainerInterface $container, private readonly array $groups, ) { + $this->enabledMiddlewareGroups = self::normalizeGroups($this->groups); } /** @@ -24,7 +33,7 @@ public function __construct( */ public function byName(string $middlewareFqcnOrGroup): array { - if ($this->container->has($middlewareFqcnOrGroup) === false && !isset($this->groups[$middlewareFqcnOrGroup])) { + if (!$this->container->has($middlewareFqcnOrGroup) && !isset($this->groups[$middlewareFqcnOrGroup])) { throw MiddlewareNotConfigured::forMiddleware($middlewareFqcnOrGroup); } @@ -33,18 +42,57 @@ public function byName(string $middlewareFqcnOrGroup): array return [$this->container->get($middlewareFqcnOrGroup)]; } - /** @var MiddlewareInterface[] $middlewares */ - $middlewares = []; + return array_map(function (string $middleware): MiddlewareInterface { + /** @var MiddlewareInterface */ + return $this->container->get($middleware); + }, $this->enabledMiddlewareGroups[$middlewareFqcnOrGroup] ?? []); + } + + /** + * @param array $groups + * + * @throws MiddlewareNotConfigured + * + * @return array[]> + */ + private static function normalizeGroups(array $groups): array + { + $middlewareGroups = []; + + foreach ($groups as $name => $group) { + if ($group['if'] ?? true) { + $middlewareGroups[$name] = self::normalizeMiddlewares($groups, $group['middlewares'] ?? throw MiddlewareNotConfigured::becauseGroupIsEmpty($name)); + } + } + + return $middlewareGroups; + } + + /** + * @param array $groups + * @param list> $middlewares + * + * @throws MiddlewareNotConfigured + * + * @return class-string[] + */ + private static function normalizeMiddlewares(array $groups, array $middlewares): array + { + $groupMiddlewares = []; - if (!isset($this->groups[$middlewareFqcnOrGroup]['if']) || $this->groups[$middlewareFqcnOrGroup]['if']) { - $middlewares = array_map(function (string $middlewareFqcn): MiddlewareInterface { - /** @var MiddlewareInterface */ - return $this->container->get($middlewareFqcn); - }, $this->groups[$middlewareFqcnOrGroup]['middlewares'] - ?? throw MiddlewareNotConfigured::becauseGroupIsEmpty($middlewareFqcnOrGroup) - ); + foreach ($middlewares as $middleware) { + if (\is_a($middleware, MiddlewareInterface::class, true)) { + $groupMiddlewares = array_merge($groupMiddlewares, [$middleware]); + } elseif (isset($groups[$middleware]) && ($groups[$middleware]['if'] ?? true)) { + $groupMiddlewares = array_merge( + $groupMiddlewares, + self::normalizeMiddlewares($groups, $groups[$middleware]['middlewares'] ?? throw MiddlewareNotConfigured::becauseGroupIsEmpty($middleware)), + ); + } elseif(!isset($groups[$middleware])) { + throw MiddlewareNotConfigured::forMiddleware($middleware); + } } - return $middlewares; + return $groupMiddlewares; } } diff --git a/tests/ServiceLocatorMiddlewareRegistryTest.php b/tests/ServiceLocatorMiddlewareRegistryTest.php index e9b5018..1ce0c62 100644 --- a/tests/ServiceLocatorMiddlewareRegistryTest.php +++ b/tests/ServiceLocatorMiddlewareRegistryTest.php @@ -137,7 +137,9 @@ public function testMiddlewareCannotBeFound(): void public function testEmptyGroup(): void { - $registry = new ServiceLocatorMiddlewareRegistry( + self::expectException(MiddlewareNotConfigured::class); + self::expectExceptionMessage('Middlewares groups cannot empty, but the group "api" is.'); + new ServiceLocatorMiddlewareRegistry( new ArrayContainer([ ModifyRequestMiddleware::class => new ModifyRequestMiddleware(), ModifyResponseMiddleware::class => new ModifyResponseMiddleware(), @@ -147,9 +149,5 @@ public function testEmptyGroup(): void 'api' => [] ] ); - - self::expectException(MiddlewareNotConfigured::class); - self::expectExceptionMessage('Middlewares groups cannot empty, but the group "api" is.'); - $registry->byName('api'); } } From 6541adede9f560b9dd6e2e8890a3df45395c79f3 Mon Sep 17 00:00:00 2001 From: kafkiansky Date: Wed, 13 Dec 2023 14:30:48 +0300 Subject: [PATCH 6/6] remove unused imports, add more description in readme --- README.md | 18 ++++++++++++++++++ .../SymiddlewareCompilerPass.php | 1 - 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e20124b..5545b7e 100644 --- a/README.md +++ b/README.md @@ -242,6 +242,24 @@ final class SomeController } ``` +Also, you can use nested groups: + +```yaml +symiddleware: + global: + - App\Controller\SetCorsHeaders + - web + groups: + web: + middlewares: + - 'App\Middleware\ModifyRequestMiddleware' + - debug + debug: + if: false + middlewares: + - 'App\Middleware\LogSqlQuery' +``` + Duplicated middlewares will be removed. ## Customization diff --git a/src/DependencyInjection/SymiddlewareCompilerPass.php b/src/DependencyInjection/SymiddlewareCompilerPass.php index ecf4d07..4c3e3e7 100644 --- a/src/DependencyInjection/SymiddlewareCompilerPass.php +++ b/src/DependencyInjection/SymiddlewareCompilerPass.php @@ -8,7 +8,6 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Psr\Http\Server\MiddlewareInterface; use Kafkiansky\SymfonyMiddleware\Middleware\Registry\MiddlewareRegistry; -use Symfony\Component\DependencyInjection\Loader\Configurator\ReferenceConfigurator; use function Symfony\Component\DependencyInjection\Loader\Configurator\service; use function Symfony\Component\DependencyInjection\Loader\Configurator\service_locator;