From 6d49d2bc0d3740a7bd12849efe71bd660246b156 Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Sat, 25 May 2024 21:45:03 +0200 Subject: [PATCH] Added logging capabilities for debugging Implemented the CanLogInterface to enable logging capabilities. This provides more insight into events and actions for easier debugging. Updated various classes, including ManifestCache, ResourceCaches, GoogleFontCache, and more. --- composer.json | 2 + phpstan-baseline.neon | 5 ++ src/CachingStrategy/AssetCache.php | 32 +++++++++--- src/CachingStrategy/BackgroundSync.php | 21 ++++++-- src/CachingStrategy/FontCache.php | 29 +++++++++-- src/CachingStrategy/GoogleFontCache.php | 23 +++++++-- src/CachingStrategy/ImageCache.php | 24 +++++++-- src/CachingStrategy/ManifestCache.php | 24 +++++++-- .../PreloadUrlsGeneratorManager.php | 20 +++++++- src/CachingStrategy/ResourceCaches.php | 27 +++++++--- src/CompilerPass/LoggerCompilerPass.php | 31 +++++++++++ .../PreloadUrlCompilerPass.php | 6 ++- src/DataCollector/PwaCollector.php | 1 - src/EventSubscriber/ScreenshotSubscriber.php | 28 ++++++++-- .../DestinationMatchCallbackHandler.php | 22 +++++++- .../ExactPathnameMatchCallbackHandler.php | 22 +++++++- .../NavigationMatchCallbackHandler.php | 22 +++++++- .../OriginMatchCallbackHandler.php | 22 +++++++- .../PathnameEndsWithMatchCallbackHandler.php | 22 +++++++- ...PathnameStartsWithMatchCallbackHandler.php | 22 +++++++- .../RouteMatchCallbackHandler.php | 19 ++++++- src/Resources/config/definition/logging.php | 15 ++++++ src/Resources/config/services.php | 3 ++ src/Service/CanLogInterface.php | 12 +++++ src/Service/FaviconsBuilder.php | 18 ++++++- src/Service/FaviconsCompiler.php | 36 ++++++++++++- src/Service/ManifestCompiler.php | 51 ++++++++++++++++--- src/Service/ServiceWorkerCompiler.php | 33 +++++++++--- src/ServiceWorkerRule/ClearCache.php | 22 +++++++- src/ServiceWorkerRule/OfflineFallback.php | 22 ++++++-- src/ServiceWorkerRule/SkipWaiting.php | 19 ++++++- src/ServiceWorkerRule/WindowsWidgets.php | 20 ++++++-- src/ServiceWorkerRule/WorkboxImport.php | 19 ++++++- src/SpomkyLabsPwaBundle.php | 8 ++- src/Subscriber/FileCompileEventListener.php | 26 ++++++++-- src/Subscriber/PwaDevServerSubscriber.php | 26 +++++++--- tests/AppKernel.php | 3 +- tests/config.php | 9 ++++ 38 files changed, 679 insertions(+), 87 deletions(-) create mode 100644 src/CompilerPass/LoggerCompilerPass.php rename src/{Attribute => CompilerPass}/PreloadUrlCompilerPass.php (97%) create mode 100644 src/Resources/config/definition/logging.php create mode 100644 src/Service/CanLogInterface.php diff --git a/composer.json b/composer.json index 82ac2fa..cdacc0b 100644 --- a/composer.json +++ b/composer.json @@ -33,6 +33,7 @@ "require": { "php": ">=8.2", "phpdocumentor/reflection-docblock": "^5.3", + "psr/log": "^1.0|^2.0|^3.0", "symfony/asset-mapper": "^6.4|^7.0", "symfony/config": "^6.4|^7.0", "symfony/dependency-injection": "^6.4|^7.0", @@ -67,6 +68,7 @@ "symfony/filesystem": "^6.4|^7.0", "symfony/framework-bundle": "^6.4|^7.0", "symfony/mime": "^6.4|^7.0", + "symfony/monolog-bundle": "^3.10", "symfony/panther": "^2.1", "symfony/phpunit-bridge": "^6.4|^7.0", "symfony/translation": "^7.0", diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 22f94e0..0e60f4e 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -445,6 +445,11 @@ parameters: count: 1 path: src/Resources/config/definition/image_processor.php + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeDefinition\\:\\:children\\(\\)\\.$#" + count: 1 + path: src/Resources/config/definition/logging.php + - message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeDefinition\\:\\:children\\(\\)\\.$#" count: 1 diff --git a/src/CachingStrategy/AssetCache.php b/src/CachingStrategy/AssetCache.php index 24e8e98..c4dc039 100644 --- a/src/CachingStrategy/AssetCache.php +++ b/src/CachingStrategy/AssetCache.php @@ -4,8 +4,11 @@ namespace SpomkyLabs\PwaBundle\CachingStrategy; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use SpomkyLabs\PwaBundle\Dto\ServiceWorker; use SpomkyLabs\PwaBundle\Dto\Workbox; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; use SpomkyLabs\PwaBundle\WorkboxPlugin\ExpirationPlugin; use Symfony\Component\AssetMapper\AssetMapperInterface; use Symfony\Component\AssetMapper\Path\PublicAssetsPathResolverInterface; @@ -18,20 +21,22 @@ use const JSON_UNESCAPED_SLASHES; use const JSON_UNESCAPED_UNICODE; -final readonly class AssetCache implements HasCacheStrategiesInterface +final class AssetCache implements HasCacheStrategiesInterface, CanLogInterface { - private int $jsonOptions; + private readonly int $jsonOptions; - private string $assetPublicPrefix; + private readonly string $assetPublicPrefix; - private Workbox $workbox; + private readonly Workbox $workbox; + + private LoggerInterface $logger; public function __construct( ServiceWorker $serviceWorker, #[Autowire(service: 'asset_mapper.public_assets_path_resolver')] PublicAssetsPathResolverInterface $publicAssetsPathResolver, - private AssetMapperInterface $assetMapper, - private SerializerInterface $serializer, + private readonly AssetMapperInterface $assetMapper, + private readonly SerializerInterface $serializer, #[Autowire('%kernel.debug%')] bool $debug, ) { @@ -42,10 +47,12 @@ public function __construct( $options |= JSON_PRETTY_PRINT; } $this->jsonOptions = $options; + $this->logger = new NullLogger(); } public function getCacheStrategies(): array { + $this->logger->debug('Getting cache strategies for assets'); $urls = json_decode($this->serializer->serialize($this->getAssets(), 'json', [ JsonEncode::OPTIONS => $this->jsonOptions, ]), true); @@ -67,9 +74,18 @@ public function getCacheStrategies(): array if (count($urls) > 0) { $strategy = $strategy->withPreloadUrl(...$urls); } + $this->logger->debug('Cache strategy for assets', [ + 'strategies' => [$strategy], + ]); + return [$strategy]; } + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } + /** * @return array */ @@ -81,6 +97,10 @@ private function getAssets(): array $assets[] = $asset->publicPath; } } + $this->logger->debug('Preloading assets', [ + 'assets' => $assets, + ]); + return $assets; } } diff --git a/src/CachingStrategy/BackgroundSync.php b/src/CachingStrategy/BackgroundSync.php index 1386e63..a62ae84 100644 --- a/src/CachingStrategy/BackgroundSync.php +++ b/src/CachingStrategy/BackgroundSync.php @@ -4,15 +4,20 @@ namespace SpomkyLabs\PwaBundle\CachingStrategy; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use SpomkyLabs\PwaBundle\Dto\ServiceWorker; use SpomkyLabs\PwaBundle\Dto\Workbox; use SpomkyLabs\PwaBundle\MatchCallbackHandler\MatchCallbackHandlerInterface; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; use SpomkyLabs\PwaBundle\WorkboxPlugin\BackgroundSyncPlugin; use Symfony\Component\DependencyInjection\Attribute\TaggedIterator; -final readonly class BackgroundSync implements HasCacheStrategiesInterface +final class BackgroundSync implements HasCacheStrategiesInterface, CanLogInterface { - private Workbox $workbox; + private readonly Workbox $workbox; + + private LoggerInterface $logger; /** * @param iterable $matchCallbackHandlers @@ -20,9 +25,10 @@ public function __construct( ServiceWorker $serviceWorker, #[TaggedIterator('spomky_labs_pwa.match_callback_handler')] - private iterable $matchCallbackHandlers, + private readonly iterable $matchCallbackHandlers, ) { $this->workbox = $serviceWorker->workbox; + $this->logger = new NullLogger(); } /** @@ -30,6 +36,7 @@ public function __construct( */ public function getCacheStrategies(): array { + $this->logger->debug('Getting cache strategies for background sync'); $strategies = []; foreach ($this->workbox->backgroundSync as $sync) { $strategies[] = WorkboxCacheStrategy::create( @@ -49,10 +56,18 @@ public function getCacheStrategies(): array ) ->withMethod($sync->method); } + $this->logger->debug('Background sync strategies', [ + 'strategies' => $strategies, + ]); return $strategies; } + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } + private function prepareMatchCallback(string $matchCallback): string { foreach ($this->matchCallbackHandlers as $handler) { diff --git a/src/CachingStrategy/FontCache.php b/src/CachingStrategy/FontCache.php index 9bd1cb7..827f96f 100644 --- a/src/CachingStrategy/FontCache.php +++ b/src/CachingStrategy/FontCache.php @@ -4,8 +4,11 @@ namespace SpomkyLabs\PwaBundle\CachingStrategy; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use SpomkyLabs\PwaBundle\Dto\ServiceWorker; use SpomkyLabs\PwaBundle\Dto\Workbox; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; use SpomkyLabs\PwaBundle\WorkboxPlugin\CacheableResponsePlugin; use SpomkyLabs\PwaBundle\WorkboxPlugin\ExpirationPlugin; use Symfony\Component\AssetMapper\AssetMapperInterface; @@ -18,16 +21,18 @@ use const JSON_UNESCAPED_SLASHES; use const JSON_UNESCAPED_UNICODE; -final readonly class FontCache implements HasCacheStrategiesInterface +final class FontCache implements HasCacheStrategiesInterface, CanLogInterface { - private int $jsonOptions; + private readonly int $jsonOptions; - private Workbox $workbox; + private readonly Workbox $workbox; + + private LoggerInterface $logger; public function __construct( ServiceWorker $serviceWorker, - private AssetMapperInterface $assetMapper, - private SerializerInterface $serializer, + private readonly AssetMapperInterface $assetMapper, + private readonly SerializerInterface $serializer, #[Autowire('%kernel.debug%')] bool $debug, ) { @@ -37,10 +42,12 @@ public function __construct( $options |= JSON_PRETTY_PRINT; } $this->jsonOptions = $options; + $this->logger = new NullLogger(); } public function getCacheStrategies(): array { + $this->logger->debug('Getting cache strategies for fonts'); $urls = json_decode($this->serializer->serialize($this->getFonts(), 'json', [ JsonEncode::OPTIONS => $this->jsonOptions, ]), true); @@ -64,10 +71,18 @@ public function getCacheStrategies(): array if (count($urls) > 0) { $strategy = $strategy->withPreloadUrl(...$urls); } + $this->logger->debug('Font cache strategy', [ + 'strategy' => $strategy, + ]); return [$strategy]; } + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } + /** * @return array */ @@ -79,6 +94,10 @@ private function getFonts(): array $fonts[] = $asset->publicPath; } } + $this->logger->debug('Preloading fonts', [ + 'fonts' => $fonts, + ]); + return $fonts; } } diff --git a/src/CachingStrategy/GoogleFontCache.php b/src/CachingStrategy/GoogleFontCache.php index d29f36a..23d9101 100644 --- a/src/CachingStrategy/GoogleFontCache.php +++ b/src/CachingStrategy/GoogleFontCache.php @@ -4,29 +4,36 @@ namespace SpomkyLabs\PwaBundle\CachingStrategy; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use SpomkyLabs\PwaBundle\Dto\ServiceWorker; use SpomkyLabs\PwaBundle\Dto\Workbox; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; use SpomkyLabs\PwaBundle\WorkboxPlugin\CacheableResponsePlugin; use SpomkyLabs\PwaBundle\WorkboxPlugin\ExpirationPlugin; -final readonly class GoogleFontCache implements HasCacheStrategiesInterface +final class GoogleFontCache implements HasCacheStrategiesInterface, CanLogInterface { - private Workbox $workbox; + private readonly Workbox $workbox; + + private LoggerInterface $logger; public function __construct( ServiceWorker $serviceWorker, ) { $this->workbox = $serviceWorker->workbox; + $this->logger = new NullLogger(); } public function getCacheStrategies(): array { + $this->logger->debug('Getting cache strategies for Google Fonts'); $prefix = $this->workbox->googleFontCache->cachePrefix ?? ''; if ($prefix !== '') { $prefix .= '-'; } - return [ + $strategies = [ WorkboxCacheStrategy::create( $this->workbox->enabled && $this->workbox->googleFontCache->enabled, true, @@ -49,5 +56,15 @@ public function getCacheStrategies(): array ), ), ]; + $this->logger->debug('Google Fonts cache strategies', [ + 'strategies' => $strategies, + ]); + + return $strategies; + } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; } } diff --git a/src/CachingStrategy/ImageCache.php b/src/CachingStrategy/ImageCache.php index c667661..daaeffe 100644 --- a/src/CachingStrategy/ImageCache.php +++ b/src/CachingStrategy/ImageCache.php @@ -4,16 +4,21 @@ namespace SpomkyLabs\PwaBundle\CachingStrategy; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use SpomkyLabs\PwaBundle\Dto\ServiceWorker; use SpomkyLabs\PwaBundle\Dto\Workbox; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; use Symfony\Component\AssetMapper\Path\PublicAssetsPathResolverInterface; use Symfony\Component\DependencyInjection\Attribute\Autowire; -final readonly class ImageCache implements HasCacheStrategiesInterface +final class ImageCache implements HasCacheStrategiesInterface, CanLogInterface { - private string $assetPublicPrefix; + private readonly string $assetPublicPrefix; - private Workbox $workbox; + private readonly Workbox $workbox; + + private LoggerInterface $logger; public function __construct( ServiceWorker $serviceWorker, @@ -22,11 +27,12 @@ public function __construct( ) { $this->workbox = $serviceWorker->workbox; $this->assetPublicPrefix = rtrim($publicAssetsPathResolver->resolvePublicPath(''), '/'); + $this->logger = new NullLogger(); } public function getCacheStrategies(): array { - return [ + $strategies = [ WorkboxCacheStrategy::create( $this->workbox->enabled && $this->workbox->imageCache->enabled, true, @@ -38,5 +44,15 @@ public function getCacheStrategies(): array ) ->withName($this->workbox->imageCache->cacheName ?? 'images'), ]; + $this->logger->debug('Image cache strategies', [ + 'strategies' => $strategies, + ]); + + return $strategies; + } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; } } diff --git a/src/CachingStrategy/ManifestCache.php b/src/CachingStrategy/ManifestCache.php index 805616d..2447720 100644 --- a/src/CachingStrategy/ManifestCache.php +++ b/src/CachingStrategy/ManifestCache.php @@ -4,15 +4,20 @@ namespace SpomkyLabs\PwaBundle\CachingStrategy; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use SpomkyLabs\PwaBundle\Dto\ServiceWorker; use SpomkyLabs\PwaBundle\Dto\Workbox; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; use Symfony\Component\DependencyInjection\Attribute\Autowire; -final readonly class ManifestCache implements HasCacheStrategiesInterface +final class ManifestCache implements HasCacheStrategiesInterface, CanLogInterface { - private string $manifestPublicUrl; + private readonly string $manifestPublicUrl; - private Workbox $workbox; + private readonly Workbox $workbox; + + private LoggerInterface $logger; public function __construct( ServiceWorker $serviceWorker, @@ -21,11 +26,12 @@ public function __construct( ) { $this->workbox = $serviceWorker->workbox; $this->manifestPublicUrl = '/' . trim($manifestPublicUrl, '/'); + $this->logger = new NullLogger(); } public function getCacheStrategies(): array { - return [ + $strategies = [ WorkboxCacheStrategy::create( $this->workbox->enabled && $this->workbox->cacheManifest, true, @@ -34,5 +40,15 @@ public function getCacheStrategies(): array ) ->withName('manifest'), ]; + $this->logger->debug('Manifest cache strategies', [ + 'strategies' => $strategies, + ]); + + return $strategies; + } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; } } diff --git a/src/CachingStrategy/PreloadUrlsGeneratorManager.php b/src/CachingStrategy/PreloadUrlsGeneratorManager.php index ef6f5f4..c9ac49c 100644 --- a/src/CachingStrategy/PreloadUrlsGeneratorManager.php +++ b/src/CachingStrategy/PreloadUrlsGeneratorManager.php @@ -5,16 +5,21 @@ namespace SpomkyLabs\PwaBundle\CachingStrategy; use InvalidArgumentException; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; use Symfony\Component\DependencyInjection\Attribute\TaggedIterator; use function array_key_exists; -final class PreloadUrlsGeneratorManager +final class PreloadUrlsGeneratorManager implements CanLogInterface { /** * @var array */ private array $generators = []; + private LoggerInterface $logger; + /** * @param PreloadUrlsGeneratorInterface[] $generators */ @@ -22,6 +27,7 @@ public function __construct( #[TaggedIterator('spomky_labs_pwa.preload_urls_generator')] iterable $generators ) { + $this->logger = new NullLogger(); foreach ($generators as $generator) { $this->add($generator); } @@ -31,6 +37,9 @@ public function add(PreloadUrlsGeneratorInterface $generator, PreloadUrlsGenerat { $this->generators[$generator->getAlias()] = $generator; foreach ($generators as $value) { + $this->logger->debug('Adding preload URL generator', [ + 'alias' => $value->getAlias(), + ]); $this->generators[$generator->getAlias()] = $value; } } @@ -38,8 +47,17 @@ public function add(PreloadUrlsGeneratorInterface $generator, PreloadUrlsGenerat public function get(string $alias): PreloadUrlsGeneratorInterface { if (! array_key_exists($alias, $this->generators)) { + $this->logger->error('The generator with alias does not exist', [ + 'alias' => $alias, + ]); + throw new InvalidArgumentException(sprintf('The generator with alias "%s" does not exist.', $alias)); } return $this->generators[$alias]; } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } } diff --git a/src/CachingStrategy/ResourceCaches.php b/src/CachingStrategy/ResourceCaches.php index 15cb0f5..9dc0b9b 100644 --- a/src/CachingStrategy/ResourceCaches.php +++ b/src/CachingStrategy/ResourceCaches.php @@ -4,10 +4,13 @@ namespace SpomkyLabs\PwaBundle\CachingStrategy; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use SpomkyLabs\PwaBundle\Dto\ServiceWorker; use SpomkyLabs\PwaBundle\Dto\Url; use SpomkyLabs\PwaBundle\Dto\Workbox; use SpomkyLabs\PwaBundle\MatchCallbackHandler\MatchCallbackHandlerInterface; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; use SpomkyLabs\PwaBundle\WorkboxPlugin\BroadcastUpdatePlugin; use SpomkyLabs\PwaBundle\WorkboxPlugin\CacheableResponsePlugin; use SpomkyLabs\PwaBundle\WorkboxPlugin\ExpirationPlugin; @@ -22,21 +25,23 @@ use const JSON_UNESCAPED_SLASHES; use const JSON_UNESCAPED_UNICODE; -final readonly class ResourceCaches implements HasCacheStrategiesInterface +final class ResourceCaches implements HasCacheStrategiesInterface, CanLogInterface { - private int $jsonOptions; + private readonly int $jsonOptions; - private Workbox $workbox; + private readonly Workbox $workbox; + + private LoggerInterface $logger; /** * @param iterable $matchCallbackHandlers */ public function __construct( - private PreloadUrlsGeneratorManager $preloadUrlsGeneratorManager, + private readonly PreloadUrlsGeneratorManager $preloadUrlsGeneratorManager, ServiceWorker $serviceWorker, - private SerializerInterface $serializer, + private readonly SerializerInterface $serializer, #[TaggedIterator('spomky_labs_pwa.match_callback_handler')] - private iterable $matchCallbackHandlers, + private readonly iterable $matchCallbackHandlers, #[Autowire('%kernel.debug%')] bool $debug, ) { @@ -46,10 +51,12 @@ public function __construct( $options |= JSON_PRETTY_PRINT; } $this->jsonOptions = $options; + $this->logger = new NullLogger(); } public function getCacheStrategies(): array { + $this->logger->debug('Getting cache strategies for resources'); $strategies = []; foreach ($this->workbox->resourceCaches as $id => $resourceCache) { $routes = $this->serializer->serialize($this->getUrls($resourceCache->urls), 'json', [ @@ -91,10 +98,18 @@ public function getCacheStrategies(): array $strategies[] = $strategy; } + $this->logger->debug('Resource cache strategies', [ + 'strategies' => $strategies, + ]); return $strategies; } + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } + private function prepareMatchCallback(string $matchCallback): string { foreach ($this->matchCallbackHandlers as $handler) { diff --git a/src/CompilerPass/LoggerCompilerPass.php b/src/CompilerPass/LoggerCompilerPass.php new file mode 100644 index 0000000..c21132f --- /dev/null +++ b/src/CompilerPass/LoggerCompilerPass.php @@ -0,0 +1,31 @@ +has('spomky_labs_pwa.logger')) { + return; + } + + $logger = $container->findDefinition(LoggerInterface::class); + $services = $container->findTaggedServiceIds(self::TAG); + foreach ($services as $id => $tags) { + $idDefinition = $container->findDefinition($id); + $idDefinition->addMethodCall('setLogger', [$logger]); + } + } +} diff --git a/src/Attribute/PreloadUrlCompilerPass.php b/src/CompilerPass/PreloadUrlCompilerPass.php similarity index 97% rename from src/Attribute/PreloadUrlCompilerPass.php rename to src/CompilerPass/PreloadUrlCompilerPass.php index 327cb53..286e173 100644 --- a/src/Attribute/PreloadUrlCompilerPass.php +++ b/src/CompilerPass/PreloadUrlCompilerPass.php @@ -2,12 +2,13 @@ declare(strict_types=1); -namespace SpomkyLabs\PwaBundle\Attribute; +namespace SpomkyLabs\PwaBundle\CompilerPass; use ReflectionAttribute; use ReflectionClass; use ReflectionMethod; use RuntimeException; +use SpomkyLabs\PwaBundle\Attribute\PreloadUrl; use SpomkyLabs\PwaBundle\CachingStrategy\PreloadUrlsTagGenerator; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; @@ -17,6 +18,9 @@ use function array_key_exists; use function is_string; +/** + * @internal + */ final class PreloadUrlCompilerPass implements CompilerPassInterface { public function process(ContainerBuilder $container): void diff --git a/src/DataCollector/PwaCollector.php b/src/DataCollector/PwaCollector.php index 5e6a294..e53765c 100644 --- a/src/DataCollector/PwaCollector.php +++ b/src/DataCollector/PwaCollector.php @@ -69,7 +69,6 @@ public function collect(Request $request, Response $response, Throwable $excepti 'files' => $swFiles, ]; $manifestFiles = $this->manifestCompiler->getFiles(); - $manifestFiles = is_array($manifestFiles) ? $manifestFiles : iterator_to_array($manifestFiles); $this->data['manifest'] = [ 'enabled' => $this->serviceWorker->enabled, 'data' => $this->manifest, diff --git a/src/EventSubscriber/ScreenshotSubscriber.php b/src/EventSubscriber/ScreenshotSubscriber.php index c164f08..5a74dc0 100644 --- a/src/EventSubscriber/ScreenshotSubscriber.php +++ b/src/EventSubscriber/ScreenshotSubscriber.php @@ -4,19 +4,25 @@ namespace SpomkyLabs\PwaBundle\EventSubscriber; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; use Symfony\Component\DependencyInjection\Attribute\Autowire; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\Profiler\Profiler; -final readonly class ScreenshotSubscriber implements EventSubscriberInterface +final class ScreenshotSubscriber implements EventSubscriberInterface, CanLogInterface { + private LoggerInterface $logger; + public function __construct( #[Autowire(service: 'profiler')] - private ?Profiler $profiler = null, + private readonly ?Profiler $profiler = null, #[Autowire(param: 'spomky_labs_pwa.screenshot_user_agent')] - private null|string $userAgent = null, + private readonly null|string $userAgent = null, ) { + $this->logger = new NullLogger(); } public static function getSubscribedEvents(): array @@ -31,17 +37,33 @@ public function onRequest(RequestEvent $event): void if (! $event->isMainRequest() || $this->profiler === null) { return; } + $this->logger->debug('Checking user agent.'); $userAgent = $event->getRequest() ->headers->get('user-agent'); if ($userAgent === null) { + $this->logger->debug('No user agent found.'); return; } + $this->logger->debug('User agent found.', [ + 'user_agent' => $userAgent, + ]); $userAgentToFind = $this->userAgent ?? 'HeadlessChrome'; if (! str_contains($userAgent, $userAgentToFind)) { + $this->logger->debug('User agent does not match.', [ + 'user_agent' => $userAgent, + ]); return; } + $this->logger->debug('User agent matches.', [ + 'user_agent' => $userAgent, + ]); $this->profiler->disable(); } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } } diff --git a/src/MatchCallbackHandler/DestinationMatchCallbackHandler.php b/src/MatchCallbackHandler/DestinationMatchCallbackHandler.php index 16f406f..ba4ff12 100644 --- a/src/MatchCallbackHandler/DestinationMatchCallbackHandler.php +++ b/src/MatchCallbackHandler/DestinationMatchCallbackHandler.php @@ -4,8 +4,19 @@ namespace SpomkyLabs\PwaBundle\MatchCallbackHandler; -final readonly class DestinationMatchCallbackHandler implements MatchCallbackHandlerInterface +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; + +final class DestinationMatchCallbackHandler implements MatchCallbackHandlerInterface, CanLogInterface { + private LoggerInterface $logger; + + public function __construct() + { + $this->logger = new NullLogger(); + } + public function supports(string $matchCallback): bool { return str_starts_with($matchCallback, 'destination:'); @@ -13,6 +24,15 @@ public function supports(string $matchCallback): bool public function handle(string $matchCallback): string { + $this->logger->debug('Destination match callback found.', [ + 'match_callback' => $matchCallback, + ]); + return sprintf("({request}) => request.destination === '%s'", trim(mb_substr($matchCallback, 12))); } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } } diff --git a/src/MatchCallbackHandler/ExactPathnameMatchCallbackHandler.php b/src/MatchCallbackHandler/ExactPathnameMatchCallbackHandler.php index 89bf397..050177a 100644 --- a/src/MatchCallbackHandler/ExactPathnameMatchCallbackHandler.php +++ b/src/MatchCallbackHandler/ExactPathnameMatchCallbackHandler.php @@ -4,8 +4,19 @@ namespace SpomkyLabs\PwaBundle\MatchCallbackHandler; -final readonly class ExactPathnameMatchCallbackHandler implements MatchCallbackHandlerInterface +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; + +final class ExactPathnameMatchCallbackHandler implements MatchCallbackHandlerInterface, CanLogInterface { + private LoggerInterface $logger; + + public function __construct() + { + $this->logger = new NullLogger(); + } + public function supports(string $matchCallback): bool { return str_starts_with($matchCallback, 'pathname:'); @@ -13,6 +24,15 @@ public function supports(string $matchCallback): bool public function handle(string $matchCallback): string { + $this->logger->debug('Exact pathname match callback found.', [ + 'match_callback' => $matchCallback, + ]); + return sprintf("({url}) => url.pathname === '%s'", trim(mb_substr($matchCallback, 9))); } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } } diff --git a/src/MatchCallbackHandler/NavigationMatchCallbackHandler.php b/src/MatchCallbackHandler/NavigationMatchCallbackHandler.php index 5b42df9..11c8bcc 100644 --- a/src/MatchCallbackHandler/NavigationMatchCallbackHandler.php +++ b/src/MatchCallbackHandler/NavigationMatchCallbackHandler.php @@ -4,8 +4,19 @@ namespace SpomkyLabs\PwaBundle\MatchCallbackHandler; -final readonly class NavigationMatchCallbackHandler implements MatchCallbackHandlerInterface +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; + +final class NavigationMatchCallbackHandler implements MatchCallbackHandlerInterface, CanLogInterface { + private LoggerInterface $logger; + + public function __construct() + { + $this->logger = new NullLogger(); + } + public function supports(string $matchCallback): bool { return $matchCallback === 'navigate'; @@ -13,6 +24,15 @@ public function supports(string $matchCallback): bool public function handle(string $matchCallback): string { + $this->logger->debug('Navigation match callback found.', [ + 'match_callback' => $matchCallback, + ]); + return "({request}) => request.mode === 'navigate'"; } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } } diff --git a/src/MatchCallbackHandler/OriginMatchCallbackHandler.php b/src/MatchCallbackHandler/OriginMatchCallbackHandler.php index 12fd515..7d0eb7a 100644 --- a/src/MatchCallbackHandler/OriginMatchCallbackHandler.php +++ b/src/MatchCallbackHandler/OriginMatchCallbackHandler.php @@ -4,8 +4,19 @@ namespace SpomkyLabs\PwaBundle\MatchCallbackHandler; -final readonly class OriginMatchCallbackHandler implements MatchCallbackHandlerInterface +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; + +final class OriginMatchCallbackHandler implements MatchCallbackHandlerInterface, CanLogInterface { + private LoggerInterface $logger; + + public function __construct() + { + $this->logger = new NullLogger(); + } + public function supports(string $matchCallback): bool { return str_starts_with($matchCallback, 'origin:'); @@ -13,6 +24,15 @@ public function supports(string $matchCallback): bool public function handle(string $matchCallback): string { + $this->logger->debug('Origin match callback found.', [ + 'match_callback' => $matchCallback, + ]); + return sprintf("({url}) => url.origin === '%s'", trim(mb_substr($matchCallback, 7))); } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } } diff --git a/src/MatchCallbackHandler/PathnameEndsWithMatchCallbackHandler.php b/src/MatchCallbackHandler/PathnameEndsWithMatchCallbackHandler.php index 09e898f..f134627 100644 --- a/src/MatchCallbackHandler/PathnameEndsWithMatchCallbackHandler.php +++ b/src/MatchCallbackHandler/PathnameEndsWithMatchCallbackHandler.php @@ -4,8 +4,19 @@ namespace SpomkyLabs\PwaBundle\MatchCallbackHandler; -final readonly class PathnameEndsWithMatchCallbackHandler implements MatchCallbackHandlerInterface +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; + +final class PathnameEndsWithMatchCallbackHandler implements MatchCallbackHandlerInterface, CanLogInterface { + private LoggerInterface $logger; + + public function __construct() + { + $this->logger = new NullLogger(); + } + public function supports(string $matchCallback): bool { return str_starts_with($matchCallback, 'endsWith:'); @@ -13,6 +24,15 @@ public function supports(string $matchCallback): bool public function handle(string $matchCallback): string { + $this->logger->debug('Pathname ends with match callback found.', [ + 'match_callback' => $matchCallback, + ]); + return sprintf("({url}) => url.pathname.endsWith('%s')", trim(mb_substr($matchCallback, 9))); } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } } diff --git a/src/MatchCallbackHandler/PathnameStartsWithMatchCallbackHandler.php b/src/MatchCallbackHandler/PathnameStartsWithMatchCallbackHandler.php index c8e0e9a..b643f86 100644 --- a/src/MatchCallbackHandler/PathnameStartsWithMatchCallbackHandler.php +++ b/src/MatchCallbackHandler/PathnameStartsWithMatchCallbackHandler.php @@ -4,8 +4,19 @@ namespace SpomkyLabs\PwaBundle\MatchCallbackHandler; -final readonly class PathnameStartsWithMatchCallbackHandler implements MatchCallbackHandlerInterface +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; + +final class PathnameStartsWithMatchCallbackHandler implements MatchCallbackHandlerInterface, CanLogInterface { + private LoggerInterface $logger; + + public function __construct() + { + $this->logger = new NullLogger(); + } + public function supports(string $matchCallback): bool { return str_starts_with($matchCallback, 'startsWith:'); @@ -13,6 +24,15 @@ public function supports(string $matchCallback): bool public function handle(string $matchCallback): string { + $this->logger->debug('Pathname starts with match callback found.', [ + 'match_callback' => $matchCallback, + ]); + return sprintf("({url}) => url.pathname.startsWith('%s')", trim(mb_substr($matchCallback, 11))); } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } } diff --git a/src/MatchCallbackHandler/RouteMatchCallbackHandler.php b/src/MatchCallbackHandler/RouteMatchCallbackHandler.php index 1663164..2494790 100644 --- a/src/MatchCallbackHandler/RouteMatchCallbackHandler.php +++ b/src/MatchCallbackHandler/RouteMatchCallbackHandler.php @@ -4,13 +4,19 @@ namespace SpomkyLabs\PwaBundle\MatchCallbackHandler; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; use Symfony\Component\Routing\RouterInterface; -final readonly class RouteMatchCallbackHandler implements MatchCallbackHandlerInterface +final class RouteMatchCallbackHandler implements MatchCallbackHandlerInterface, CanLogInterface { + private LoggerInterface $logger; + public function __construct( - private RouterInterface $router + private readonly RouterInterface $router ) { + $this->logger = new NullLogger(); } public function supports(string $matchCallback): bool @@ -22,7 +28,16 @@ public function handle(string $matchCallback): string { $routeName = trim(mb_substr($matchCallback, 6)); $route = $this->router->generate($routeName); + $this->logger->debug('Route match callback found.', [ + 'match_callback' => $matchCallback, + 'route' => $route, + ]); return sprintf("({url}) => url.pathname === '%s'", $route); } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } } diff --git a/src/Resources/config/definition/logging.php b/src/Resources/config/definition/logging.php new file mode 100644 index 0000000..6c2bed6 --- /dev/null +++ b/src/Resources/config/definition/logging.php @@ -0,0 +1,15 @@ +rootNode() + ->children() + ->scalarNode('logger') + ->defaultNull() + ->info('The logger service to use. If not set, the default logger will be used.') + ->end() + ->end(); +}; diff --git a/src/Resources/config/services.php b/src/Resources/config/services.php index fb9d830..435837e 100644 --- a/src/Resources/config/services.php +++ b/src/Resources/config/services.php @@ -9,6 +9,7 @@ use SpomkyLabs\PwaBundle\Command\CreateIconsCommand; use SpomkyLabs\PwaBundle\Command\CreateScreenshotCommand; use SpomkyLabs\PwaBundle\Command\ListCacheStrategiesCommand; +use SpomkyLabs\PwaBundle\CompilerPass\LoggerCompilerPass; use SpomkyLabs\PwaBundle\DataCollector\PwaCollector; use SpomkyLabs\PwaBundle\Dto\Favicons; use SpomkyLabs\PwaBundle\Dto\Manifest; @@ -17,6 +18,7 @@ use SpomkyLabs\PwaBundle\ImageProcessor\GDImageProcessor; use SpomkyLabs\PwaBundle\ImageProcessor\ImagickImageProcessor; use SpomkyLabs\PwaBundle\MatchCallbackHandler\MatchCallbackHandlerInterface; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; use SpomkyLabs\PwaBundle\Service\FaviconsBuilder; use SpomkyLabs\PwaBundle\Service\FaviconsCompiler; use SpomkyLabs\PwaBundle\Service\FileCompilerInterface; @@ -46,6 +48,7 @@ ->autowire() ; + $container->instanceof(CanLogInterface::class)->tag(LoggerCompilerPass::TAG); $container->instanceof(FileCompilerInterface::class)->tag('spomky_labs_pwa.compiler'); /*** Manifest ***/ diff --git a/src/Service/CanLogInterface.php b/src/Service/CanLogInterface.php new file mode 100644 index 0000000..e294bd3 --- /dev/null +++ b/src/Service/CanLogInterface.php @@ -0,0 +1,12 @@ + $config */ @@ -19,16 +23,28 @@ public function __construct( private readonly DenormalizerInterface $denormalizer, private readonly array $config, ) { + $this->logger = new NullLogger(); } public function create(): Favicons { if ($this->favicons === null) { + $this->logger->debug('Creating favicons.', [ + 'config' => $this->config, + ]); $result = $this->denormalizer->denormalize($this->config, Favicons::class); assert($result instanceof Favicons); $this->favicons = $result; + $this->logger->debug('Favicons created.', [ + 'favicons' => $this->favicons, + ]); } return $this->favicons; } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } } diff --git a/src/Service/FaviconsCompiler.php b/src/Service/FaviconsCompiler.php index 44f13c0..02394bf 100644 --- a/src/Service/FaviconsCompiler.php +++ b/src/Service/FaviconsCompiler.php @@ -4,6 +4,8 @@ namespace SpomkyLabs\PwaBundle\Service; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use RuntimeException; use SpomkyLabs\PwaBundle\Dto\Favicons; use SpomkyLabs\PwaBundle\ImageProcessor\Configuration; @@ -15,13 +17,15 @@ use function assert; use const PHP_EOL; -final class FaviconsCompiler implements FileCompilerInterface +final class FaviconsCompiler implements FileCompilerInterface, CanLogInterface { /** * @var null|array */ private null|array $files = null; + private LoggerInterface $logger; + public function __construct( private readonly null|ImageProcessorInterface $imageProcessor, private readonly Favicons $favicons, @@ -29,6 +33,7 @@ public function __construct( #[Autowire('%kernel.debug%')] public readonly bool $debug, ) { + $this->logger = new NullLogger(); } /** @@ -39,8 +44,14 @@ public function getFiles(): array if ($this->files !== null) { return $this->files; } + $this->logger->debug('Compiling favicons.', [ + 'favicons' => $this->favicons, + ]); if ($this->imageProcessor === null || $this->favicons->enabled === false) { - return []; + $this->logger->debug('Favicons are disabled or no image processor is available.'); + $this->files = []; + + return $this->files; } [$asset, $hash] = $this->getFavicon(); assert($asset !== null, 'The asset does not exist.'); @@ -241,16 +252,25 @@ public function getFiles(): array ); } if ($this->favicons->tileColor !== null) { + $this->logger->debug('Creating browserconfig.xml.'); $this->files = [...$this->files, ...$this->processBrowserConfig($asset, $hash)]; } if ($this->favicons->safariPinnedTabColor !== null && $this->favicons->useSilhouette === true) { $safariPinnedTab = $this->generateSafariPinnedTab($asset); $this->files[$safariPinnedTab->url] = $safariPinnedTab; } + $this->logger->debug('Favicons created.', [ + 'files' => $this->files, + ]); return $this->files; } + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } + private function processIcon( string $asset, string $publicUrl, @@ -258,6 +278,12 @@ private function processIcon( string $mimeType, null|string $rel, ): Data { + $this->logger->debug('Processing icon.', [ + 'publicUrl' => $publicUrl, + 'configuration' => $configuration, + 'mimeType' => $mimeType, + 'rel' => $rel, + ]); $closure = fn (): string => $this->imageProcessor->process($asset, null, null, null, $configuration); if ($this->debug === true) { $html = $rel === null ? null : sprintf( @@ -307,6 +333,7 @@ private function processBrowserConfig(string $asset, string $hash): array if ($this->favicons->useSilhouette === true) { $asset = $this->generateSilhouette($asset); } + $this->logger->debug('Processing browserconfig.xml.'); $configuration = Configuration::create(70, 70, 'png', null, null, $this->favicons->imageScale); $hash = hash('xxh128', $hash . $configuration); $icon70x70 = $this->processIcon( @@ -358,8 +385,10 @@ private function processBrowserConfig(string $asset, string $hash): array ); if ($this->favicons->tileColor === null) { + $this->logger->debug('No tile color defined.'); $tileColor = ''; } else { + $this->logger->debug('Tile color defined.'); $tileColor = PHP_EOL . sprintf(' %s', $this->favicons->tileColor); } @@ -411,6 +440,9 @@ private function processBrowserConfig(string $asset, string $hash): array private function getFavicon(): array { $source = $this->favicons->src; + $this->logger->debug('Favicons are enabled. Trying to get the asset.', [ + 'src' => $source, + ]); if (! str_starts_with($source->src, '/')) { $asset = $this->assetMapper->getAsset($source->src); assert($asset !== null, 'Unable to find the favicon source asset'); diff --git a/src/Service/ManifestCompiler.php b/src/Service/ManifestCompiler.php index a088053..07e465c 100644 --- a/src/Service/ManifestCompiler.php +++ b/src/Service/ManifestCompiler.php @@ -5,6 +5,8 @@ namespace SpomkyLabs\PwaBundle\Service; use Psr\EventDispatcher\EventDispatcherInterface; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use SpomkyLabs\PwaBundle\Dto\Manifest; use SpomkyLabs\PwaBundle\Event\NullEventDispatcher; use SpomkyLabs\PwaBundle\Event\PostManifestCompileEvent; @@ -21,7 +23,7 @@ use const JSON_UNESCAPED_SLASHES; use const JSON_UNESCAPED_UNICODE; -final class ManifestCompiler implements FileCompilerInterface +final class ManifestCompiler implements FileCompilerInterface, CanLogInterface { private readonly EventDispatcherInterface $dispatcher; @@ -32,6 +34,13 @@ final class ManifestCompiler implements FileCompilerInterface */ private readonly array $jsonOptions; + private LoggerInterface $logger; + + /** + * @var array + */ + private null|array $files = null; + /** * @param array $locales */ @@ -58,28 +67,54 @@ public function __construct( $options[JsonEncode::OPTIONS] |= JSON_PRETTY_PRINT; } $this->jsonOptions = $options; + $this->logger = new NullLogger(); } /** - * @return iterable + * @return array */ - public function getFiles(): iterable + public function getFiles(): array { - if ($this->manifest->enabled === false) { - return []; + if ($this->files !== null) { + return $this->files; } + $this->logger->debug('Compiling manifest files.', [ + 'manifest' => $this->manifest, + ]); + if ($this->manifest->enabled === false) { + $this->logger->debug('Manifest is disabled. No file to compile.'); + $this->files = []; + return $this->files; + } + $this->files = []; if ($this->locales === []) { - yield $this->manifestPublicUrl => $this->compileManifest(null); + $this->logger->debug('No locale defined. Compiling default manifest.'); + $this->files[$this->manifestPublicUrl] = $this->compileManifest(null); } - foreach ($this->locales as $locale) { - yield str_replace('{locale}', $locale, $this->manifestPublicUrl) => $this->compileManifest($locale); + $this->logger->debug('Compiling manifest for locale.', [ + 'locale' => $locale, + ]); + $this->files[str_replace('{locale}', $locale, $this->manifestPublicUrl)] = $this->compileManifest($locale); } + $this->logger->debug('Manifest files compiled.', [ + 'files' => $this->files, + ]); + + return $this->files; + } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; } private function compileManifest(null|string $locale): Data { + $this->logger->debug('Compiling manifest.', [ + 'locale' => $locale, + ]); $manifest = clone $this->manifest; $preEvent = new PreManifestCompileEvent($manifest); $preEvent = $this->dispatcher->dispatch($preEvent); diff --git a/src/Service/ServiceWorkerCompiler.php b/src/Service/ServiceWorkerCompiler.php index ffedc8e..ad58520 100644 --- a/src/Service/ServiceWorkerCompiler.php +++ b/src/Service/ServiceWorkerCompiler.php @@ -4,6 +4,8 @@ namespace SpomkyLabs\PwaBundle\Service; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use SpomkyLabs\PwaBundle\Dto\ServiceWorker; use SpomkyLabs\PwaBundle\ServiceWorkerRule\ServiceWorkerRuleInterface; use Symfony\Component\AssetMapper\AssetMapperInterface; @@ -16,24 +18,26 @@ use function is_array; use function is_string; -final readonly class ServiceWorkerCompiler implements FileCompilerInterface +final class ServiceWorkerCompiler implements FileCompilerInterface, CanLogInterface { - private string $serviceWorkerPublicUrl; + private readonly string $serviceWorkerPublicUrl; - private null|string $workboxPublicUrl; + private readonly null|string $workboxPublicUrl; - private null|string $workboxVersion; + private readonly null|string $workboxVersion; + + private LoggerInterface $logger; /** * @param iterable $serviceworkerRules */ public function __construct( - private ServiceWorker $serviceWorker, - private AssetMapperInterface $assetMapper, + private readonly ServiceWorker $serviceWorker, + private readonly AssetMapperInterface $assetMapper, #[TaggedIterator('spomky_labs_pwa.service_worker_rule', defaultPriorityMethod: 'getPriority')] - private iterable $serviceworkerRules, + private readonly iterable $serviceworkerRules, #[Autowire('%kernel.debug%')] - public bool $debug, + public readonly bool $debug, ) { $serviceWorkerPublicUrl = $serviceWorker->dest; $this->serviceWorkerPublicUrl = '/' . trim($serviceWorkerPublicUrl, '/'); @@ -45,6 +49,7 @@ public function __construct( $this->workboxVersion = null; $this->workboxPublicUrl = null; } + $this->logger = new NullLogger(); } /** @@ -56,11 +61,20 @@ public function getFiles(): iterable yield from $this->getWorkboxFiles(); } + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } + private function compileSW(): Data { + $this->logger->debug('Service Worker compilation started.'); $body = ''; foreach ($this->serviceworkerRules as $rule) { + $this->logger->debug('Processing service worker rule.', [ + 'rule' => $rule::class, + ]); $ruleBody = $rule->process($this->debug); if ($this->debug === false) { $ruleBody = trim($ruleBody); @@ -68,6 +82,9 @@ private function compileSW(): Data $body .= $ruleBody; } $body .= $this->includeRootSW(); + $this->logger->debug('Service Worker compilation completed.', [ + 'body' => $body, + ]); return Data::create( $this->serviceWorkerPublicUrl, diff --git a/src/ServiceWorkerRule/ClearCache.php b/src/ServiceWorkerRule/ClearCache.php index 6d959ac..3a4832a 100644 --- a/src/ServiceWorkerRule/ClearCache.php +++ b/src/ServiceWorkerRule/ClearCache.php @@ -4,25 +4,35 @@ namespace SpomkyLabs\PwaBundle\ServiceWorkerRule; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use SpomkyLabs\PwaBundle\Dto\ServiceWorker; use SpomkyLabs\PwaBundle\Dto\Workbox; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; -final readonly class ClearCache implements ServiceWorkerRuleInterface +final class ClearCache implements ServiceWorkerRuleInterface, CanLogInterface { - private Workbox $workbox; + private readonly Workbox $workbox; + + private LoggerInterface $logger; public function __construct( ServiceWorker $serviceWorker, ) { $this->workbox = $serviceWorker->workbox; + $this->logger = new NullLogger(); } public function process(bool $debug = false): string { if ($this->workbox->enabled === false) { + $this->logger->debug('Workbox is disabled. The rule will not be applied.'); return ''; } if ($this->workbox->clearCache === false) { + $this->logger->debug( + 'Workbox is enabled but the cache is not set to be cleared. The rule will not be applied.' + ); return ''; } @@ -61,7 +71,15 @@ public function process(bool $debug = false): string DEBUG_COMMENT; } + $this->logger->debug('Cache clear rule added.', [ + 'declaration' => $declaration, + ]); return $declaration; } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } } diff --git a/src/ServiceWorkerRule/OfflineFallback.php b/src/ServiceWorkerRule/OfflineFallback.php index 49d45a3..ee8b77d 100644 --- a/src/ServiceWorkerRule/OfflineFallback.php +++ b/src/ServiceWorkerRule/OfflineFallback.php @@ -4,8 +4,11 @@ namespace SpomkyLabs\PwaBundle\ServiceWorkerRule; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use SpomkyLabs\PwaBundle\Dto\ServiceWorker; use SpomkyLabs\PwaBundle\Dto\Workbox; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; use Symfony\Component\Serializer\Encoder\JsonEncode; use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Symfony\Component\Serializer\SerializerInterface; @@ -15,20 +18,24 @@ use const JSON_UNESCAPED_SLASHES; use const JSON_UNESCAPED_UNICODE; -final readonly class OfflineFallback implements ServiceWorkerRuleInterface +final class OfflineFallback implements ServiceWorkerRuleInterface, CanLogInterface { - private Workbox $workbox; + private readonly Workbox $workbox; + + private LoggerInterface $logger; public function __construct( ServiceWorker $serviceWorker, - private SerializerInterface $serializer, + private readonly SerializerInterface $serializer, ) { $this->workbox = $serviceWorker->workbox; + $this->logger = new NullLogger(); } public function process(bool $debug = false): string { if ($this->workbox->enabled === false || ! isset($this->workbox->offlineFallback)) { + $this->logger->debug('Workbox is disabled or offline fallback is not set. The rule will not be applied.'); return ''; } @@ -73,9 +80,18 @@ public function process(bool $debug = false): string DEBUG_COMMENT; } + $this->logger->debug('Offline fallback rule added.', [ + 'declaration' => $declaration, + ]); + return $declaration; } + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } + /** * @return array */ diff --git a/src/ServiceWorkerRule/SkipWaiting.php b/src/ServiceWorkerRule/SkipWaiting.php index f96f431..eec1fe5 100644 --- a/src/ServiceWorkerRule/SkipWaiting.php +++ b/src/ServiceWorkerRule/SkipWaiting.php @@ -4,18 +4,25 @@ namespace SpomkyLabs\PwaBundle\ServiceWorkerRule; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use SpomkyLabs\PwaBundle\Dto\ServiceWorker; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; -final readonly class SkipWaiting implements ServiceWorkerRuleInterface +final class SkipWaiting implements ServiceWorkerRuleInterface, CanLogInterface { + private LoggerInterface $logger; + public function __construct( - private ServiceWorker $serviceWorker + private readonly ServiceWorker $serviceWorker ) { + $this->logger = new NullLogger(); } public function process(bool $debug = false): string { if ($this->serviceWorker->skipWaiting === false) { + $this->logger->debug('Skip waiting is disabled. The rule will not be applied.'); return ''; } @@ -48,7 +55,15 @@ public function process(bool $debug = false): string DEBUG_COMMENT; } + $this->logger->debug('Skip waiting rule applied.', [ + 'declaration' => $declaration, + ]); return $declaration; } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } } diff --git a/src/ServiceWorkerRule/WindowsWidgets.php b/src/ServiceWorkerRule/WindowsWidgets.php index 0e24899..0942f9d 100644 --- a/src/ServiceWorkerRule/WindowsWidgets.php +++ b/src/ServiceWorkerRule/WindowsWidgets.php @@ -4,7 +4,10 @@ namespace SpomkyLabs\PwaBundle\ServiceWorkerRule; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use SpomkyLabs\PwaBundle\Dto\Manifest; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; use Symfony\Component\Serializer\Encoder\JsonEncode; use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Symfony\Component\Serializer\SerializerInterface; @@ -14,12 +17,15 @@ use const JSON_UNESCAPED_SLASHES; use const JSON_UNESCAPED_UNICODE; -final readonly class WindowsWidgets implements ServiceWorkerRuleInterface +final class WindowsWidgets implements ServiceWorkerRuleInterface, CanLogInterface { + private LoggerInterface $logger; + public function __construct( - private Manifest $manifest, - private SerializerInterface $serializer + private readonly Manifest $manifest, + private readonly SerializerInterface $serializer ) { + $this->logger = new NullLogger(); } public function process(bool $debug = false): string @@ -115,10 +121,18 @@ public function process(bool $debug = false): string DEBUG_COMMENT; } + $this->logger->debug('Windows widgets rule added.', [ + 'declaration' => $declaration, + ]); return $declaration; } + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } + /** * @return array */ diff --git a/src/ServiceWorkerRule/WorkboxImport.php b/src/ServiceWorkerRule/WorkboxImport.php index 547ffdc..2c0ef3c 100644 --- a/src/ServiceWorkerRule/WorkboxImport.php +++ b/src/ServiceWorkerRule/WorkboxImport.php @@ -4,22 +4,29 @@ namespace SpomkyLabs\PwaBundle\ServiceWorkerRule; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use SpomkyLabs\PwaBundle\Dto\ServiceWorker; use SpomkyLabs\PwaBundle\Dto\Workbox; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; -final readonly class WorkboxImport implements ServiceWorkerRuleInterface +final class WorkboxImport implements ServiceWorkerRuleInterface, CanLogInterface { - private Workbox $workbox; + private readonly Workbox $workbox; + + private LoggerInterface $logger; public function __construct( ServiceWorker $serviceWorker ) { $this->workbox = $serviceWorker->workbox; + $this->logger = new NullLogger(); } public function process(bool $debug = false): string { if ($this->workbox->enabled === false) { + $this->logger->debug('Workbox is disabled. The rule will not be applied.'); return ''; } $declaration = ''; @@ -68,6 +75,9 @@ public function process(bool $debug = false): string DEBUG_COMMENT; } + $this->logger->debug('Workbox import rule added.', [ + 'declaration' => $declaration, + ]); return $declaration; } @@ -76,4 +86,9 @@ public static function getPriority(): int { return 1024; } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } } diff --git a/src/SpomkyLabsPwaBundle.php b/src/SpomkyLabsPwaBundle.php index 2ed4dc5..fb3e29e 100644 --- a/src/SpomkyLabsPwaBundle.php +++ b/src/SpomkyLabsPwaBundle.php @@ -4,7 +4,8 @@ namespace SpomkyLabs\PwaBundle; -use SpomkyLabs\PwaBundle\Attribute\PreloadUrlCompilerPass; +use SpomkyLabs\PwaBundle\CompilerPass\LoggerCompilerPass; +use SpomkyLabs\PwaBundle\CompilerPass\PreloadUrlCompilerPass; use SpomkyLabs\PwaBundle\ImageProcessor\ImageProcessorInterface; use SpomkyLabs\PwaBundle\Subscriber\PwaDevServerSubscriber; use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator; @@ -25,6 +26,7 @@ public function configure(DefinitionConfigurator $definition): void public function build(ContainerBuilder $container): void { $container->addCompilerPass(new PreloadUrlCompilerPass()); + $container->addCompilerPass(new LoggerCompilerPass()); } public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void @@ -45,6 +47,10 @@ public function loadExtension(array $config, ContainerConfigurator $container, C $manifestConfig['serviceworker'] = $serviceWorkerConfig; } + if ($config['logger'] !== null) { + $builder->setAlias('spomky_labs_pwa.logger', $config['logger']); + } + /*** Manifest ***/ $builder->setParameter('spomky_labs_pwa.manifest.enabled', $config['manifest']['enabled']); $builder->setParameter('spomky_labs_pwa.manifest.public_url', $config['manifest']['public_url'] ?? null); diff --git a/src/Subscriber/FileCompileEventListener.php b/src/Subscriber/FileCompileEventListener.php index 7730406..b00d8d2 100644 --- a/src/Subscriber/FileCompileEventListener.php +++ b/src/Subscriber/FileCompileEventListener.php @@ -4,6 +4,9 @@ namespace SpomkyLabs\PwaBundle\Subscriber; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; use SpomkyLabs\PwaBundle\Service\FileCompilerInterface; use Symfony\Component\AssetMapper\Event\PreAssetsCompileEvent; use Symfony\Component\AssetMapper\Path\PublicAssetsFilesystemInterface; @@ -12,25 +15,42 @@ use Symfony\Component\EventDispatcher\Attribute\AsEventListener; #[AsEventListener(PreAssetsCompileEvent::class)] -final readonly class FileCompileEventListener +final class FileCompileEventListener implements CanLogInterface { + private LoggerInterface $logger; + /** * @param iterable $fileCompilers */ public function __construct( #[TaggedIterator('spomky_labs_pwa.compiler')] - private iterable $fileCompilers, + private readonly iterable $fileCompilers, #[Autowire('@asset_mapper.local_public_assets_filesystem')] - private PublicAssetsFilesystemInterface $assetsFilesystem, + private readonly PublicAssetsFilesystemInterface $assetsFilesystem, ) { + $this->logger = new NullLogger(); } public function __invoke(PreAssetsCompileEvent $event): void { + $this->logger->debug('Compiling files...'); foreach ($this->fileCompilers as $fileCompiler) { + $this->logger->debug('Compiling files with compiler.', [ + 'compiler' => $fileCompiler, + ]); foreach ($fileCompiler->getFiles() as $data) { + $this->logger->debug('Compiling file.', [ + 'url' => $data->url, + 'data' => $data, + ]); $this->assetsFilesystem->write($data->url, $data->getData()); } } + $this->logger->debug('Files compiled.'); + } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; } } diff --git a/src/Subscriber/PwaDevServerSubscriber.php b/src/Subscriber/PwaDevServerSubscriber.php index 149fce0..2d13250 100644 --- a/src/Subscriber/PwaDevServerSubscriber.php +++ b/src/Subscriber/PwaDevServerSubscriber.php @@ -4,6 +4,9 @@ namespace SpomkyLabs\PwaBundle\Subscriber; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use SpomkyLabs\PwaBundle\Service\CanLogInterface; use SpomkyLabs\PwaBundle\Service\Data; use SpomkyLabs\PwaBundle\Service\FileCompilerInterface; use Symfony\Component\DependencyInjection\Attribute\TaggedIterator; @@ -14,16 +17,19 @@ use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\Profiler\Profiler; -final readonly class PwaDevServerSubscriber implements EventSubscriberInterface +final class PwaDevServerSubscriber implements EventSubscriberInterface, CanLogInterface { + private LoggerInterface $logger; + /** * @param iterable $fileCompilers */ public function __construct( #[TaggedIterator('spomky_labs_pwa.compiler')] - private iterable $fileCompilers, - private null|Profiler $profiler, + private readonly iterable $fileCompilers, + private readonly null|Profiler $profiler, ) { + $this->logger = new NullLogger(); } public function onKernelRequest(RequestEvent $event): void @@ -35,11 +41,14 @@ public function onKernelRequest(RequestEvent $event): void $request = $event->getRequest(); $pathInfo = $request->getPathInfo(); foreach ($this->fileCompilers as $fileCompiler) { - $files = iterator_to_array($fileCompiler->getFiles()); - foreach ($files as $data) { + foreach ($fileCompiler->getFiles() as $data) { if ($data->url !== $pathInfo) { continue; } + $this->logger->debug('PWA Dev Server file found.', [ + 'url' => $data->url, + 'data' => $data, + ]); $this->serveFile($event, $data); return; } @@ -50,7 +59,7 @@ public function onKernelResponse(ResponseEvent $event): void { $headers = $event->getResponse() ->headers; - if ($headers->has('X-Manifest-Dev') || $headers->has('X-SW-Dev')) { + if ($headers->has('X-Manifest-Dev') || $headers->has('X-SW-Dev') || $headers->has('X-Favicons-Dev')) { $event->stopPropagation(); } } @@ -65,6 +74,11 @@ public static function getSubscribedEvents(): array ]; } + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } + private function serveFile(RequestEvent $event, Data $data): void { $this->profiler?->disable(); diff --git a/tests/AppKernel.php b/tests/AppKernel.php index fac97c4..655bf1f 100644 --- a/tests/AppKernel.php +++ b/tests/AppKernel.php @@ -6,6 +6,7 @@ use SpomkyLabs\PwaBundle\SpomkyLabsPwaBundle; use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\MonologBundle\MonologBundle; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\HttpKernel\Bundle\BundleInterface; use Symfony\Component\HttpKernel\Kernel; @@ -25,7 +26,7 @@ public function __construct(string $environment) */ public function registerBundles(): array { - return [new FrameworkBundle(), new SpomkyLabsPwaBundle()]; + return [new FrameworkBundle(), new MonologBundle(), new SpomkyLabsPwaBundle()]; } public function registerContainerConfiguration(LoaderInterface $loader): void diff --git a/tests/config.php b/tests/config.php index cbcbba1..c19a04b 100644 --- a/tests/config.php +++ b/tests/config.php @@ -53,6 +53,15 @@ 'log' => true, ], ]); + $container->extension('monolog', [ + 'handlers' => [ + 'main' => [ + 'type' => 'stream', + 'path' => '%kernel.logs_dir%/%kernel.environment%.log', + 'level' => 'debug', + ], + ], + ]); $container->extension('pwa', [ 'image_processor' => DummyImageProcessor::class, 'favicons' => [