Skip to content

Commit

Permalink
Refactor file compiler services to use iterable (#214)
Browse files Browse the repository at this point in the history
The file compiler services have been refactored to use iterable instead of arrays. This includes the FaviconsCompiler, ManifestCompiler, and ServiceWorkerCompiler. Now, the getFiles() method of these services returns iterable<Data>, which improves the application's efficiency by avoiding the need to load all the data into memory at once.
  • Loading branch information
Spomky authored Jun 2, 2024
1 parent 4612285 commit c1419d2
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 102 deletions.
47 changes: 46 additions & 1 deletion phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ parameters:
count: 1
path: src/CachingStrategy/AssetCache.php

-
message: "#^Attribute class Symfony\\\\Component\\\\DependencyInjection\\\\Attribute\\\\TaggedIterator is deprecated\\: since Symfony 7\\.1, use \\{@see AutowireIterator\\} instead\\.$#"
count: 1
path: src/CachingStrategy/BackgroundSync.php

-
message: "#^Only iterables can be unpacked, mixed given in argument \\#1\\.$#"
count: 1
Expand All @@ -35,6 +40,16 @@ parameters:
count: 2
path: src/CachingStrategy/FontCache.php

-
message: "#^Attribute class Symfony\\\\Component\\\\DependencyInjection\\\\Attribute\\\\TaggedIterator is deprecated\\: since Symfony 7\\.1, use \\{@see AutowireIterator\\} instead\\.$#"
count: 1
path: src/CachingStrategy/PreloadUrlsGeneratorManager.php

-
message: "#^Attribute class Symfony\\\\Component\\\\DependencyInjection\\\\Attribute\\\\TaggedIterator is deprecated\\: since Symfony 7\\.1, use \\{@see AutowireIterator\\} instead\\.$#"
count: 1
path: src/CachingStrategy/ResourceCaches.php

-
message: "#^Only iterables can be unpacked, mixed given in argument \\#1\\.$#"
count: 1
Expand Down Expand Up @@ -120,6 +135,16 @@ parameters:
count: 1
path: src/Command/CreateScreenshotCommand.php

-
message: "#^Attribute class Symfony\\\\Component\\\\DependencyInjection\\\\Attribute\\\\TaggedIterator is deprecated\\: since Symfony 7\\.1, use \\{@see AutowireIterator\\} instead\\.$#"
count: 1
path: src/Command/ListCacheStrategiesCommand.php

-
message: "#^Attribute class Symfony\\\\Component\\\\DependencyInjection\\\\Attribute\\\\TaggedIterator is deprecated\\: since Symfony 7\\.1, use \\{@see AutowireIterator\\} instead\\.$#"
count: 1
path: src/DataCollector/PwaCollector.php

-
message: "#^Class SpomkyLabs\\\\PwaBundle\\\\Dto\\\\BackgroundSync has an uninitialized property \\$forceSyncFallback\\. Give it default value or assign it in the constructor\\.$#"
count: 1
Expand Down Expand Up @@ -580,6 +605,16 @@ parameters:
count: 1
path: src/Service/FaviconsCompiler.php

-
message: "#^Attribute class Symfony\\\\Component\\\\DependencyInjection\\\\Attribute\\\\TaggedIterator is deprecated\\: since Symfony 7\\.1, use \\{@see AutowireIterator\\} instead\\.$#"
count: 1
path: src/Service/ServiceWorkerCompiler.php

-
message: "#^Attribute class Symfony\\\\Component\\\\DependencyInjection\\\\Attribute\\\\TaggedIterator is deprecated\\: since Symfony 7\\.1, use \\{@see AutowireIterator\\} instead\\.$#"
count: 1
path: src/ServiceWorkerRule/AppendCacheStrategies.php

-
message: "#^Parameter \\#2 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, mixed given\\.$#"
count: 1
Expand All @@ -593,4 +628,14 @@ parameters:
-
message: "#^Method SpomkyLabs\\\\PwaBundle\\\\SpomkyLabsPwaBundle\\:\\:loadExtension\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#"
count: 1
path: src/SpomkyLabsPwaBundle.php
path: src/SpomkyLabsPwaBundle.php

-
message: "#^Attribute class Symfony\\\\Component\\\\DependencyInjection\\\\Attribute\\\\TaggedIterator is deprecated\\: since Symfony 7\\.1, use \\{@see AutowireIterator\\} instead\\.$#"
count: 1
path: src/Subscriber/FileCompileEventListener.php

-
message: "#^Attribute class Symfony\\\\Component\\\\DependencyInjection\\\\Attribute\\\\TaggedIterator is deprecated\\: since Symfony 7\\.1, use \\{@see AutowireIterator\\} instead\\.$#"
count: 1
path: src/Subscriber/PwaDevServerSubscriber.php
1 change: 1 addition & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<ini name="intl.error_level" value="0"/>
<ini name="memory_limit" value="-1"/>
<server name="KERNEL_CLASS" value="SpomkyLabs\PwaBundle\Tests\AppKernel"/>
<server name="SYMFONY_DEPRECATIONS_HELPER" value="disabled"/>
</php>
<source>
<include>
Expand Down
31 changes: 21 additions & 10 deletions src/DataCollector/PwaCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,29 +66,24 @@ public function collect(Request $request, Response $response, Throwable $excepti
$this->data['serviceWorker'] = [
'enabled' => $this->serviceWorker->enabled,
'data' => $this->serviceWorker,
'files' => $swFiles,
'files' => $this->dataToFiles($swFiles),
];
$manifestFiles = $this->manifestCompiler->getFiles();
$manifestFiles = is_array($manifestFiles) ? $manifestFiles : iterator_to_array($manifestFiles);
$this->data['manifest'] = [
'enabled' => $this->serviceWorker->enabled,
'data' => $this->manifest,
'installable' => $this->isInstallable(),
'output' => $this->serializer->serialize($this->manifest, 'json', $jsonOptions),
'files' => $manifestFiles,
'files' => $this->dataToFiles($manifestFiles),
];

$faviconsFiles = $this->faviconsCompiler->getFiles();
$faviconsFiles = is_array($faviconsFiles) ? $faviconsFiles : iterator_to_array($faviconsFiles);
$this->data['favicons'] = [
'enabled' => $this->favicons->enabled,
'data' => $this->favicons,
'files' => array_map(
static fn (\SpomkyLabs\PwaBundle\Service\Data $data): array => [
'url' => $data->url,
'headers' => $data->headers,
'html' => $data->html,
],
$faviconsFiles
),
'files' => $this->dataToFiles($faviconsFiles),
];
}

Expand Down Expand Up @@ -157,6 +152,22 @@ public function getName(): string
return 'pwa';
}

/**
* @param \SpomkyLabs\PwaBundle\Service\Data[] $data
* @return array{url: string, html: string|null, headers: array<string, string|bool>}[]
*/
private function dataToFiles(array $data): array
{
return array_map(
static fn (\SpomkyLabs\PwaBundle\Service\Data $data): array => [
'url' => $data->url,
'headers' => $data->headers,
'html' => $data->html,
],
$data
);
}

/**
* @return array{status: bool, reasons: array<string, bool>}
*/
Expand Down
45 changes: 12 additions & 33 deletions src/Service/FaviconsCompiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,6 @@

final class FaviconsCompiler implements FileCompilerInterface, CanLogInterface
{
/**
* @var null|array<string, Data>
*/
private null|array $files = null;

private LoggerInterface $logger;

public function __construct(
Expand All @@ -37,25 +32,21 @@ public function __construct(
}

/**
* @return array<string, Data>
* @return iterable<Data>
*/
public function getFiles(): array
public function getFiles(): iterable
{
if ($this->files !== null) {
return $this->files;
}
$this->logger->debug('Compiling favicons.', [
'favicons' => $this->favicons,
]);
if ($this->imageProcessor === null || $this->favicons->enabled === false) {
$this->logger->debug('Favicons are disabled or no image processor is available.');
$this->files = [];
yield from [];

return $this->files;
return;
}
[$asset, $hash] = $this->getFavicon();
assert($asset !== null, 'The asset does not exist.');
$this->files = [];
$sizes = [
//Always
[
Expand Down Expand Up @@ -243,27 +234,16 @@ public function getFiles(): array
);
$completeHash = hash('xxh128', $hash . $configuration);
$filename = sprintf($size['url'], $size['width'], $size['height'], $completeHash);
$this->files[$filename] = $this->processIcon(
$asset,
$filename,
$configuration,
$size['mimetype'],
$size['rel'],
);
yield $this->processIcon($asset, $filename, $configuration, $size['mimetype'], $size['rel']);
}
if ($this->favicons->tileColor !== null) {
$this->logger->debug('Creating browserconfig.xml.');
$this->files = [...$this->files, ...$this->processBrowserConfig($asset, $hash)];
yield from $this->processBrowserConfig($asset, $hash);
}
if ($this->favicons->safariPinnedTabColor !== null && $this->favicons->useSilhouette === true) {
$safariPinnedTab = $this->generateSafariPinnedTab($asset);
$this->files[$safariPinnedTab->url] = $safariPinnedTab;
yield $this->generateSafariPinnedTab($asset, $hash);
}
$this->logger->debug('Favicons created.', [
'files' => $this->files,
]);

return $this->files;
$this->logger->debug('Favicons created.');
}

public function setLogger(LoggerInterface $logger): void
Expand Down Expand Up @@ -330,7 +310,7 @@ private function processIcon(
*/
private function processBrowserConfig(string $asset, string $hash): array
{
if ($this->favicons->useSilhouette === true) {
if ($this->favicons->useSilhouette === true && $this->debug === false) {
$asset = $this->generateSilhouette($asset);
}
$this->logger->debug('Processing browserconfig.xml.');
Expand Down Expand Up @@ -456,15 +436,14 @@ private function getFavicon(): array
return [$data, $hash];
}

private function generateSafariPinnedTab(string $content): Data
private function generateSafariPinnedTab(string $content, string $hash): Data
{
$silhouette = $this->generateSilhouette($content);
$hash = hash('xxh128', $silhouette);
$callback = fn (): string => $this->generateSilhouette($content);
$url = sprintf('/safari-pinned-tab-%s.svg', $hash);

return Data::create(
$url,
$silhouette,
$callback,
[
'Cache-Control' => 'public, max-age=604800, immutable',
'Content-Type' => 'image/svg+xml',
Expand Down
2 changes: 1 addition & 1 deletion src/Service/FileCompilerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
interface FileCompilerInterface
{
/**
* @return iterable<string, Data>
* @return iterable<Data>
*/
public function getFiles(): iterable;
}
56 changes: 24 additions & 32 deletions src/Service/ManifestCompiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,6 @@ final class ManifestCompiler implements FileCompilerInterface, CanLogInterface

private LoggerInterface $logger;

/**
* @var array<string, Data>
*/
private null|array $files = null;

/**
* @param array<string> $locales
*/
Expand Down Expand Up @@ -71,38 +66,30 @@ public function __construct(
}

/**
* @return array<string, Data>
* @return iterable<Data>
*/
public function getFiles(): array
public function getFiles(): iterable
{
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 = [];
yield from [];

return $this->files;
return;
}
$this->files = [];
if ($this->locales === []) {
$this->logger->debug('No locale defined. Compiling default manifest.');
$this->files[$this->manifestPublicUrl] = $this->compileManifest(null);
yield $this->compileManifest(null);
}
foreach ($this->locales as $locale) {
$this->logger->debug('Compiling manifest for locale.', [
'locale' => $locale,
]);
$this->files[str_replace('{locale}', $locale, $this->manifestPublicUrl)] = $this->compileManifest($locale);
yield $this->compileManifest($locale);
}
$this->logger->debug('Manifest files compiled.', [
'files' => $this->files,
]);

return $this->files;
$this->logger->debug('Manifest files compiled.');
}

public function setLogger(LoggerInterface $logger): void
Expand All @@ -116,30 +103,35 @@ private function compileManifest(null|string $locale): Data
'locale' => $locale,
]);
$manifest = clone $this->manifest;
$preEvent = new PreManifestCompileEvent($manifest);
$preEvent = $this->dispatcher->dispatch($preEvent);
assert($preEvent instanceof PreManifestCompileEvent);

$options = $this->jsonOptions;
$manifestPublicUrl = $this->manifestPublicUrl;
if ($locale !== null) {
$options[TranslatableNormalizer::NORMALIZATION_LOCALE_KEY] = $locale;
$manifestPublicUrl = str_replace('{locale}', $locale, $this->manifestPublicUrl);
}
$data = $this->serializer->serialize($preEvent->manifest, 'json', $options);

$postEvent = new PostManifestCompileEvent($manifest, $data);
$postEvent = $this->dispatcher->dispatch($postEvent);
assert($postEvent instanceof PostManifestCompileEvent);
$callback = function () use ($manifest, $locale): string {
$preEvent = new PreManifestCompileEvent($manifest);
$preEvent = $this->dispatcher->dispatch($preEvent);
assert($preEvent instanceof PreManifestCompileEvent);

$options = $this->jsonOptions;
if ($locale !== null) {
$options[TranslatableNormalizer::NORMALIZATION_LOCALE_KEY] = $locale;
}
$data = $this->serializer->serialize($preEvent->manifest, 'json', $options);
$postEvent = new PostManifestCompileEvent($manifest, $data);
$postEvent = $this->dispatcher->dispatch($postEvent);
assert($postEvent instanceof PostManifestCompileEvent);

return $postEvent->data;
};

return Data::create(
$manifestPublicUrl,
$postEvent->data,
$callback,
[
'Cache-Control' => 'public, max-age=604800, immutable',
'Content-Type' => 'application/manifest+json',
'X-Manifest-Dev' => true,
'Etag' => hash('xxh128', $data),
]
);
}
Expand Down
Loading

0 comments on commit c1419d2

Please sign in to comment.