From 83f6a4ea7c0fc8dc52c060c140337ed52aa18c15 Mon Sep 17 00:00:00 2001 From: "Paul M. Jones" Date: Mon, 11 Mar 2024 16:42:16 -0500 Subject: [PATCH 1/3] Move DataGenerator param from constructor to processedRoutes() --- src/ConfigureRoutes.php | 2 +- src/FastRoute.php | 4 ++-- src/RouteCollector.php | 15 +++++++++------ src/functions.php | 5 ++--- test/RouteCollectorTest.php | 8 +++++--- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/ConfigureRoutes.php b/src/ConfigureRoutes.php index bcd97fc..43eaa20 100644 --- a/src/ConfigureRoutes.php +++ b/src/ConfigureRoutes.php @@ -109,5 +109,5 @@ public function options(string $route, mixed $handler, array $extraParameters = * * @return ProcessedData */ - public function processedRoutes(): array; + public function processedRoutes(DataGenerator $dataGenerator): array; } diff --git a/src/FastRoute.php b/src/FastRoute.php index 6187833..e471756 100644 --- a/src/FastRoute.php +++ b/src/FastRoute.php @@ -150,12 +150,12 @@ private function buildConfiguration(): array $loader = function (): array { $configuredRoutes = new $this->routesConfiguration( new $this->routeParser(), - new $this->dataGenerator(), ); ($this->routeDefinitionCallback)($configuredRoutes); - return $configuredRoutes->processedRoutes(); + $dataGenerator = new $this->dataGenerator(); + return $configuredRoutes->processedRoutes($dataGenerator); }; if ($this->cacheDriver === null) { diff --git a/src/RouteCollector.php b/src/RouteCollector.php index 9de94b6..54beeca 100644 --- a/src/RouteCollector.php +++ b/src/RouteCollector.php @@ -21,9 +21,10 @@ class RouteCollector implements ConfigureRoutes /** @var RoutesForUriGeneration */ private array $namedRoutes = []; + private array $addedRoutes = []; + public function __construct( protected readonly RouteParser $routeParser, - protected readonly DataGenerator $dataGenerator, ) { } @@ -32,12 +33,11 @@ public function addRoute(string|array $httpMethod, string $route, mixed $handler { $route = $this->currentGroupPrefix . $route; $parsedRoutes = $this->routeParser->parse($route); - $extraParameters = [self::ROUTE_REGEX => $route] + $extraParameters; foreach ((array) $httpMethod as $method) { foreach ($parsedRoutes as $parsedRoute) { - $this->dataGenerator->addRoute($method, $parsedRoute, $handler, $extraParameters); + $this->addedRoutes[] = [$method, $parsedRoute, $handler, $extraParameters]; } } @@ -117,11 +117,14 @@ public function options(string $route, mixed $handler, array $extraParameters = } /** @inheritDoc */ - public function processedRoutes(): array + public function processedRoutes(DataGenerator $dataGenerator): array { - $data = $this->dataGenerator->getData(); - $data[] = $this->namedRoutes; + foreach ($this->addedRoutes as $addedRoute) { + $dataGenerator->addRoute(...$addedRoute); + } + $data = $dataGenerator->getData(); + $data[] = $this->namedRoutes; return $data; } diff --git a/src/functions.php b/src/functions.php index 6e45d4e..73193a6 100644 --- a/src/functions.php +++ b/src/functions.php @@ -49,12 +49,11 @@ function cachedDispatcher(callable $routeDefinitionCallback, array $options = [] $loader = static function () use ($routeDefinitionCallback, $options): array { $routeCollector = new $options['routeCollector']( new $options['routeParser'](), - new $options['dataGenerator']() ); $routeDefinitionCallback($routeCollector); - - return $routeCollector->processedRoutes(); + $dataGenerator = new $options['dataGenerator'](); + return $routeCollector->processedRoutes($dataGenerator); }; if ($options['cacheDisabled'] === true) { diff --git a/test/RouteCollectorTest.php b/test/RouteCollectorTest.php index 4dd52ce..6ae1f24 100644 --- a/test/RouteCollectorTest.php +++ b/test/RouteCollectorTest.php @@ -43,6 +43,7 @@ public function shortcutsCanBeUsedToRegisterRoutes(): void ['OPTIONS', '/options', 'options', ['_route' => '/options']], ]; + $r->processedRoutes($dataGenerator); self::assertObjectHasProperty('routes', $dataGenerator); self::assertSame($expected, $dataGenerator->routes); } @@ -51,7 +52,7 @@ public function shortcutsCanBeUsedToRegisterRoutes(): void public function routesCanBeGrouped(): void { $dataGenerator = self::dummyDataGenerator(); - $r = new RouteCollector(new Std(), $dataGenerator); + $r = new RouteCollector(new Std()); $r->delete('/delete', 'delete'); $r->get('/get', 'get'); @@ -114,6 +115,7 @@ public function routesCanBeGrouped(): void ['GET', '/admin-more-info', 'admin-more-info', ['_route' => '/admin-more-info']], ]; + $r->processedRoutes($dataGenerator); self::assertObjectHasProperty('routes', $dataGenerator); self::assertSame($expected, $dataGenerator->routes); } @@ -123,11 +125,11 @@ public function namedRoutesShouldBeRegistered(): void { $dataGenerator = self::dummyDataGenerator(); - $r = new RouteCollector(new Std(), $dataGenerator); + $r = new RouteCollector(new Std()); $r->get('/', 'index-handler', ['_name' => 'index']); $r->get('/users/me', 'fetch-user-handler', ['_name' => 'users.fetch']); - self::assertSame(['index' => [['/']], 'users.fetch' => [['/users/me']]], $r->processedRoutes()[2]); + self::assertSame(['index' => [['/']], 'users.fetch' => [['/users/me']]], $r->processedRoutes($dataGenerator)[2]); } #[PHPUnit\Test] From bd9b2900cbb63a0c7efabbe1d980829336187933 Mon Sep 17 00:00:00 2001 From: "Paul M. Jones" Date: Mon, 11 Mar 2024 18:26:08 -0500 Subject: [PATCH 2/3] Make Dispatcher and GenerateUri immutable --- src/Dispatcher.php | 2 ++ src/Dispatcher/RegexBasedAbstract.php | 7 +++++-- src/FastRoute.php | 6 ++++-- src/GenerateUri.php | 2 ++ src/GenerateUri/FromProcessedConfiguration.php | 7 ++++++- src/functions.php | 6 ++++-- test/FastRouteTest.php | 5 +++++ test/GenerateUri/FromProcessedConfigurationTest.php | 3 ++- 8 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/Dispatcher.php b/src/Dispatcher.php index 6a62c3a..5e10a5c 100644 --- a/src/Dispatcher.php +++ b/src/Dispatcher.php @@ -13,6 +13,8 @@ interface Dispatcher public const FOUND = 1; public const METHOD_NOT_ALLOWED = 2; + public function with(array $processedData): self; + /** * Dispatches against the provided HTTP method verb and URI. * diff --git a/src/Dispatcher/RegexBasedAbstract.php b/src/Dispatcher/RegexBasedAbstract.php index 9dce400..7642372 100644 --- a/src/Dispatcher/RegexBasedAbstract.php +++ b/src/Dispatcher/RegexBasedAbstract.php @@ -27,9 +27,12 @@ abstract class RegexBasedAbstract implements Dispatcher protected array $variableRouteData = []; /** @param RouteData $data */ - public function __construct(array $data) + public function with(array $data): self { - [$this->staticRouteMap, $this->variableRouteData] = $data; + $clone = clone $this; + $clone->staticRouteMap = $data[0]; + $clone->variableRouteData = $data[1]; + return $clone; } /** @param DynamicRouteChunks $routeData */ diff --git a/src/FastRoute.php b/src/FastRoute.php index e471756..5a0ce45 100644 --- a/src/FastRoute.php +++ b/src/FastRoute.php @@ -173,11 +173,13 @@ private function buildConfiguration(): array public function dispatcher(): Dispatcher { - return new $this->dispatcher($this->buildConfiguration()); + $dispatcher = new $this->dispatcher(); + return $dispatcher->with($this->buildConfiguration()); } public function uriGenerator(): GenerateUri { - return new $this->uriGenerator($this->buildConfiguration()[2]); + $uriGenerator = new $this->uriGenerator(); + return $uriGenerator->with($this->buildConfiguration()[2]); } } diff --git a/src/GenerateUri.php b/src/GenerateUri.php index 4291bcf..3050e2d 100644 --- a/src/GenerateUri.php +++ b/src/GenerateUri.php @@ -12,6 +12,8 @@ */ interface GenerateUri { + public function with(array $processedConfiguration): self; + /** * @param UriSubstitutions $substitutions * diff --git a/src/GenerateUri/FromProcessedConfiguration.php b/src/GenerateUri/FromProcessedConfiguration.php index aae03b1..e2ba6a7 100644 --- a/src/GenerateUri/FromProcessedConfiguration.php +++ b/src/GenerateUri/FromProcessedConfiguration.php @@ -21,8 +21,13 @@ final class FromProcessedConfiguration implements GenerateUri { /** @param RoutesForUriGeneration $processedConfiguration */ - public function __construct(private readonly array $processedConfiguration) + private array $processedConfiguration = []; + + public function with(array $processedConfiguration): self { + $clone = clone $this; + $clone->processedConfiguration = $processedConfiguration; + return $clone; } /** @inheritDoc */ diff --git a/src/functions.php b/src/functions.php index 73193a6..8b586c8 100644 --- a/src/functions.php +++ b/src/functions.php @@ -56,8 +56,10 @@ function cachedDispatcher(callable $routeDefinitionCallback, array $options = [] return $routeCollector->processedRoutes($dataGenerator); }; + $protoDispatcher = new $options['dispatcher'](); + if ($options['cacheDisabled'] === true) { - return new $options['dispatcher']($loader()); + return $protoDispatcher->with($loader()); } $cacheKey = $options['cacheKey'] ?? $options['cacheFile'] ?? null; @@ -72,6 +74,6 @@ function cachedDispatcher(callable $routeDefinitionCallback, array $options = [] $cache = new $cache(); } - return new $options['dispatcher']($cache->get($cacheKey, $loader)); + return $protoDispatcher->with($cache->get($cacheKey, $loader)); } } diff --git a/test/FastRouteTest.php b/test/FastRouteTest.php index 8afe5f5..91c5516 100644 --- a/test/FastRouteTest.php +++ b/test/FastRouteTest.php @@ -119,6 +119,11 @@ public function forRoute(string $name, array $substitutions = []): string { return ''; } + + public function with(array $processedConfiguration): self + { + return clone $this; + } }; $uriGenerator = FastRoute::recommendedSettings(self::routes(...), 'test') diff --git a/test/GenerateUri/FromProcessedConfigurationTest.php b/test/GenerateUri/FromProcessedConfigurationTest.php index 704b8ab..64229ad 100644 --- a/test/GenerateUri/FromProcessedConfigurationTest.php +++ b/test/GenerateUri/FromProcessedConfigurationTest.php @@ -132,6 +132,7 @@ private static function routeGeneratorFor(array $routeMap): GenerateUri return array_reverse((new RouteParser\Std())->parse($route)); }; - return new GenerateUri\FromProcessedConfiguration(array_map($parseRoutes, $routeMap)); + $protoGenerateUri = new GenerateUri\FromProcessedConfiguration(); + return $protoGenerateUri->with(array_map($parseRoutes, $routeMap)); } } From fcc627835474dc76492799a4ed72a6d178b1e0cc Mon Sep 17 00:00:00 2001 From: "Paul M. Jones" Date: Mon, 11 Mar 2024 18:36:32 -0500 Subject: [PATCH 3/3] Enable something more like 'real' dependency injection --- src/FastRoute.php | 69 +++++++++++++++--------------------------- test/FastRouteTest.php | 2 +- 2 files changed, 26 insertions(+), 45 deletions(-) diff --git a/src/FastRoute.php b/src/FastRoute.php index 5a0ce45..54715ad 100644 --- a/src/FastRoute.php +++ b/src/FastRoute.php @@ -17,7 +17,6 @@ final class FastRoute /** * @param Closure(ConfigureRoutes):void $routeDefinitionCallback - * @param class-string $routeParser * @param class-string $dataGenerator * @param class-string $dispatcher * @param class-string $routesConfiguration @@ -25,15 +24,14 @@ final class FastRoute * @param Cache|class-string|null $cacheDriver * @param non-empty-string|null $cacheKey */ - private function __construct( - private readonly Closure $routeDefinitionCallback, - private readonly string $routeParser, - private readonly string $dataGenerator, - private readonly string $dispatcher, - private readonly string $routesConfiguration, - private readonly string $uriGenerator, - private readonly Cache|string|null $cacheDriver, - private readonly ?string $cacheKey, + public function __construct( + private Closure $routeDefinitionCallback, + private DataGenerator $dataGenerator, + private Dispatcher $dispatcher, + private ConfigureRoutes $routesConfiguration, + private GenerateUri $uriGenerator, + private ?Cache $cacheDriver, + private ?string $cacheKey, ) { } @@ -45,12 +43,11 @@ public static function recommendedSettings(Closure $routeDefinitionCallback, str { return new self( $routeDefinitionCallback, - RouteParser\Std::class, - DataGenerator\MarkBased::class, - Dispatcher\MarkBased::class, - RouteCollector::class, - GenerateUri\FromProcessedConfiguration::class, - FileCache::class, + new DataGenerator\MarkBased(), + new Dispatcher\MarkBased(), + new RouteCollector(new RouteParser\Std()), + new GenerateUri\FromProcessedConfiguration, + new FileCache(), $cacheKey, ); } @@ -59,7 +56,6 @@ public function disableCache(): self { return new self( $this->routeDefinitionCallback, - $this->routeParser, $this->dataGenerator, $this->dispatcher, $this->routesConfiguration, @@ -73,11 +69,10 @@ public function disableCache(): self * @param Cache|class-string $driver * @param non-empty-string $cacheKey */ - public function withCache(Cache|string $driver, string $cacheKey): self + public function withCache(Cache $driver, string $cacheKey): self { return new self( $this->routeDefinitionCallback, - $this->routeParser, $this->dataGenerator, $this->dispatcher, $this->routesConfiguration, @@ -89,33 +84,32 @@ public function withCache(Cache|string $driver, string $cacheKey): self public function useCharCountDispatcher(): self { - return $this->useCustomDispatcher(DataGenerator\CharCountBased::class, Dispatcher\CharCountBased::class); + return $this->useCustomDispatcher(new DataGenerator\CharCountBased(), new Dispatcher\CharCountBased()); } public function useGroupCountDispatcher(): self { - return $this->useCustomDispatcher(DataGenerator\GroupCountBased::class, Dispatcher\GroupCountBased::class); + return $this->useCustomDispatcher(new DataGenerator\GroupCountBased(), new Dispatcher\GroupCountBased()); } public function useGroupPosDispatcher(): self { - return $this->useCustomDispatcher(DataGenerator\GroupPosBased::class, Dispatcher\GroupPosBased::class); + return $this->useCustomDispatcher(new DataGenerator\GroupPosBased(), new Dispatcher\GroupPosBased()); } public function useMarkDispatcher(): self { - return $this->useCustomDispatcher(DataGenerator\MarkBased::class, Dispatcher\MarkBased::class); + return $this->useCustomDispatcher(new DataGenerator\MarkBased(), new Dispatcher\MarkBased()); } /** * @param class-string $dataGenerator * @param class-string $dispatcher */ - public function useCustomDispatcher(string $dataGenerator, string $dispatcher): self + public function useCustomDispatcher(DataGenerator $dataGenerator, Dispatcher $dispatcher): self { return new self( $this->routeDefinitionCallback, - $this->routeParser, $dataGenerator, $dispatcher, $this->routesConfiguration, @@ -126,11 +120,10 @@ public function useCustomDispatcher(string $dataGenerator, string $dispatcher): } /** @param class-string $uriGenerator */ - public function withUriGenerator(string $uriGenerator): self + public function withUriGenerator(GenerateUri $uriGenerator): self { return new self( $this->routeDefinitionCallback, - $this->routeParser, $this->dataGenerator, $this->dispatcher, $this->routesConfiguration, @@ -148,14 +141,8 @@ private function buildConfiguration(): array } $loader = function (): array { - $configuredRoutes = new $this->routesConfiguration( - new $this->routeParser(), - ); - - ($this->routeDefinitionCallback)($configuredRoutes); - - $dataGenerator = new $this->dataGenerator(); - return $configuredRoutes->processedRoutes($dataGenerator); + ($this->routeDefinitionCallback)($this->routesConfiguration); + return $this->routesConfiguration->processedRoutes($this->dataGenerator); }; if ($this->cacheDriver === null) { @@ -164,22 +151,16 @@ private function buildConfiguration(): array assert(is_string($this->cacheKey)); - $cache = is_string($this->cacheDriver) - ? new $this->cacheDriver() - : $this->cacheDriver; - - return $this->processedConfiguration = $cache->get($this->cacheKey, $loader); + return $this->processedConfiguration = $this->cacheDriver->get($this->cacheKey, $loader); } public function dispatcher(): Dispatcher { - $dispatcher = new $this->dispatcher(); - return $dispatcher->with($this->buildConfiguration()); + return $this->dispatcher->with($this->buildConfiguration()); } public function uriGenerator(): GenerateUri { - $uriGenerator = new $this->uriGenerator(); - return $uriGenerator->with($this->buildConfiguration()[2]); + return $this->uriGenerator->with($this->buildConfiguration()[2]); } } diff --git a/test/FastRouteTest.php b/test/FastRouteTest.php index 91c5516..5611564 100644 --- a/test/FastRouteTest.php +++ b/test/FastRouteTest.php @@ -128,7 +128,7 @@ public function with(array $processedConfiguration): self $uriGenerator = FastRoute::recommendedSettings(self::routes(...), 'test') ->disableCache() - ->withUriGenerator($generator::class) + ->withUriGenerator($generator) ->uriGenerator(); self::assertInstanceOf($generator::class, $uriGenerator);