Skip to content

Commit

Permalink
Split in Section Processors instead of a very large command
Browse files Browse the repository at this point in the history
  • Loading branch information
Spomky committed Jan 10, 2024
1 parent db382c8 commit f90fe93
Show file tree
Hide file tree
Showing 13 changed files with 800 additions and 451 deletions.
465 changes: 14 additions & 451 deletions src/Command/GenerateManifestCommand.php

Large diffs are not rendered by default.

40 changes: 40 additions & 0 deletions src/Command/SectionProcessor/ActionsSectionProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

declare(strict_types=1);

namespace SpomkyLabs\PwaBundle\Command\SectionProcessor;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Routing\RouterInterface;

final readonly class ActionsSectionProcessor implements SectionProcessor
{
public function __construct(
private ?RouterInterface $router = null,
) {
}

public function process(SymfonyStyle $io, array $config, array $manifest): array|int

Check failure on line 18 in src/Command/SectionProcessor/ActionsSectionProcessor.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test on ubuntu-latest

Method SpomkyLabs\PwaBundle\Command\SectionProcessor\ActionsSectionProcessor::process() has parameter $config with no value type specified in iterable type array.

Check failure on line 18 in src/Command/SectionProcessor/ActionsSectionProcessor.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test on ubuntu-latest

Method SpomkyLabs\PwaBundle\Command\SectionProcessor\ActionsSectionProcessor::process() has parameter $manifest with no value type specified in iterable type array.

Check failure on line 18 in src/Command/SectionProcessor/ActionsSectionProcessor.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test on ubuntu-latest

Method SpomkyLabs\PwaBundle\Command\SectionProcessor\ActionsSectionProcessor::process() return type has no value type specified in iterable type array.
{
if ($config['file_handlers'] === []) {
return $manifest;
}
foreach ($manifest['file_handlers'] as $id => $handler) {
if (str_starts_with((string) $handler['action'], '/')) {
continue;
}
if ($this->router === null) {
$io->error('The router is not available. Unable to generate the file handler action URL.');
return Command::FAILURE;
}
$manifest['file_handlers'][$id]['action'] = $this->router->generate(
$handler['action'],
[],
RouterInterface::RELATIVE_PATH
);
}

return $manifest;
}
}
59 changes: 59 additions & 0 deletions src/Command/SectionProcessor/ApplicationIconsSectionProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

declare(strict_types=1);

namespace SpomkyLabs\PwaBundle\Command\SectionProcessor;

use SpomkyLabs\PwaBundle\ImageProcessor\ImageProcessor;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\Filesystem\Filesystem;
use function is_int;

final class ApplicationIconsSectionProcessor implements SectionProcessor
{
use IconsSectionProcessorTrait;

public function __construct(

Check failure on line 17 in src/Command/SectionProcessor/ApplicationIconsSectionProcessor.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test on ubuntu-latest

Method SpomkyLabs\PwaBundle\Command\SectionProcessor\ApplicationIconsSectionProcessor::__construct() has parameter $dest with no value type specified in iterable type array.
private readonly Filesystem $filesystem,
#[Autowire('%spomky_labs_pwa.dest%')]
private readonly array $dest,
private readonly null|ImageProcessor $imageProcessor = null,
) {
}

public function process(SymfonyStyle $io, array $config, array $manifest): array|int

Check failure on line 25 in src/Command/SectionProcessor/ApplicationIconsSectionProcessor.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test on ubuntu-latest

Method SpomkyLabs\PwaBundle\Command\SectionProcessor\ApplicationIconsSectionProcessor::process() has parameter $config with no value type specified in iterable type array.

Check failure on line 25 in src/Command/SectionProcessor/ApplicationIconsSectionProcessor.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test on ubuntu-latest

Method SpomkyLabs\PwaBundle\Command\SectionProcessor\ApplicationIconsSectionProcessor::process() has parameter $manifest with no value type specified in iterable type array.

Check failure on line 25 in src/Command/SectionProcessor/ApplicationIconsSectionProcessor.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test on ubuntu-latest

Method SpomkyLabs\PwaBundle\Command\SectionProcessor\ApplicationIconsSectionProcessor::process() return type has no value type specified in iterable type array.
{
if ($config['icons'] === []) {
return $manifest;
}
$result = $this->processIcons($io, $config['icons']);
if (is_int($result)) {
return $result;
}
$manifest['icons'] = $result;
$io->info('Icons are built');

return $manifest;
}

protected function getFilesystem(): Filesystem
{
return $this->filesystem;
}

protected function getIconPrefixUrl(): string
{
return $this->dest['icon_prefix_url'];
}

protected function getIconFolder(): string
{
return $this->dest['icon_folder'];
}

protected function getImageProcessor(): ?ImageProcessor
{
return $this->imageProcessor;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

namespace SpomkyLabs\PwaBundle\Command\SectionProcessor;

use SpomkyLabs\PwaBundle\Command\Client;
use SpomkyLabs\PwaBundle\ImageProcessor\ImageProcessor;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\Filesystem\Filesystem;
use function is_int;

final class ApplicationScreenshotsSectionProcessor implements SectionProcessor
{
use ScreenshotsProcessorTrait;

private readonly null|Client $webClient;

Check failure on line 18 in src/Command/SectionProcessor/ApplicationScreenshotsSectionProcessor.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 Test on ubuntu-latest

Property SpomkyLabs\PwaBundle\Command\SectionProcessor\ApplicationScreenshotsSectionProcessor::$webClient has unknown class SpomkyLabs\PwaBundle\Command\Client as its type.

public function __construct(
private readonly Filesystem $filesystem,
#[Autowire('%spomky_labs_pwa.dest%')]
private readonly array $dest,
#[Autowire('@pwa.web_client')]
null|Client $webClient,
private readonly null|ImageProcessor $imageProcessor = null,
) {
if ($webClient === null && class_exists(Client::class)) {
$webClient = Client::createChromeClient();
}
$this->webClient = $webClient;
}

public function process(SymfonyStyle $io, array $config, array $manifest): array|int
{
if ($config['screenshots'] === []) {
return $manifest;
}
$result = $this->processScreenshots($io, $config['screenshots']);
if (is_int($result)) {
return $result;
}
$manifest['screenshots'] = $result;

return $manifest;
}

protected function getFilesystem(): Filesystem
{
return $this->filesystem;
}

protected function getImageProcessor(): ?ImageProcessor
{
return $this->imageProcessor;
}

protected function getWebClient(): ?Client
{
return $this->webClient;
}

protected function getScreenshotPrefixUrl(): string
{
return $this->dest['screenshot_prefix_url'];
}

protected function getScreenshotFolder(): string
{
return $this->dest['screenshot_folder'];
}
}
72 changes: 72 additions & 0 deletions src/Command/SectionProcessor/FileProcessorTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

namespace SpomkyLabs\PwaBundle\Command\SectionProcessor;

use RuntimeException;
use Symfony\Component\Filesystem\Exception\IOExceptionInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Mime\MimeTypes;

trait FileProcessorTrait
{
private null|MimeTypes $mime = null;

abstract protected function getFilesystem(): Filesystem;

protected function getMime(): MimeTypes
{
if (! isset($this->mime)) {
$this->mime = MimeTypes::getDefault();
}
return $this->mime;
}

protected function createDirectoryIfNotExists(string $folder): bool
{
try {
if (! $this->getFilesystem()->exists($folder)) {
$this->getFilesystem()
->mkdir($folder);
}
} catch (IOExceptionInterface) {
return false;
}

return true;
}

/**
* @param array<string|null> $components
* @return array{src: string, type: string}
*/
protected function storeFile(string $data, string $prefixUrl, string $storageFolder, array $components): array
{
$tempFilename = $this->getFilesystem()
->tempnam($storageFolder, 'pwa-');
$hash = mb_substr(hash('sha256', $data), 0, 8);
file_put_contents($tempFilename, $data);
$mime = $this->getMime()
->guessMimeType($tempFilename);
$extension = $this->getMime()
->getExtensions($mime);

if (empty($extension)) {
throw new RuntimeException(sprintf('Unable to guess the extension for the mime type "%s"', $mime));
}

$components[] = $hash;
$filename = sprintf('%s.%s', implode('-', $components), $extension[0]);
$localFilename = sprintf('%s/%s', rtrim($storageFolder, '/'), $filename);

file_put_contents($localFilename, $data);
$this->getFilesystem()
->remove($tempFilename);

return [
'src' => sprintf('%s/%s', $prefixUrl, $filename),
'type' => $mime,
];
}
}
62 changes: 62 additions & 0 deletions src/Command/SectionProcessor/IconsSectionProcessorTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

declare(strict_types=1);

namespace SpomkyLabs\PwaBundle\Command\SectionProcessor;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Style\SymfonyStyle;
use function is_int;

trait IconsSectionProcessorTrait
{
use ImageSectionProcessorTrait;

abstract protected function getIconPrefixUrl(): string;

abstract protected function getIconFolder(): string;

/**
* @param array{src: string, sizes: array<int>, format: ?string, purpose: ?string} $icons
*/
protected function processIcons(SymfonyStyle $io, array $icons): array|int
{
if (! $this->createDirectoryIfNotExists($this->getIconFolder()) || ! $this->checkImageProcessor($io)) {
return Command::FAILURE;
}
$result = [];
foreach ($icons as $icon) {
foreach ($icon['sizes'] as $size) {
if (! is_int($size) || $size < 0) {
$io->error('The icon size must be a positive integer');
return Command::FAILURE;
}
$data = $this->loadFileAndConvert($icon['src'], $size, $icon['format'] ?? null);
if ($data === null) {
$io->error(sprintf('Unable to read the icon "%s"', $icon['src']));
return Command::FAILURE;
}

$iconManifest = $this->storeIcon($data, $size, $icon['purpose'] ?? null);
$result[] = $iconManifest;
}
}

return $result;
}

/**
* @return array{src: string, sizes: string, type: string, purpose: ?string}
*/
private function storeIcon(string $data, int $size, ?string $purpose): array
{
$fileData = $this->storeFile(
$data,
$this->getIconPrefixUrl(),
$this->getIconFolder(),
['icon', $purpose, $size === 0 ? 'any' : $size . 'x' . $size]
);

return $this->handleSizeAndPurpose($purpose, $size, $fileData);
}
}
55 changes: 55 additions & 0 deletions src/Command/SectionProcessor/ImageSectionProcessorTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace SpomkyLabs\PwaBundle\Command\SectionProcessor;

use SpomkyLabs\PwaBundle\ImageProcessor\ImageProcessor;
use Symfony\Component\Console\Style\SymfonyStyle;

trait ImageSectionProcessorTrait
{
use FileProcessorTrait;

abstract protected function getImageProcessor(): ?ImageProcessor;

protected function handleSizeAndPurpose(?string $purpose, int $size, array $fileData): array
{
$sizes = $size === 0 ? 'any' : $size . 'x' . $size;
$fileData += [
'sizes' => $sizes,
];

if ($purpose !== null) {
$fileData += [
'purpose' => $purpose,
];
}

return $fileData;
}

protected function loadFileAndConvert(string $src, ?int $size, ?string $format): ?string
{
$data = file_get_contents($src);
if ($data === false) {
return null;
}
if ($size !== 0 && $size !== null) {
$data = $this->getImageProcessor()
->process($data, $size, $size, $format);
}

return $data;
}

protected function checkImageProcessor(SymfonyStyle $io): bool
{
if ($this->getImageProcessor() === null) {
$io->error('Image processor not found');
return false;
}

return true;
}
}
Loading

0 comments on commit f90fe93

Please sign in to comment.