From b19222187a40cb50f492d16bb6cfe624ceebd6ee Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Sat, 25 May 2024 22:18:45 +0200 Subject: [PATCH] Added logging capabilities for debugging (#197) 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' => [