Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"php": ">=8.2",
"ext-fileinfo": "*",
"cakephp/cakephp": "^5.1",
"intervention/image": "^2.7.2",
"intervention/image": "^2 || ^3",
"josegonzalez/cakephp-upload": "^8",
"league/csv": "^9.8",
"nette/utils": "^3.2 || ^4.0.0"
Expand Down
11 changes: 11 additions & 0 deletions src/Error/ModificationFailedException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace Assets\Error;

use RuntimeException;

final class ModificationFailedException extends RuntimeException
{
}
12 changes: 12 additions & 0 deletions src/ImageCreation/FilterInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Assets\ImageCreation;

interface FilterInterface
{
public static function create(ImageManagerInterface $manager, mixed ...$params): FilterInterface;

public function applyFilter(ImageInterface $image): ImageInterface;
}
36 changes: 36 additions & 0 deletions src/ImageCreation/ImageInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace Assets\ImageCreation;

use Assets\Error\ModificationFailedException;
use RuntimeException;

interface ImageInterface
{
public function width(): int;

public function height(): int;

public function mime(): string;

public function save(string $absolutePath, ?int $quality, ?string $format): void;

/**
* Modify the image based on the underlying implementation.
* @throws ModificationFailedException
*/
public function modify(string $modifier, array $params): self;

/**
* @return \Intervention\Image\Interfaces\ImageInterface|\Intervention\Image\Image|null
*/
public function getInterventionImage(): ?object;

/**
* @return \Intervention\Image\Interfaces\ImageInterface|\Intervention\Image\Image
* @throws RuntimeException
*/
public function requireInterventionImage(): object;
}
19 changes: 19 additions & 0 deletions src/ImageCreation/ImageManagerInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace Assets\ImageCreation;

use Assets\ImageCreation\Intervention\InterventionImageFacade;

interface ImageManagerInterface
{
public function read(string $absolutePath): ImageInterface;

public function create(int $width, int $height): ImageInterface;

/**
* @deprecated use {@see self::create()}
*/
public function canvas(int $width, int $height): ImageInterface;
}
73 changes: 73 additions & 0 deletions src/ImageCreation/ImageManagerLocator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

declare(strict_types=1);

namespace Assets\ImageCreation;

use Assets\ImageCreation\Intervention\InterventionImageManagerFacade;
use Assets\ImageCreation\LegacyV2Intervention as V2;
use Assets\ImageCreation\Intervention\LegacySupport;
use Cake\Core\Configure;
use Intervention\Image\Drivers\Imagick\Driver as ImagickDriver;
use Intervention\Image\Drivers\Gd\Driver as GdDriver;
use Intervention\Image\ImageManager;
use LogicException;

final class ImageManagerLocator
{
private static ?ImageManagerInterface $manager = null;

public static function getImageManager(): ImageManagerInterface
{
if (self::$manager !== null) {
return self::$manager;
}

if (interface_exists('\Intervention\Image\Interfaces\ImageInterface')) {
return self::$manager = self::createV3ManagerFacade();
}

if (class_exists('\Intervention\Image\ImageManager')) {
return self::$manager = self::createV2ManagerFacade();
}

throw new LogicException(
'intervention/image must be installed in v2 or v3',
);
}

public static function setImageManager(ImageManagerInterface $manager): void
{
self::$manager = $manager;
}

private static function createV2ManagerFacade(): ImageManagerInterface
{
$driver = Configure::read('AssetsPlugin.ImageAsset.driver', 'gd');

return new V2\InterventionImageManagerFacade(new ImageManager(['driver' => $driver]));
}

private static function createV3ManagerFacade(): ImageManagerInterface
{
$driver = Configure::read('AssetsPlugin.ImageAsset.driver', 'gd');
$legacyModifiersMap = Configure::read('AssetsPlugin.ImageAsset.legacyModifiersMap');

return new InterventionImageManagerFacade(
match ($driver) {
'imagick',
ImagickDriver::class => ImageManager::imagick(
...Configure::read('AssetsPlugin.ImageAsset.imagickOptions', []),
),
'gd',
GdDriver::class => ImageManager::gd(
...Configure::read('AssetsPlugin.ImageAsset.gdOptions', []),
),
default => throw new LogicException('no driver configured'),
},
legacyModifiersMap: $legacyModifiersMap !== null
? $legacyModifiersMap
: LegacySupport::V2_TO_V3_MODIFIERS_MAP,
);
}
}
116 changes: 116 additions & 0 deletions src/ImageCreation/Intervention/InterventionImageFacade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php

declare(strict_types=1);

namespace Assets\ImageCreation\Intervention;

use Assets\Error\InvalidArgumentException;
use Assets\Error\ModificationFailedException;
use Assets\ImageCreation\ImageInterface;
use Intervention\Image\MediaType;
use Intervention\Image\Interfaces as Intervention;
use Nette\Utils\FileSystem;

final class InterventionImageFacade implements ImageInterface
{
/**
* @param array<string, string|callable> $legacyMdifiersMap
*/
public function __construct(
private Intervention\ImageInterface $interventionImage,
private array $legacyMdifiersMap,
) {
}

public function width(): int
{
return $this->interventionImage->width();
}

public function height(): int
{
return $this->interventionImage->height();
}

/**
* @see MediaType
*/
public function mime(): string
{
return $this->interventionImage->origin()->mediaType();
}

public function save(string $absolutePath, ?int $quality, ?string $format): void
{
if ($format !== null && !str_ends_with($absolutePath, '.' . $format)) {
$absolutePath .= '.' . $format;
}

$dir = dirname($absolutePath);

if (!is_dir($dir)) {
FileSystem::createDir($dir);
}

$this->interventionImage->save($absolutePath, quality: $quality);
}

public function modify(string $modifier, array $params): ImageInterface
{
if ($params === []) {
throw new ModificationFailedException('Empty params given');
}

$modify = function (string $modifier, array $params, ?string $legacyModifier = null) {
try {
$this->interventionImage->{$modifier}(...$params);
} catch (\Throwable $throwable) {

if ($legacyModifier === null) {
$callback = $this->legacyMdifiersMap[$modifier] ?? null;
if ($callback !== null && is_callable($callback)) {
$callback($this->interventionImage, ...$params);
return $this->interventionImage;
}
}

throw new ModificationFailedException(
sprintf(
'Modification `%s` failed with params: %s.%s',
$modifier,
var_export($params, true),
$legacyModifier !== null ? ' Legacy: ' . $legacyModifier : '',
),
previous: $throwable,
);
}
};

if (method_exists($this->interventionImage, $modifier)) {
$modify($modifier, $params);
return $this;
}

$mappedFromLegacy = $this->legacyMdifiersMap[$modifier] ?? null;

if (
$mappedFromLegacy !== null
&& method_exists($this->interventionImage, $mappedFromLegacy)
) {
$modify($mappedFromLegacy, $params, $modifier);
return $this;
}

throw new ModificationFailedException(sprintf('Modifier `%s` does not exist', $modifier));
}

public function getInterventionImage(): Intervention\ImageInterface
{
return $this->requireInterventionImage();
}

public function requireInterventionImage(): Intervention\ImageInterface
{
return $this->interventionImage;
}
}
42 changes: 42 additions & 0 deletions src/ImageCreation/Intervention/InterventionImageManagerFacade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace Assets\ImageCreation\Intervention;

use Assets\ImageCreation\ImageInterface;
use Assets\ImageCreation\ImageManagerInterface;
use Intervention\Image\Interfaces as Intervention;

final class InterventionImageManagerFacade implements ImageManagerInterface
{
/**
* @param array<string, string|callable> $legacyModifiersMap
*/
public function __construct(
private Intervention\ImageManagerInterface $interventionImageManager,
private array $legacyModifiersMap,
) {
}

public function read(string $absolutePath): ImageInterface
{
return new InterventionImageFacade(
$this->interventionImageManager->read($absolutePath),
$this->legacyModifiersMap,
);
}

public function create(int $width, int $height): ImageInterface
{
return new InterventionImageFacade(
$this->interventionImageManager->create($width, $height),
$this->legacyModifiersMap,
);
}

public function canvas(int $width, int $height): ImageInterface
{
return $this->create($width, $height);
}
}
Loading
Loading