From 07da81167a08c80af69faf4555428f565d27c01e Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Thu, 21 Mar 2024 07:03:01 +0100 Subject: [PATCH] features: CachePlugin and CacheStrategy are now interfaces --- phpstan-baseline.neon | 50 +++-- src/CachingStrategy/AssetCache.php | 36 ++-- src/CachingStrategy/BackgroundSync.php | 14 +- src/CachingStrategy/CacheStrategy.php | 15 +- src/CachingStrategy/FontCache.php | 38 ++-- src/CachingStrategy/GoogleFontCache.php | 21 +- src/CachingStrategy/ImageCache.php | 20 +- src/CachingStrategy/ManifestCache.php | 8 +- src/CachingStrategy/ResourceCaches.php | 33 +-- src/CachingStrategy/WorkboxCacheStrategy.php | 192 +++++++++++++----- src/Command/ListCacheStrategiesCommand.php | 18 +- .../RegexMatchCallbackHandler.php | 18 -- .../AppendCacheStrategies.php | 21 +- src/WorkboxPlugin/BackgroundSyncPlugin.php | 44 ++-- src/WorkboxPlugin/BroadcastUpdatePlugin.php | 33 ++- src/WorkboxPlugin/CachePlugin.php | 13 +- src/WorkboxPlugin/CacheableResponsePlugin.php | 37 +++- src/WorkboxPlugin/ExpirationPlugin.php | 29 ++- src/WorkboxPlugin/RangeRequestsPlugin.php | 11 +- 19 files changed, 393 insertions(+), 258 deletions(-) delete mode 100644 src/MatchCallbackHandler/RegexMatchCallbackHandler.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 33ad337..3bf735d 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,27 +1,52 @@ parameters: ignoreErrors: - - message: "#^Parameter \\#1 \\$name of static method SpomkyLabs\\\\PwaBundle\\\\CachingStrategy\\\\WorkboxCacheStrategy\\:\\:create\\(\\) expects string, string\\|null given\\.$#" + message: "#^Only iterables can be unpacked, mixed given in argument \\#1\\.$#" count: 1 path: src/CachingStrategy/AssetCache.php - - message: "#^Parameter \\#8 \\$preloadUrls of static method SpomkyLabs\\\\PwaBundle\\\\CachingStrategy\\\\WorkboxCacheStrategy\\:\\:create\\(\\) expects array\\, mixed given\\.$#" + message: "#^Parameter \\#1 \\$name of method SpomkyLabs\\\\PwaBundle\\\\CachingStrategy\\\\WorkboxCacheStrategy\\:\\:withName\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/CachingStrategy/AssetCache.php + + - + message: "#^Parameter \\#1 \\$preloadUrl of method SpomkyLabs\\\\PwaBundle\\\\CachingStrategy\\\\WorkboxCacheStrategy\\:\\:withPreloadUrl\\(\\) expects string, mixed given\\.$#" count: 1 path: src/CachingStrategy/AssetCache.php - message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, mixed given\\.$#" count: 1 + path: src/CachingStrategy/AssetCache.php + + - + message: "#^Only iterables can be unpacked, mixed given in argument \\#1\\.$#" + count: 1 path: src/CachingStrategy/FontCache.php - - message: "#^Parameter \\#8 \\$preloadUrls of static method SpomkyLabs\\\\PwaBundle\\\\CachingStrategy\\\\WorkboxCacheStrategy\\:\\:create\\(\\) expects array\\, mixed given\\.$#" + message: "#^Parameter \\#1 \\$preloadUrl of method SpomkyLabs\\\\PwaBundle\\\\CachingStrategy\\\\WorkboxCacheStrategy\\:\\:withPreloadUrl\\(\\) expects string, mixed given\\.$#" count: 1 path: src/CachingStrategy/FontCache.php - - message: "#^Parameter \\#8 \\$preloadUrls of static method SpomkyLabs\\\\PwaBundle\\\\CachingStrategy\\\\WorkboxCacheStrategy\\:\\:create\\(\\) expects array\\, mixed given\\.$#" + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, mixed given\\.$#" + count: 2 + path: src/CachingStrategy/FontCache.php + + - + message: "#^Only iterables can be unpacked, mixed given in argument \\#1\\.$#" + count: 1 + path: src/CachingStrategy/ResourceCaches.php + + - + message: "#^Parameter \\#1 \\$preloadUrl of method SpomkyLabs\\\\PwaBundle\\\\CachingStrategy\\\\WorkboxCacheStrategy\\:\\:withPreloadUrl\\(\\) expects string, mixed given\\.$#" + count: 1 + path: src/CachingStrategy/ResourceCaches.php + + - + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, mixed given\\.$#" count: 1 path: src/CachingStrategy/ResourceCaches.php @@ -703,19 +728,4 @@ parameters: - message: "#^Property SpomkyLabs\\\\PwaBundle\\\\Subscriber\\\\PwaDevServerSubscriber\\:\\:\\$jsonOptions type has no value type specified in iterable type array\\.$#" count: 1 - path: src/Subscriber/PwaDevServerSubscriber.php - - - - message: "#^Part \\$broadcastChannel \\(mixed\\) of encapsed string cannot be cast to string\\.$#" - count: 1 - path: src/WorkboxPlugin/BackgroundSyncPlugin.php - - - - message: "#^Part \\$maxRetentionTime \\(mixed\\) of encapsed string cannot be cast to string\\.$#" - count: 1 - path: src/WorkboxPlugin/BackgroundSyncPlugin.php - - - - message: "#^Part \\$queueName \\(mixed\\) of encapsed string cannot be cast to string\\.$#" - count: 2 - path: src/WorkboxPlugin/BackgroundSyncPlugin.php \ No newline at end of file + path: src/Subscriber/PwaDevServerSubscriber.php \ No newline at end of file diff --git a/src/CachingStrategy/AssetCache.php b/src/CachingStrategy/AssetCache.php index 8cd8f5a..927ccf4 100644 --- a/src/CachingStrategy/AssetCache.php +++ b/src/CachingStrategy/AssetCache.php @@ -49,23 +49,25 @@ public function getCacheStrategies(): array $urls = json_decode($this->serializer->serialize($this->getAssets(), 'json', [ JsonEncode::OPTIONS => $this->jsonOptions, ]), true); - return [ - WorkboxCacheStrategy::create( - $this->workbox->assetCache->cacheName, - CacheStrategy::STRATEGY_CACHE_FIRST, - sprintf("({url}) => url.pathname.startsWith('%s')", $this->assetPublicPrefix), - $this->workbox->enabled && $this->workbox->assetCache->enabled, - true, - null, - [ - ExpirationPlugin::create( - count($this->getAssets()) * 2, - $this->workbox->assetCache->maxAgeInSeconds() ?? 60 * 60 * 24 * 365, - ), - ], - $urls - ), - ]; + + $strategy = WorkboxCacheStrategy::create( + $this->workbox->enabled && $this->workbox->assetCache->enabled, + true, + CacheStrategy::STRATEGY_CACHE_FIRST, + sprintf("({url}) => url.pathname.startsWith('%s')", $this->assetPublicPrefix), + ) + ->withName($this->workbox->assetCache->cacheName) + ->withPlugin( + ExpirationPlugin::create( + count($this->getAssets()) * 2, + $this->workbox->assetCache->maxAgeInSeconds() ?? 60 * 60 * 24 * 365, + ), + ); + + if (count($urls) > 0) { + $strategy = $strategy->withPreloadUrl(...$urls); + } + return [$strategy]; } /** diff --git a/src/CachingStrategy/BackgroundSync.php b/src/CachingStrategy/BackgroundSync.php index 9316a8e..29c9d89 100644 --- a/src/CachingStrategy/BackgroundSync.php +++ b/src/CachingStrategy/BackgroundSync.php @@ -33,21 +33,21 @@ public function getCacheStrategies(): array $strategies = []; foreach ($this->workbox->backgroundSync as $sync) { $strategies[] = WorkboxCacheStrategy::create( - 'BackgroundSync API', - CacheStrategy::STRATEGY_NETWORK_ONLY, - $this->prepareMatchCallback($sync->matchCallback), $this->workbox->enabled, true, - null, - [ + CacheStrategy::STRATEGY_NETWORK_ONLY, + $this->prepareMatchCallback($sync->matchCallback) + ) + ->withName('Background Sync') + ->withPlugin( BackgroundSyncPlugin::create( $sync->queueName, $sync->maxRetentionTime, $sync->forceSyncFallback, $sync->broadcastChannel ), - ] - ); + ) + ->withMethod($sync->method); } return $strategies; diff --git a/src/CachingStrategy/CacheStrategy.php b/src/CachingStrategy/CacheStrategy.php index bc4f459..ccfb686 100644 --- a/src/CachingStrategy/CacheStrategy.php +++ b/src/CachingStrategy/CacheStrategy.php @@ -4,7 +4,7 @@ namespace SpomkyLabs\PwaBundle\CachingStrategy; -abstract readonly class CacheStrategy +interface CacheStrategy { public const STRATEGY_CACHE_FIRST = 'CacheFirst'; @@ -24,12 +24,11 @@ self::STRATEGY_STALE_WHILE_REVALIDATE, ]; - public function __construct( - public string $name, - public bool $enabled, - public bool $requireWorkbox, - ) { - } + public function getName(): ?string; - abstract public function render(string $cacheObjectName, int $jsonOptions = 0): string; + public function isEnabled(): bool; + + public function needsWorkbox(): bool; + + public function render(string $cacheObjectName, bool $debug = false): string; } diff --git a/src/CachingStrategy/FontCache.php b/src/CachingStrategy/FontCache.php index 050eb41..775e6f3 100644 --- a/src/CachingStrategy/FontCache.php +++ b/src/CachingStrategy/FontCache.php @@ -46,24 +46,26 @@ public function getCacheStrategies(): array ]), true); $maxEntries = count($urls) + ($this->workbox->fontCache->maxEntries ?? 60); - return [ - WorkboxCacheStrategy::create( - $this->workbox->fontCache->cacheName ?? 'fonts', - CacheStrategy::STRATEGY_CACHE_FIRST, - "({request}) => request.destination === 'font'", - $this->workbox->enabled && $this->workbox->fontCache->enabled, - true, - null, - [ - ExpirationPlugin::create( - $maxEntries, - $this->workbox->fontCache->maxAgeInSeconds() ?? 60 * 60 * 24 * 365, - ), - CacheableResponsePlugin::create(), - ], - $urls - ), - ]; + $strategy = WorkboxCacheStrategy::create( + $this->workbox->enabled && $this->workbox->fontCache->enabled, + true, + CacheStrategy::STRATEGY_CACHE_FIRST, + "({request}) => request.destination === 'font'" + ) + ->withName($this->workbox->fontCache->cacheName ?? 'fonts') + ->withMethod('GET') + ->withPlugin( + CacheableResponsePlugin::create(), + ExpirationPlugin::create( + $maxEntries, + $this->workbox->fontCache->maxAgeInSeconds() ?? 60 * 60 * 24 * 365 + ), + ); + if (count($urls) > 0) { + $strategy = $strategy->withPreloadUrl(...$urls); + } + + return [$strategy]; } /** diff --git a/src/CachingStrategy/GoogleFontCache.php b/src/CachingStrategy/GoogleFontCache.php index 12db19e..94cf243 100644 --- a/src/CachingStrategy/GoogleFontCache.php +++ b/src/CachingStrategy/GoogleFontCache.php @@ -28,27 +28,26 @@ public function getCacheStrategies(): array return [ WorkboxCacheStrategy::create( - $prefix . 'google-fonts-stylesheets', + $this->workbox->enabled && $this->workbox->googleFontCache->enabled, + true, CacheStrategy::STRATEGY_STALE_WHILE_REVALIDATE, "({url}) => url.origin === 'https://fonts.googleapis.com'", - $this->workbox->enabled && $this->workbox->googleFontCache->enabled, - true - ), + ) + ->withName($prefix . 'google-fonts-stylesheets'), WorkboxCacheStrategy::create( - $prefix . 'google-fonts-webfonts', - CacheStrategy::STRATEGY_CACHE_FIRST, - "({url}) => url.origin === 'https://fonts.gstatic.com'", $this->workbox->enabled && $this->workbox->googleFontCache->enabled, true, - null, - [ + CacheStrategy::STRATEGY_CACHE_FIRST, + "({url}) => url.origin === 'https://fonts.gstatic.com'" + ) + ->withName($prefix . 'google-fonts-webfonts') + ->withPlugin( CacheableResponsePlugin::create(), ExpirationPlugin::create( $this->workbox->googleFontCache->maxAgeInSeconds() ?? 60 * 60 * 24 * 365, $this->workbox->googleFontCache->maxEntries ?? 30 ), - ] - ), + ), ]; } } diff --git a/src/CachingStrategy/ImageCache.php b/src/CachingStrategy/ImageCache.php index b5809e3..db46eee 100644 --- a/src/CachingStrategy/ImageCache.php +++ b/src/CachingStrategy/ImageCache.php @@ -6,8 +6,6 @@ use SpomkyLabs\PwaBundle\Dto\ServiceWorker; use SpomkyLabs\PwaBundle\Dto\Workbox; -use SpomkyLabs\PwaBundle\WorkboxPlugin\CacheableResponsePlugin; -use SpomkyLabs\PwaBundle\WorkboxPlugin\ExpirationPlugin; use Symfony\Component\AssetMapper\Path\PublicAssetsPathResolverInterface; use Symfony\Component\DependencyInjection\Attribute\Autowire; @@ -30,23 +28,15 @@ public function getCacheStrategies(): array { return [ WorkboxCacheStrategy::create( - $this->workbox->imageCache->cacheName ?? 'images', + $this->workbox->enabled && $this->workbox->imageCache->enabled, + true, CacheStrategy::STRATEGY_CACHE_FIRST, sprintf( "({request, url}) => (request.destination === 'image' && !url.pathname.startsWith('%s'))", $this->assetPublicPrefix - ), - $this->workbox->enabled && $this->workbox->imageCache->enabled, - true, - null, - [ - CacheableResponsePlugin::create(), - ExpirationPlugin::create( - $this->workbox->imageCache->maxEntries ?? 60, - $this->workbox->imageCache->maxAgeInSeconds() ?? 60 * 60 * 24 * 7 - ), - ] - ), + ) + ) + ->withName($this->workbox->imageCache->cacheName ?? 'images'), ]; } } diff --git a/src/CachingStrategy/ManifestCache.php b/src/CachingStrategy/ManifestCache.php index 3fa985a..4840298 100644 --- a/src/CachingStrategy/ManifestCache.php +++ b/src/CachingStrategy/ManifestCache.php @@ -27,12 +27,12 @@ public function getCacheStrategies(): array { return [ WorkboxCacheStrategy::create( - 'manifest', + $this->workbox->enabled && $this->workbox->cacheManifest, + true, CacheStrategy::STRATEGY_STALE_WHILE_REVALIDATE, sprintf("({url}) => '%s' === url.pathname", $this->manifestPublicUrl), - $this->workbox->enabled && $this->workbox->cacheManifest, - true - ), + ) + ->withName('manifest'), ]; } } diff --git a/src/CachingStrategy/ResourceCaches.php b/src/CachingStrategy/ResourceCaches.php index 6ccb90a..62ed80f 100644 --- a/src/CachingStrategy/ResourceCaches.php +++ b/src/CachingStrategy/ResourceCaches.php @@ -15,6 +15,7 @@ use Symfony\Component\DependencyInjection\Attribute\TaggedIterator; use Symfony\Component\Serializer\Encoder\JsonEncode; use Symfony\Component\Serializer\SerializerInterface; +use function count; use const JSON_PRETTY_PRINT; use const JSON_THROW_ON_ERROR; use const JSON_UNESCAPED_SLASHES; @@ -52,7 +53,7 @@ public function getCacheStrategies(): array $routes = $this->serializer->serialize($resourceCache->urls, 'json', [ JsonEncode::OPTIONS => $this->jsonOptions, ]); - $url = json_decode($routes, true, 512, JSON_THROW_ON_ERROR); + $urls = json_decode($routes, true, 512, JSON_THROW_ON_ERROR); $cacheName = $resourceCache->cacheName ?? sprintf('page-cache-%d', $id); @@ -72,20 +73,22 @@ public function getCacheStrategies(): array $plugins[] = ExpirationPlugin::create($resourceCache->maxEntries, $resourceCache->maxAgeInSeconds()); } - $strategies[] = - WorkboxCacheStrategy::create( - $cacheName, - $resourceCache->strategy, - $this->prepareMatchCallback($resourceCache->matchCallback), - $this->workbox->enabled, - true, - null, - $plugins, - $url, - [ - 'networkTimeoutSeconds' => $resourceCache->networkTimeout, - ] - ); + $strategy = WorkboxCacheStrategy::create( + $this->workbox->enabled, + true, + $resourceCache->strategy, + $this->prepareMatchCallback($resourceCache->matchCallback) + ) + ->withName($cacheName) + ->withPlugin(...$plugins) + ->withOptions([ + 'networkTimeoutSeconds' => $resourceCache->networkTimeout, + ]); + if (count($urls) > 0) { + $strategy = $strategy->withPreloadUrl(...$urls); + } + + $strategies[] = $strategy; } return $strategies; diff --git a/src/CachingStrategy/WorkboxCacheStrategy.php b/src/CachingStrategy/WorkboxCacheStrategy.php index 892fb31..7fb4ec7 100644 --- a/src/CachingStrategy/WorkboxCacheStrategy.php +++ b/src/CachingStrategy/WorkboxCacheStrategy.php @@ -6,62 +6,108 @@ use SpomkyLabs\PwaBundle\WorkboxPlugin\CachePlugin; use function in_array; +use const JSON_PRETTY_PRINT; +use const JSON_THROW_ON_ERROR; +use const JSON_UNESCAPED_SLASHES; +use const JSON_UNESCAPED_UNICODE; -final readonly class WorkboxCacheStrategy extends CacheStrategy +final class WorkboxCacheStrategy implements CacheStrategy { + private null|string $name = null; + + private null|string $method = null; + /** - * @param array $plugins - * @param array $preloadUrls - * @param array $options + * @var array + */ + private array $plugins = []; + + /** + * @var array */ + private array $preloadUrls = []; + + /** + * @var array + */ + private array $options = []; + public function __construct( - string $name, - public string $strategy, - public string $matchCallback, - bool $enabled, - bool $requireWorkbox, - public null|string $method = null, - public array $plugins = [], - public array $preloadUrls = [], - public array $options = [], + public readonly bool $enabled, + public readonly bool $needsWorkbox, + public readonly string $strategy, + public readonly string $matchCallback, ) { - parent::__construct($name, $enabled, $requireWorkbox); } - /** - * @param array $plugins - * @param array $preloadUrls - * @param array $options - */ public static function create( - string $name, - string $strategy, - string $matchCallback, bool $enabled, bool $requireWorkbox, - null|string $method = null, - array $plugins = [], - array $preloadUrls = [], - array $options = [], + string $strategy, + string $matchCallback, ): static { - return new static( - $name, - $strategy, - $matchCallback, - $enabled, - $requireWorkbox, - $method, - $plugins, - $preloadUrls, - $options - ); + return new static($enabled, $requireWorkbox, $strategy, $matchCallback,); + } + + public function withName(string $name): static + { + $this->name = $name; + + return $this; + } + + public function withMethod(string $method): static + { + $this->method = $method; + + return $this; + } + + public function withPlugin(CachePlugin $plugin, CachePlugin ...$plugins): static + { + $this->plugins = array_merge([$plugin], $plugins); + return $this; + } + + public function withPreloadUrl(string $preloadUrl, string ...$preloadUrls): static + { + $this->preloadUrls = array_merge([$preloadUrl], $preloadUrls); + return $this; + } + + /** + * @param array $options + */ + public function withOptions(array $options): static + { + $this->options = $options; + return $this; + } + + public function getName(): ?string + { + return $this->name; + } + + public function isEnabled(): bool + { + return $this->enabled; } - public function render(string $cacheObjectName, int $jsonOptions = 0): string + public function needsWorkbox(): bool + { + return $this->needsWorkbox; + } + + public function render(string $cacheObjectName, bool $debug = false): string { if ($this->enabled === false) { return ''; } + $jsonOptions = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR; + if ($debug === true) { + $jsonOptions |= JSON_PRETTY_PRINT; + } $timeout = ''; if (in_array( @@ -73,22 +119,39 @@ public function render(string $cacheObjectName, int $jsonOptions = 0): string } $cacheName = ''; if ($this->strategy !== self::STRATEGY_NETWORK_ONLY) { - $cacheName = sprintf("cacheName: '%s',", $this->name ?? $cacheObjectName); + $cacheName = sprintf("cacheName: '%s',", $this->getName() ?? $cacheObjectName); } $plugins = sprintf('[%s]', implode(', ', array_map( fn (CachePlugin $plugin) => $plugin->render($jsonOptions), $this->plugins ))); + $method = $this->method !== null ? ",'{$this->method}'" : ''; + + $declaration = ''; + if ($debug) { + $declaration .= <<strategy} +// Match: {$this->matchCallback} +// Cache Name: {$this->getName()} +// Enabled: {$this->enabled} +// Needs Workbox: {$this->needsWorkbox()} +// Method: {$this->method} + + +DEBUG_STATEMENT; + } + + $declaration .= <<strategy}({ {$timeout}{$cacheName}plugins: {$plugins} }); -workbox.routing.registerRoute( - {$this->matchCallback}, - {$cacheObjectName} -); -FONT_CACHE_RULE_STRATEGY; +workbox.routing.registerRoute({$this->matchCallback},{$cacheObjectName}{$method}); + +ROUTE_REGISTRATION; if ($this->preloadUrls !== []) { $fontUrls = json_encode($this->preloadUrls, $jsonOptions); @@ -103,9 +166,46 @@ public function render(string $cacheObjectName, int $jsonOptions = 0): string ); event.waitUntil(Promise.all(done)); }); + + ASSET_CACHE_RULE_PRELOAD; } - return trim($declaration); + $declaration .= <<method; + } + + /** + * @return array + */ + public function getPlugins(): array + { + return $this->plugins; + } + + /** + * @return array + */ + public function getPreloadUrls(): array + { + return $this->preloadUrls; + } + + /** + * @return array + */ + public function getOptions(): array + { + return $this->options; } } diff --git a/src/Command/ListCacheStrategiesCommand.php b/src/Command/ListCacheStrategiesCommand.php index f20efd2..52075b5 100644 --- a/src/Command/ListCacheStrategiesCommand.php +++ b/src/Command/ListCacheStrategiesCommand.php @@ -43,22 +43,22 @@ protected function execute(InputInterface $input, OutputInterface $output): int foreach ($strategies as $strategy) { if ($strategy instanceof WorkboxCacheStrategy) { $table->addRow([ - $strategy->name, + $strategy->getName(), $strategy->strategy, $strategy->matchCallback, - $strategy->enabled ? 'Yes' : 'No', - $strategy->requireWorkbox ? 'Yes' : 'No', - Yaml::dump(array_map(fn (CachePlugin $v): string => $v->name, $strategy->plugins)), - count($strategy->preloadUrls), - Yaml::dump($strategy->options), + $strategy->isEnabled() ? 'Yes' : 'No', + $strategy->needsWorkbox() ? 'Yes' : 'No', + Yaml::dump(array_map(fn (CachePlugin $v): string => $v->getName(), $strategy->getPlugins())), + count($strategy->getPreloadUrls()), + Yaml::dump($strategy->getOptions()), ]); } else { $table->addRow([ - $strategy->name, + $strategy->getName(), '---', '---', - $strategy->enabled ? 'Yes' : 'No', - $strategy->requireWorkbox ? 'Yes' : 'No', + $strategy->isEnabled() ? 'Yes' : 'No', + $strategy->needsWorkbox() ? 'Yes' : 'No', '', '', '', diff --git a/src/MatchCallbackHandler/RegexMatchCallbackHandler.php b/src/MatchCallbackHandler/RegexMatchCallbackHandler.php deleted file mode 100644 index 3dbb669..0000000 --- a/src/MatchCallbackHandler/RegexMatchCallbackHandler.php +++ /dev/null @@ -1,18 +0,0 @@ - $cacheStrategies */ @@ -24,13 +18,8 @@ public function __construct( #[TaggedIterator('spomky_labs_pwa.cache_strategy')] private iterable $cacheStrategies, #[Autowire('%kernel.debug%')] - bool $debug, + public bool $debug, ) { - $options = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR; - if ($debug === true) { - $options |= JSON_PRETTY_PRINT; - } - $this->jsonOptions = $options; } public function process(): string @@ -38,14 +27,14 @@ public function process(): string $body = ''; foreach ($this->cacheStrategies as $idCacheStrategy => $cacheStrategy) { foreach ($cacheStrategy->getCacheStrategies() as $idStrategy => $strategy) { - if ($strategy->enabled === false) { + if ($strategy->isEnabled() === false) { continue; } - $body .= PHP_EOL . trim($strategy->render( + $body .= PHP_EOL . $strategy->render( sprintf('cache_%d_%d', $idCacheStrategy, $idStrategy), - $this->jsonOptions - )); + $this->debug + ); } } diff --git a/src/WorkboxPlugin/BackgroundSyncPlugin.php b/src/WorkboxPlugin/BackgroundSyncPlugin.php index e8ee165..21c8008 100644 --- a/src/WorkboxPlugin/BackgroundSyncPlugin.php +++ b/src/WorkboxPlugin/BackgroundSyncPlugin.php @@ -4,16 +4,28 @@ namespace SpomkyLabs\PwaBundle\WorkboxPlugin; -final readonly class BackgroundSyncPlugin extends CachePlugin +final readonly class BackgroundSyncPlugin implements CachePlugin { + private const NAME = 'BackgroundSyncPlugin'; + + public function __construct( + public string $queueName, + public bool $forceSyncFallback, + public null|string $broadcastChannel, + public int $maxRetentionTime, + ) { + } + + public function getName(): string + { + return self::NAME; + } + public function render(int $jsonOptions = 0): string { - $forceSyncFallback = $this->options['forceSyncFallback'] === true ? 'true' : 'false'; - $broadcastChannel = $this->options['broadcastChannel']; - $maxRetentionTime = $this->options['maxRetentionTime']; - $queueName = $this->options['queueName']; + $forceSyncFallback = $this->forceSyncFallback === true ? 'true' : 'false'; $broadcastChannelSection = ''; - if ($broadcastChannel !== null) { + if ($this->broadcastChannel !== null) { $broadcastChannelSection = << { try { @@ -22,8 +34,8 @@ public function render(int $jsonOptions = 0): string // Failed to replay one or more requests } finally { remainingRequests = await queue.getAll(); - const bc = new BroadcastChannel('{$broadcastChannel}'); - bc.postMessage({name: '{$queueName}', remaining: remainingRequests.length}); + const bc = new BroadcastChannel('{$this->broadcastChannel}'); + bc.postMessage({name: '{$this->queueName}', remaining: remainingRequests.length}); bc.close(); } } @@ -31,8 +43,8 @@ public function render(int $jsonOptions = 0): string } $declaration = <<queueName}',{ + "maxRetentionTime": {$this->maxRetentionTime}, "forceSyncFallback": {$forceSyncFallback}{$broadcastChannelSection} }) BACKGROUND_SYNC_RULE_STRATEGY; @@ -44,16 +56,8 @@ public static function create( string $queueName, int $maxRetentionTime, bool $forceSyncFallback, - ?string $broadcastChannel + null|string $broadcastChannel ): static { - return new self( - 'BackgroundSyncPlugin', - [ - 'queueName' => $queueName, - 'maxRetentionTime' => $maxRetentionTime, - 'forceSyncFallback' => $forceSyncFallback, - 'broadcastChannel' => $broadcastChannel, - ] - ); + return new self($queueName, $forceSyncFallback, $broadcastChannel, $maxRetentionTime); } } diff --git a/src/WorkboxPlugin/BroadcastUpdatePlugin.php b/src/WorkboxPlugin/BroadcastUpdatePlugin.php index c8e527b..a50ee43 100644 --- a/src/WorkboxPlugin/BroadcastUpdatePlugin.php +++ b/src/WorkboxPlugin/BroadcastUpdatePlugin.php @@ -4,13 +4,36 @@ namespace SpomkyLabs\PwaBundle\WorkboxPlugin; -final readonly class BroadcastUpdatePlugin extends CachePlugin +final readonly class BroadcastUpdatePlugin implements CachePlugin { + private const NAME = 'BroadcastUpdatePlugin'; + + /** + * @var array + */ + private array $headersToCheck; + + /** + * @param array $headersToCheck + */ + public function __construct( + array $headersToCheck = [] + ) { + $this->headersToCheck = $headersToCheck === [] ? ['Content-Type', 'ETag', 'Last-Modified'] : $headersToCheck; + } + + public function getName(): string + { + return self::NAME; + } + public function render(int $jsonOptions = 0): string { return sprintf( 'new workbox.broadcastUpdate.BroadcastUpdatePlugin(%s)', - json_encode($this->options, $jsonOptions) + json_encode([ + 'headersToCheck' => $this->headersToCheck, + ], $jsonOptions) ); } @@ -19,10 +42,6 @@ public function render(int $jsonOptions = 0): string */ public static function create(array $headersToCheck = []): static { - $headersToCheck = $headersToCheck === [] ? ['Content-Type', 'ETag', 'Last-Modified'] : $headersToCheck; - - return new self('BroadcastUpdatePlugin', [ - 'headersToCheck' => $headersToCheck, - ]); + return new self($headersToCheck); } } diff --git a/src/WorkboxPlugin/CachePlugin.php b/src/WorkboxPlugin/CachePlugin.php index cba3e23..c778a8b 100644 --- a/src/WorkboxPlugin/CachePlugin.php +++ b/src/WorkboxPlugin/CachePlugin.php @@ -4,16 +4,9 @@ namespace SpomkyLabs\PwaBundle\WorkboxPlugin; -abstract readonly class CachePlugin +interface CachePlugin { - /** - * @param array $options - */ - public function __construct( - public string $name, - public array $options = [] - ) { - } + public function getName(): string; - abstract public function render(int $jsonOptions = 0): string; + public function render(int $jsonOptions = 0): string; } diff --git a/src/WorkboxPlugin/CacheableResponsePlugin.php b/src/WorkboxPlugin/CacheableResponsePlugin.php index d0b5096..3f1a507 100644 --- a/src/WorkboxPlugin/CacheableResponsePlugin.php +++ b/src/WorkboxPlugin/CacheableResponsePlugin.php @@ -4,8 +4,30 @@ namespace SpomkyLabs\PwaBundle\WorkboxPlugin; -final readonly class CacheableResponsePlugin extends CachePlugin +final readonly class CacheableResponsePlugin implements CachePlugin { + private const NAME = 'CacheableResponsePlugin'; + + /** + * @var array{options?: array{statuses: array, headers?: array}} + */ + private array $options; + + /** + * @param array $statuses + * @param array $headers + */ + public function __construct(array $statuses = [0, 200], array $headers = []) + { + $options = array_filter([ + 'statuses' => $statuses, + 'headers' => $headers, + ], fn ($value) => $value !== []); + $this->options = $options === [] ? [ + 'statuses' => [0, 200], + ] : $options; + } + public function render(int $jsonOptions = 0): string { return sprintf( @@ -20,14 +42,11 @@ public function render(int $jsonOptions = 0): string */ public static function create(array $statuses = [0, 200], array $headers = []): static { - $options = array_filter([ - 'statuses' => $statuses, - 'headers' => $headers, - ], fn ($value) => $value !== []); - $options = $options === [] ? [ - 'statuses' => [0, 200], - ] : $options; + return new self($statuses, $headers); + } - return new self('CacheableResponsePlugin', $options); + public function getName(): string + { + return self::NAME; } } diff --git a/src/WorkboxPlugin/ExpirationPlugin.php b/src/WorkboxPlugin/ExpirationPlugin.php index d260098..e048d2f 100644 --- a/src/WorkboxPlugin/ExpirationPlugin.php +++ b/src/WorkboxPlugin/ExpirationPlugin.php @@ -4,18 +4,35 @@ namespace SpomkyLabs\PwaBundle\WorkboxPlugin; -final readonly class ExpirationPlugin extends CachePlugin +final readonly class ExpirationPlugin implements CachePlugin { + private const NAME = 'ExpirationPlugin'; + + /** + * @var array{maxEntries: null|int, maxAgeSeconds: null|int} + */ + private array $options; + + public function __construct(null|int $maxEntries, null|int $maxAgeSeconds) + { + $this->options = [ + 'maxEntries' => $maxEntries, + 'maxAgeSeconds' => $maxAgeSeconds, + ]; + } + public function render(int $jsonOptions = 0): string { return sprintf('new workbox.expiration.ExpirationPlugin(%s)', json_encode($this->options, $jsonOptions)); } - public static function create(null|int $maxEntries, null|string|int $maxAgeSeconds): static + public static function create(null|int $maxEntries, null|int $maxAgeSeconds): static { - return new self('ExpirationPlugin', [ - 'maxEntries' => $maxEntries, - 'maxAgeSeconds' => $maxAgeSeconds, - ]); + return new self($maxEntries, $maxAgeSeconds); + } + + public function getName(): string + { + return self::NAME; } } diff --git a/src/WorkboxPlugin/RangeRequestsPlugin.php b/src/WorkboxPlugin/RangeRequestsPlugin.php index 42a0c40..d300855 100644 --- a/src/WorkboxPlugin/RangeRequestsPlugin.php +++ b/src/WorkboxPlugin/RangeRequestsPlugin.php @@ -4,8 +4,10 @@ namespace SpomkyLabs\PwaBundle\WorkboxPlugin; -final readonly class RangeRequestsPlugin extends CachePlugin +final readonly class RangeRequestsPlugin implements CachePlugin { + private const NAME = 'RangeRequestsPlugin'; + public function render(int $jsonOptions = 0): string { return 'new workbox.rangeRequests.RangeRequestsPlugin()'; @@ -13,6 +15,11 @@ public function render(int $jsonOptions = 0): string public static function create(): static { - return new self('RangeRequestsPlugin'); + return new self(); + } + + public function getName(): string + { + return self::NAME; } }