-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement preload URL generation for caching strategies
Revised the caching strategy to generate preload URLs dynamically. A new class, PreloadUrlsGeneratorManager, was added to manage all instances of URL generators. URL generation is now supported in the CacheStrategies service to generate additional or specific URLs for caches. An attribute 'PreloadUrl' was also implemented to attach directly to controllers or methods. Removed depreciated DummyController test.
- Loading branch information
Showing
21 changed files
with
493 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace SpomkyLabs\PwaBundle\Attribute; | ||
|
||
use Attribute; | ||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | ||
|
||
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] | ||
final readonly class PreloadUrl | ||
{ | ||
/** | ||
* @param array<string, mixed> $params | ||
*/ | ||
public function __construct( | ||
public string $alias, | ||
public array $params = [], | ||
public int $pathTypeReference = UrlGeneratorInterface::ABSOLUTE_PATH, | ||
) { | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace SpomkyLabs\PwaBundle\Attribute; | ||
|
||
use ReflectionAttribute; | ||
use ReflectionClass; | ||
use ReflectionMethod; | ||
use RuntimeException; | ||
use SpomkyLabs\PwaBundle\CachingStrategy\PreloadUrlsTagGenerator; | ||
use Symfony\Component\DependencyInjection\ChildDefinition; | ||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; | ||
use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
use Symfony\Component\Routing\Attribute\Route; | ||
use Throwable; | ||
use function array_key_exists; | ||
use function is_string; | ||
|
||
final class PreloadUrlCompilerPass implements CompilerPassInterface | ||
{ | ||
public function process(ContainerBuilder $container): void | ||
{ | ||
foreach ($this->findAllTaggedRoutes($container) as $alias => $urls) { | ||
$definitionId = sprintf('spomky_labs_pwa.preload_urls_tag_generator.%s', $alias); | ||
$definition = new ChildDefinition(PreloadUrlsTagGenerator::class); | ||
$definition | ||
->setArguments([ | ||
'$alias' => $alias, | ||
'$urls' => $urls, | ||
]) | ||
->addTag('spomky_labs_pwa.preload_urls_generator') | ||
; | ||
$container->setDefinition($definitionId, $definition); | ||
} | ||
} | ||
|
||
/** | ||
* @return array<string, array{route: string, alias: string, params?: array<string, mixed>, pathTypeReference: int}[]> | ||
*/ | ||
private function findAllTaggedRoutes(ContainerBuilder $container): array | ||
{ | ||
$routes = []; | ||
$controllers = $container->findTaggedServiceIds('controller.service_arguments'); | ||
foreach (array_keys($controllers) as $controller) { | ||
if (! is_string($controller) || ! class_exists($controller)) { | ||
continue; | ||
} | ||
$reflectionClass = new ReflectionClass($controller); | ||
$result = $this->findAllPreloadAttributesForClass($reflectionClass); | ||
foreach ($result as $route) { | ||
if (! array_key_exists($route['alias'], $routes)) { | ||
$routes[$route['alias']] = []; | ||
} | ||
$routes[$route['alias']][] = $route; | ||
} | ||
} | ||
|
||
return $routes; | ||
} | ||
|
||
/** | ||
* @param ReflectionClass<object> $reflectionClass | ||
* @return iterable<array{alias: string, route: string, params: array<string, mixed>, pathTypeReference: int}> | ||
*/ | ||
private function findAllPreloadAttributesForClass(ReflectionClass $reflectionClass): iterable | ||
{ | ||
foreach ($reflectionClass->getAttributes(PreloadUrl::class, ReflectionAttribute::IS_INSTANCEOF) as $attribute) { | ||
try { | ||
/** @var PreloadUrl $preloadAttribute */ | ||
$preloadAttribute = $attribute->newInstance(); | ||
yield from $this->findAllRoutesToPreload( | ||
$preloadAttribute, | ||
$reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) | ||
); | ||
} catch (Throwable $e) { | ||
throw new RuntimeException(sprintf('Unable to create attribute instance: %s', $e->getMessage()), 0, $e); | ||
} | ||
} | ||
foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { | ||
foreach ($method->getAttributes(PreloadUrl::class, ReflectionAttribute::IS_INSTANCEOF) as $attribute) { | ||
try { | ||
/** @var PreloadUrl $preloadAttribute */ | ||
$preloadAttribute = $attribute->newInstance(); | ||
yield from $this->findAllRoutesForMethod($preloadAttribute, $method); | ||
} catch (Throwable $e) { | ||
throw new RuntimeException(sprintf( | ||
'Unable to create attribute instance: %s', | ||
$e->getMessage() | ||
), 0, $e); | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* @param array<ReflectionMethod> $methods | ||
* @return iterable<array{alias: string, route: string, params: array<string, mixed>, pathTypeReference: int}> | ||
*/ | ||
private function findAllRoutesToPreload(PreloadUrl $preloadAttribute, array $methods): iterable | ||
{ | ||
foreach ($methods as $method) { | ||
yield from $this->findAllRoutesForMethod($preloadAttribute, $method); | ||
} | ||
} | ||
|
||
/** | ||
* @return iterable<array{alias: string, route: string, params: array<string, mixed>, pathTypeReference: int}> | ||
*/ | ||
private function findAllRoutesForMethod(PreloadUrl $preloadAttribute, ReflectionMethod $method): iterable | ||
{ | ||
foreach ($method->getAttributes(Route::class, ReflectionAttribute::IS_INSTANCEOF) as $attribute) { | ||
try { | ||
/** @var Route $routeAttribute */ | ||
$routeAttribute = $attribute->newInstance(); | ||
$routeName = $routeAttribute->getName(); | ||
if ($routeName === null) { | ||
continue; | ||
} | ||
yield [ | ||
'alias' => $preloadAttribute->alias, | ||
'route' => $routeName, | ||
'params' => $preloadAttribute->params, | ||
'pathTypeReference' => $preloadAttribute->pathTypeReference, | ||
]; | ||
} catch (Throwable) { | ||
continue; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace SpomkyLabs\PwaBundle\CachingStrategy; | ||
|
||
use SpomkyLabs\PwaBundle\Dto\Url; | ||
|
||
interface PreloadUrlsGeneratorInterface | ||
{ | ||
public function getAlias(): string; | ||
|
||
/** | ||
* @return iterable<Url|string> | ||
*/ | ||
public function generateUrls(): iterable; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace SpomkyLabs\PwaBundle\CachingStrategy; | ||
|
||
use InvalidArgumentException; | ||
use Symfony\Component\DependencyInjection\Attribute\TaggedIterator; | ||
use function array_key_exists; | ||
|
||
final class PreloadUrlsGeneratorManager | ||
{ | ||
/** | ||
* @var array<string, PreloadUrlsGeneratorInterface> | ||
*/ | ||
private array $generators = []; | ||
|
||
/** | ||
* @param PreloadUrlsGeneratorInterface[] $generators | ||
*/ | ||
public function __construct( | ||
#[TaggedIterator('spomky_labs_pwa.preload_urls_generator')] | ||
iterable $generators | ||
) { | ||
foreach ($generators as $generator) { | ||
$this->add($generator); | ||
} | ||
} | ||
|
||
public function add(PreloadUrlsGeneratorInterface $generator, PreloadUrlsGeneratorInterface ...$generators): void | ||
{ | ||
$this->generators[$generator->getAlias()] = $generator; | ||
foreach ($generators as $generator) { | ||
$this->generators[$generator->getAlias()] = $generator; | ||
} | ||
} | ||
|
||
public function get(string $alias): PreloadUrlsGeneratorInterface | ||
{ | ||
if (! array_key_exists($alias, $this->generators)) { | ||
throw new InvalidArgumentException(sprintf('The generator with alias "%s" does not exist.', $alias)); | ||
} | ||
return $this->generators[$alias]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace SpomkyLabs\PwaBundle\CachingStrategy; | ||
|
||
use SpomkyLabs\PwaBundle\Dto\Url; | ||
|
||
final readonly class PreloadUrlsTagGenerator implements PreloadUrlsGeneratorInterface | ||
{ | ||
/** | ||
* @var array<Url> | ||
*/ | ||
private array $urls; | ||
|
||
/** | ||
* @param array{route: string, params: array<string, mixed>, pathTypeReference: int}[] $urls | ||
*/ | ||
public function __construct( | ||
private string $alias, | ||
array $urls | ||
) { | ||
$this->urls = array_map( | ||
static fn (array $url): Url => Url::create($url['route'], $url['params'], $url['pathTypeReference']), | ||
$urls | ||
); | ||
} | ||
|
||
public function getAlias(): string | ||
{ | ||
return $this->alias; | ||
} | ||
|
||
/** | ||
* @return iterable<Url> | ||
*/ | ||
public function generateUrls(): iterable | ||
{ | ||
return $this->urls; | ||
} | ||
} |
Oops, something went wrong.