From 3e3ca4afe8cff8b729ca80c11c157813c1eb73f9 Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Sun, 7 Apr 2024 18:02:07 +0200 Subject: [PATCH] Implement dynamic URL preloading in cache strategy This change introduces dynamic URL preloading in cache strategy. It modifies the resource cache to accept string aliases representing predefined URL generators. These generators can then produce URLs to be included in caching. This enhancement allows for more flexible and dynamic URL caching for applications. --- composer.json | 4 +- phpstan-baseline.neon | 12 +- rector.php | 5 +- src/Attribute/PreloadUrl.php | 22 +++ src/Attribute/PreloadUrlCompilerPass.php | 131 ++++++++++++++++++ .../PreloadUrlsGeneratorInterface.php | 17 +++ .../PreloadUrlsGeneratorManager.php | 45 ++++++ .../PreloadUrlsTagGenerator.php | 41 ++++++ src/CachingStrategy/ResourceCaches.php | 27 +++- src/Dto/PageCache.php | 43 +----- src/Dto/ResourceCache.php | 50 +++++++ src/Dto/Url.php | 16 +++ src/Resources/config/services.php | 18 +++ src/SpomkyLabsPwaBundle.php | 6 + src/Twig/InstanceOfExtension.php | 38 +++++ src/WorkboxPlugin/BackgroundSyncPlugin.php | 12 +- src/WorkboxPlugin/BroadcastUpdatePlugin.php | 9 +- src/WorkboxPlugin/CacheableResponsePlugin.php | 10 +- src/WorkboxPlugin/ExpirationPlugin.php | 10 +- src/WorkboxPlugin/HasDebugInterface.php | 13 ++ src/WorkboxPlugin/RangeRequestsPlugin.php | 9 +- .../Collector/serviceworker-tab.html.twig | 17 ++- ...ontroller.php => OtherPagesController.php} | 22 +-- tests/Controller/StaticPagesController.php | 26 ++++ tests/Controller/WidgetController.php | 27 ++++ tests/DummyImageProcessor.php | 4 +- tests/DummyUrlsGenerator.php | 25 ++++ tests/TestFilesystem.php | 4 +- tests/config.php | 23 +-- 29 files changed, 592 insertions(+), 94 deletions(-) create mode 100644 src/Attribute/PreloadUrl.php create mode 100644 src/Attribute/PreloadUrlCompilerPass.php create mode 100644 src/CachingStrategy/PreloadUrlsGeneratorInterface.php create mode 100644 src/CachingStrategy/PreloadUrlsGeneratorManager.php create mode 100644 src/CachingStrategy/PreloadUrlsTagGenerator.php create mode 100644 src/Dto/ResourceCache.php create mode 100644 src/Twig/InstanceOfExtension.php create mode 100644 src/WorkboxPlugin/HasDebugInterface.php rename tests/Controller/{DummyController.php => OtherPagesController.php} (54%) create mode 100644 tests/Controller/StaticPagesController.php create mode 100644 tests/Controller/WidgetController.php create mode 100644 tests/DummyUrlsGenerator.php diff --git a/composer.json b/composer.json index b92a7f8..bdab78c 100644 --- a/composer.json +++ b/composer.json @@ -47,13 +47,15 @@ "dbrekelmans/bdi": "^1.1", "infection/infection": "^0.28", "phpstan/extension-installer": "^1.1", + "phpstan/phpdoc-parser": "^1.28", "phpstan/phpstan": "^1.0", "phpstan/phpstan-beberlei-assert": "^1.0", "phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan-phpunit": "^1.0", "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^10.1", + "phpunit/phpunit": "^10.1|^11.0", "rector/rector": "^1.0", + "staabm/phpstan-todo-by": "^0.1.25", "symfony/filesystem": "^6.4|^7.0", "symfony/framework-bundle": "^6.4|^7.0", "symfony/mime": "^6.4|^7.0", diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index e53359a..f78b5f7 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -231,12 +231,7 @@ parameters: path: src/Dto/Manifest.php - - message: "#^Class SpomkyLabs\\\\PwaBundle\\\\Dto\\\\PageCache has an uninitialized property \\$matchCallback\\. Give it default value or assign it in the constructor\\.$#" - count: 1 - path: src/Dto/PageCache.php - - - - message: "#^PHPDoc tag @var for property SpomkyLabs\\\\PwaBundle\\\\Dto\\\\PageCache\\:\\:\\$cacheableResponseHeaders with type array\\\\|null is not subtype of native type array\\.$#" + message: "#^Class SpomkyLabs\\\\PwaBundle\\\\Dto\\\\PageCache extends @final class SpomkyLabs\\\\PwaBundle\\\\Dto\\\\ResourceCache\\.$#" count: 1 path: src/Dto/PageCache.php @@ -260,6 +255,11 @@ parameters: count: 1 path: src/Dto/RelatedApplication.php + - + message: "#^Class SpomkyLabs\\\\PwaBundle\\\\Dto\\\\ResourceCache has an uninitialized property \\$matchCallback\\. Give it default value or assign it in the constructor\\.$#" + count: 1 + path: src/Dto/ResourceCache.php + - message: "#^Class SpomkyLabs\\\\PwaBundle\\\\Dto\\\\ScopeExtension has an uninitialized property \\$origin\\. Give it default value or assign it in the constructor\\.$#" count: 1 diff --git a/rector.php b/rector.php index fe8046d..14591a0 100644 --- a/rector.php +++ b/rector.php @@ -3,6 +3,7 @@ declare(strict_types=1); use Rector\Config\RectorConfig; +use Rector\DeadCode\Rector\ClassMethod\RemoveEmptyClassMethodRector; use Rector\Doctrine\Set\DoctrineSetList; use Rector\PHPUnit\Set\PHPUnitSetList; use Rector\Set\ValueObject\LevelSetList; @@ -26,7 +27,9 @@ ]); $config->phpVersion(PhpVersion::PHP_82); $config->paths([__DIR__ . '/src', __DIR__ . '/tests', __DIR__ . '/ecs.php', __DIR__ . '/rector.php']); - $config->skip([__DIR__ . '/tests/Controller/DummyController.php']); + $config->skip([ + RemoveEmptyClassMethodRector::class => [__DIR__ . '/tests/Controller/'], + ]); $config->parallel(); $config->importNames(); $config->importShortClasses(); diff --git a/src/Attribute/PreloadUrl.php b/src/Attribute/PreloadUrl.php new file mode 100644 index 0000000..9fb68d0 --- /dev/null +++ b/src/Attribute/PreloadUrl.php @@ -0,0 +1,22 @@ + $params + */ + public function __construct( + public string $alias, + public array $params = [], + public int $pathTypeReference = UrlGeneratorInterface::ABSOLUTE_PATH, + ) { + } +} diff --git a/src/Attribute/PreloadUrlCompilerPass.php b/src/Attribute/PreloadUrlCompilerPass.php new file mode 100644 index 0000000..327cb53 --- /dev/null +++ b/src/Attribute/PreloadUrlCompilerPass.php @@ -0,0 +1,131 @@ +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, 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 $reflectionClass + * @return iterable, 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 $methods + * @return iterable, pathTypeReference: int}> + */ + private function findAllRoutesToPreload(PreloadUrl $preloadAttribute, array $methods): iterable + { + foreach ($methods as $method) { + yield from $this->findAllRoutesForMethod($preloadAttribute, $method); + } + } + + /** + * @return iterable, 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; + } + } + } +} diff --git a/src/CachingStrategy/PreloadUrlsGeneratorInterface.php b/src/CachingStrategy/PreloadUrlsGeneratorInterface.php new file mode 100644 index 0000000..dbc3cd1 --- /dev/null +++ b/src/CachingStrategy/PreloadUrlsGeneratorInterface.php @@ -0,0 +1,17 @@ + + */ + public function generateUrls(): iterable; +} diff --git a/src/CachingStrategy/PreloadUrlsGeneratorManager.php b/src/CachingStrategy/PreloadUrlsGeneratorManager.php new file mode 100644 index 0000000..ef6f5f4 --- /dev/null +++ b/src/CachingStrategy/PreloadUrlsGeneratorManager.php @@ -0,0 +1,45 @@ + + */ + 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 $value) { + $this->generators[$generator->getAlias()] = $value; + } + } + + 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]; + } +} diff --git a/src/CachingStrategy/PreloadUrlsTagGenerator.php b/src/CachingStrategy/PreloadUrlsTagGenerator.php new file mode 100644 index 0000000..9181ff8 --- /dev/null +++ b/src/CachingStrategy/PreloadUrlsTagGenerator.php @@ -0,0 +1,41 @@ + + */ + private array $urls; + + /** + * @param array{route: string, params: array, 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 + */ + public function generateUrls(): iterable + { + return $this->urls; + } +} diff --git a/src/CachingStrategy/ResourceCaches.php b/src/CachingStrategy/ResourceCaches.php index 52831ff..15cb0f5 100644 --- a/src/CachingStrategy/ResourceCaches.php +++ b/src/CachingStrategy/ResourceCaches.php @@ -5,6 +5,7 @@ namespace SpomkyLabs\PwaBundle\CachingStrategy; use SpomkyLabs\PwaBundle\Dto\ServiceWorker; +use SpomkyLabs\PwaBundle\Dto\Url; use SpomkyLabs\PwaBundle\Dto\Workbox; use SpomkyLabs\PwaBundle\MatchCallbackHandler\MatchCallbackHandlerInterface; use SpomkyLabs\PwaBundle\WorkboxPlugin\BroadcastUpdatePlugin; @@ -31,6 +32,7 @@ * @param iterable $matchCallbackHandlers */ public function __construct( + private PreloadUrlsGeneratorManager $preloadUrlsGeneratorManager, ServiceWorker $serviceWorker, private SerializerInterface $serializer, #[TaggedIterator('spomky_labs_pwa.match_callback_handler')] @@ -50,11 +52,10 @@ public function getCacheStrategies(): array { $strategies = []; foreach ($this->workbox->resourceCaches as $id => $resourceCache) { - $routes = $this->serializer->serialize($resourceCache->urls, 'json', [ + $routes = $this->serializer->serialize($this->getUrls($resourceCache->urls), 'json', [ JsonEncode::OPTIONS => $this->jsonOptions, ]); $urls = json_decode($routes, true, 512, JSON_THROW_ON_ERROR); - $cacheName = $resourceCache->cacheName ?? sprintf('page-cache-%d', $id); $plugins = [ @@ -104,4 +105,26 @@ private function prepareMatchCallback(string $matchCallback): string return $matchCallback; } + + /** + * @param array $urls + * @return array + */ + private function getUrls(array $urls): array + { + $result = []; + foreach ($urls as $url) { + if (str_starts_with($url->path, '@')) { + $generator = $this->preloadUrlsGeneratorManager->get(mb_substr($url->path, 1)); + $list = $generator->generateUrls(); + foreach ($list as $item) { + $result[] = $item; + } + } else { + $result[] = $url; + } + } + + return $result; + } } diff --git a/src/Dto/PageCache.php b/src/Dto/PageCache.php index 3eb1245..069010f 100644 --- a/src/Dto/PageCache.php +++ b/src/Dto/PageCache.php @@ -4,44 +4,9 @@ namespace SpomkyLabs\PwaBundle\Dto; -use Symfony\Component\Serializer\Attribute\SerializedName; - -final class PageCache extends Cache +/** + * @deprecated since 1.2.0 and will be removed in 2.0.0. Use ResourceCache instead. + */ +final class PageCache extends ResourceCache { - #[SerializedName('match_callback')] - public string $matchCallback; - - #[SerializedName('network_timeout')] - public int $networkTimeout = 3; - - public string $strategy = 'NetworkFirst'; - - public bool $broadcast = false; - - #[SerializedName('range_requests')] - public bool $rangeRequests = false; - - /** - * @var int[] - */ - #[SerializedName('cacheable_response_statuses')] - public array $cacheableResponseStatuses = [0, 200]; - - /** - * @var null|array - */ - #[SerializedName('cacheable_response_headers')] - public array $cacheableResponseHeaders = []; - - /** - * @var array - */ - #[SerializedName('broadcast_headers')] - public array $broadcastHeaders = ['Content-Type', 'ETag', 'Last-Modified']; - - /** - * @var array - */ - #[SerializedName('preload_urls')] - public array $urls = []; } diff --git a/src/Dto/ResourceCache.php b/src/Dto/ResourceCache.php new file mode 100644 index 0000000..04971b8 --- /dev/null +++ b/src/Dto/ResourceCache.php @@ -0,0 +1,50 @@ + + */ + #[SerializedName('cacheable_response_headers')] + public array $cacheableResponseHeaders = []; + + /** + * @var array + */ + #[SerializedName('broadcast_headers')] + public array $broadcastHeaders = ['Content-Type', 'ETag', 'Last-Modified']; + + /** + * @var array + */ + #[SerializedName('preload_urls')] + public array $urls = []; +} diff --git a/src/Dto/Url.php b/src/Dto/Url.php index 0e9110e..d79562c 100644 --- a/src/Dto/Url.php +++ b/src/Dto/Url.php @@ -18,4 +18,20 @@ final class Url * @var array */ public array $params = []; + + /** + * @param array $params + */ + public static function create( + string $path, + array $params = [], + int $pathTypeReference = UrlGeneratorInterface::ABSOLUTE_PATH + ): self { + $url = new self(); + $url->path = $path; + $url->pathTypeReference = $pathTypeReference; + $url->params = $params; + + return $url; + } } diff --git a/src/Resources/config/services.php b/src/Resources/config/services.php index f446019..fb2a613 100644 --- a/src/Resources/config/services.php +++ b/src/Resources/config/services.php @@ -4,6 +4,8 @@ use Facebook\WebDriver\WebDriverDimension; use SpomkyLabs\PwaBundle\CachingStrategy\HasCacheStrategiesInterface; +use SpomkyLabs\PwaBundle\CachingStrategy\PreloadUrlsGeneratorManager; +use SpomkyLabs\PwaBundle\CachingStrategy\PreloadUrlsTagGenerator; use SpomkyLabs\PwaBundle\Command\CreateIconsCommand; use SpomkyLabs\PwaBundle\Command\CreateScreenshotCommand; use SpomkyLabs\PwaBundle\Command\ListCacheStrategiesCommand; @@ -21,11 +23,14 @@ use SpomkyLabs\PwaBundle\Subscriber\PwaDevServerSubscriber; use SpomkyLabs\PwaBundle\Subscriber\ServiceWorkerCompileEventListener; use SpomkyLabs\PwaBundle\Subscriber\WorkboxCompileEventListener; +use SpomkyLabs\PwaBundle\Twig\InstanceOfExtension; use SpomkyLabs\PwaBundle\Twig\PwaExtension; use SpomkyLabs\PwaBundle\Twig\PwaRuntime; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symfony\Component\Mime\MimeTypes; use Symfony\Component\Panther\Client; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use function Symfony\Component\DependencyInjection\Loader\Configurator\abstract_arg; use function Symfony\Component\DependencyInjection\Loader\Configurator\param; use function Symfony\Component\DependencyInjection\Loader\Configurator\service; @@ -126,6 +131,18 @@ ; $container->load('SpomkyLabs\\PwaBundle\\MatchCallbackHandler\\', '../../MatchCallbackHandler/*'); + $container->set(PreloadUrlsGeneratorManager::class); + $container->instanceof(UrlGeneratorInterface::class) + ->tag('spomky_labs_pwa.preload_urls_generator') + ; + $container->set(PreloadUrlsTagGenerator::class) + ->abstract() + ->args([ + '$alias' => abstract_arg('alias'), + '$urls' => abstract_arg('urls'), + ]) + ; + if ($configurator->env() !== 'prod') { $container->set(PwaCollector::class) ->tag('data_collector', [ @@ -133,5 +150,6 @@ 'id' => 'pwa', ]) ; + $container->set(InstanceOfExtension::class); } }; diff --git a/src/SpomkyLabsPwaBundle.php b/src/SpomkyLabsPwaBundle.php index f698c7f..77e87f4 100644 --- a/src/SpomkyLabsPwaBundle.php +++ b/src/SpomkyLabsPwaBundle.php @@ -4,6 +4,7 @@ namespace SpomkyLabs\PwaBundle; +use SpomkyLabs\PwaBundle\Attribute\PreloadUrlCompilerPass; use SpomkyLabs\PwaBundle\ImageProcessor\ImageProcessorInterface; use SpomkyLabs\PwaBundle\Subscriber\PwaDevServerSubscriber; use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator; @@ -21,6 +22,11 @@ public function configure(DefinitionConfigurator $definition): void $definition->import('Resources/config/definition/*.php'); } + public function build(ContainerBuilder $container): void + { + $container->addCompilerPass(new PreloadUrlCompilerPass()); + } + public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void { $container->import('Resources/config/services.php'); diff --git a/src/Twig/InstanceOfExtension.php b/src/Twig/InstanceOfExtension.php new file mode 100644 index 0000000..7e5cbcc --- /dev/null +++ b/src/Twig/InstanceOfExtension.php @@ -0,0 +1,38 @@ +isInstanceOf(...))]; + } + + public function getFilters(): array + { + return [new TwigFilter('instanceof', $this->isInstanceOf(...))]; + } + + public function getTests(): array + { + return [new TwigTest('instanceof', $this->isInstanceOf(...))]; + } + + /** + * @param class-string $instance + */ + public function isInstanceOf(object $var, string $instance): bool + { + $reflexionClass = new ReflectionClass($instance); + return $reflexionClass->isInstance($var); + } +} diff --git a/src/WorkboxPlugin/BackgroundSyncPlugin.php b/src/WorkboxPlugin/BackgroundSyncPlugin.php index 825a528..c1c9bea 100644 --- a/src/WorkboxPlugin/BackgroundSyncPlugin.php +++ b/src/WorkboxPlugin/BackgroundSyncPlugin.php @@ -4,7 +4,7 @@ namespace SpomkyLabs\PwaBundle\WorkboxPlugin; -final readonly class BackgroundSyncPlugin implements CachePluginInterface +final readonly class BackgroundSyncPlugin implements CachePluginInterface, HasDebugInterface { private const NAME = 'BackgroundSyncPlugin'; @@ -60,4 +60,14 @@ public static function create( ): static { return new self($queueName, $forceSyncFallback, $broadcastChannel, $maxRetentionTime); } + + public function getDebug(): array + { + return [ + 'queueName' => $this->queueName, + 'forceSyncFallback' => $this->forceSyncFallback, + 'broadcastChannel' => $this->broadcastChannel, + 'maxRetentionTime' => $this->maxRetentionTime, + ]; + } } diff --git a/src/WorkboxPlugin/BroadcastUpdatePlugin.php b/src/WorkboxPlugin/BroadcastUpdatePlugin.php index 6a97005..8d5011d 100644 --- a/src/WorkboxPlugin/BroadcastUpdatePlugin.php +++ b/src/WorkboxPlugin/BroadcastUpdatePlugin.php @@ -4,7 +4,7 @@ namespace SpomkyLabs\PwaBundle\WorkboxPlugin; -final readonly class BroadcastUpdatePlugin implements CachePluginInterface +final readonly class BroadcastUpdatePlugin implements CachePluginInterface, HasDebugInterface { private const NAME = 'BroadcastUpdatePlugin'; @@ -44,4 +44,11 @@ public static function create(array $headersToCheck = []): static { return new self($headersToCheck); } + + public function getDebug(): array + { + return [ + 'headersToCheck' => $this->headersToCheck, + ]; + } } diff --git a/src/WorkboxPlugin/CacheableResponsePlugin.php b/src/WorkboxPlugin/CacheableResponsePlugin.php index b55ccb2..011d145 100644 --- a/src/WorkboxPlugin/CacheableResponsePlugin.php +++ b/src/WorkboxPlugin/CacheableResponsePlugin.php @@ -4,7 +4,7 @@ namespace SpomkyLabs\PwaBundle\WorkboxPlugin; -final readonly class CacheableResponsePlugin implements CachePluginInterface +final readonly class CacheableResponsePlugin implements CachePluginInterface, HasDebugInterface { private const NAME = 'CacheableResponsePlugin'; @@ -49,4 +49,12 @@ public function getName(): string { return self::NAME; } + + public function getDebug(): array + { + return [ + 'statuses' => $this->options['statuses'] ?? [], + 'headers' => $this->options['headers'] ?? [], + ]; + } } diff --git a/src/WorkboxPlugin/ExpirationPlugin.php b/src/WorkboxPlugin/ExpirationPlugin.php index dff905a..79eb733 100644 --- a/src/WorkboxPlugin/ExpirationPlugin.php +++ b/src/WorkboxPlugin/ExpirationPlugin.php @@ -4,7 +4,7 @@ namespace SpomkyLabs\PwaBundle\WorkboxPlugin; -final readonly class ExpirationPlugin implements CachePluginInterface +final readonly class ExpirationPlugin implements CachePluginInterface, HasDebugInterface { private const NAME = 'ExpirationPlugin'; @@ -35,4 +35,12 @@ public function getName(): string { return self::NAME; } + + public function getDebug(): array + { + return [ + 'maxEntries' => $this->options['maxEntries'] ?? null, + 'maxAgeSeconds' => $this->options['maxAgeSeconds'] ?? null, + ]; + } } diff --git a/src/WorkboxPlugin/HasDebugInterface.php b/src/WorkboxPlugin/HasDebugInterface.php new file mode 100644 index 0000000..2b6132f --- /dev/null +++ b/src/WorkboxPlugin/HasDebugInterface.php @@ -0,0 +1,13 @@ + + */ + public function getDebug(): array; +} diff --git a/src/WorkboxPlugin/RangeRequestsPlugin.php b/src/WorkboxPlugin/RangeRequestsPlugin.php index d4adf80..38edf96 100644 --- a/src/WorkboxPlugin/RangeRequestsPlugin.php +++ b/src/WorkboxPlugin/RangeRequestsPlugin.php @@ -4,7 +4,7 @@ namespace SpomkyLabs\PwaBundle\WorkboxPlugin; -final readonly class RangeRequestsPlugin implements CachePluginInterface +final readonly class RangeRequestsPlugin implements CachePluginInterface, HasDebugInterface { private const NAME = 'RangeRequestsPlugin'; @@ -13,7 +13,7 @@ public function render(int $jsonOptions = 0): string return 'new workbox.rangeRequests.RangeRequestsPlugin()'; } - public static function create(): static + public static function create(): self { return new self(); } @@ -22,4 +22,9 @@ public function getName(): string { return self::NAME; } + + public function getDebug(): array + { + return []; + } } diff --git a/templates/Collector/serviceworker-tab.html.twig b/templates/Collector/serviceworker-tab.html.twig index af145d1..6de1d18 100644 --- a/templates/Collector/serviceworker-tab.html.twig +++ b/templates/Collector/serviceworker-tab.html.twig @@ -140,10 +140,19 @@ {{ cachingStrategy.matchCallback }}
{{ cachingStrategy.getMethod() ?? "GET" }} - - {% for plugin in cachingStrategy.plugins %} - {{ dump(plugin) }} - {% endfor %} + + {% if cachingStrategy.plugins|length == 0 %} + None + {% else %} + {% for plugin in cachingStrategy.plugins %} + {% if plugin is instanceof('SpomkyLabs\\PwaBundle\\WorkboxPlugin\\HasDebugInterface') %} +

{{ plugin.getName() }}

+
{{ plugin.debug()|json_encode(constant('JSON_PRETTY_PRINT'))|nl2br }}
+ {% else %} + {{ dump(plugin) }} + {% endif %} + {% endfor %} + {% endif %} {% set preloadedUrls = cachingStrategy.preloadUrls|length %} diff --git a/tests/Controller/DummyController.php b/tests/Controller/OtherPagesController.php similarity index 54% rename from tests/Controller/DummyController.php rename to tests/Controller/OtherPagesController.php index 032af58..4fd4883 100644 --- a/tests/Controller/DummyController.php +++ b/tests/Controller/OtherPagesController.php @@ -10,18 +10,8 @@ /** * @internal */ -final class DummyController extends AbstractController +final class OtherPagesController extends AbstractController { - #[Route('/privacy-policy', name: 'privacy_policy')] - public function privacyPolicy(string $param1): void - { - } - - #[Route('/terms-of-service', name: 'terms_of_service')] - public function tos(string $param1): void - { - } - #[Route('/audio-file-handler/{param1}', name: 'audio_file_handler')] public function dummy1(string $param1): void { @@ -36,14 +26,4 @@ public function dummy2(string $param1, string $param2): void public function agenda(string $date): void { } - - #[Route('/widget/template', name: 'app_widget_template')] - public function widgetTemplate(): void - { - } - - #[Route('/widget/data', name: 'app_widget_data')] - public function widgetData(): void - { - } } diff --git a/tests/Controller/StaticPagesController.php b/tests/Controller/StaticPagesController.php new file mode 100644 index 0000000..44a7ba5 --- /dev/null +++ b/tests/Controller/StaticPagesController.php @@ -0,0 +1,26 @@ +services() - ->set(DummyImageProcessor::class); - $container->services() - ->set('asset_mapper.local_public_assets_filesystem', TestFilesystem::class) + $services = $container + ->services() + ->defaults() + ->autowire() ->autoconfigure() - ->autowire(); - - $container->services() - ->load('SpomkyLabs\\PwaBundle\\Tests\\Controller\\', __DIR__ . '/Controller/') - ->tag('controller.service_arguments') + ; + $services->set(DummyImageProcessor::class); + $services->set('asset_mapper.local_public_assets_filesystem', TestFilesystem::class); + $services->load('SpomkyLabs\\PwaBundle\\Tests\\Controller\\', __DIR__ . '/Controller/'); + $services + ->set(DummyUrlsGenerator::class) + ->tag('spomky_labs_pwa.preload_urls_generator') ; $container->extension('framework', [ @@ -232,7 +235,7 @@ 'strategy' => 'StaleWhileRevalidate', 'cache_name' => 'page-cache', 'broadcast' => true, - 'preload_urls' => ['privacy_policy', 'terms_of_service'], + 'preload_urls' => ['privacy_policy', 'terms_of_service', '@static-pages', '@widgets'], ], ], 'offline_fallback' => [