<?php
|
declare(strict_types=1);
|
|
namespace App\Service;
|
|
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
use Symfony\Component\Filesystem\Filesystem;
|
|
class CoverStorage {
|
public const array EXTENSIONS = [
|
'image/jpeg' => 'jpg',
|
'image/png' => 'png',
|
'image/webp' => 'webp',
|
];
|
|
public function __construct(
|
#[Autowire('%app.cover_originals_dir%')]
|
private readonly string $originalsDir,
|
#[Autowire('%app.cover_thumbnails_dir%')]
|
private readonly string $thumbnailsDir,
|
private readonly CoverThumbnailer $thumbnailer,
|
private readonly Filesystem $filesystem,
|
) {
|
}
|
|
/**
|
* @param string $contents the image, kept byte for byte as the original
|
*
|
* @return string the name of the stored cover
|
*
|
* @throws CoverProcessingException
|
*/
|
public function store(string $contents): string {
|
// The extension comes from the detected content, never from the client's file name.
|
$extension = self::EXTENSIONS[@getimagesizefromstring($contents)['mime'] ?? ''] ?? null;
|
|
if ($extension === null) {
|
throw new CoverProcessingException('Unsupported image type.');
|
}
|
|
$name = bin2hex(random_bytes(16)) . '.' . $extension;
|
$this->filesystem->mkdir([$this->originalsDir, $this->thumbnailsDir]);
|
|
try {
|
$this->thumbnailer->createThumbnails(
|
$contents,
|
fn(string $size): string => $this->thumbnailPath($name, $size),
|
);
|
$this->filesystem->dumpFile($this->originalPath($name), $contents);
|
} catch (\Throwable $exception) {
|
$this->delete($name);
|
|
throw $exception;
|
}
|
|
return $name;
|
}
|
|
public function delete(string $name): void {
|
$this->filesystem->remove([
|
$this->originalPath($name),
|
...array_map(
|
fn(string $size): string => $this->thumbnailPath($name, $size),
|
array_keys(CoverThumbnailer::SIZES),
|
),
|
]);
|
}
|
|
public function originalPath(string $name): string {
|
return $this->originalsDir . '/' . $name;
|
}
|
|
public function thumbnailPath(string $name, string $size): string {
|
return $this->thumbnailsDir . '/' . $name . '.' . $size . '.jpg';
|
}
|
}
|