Skip to content

Commit e91eb15

Browse files
authored
Merge pull request #15 from brandcom/feature-intervention-v3
Feature intervention v3
2 parents f8c4f70 + a4c23ce commit e91eb15

13 files changed

Lines changed: 689 additions & 44 deletions

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"php": ">=8.2",
88
"ext-fileinfo": "*",
99
"cakephp/cakephp": "^5.1",
10-
"intervention/image": "^2.7.2",
10+
"intervention/image": "^2 || ^3",
1111
"josegonzalez/cakephp-upload": "^8",
1212
"league/csv": "^9.8",
1313
"nette/utils": "^3.2 || ^4.0.0"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Assets\Error;
6+
7+
use RuntimeException;
8+
9+
final class ModificationFailedException extends RuntimeException
10+
{
11+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Assets\ImageCreation;
6+
7+
interface FilterInterface
8+
{
9+
public static function create(ImageManagerInterface $manager, mixed ...$params): FilterInterface;
10+
11+
public function applyFilter(ImageInterface $image): ImageInterface;
12+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Assets\ImageCreation;
6+
7+
use Assets\Error\ModificationFailedException;
8+
use RuntimeException;
9+
10+
interface ImageInterface
11+
{
12+
public function width(): int;
13+
14+
public function height(): int;
15+
16+
public function mime(): string;
17+
18+
public function save(string $absolutePath, ?int $quality, ?string $format): void;
19+
20+
/**
21+
* Modify the image based on the underlying implementation.
22+
* @throws ModificationFailedException
23+
*/
24+
public function modify(string $modifier, array $params): self;
25+
26+
/**
27+
* @return \Intervention\Image\Interfaces\ImageInterface|\Intervention\Image\Image|null
28+
*/
29+
public function getInterventionImage(): ?object;
30+
31+
/**
32+
* @return \Intervention\Image\Interfaces\ImageInterface|\Intervention\Image\Image
33+
* @throws RuntimeException
34+
*/
35+
public function requireInterventionImage(): object;
36+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Assets\ImageCreation;
6+
7+
use Assets\ImageCreation\Intervention\InterventionImageFacade;
8+
9+
interface ImageManagerInterface
10+
{
11+
public function read(string $absolutePath): ImageInterface;
12+
13+
public function create(int $width, int $height): ImageInterface;
14+
15+
/**
16+
* @deprecated use {@see self::create()}
17+
*/
18+
public function canvas(int $width, int $height): ImageInterface;
19+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Assets\ImageCreation;
6+
7+
use Assets\ImageCreation\Intervention\InterventionImageManagerFacade;
8+
use Assets\ImageCreation\LegacyV2Intervention as V2;
9+
use Assets\ImageCreation\Intervention\LegacySupport;
10+
use Cake\Core\Configure;
11+
use Intervention\Image\Drivers\Imagick\Driver as ImagickDriver;
12+
use Intervention\Image\Drivers\Gd\Driver as GdDriver;
13+
use Intervention\Image\ImageManager;
14+
use LogicException;
15+
16+
final class ImageManagerLocator
17+
{
18+
private static ?ImageManagerInterface $manager = null;
19+
20+
public static function getImageManager(): ImageManagerInterface
21+
{
22+
if (self::$manager !== null) {
23+
return self::$manager;
24+
}
25+
26+
if (interface_exists('\Intervention\Image\Interfaces\ImageInterface')) {
27+
return self::$manager = self::createV3ManagerFacade();
28+
}
29+
30+
if (class_exists('\Intervention\Image\ImageManager')) {
31+
return self::$manager = self::createV2ManagerFacade();
32+
}
33+
34+
throw new LogicException(
35+
'intervention/image must be installed in v2 or v3',
36+
);
37+
}
38+
39+
public static function setImageManager(ImageManagerInterface $manager): void
40+
{
41+
self::$manager = $manager;
42+
}
43+
44+
private static function createV2ManagerFacade(): ImageManagerInterface
45+
{
46+
$driver = Configure::read('AssetsPlugin.ImageAsset.driver', 'gd');
47+
48+
return new V2\InterventionImageManagerFacade(new ImageManager(['driver' => $driver]));
49+
}
50+
51+
private static function createV3ManagerFacade(): ImageManagerInterface
52+
{
53+
$driver = Configure::read('AssetsPlugin.ImageAsset.driver', 'gd');
54+
$legacyModifiersMap = Configure::read('AssetsPlugin.ImageAsset.legacyModifiersMap');
55+
56+
return new InterventionImageManagerFacade(
57+
match ($driver) {
58+
'imagick',
59+
ImagickDriver::class => ImageManager::imagick(
60+
...Configure::read('AssetsPlugin.ImageAsset.imagickOptions', []),
61+
),
62+
'gd',
63+
GdDriver::class => ImageManager::gd(
64+
...Configure::read('AssetsPlugin.ImageAsset.gdOptions', []),
65+
),
66+
default => throw new LogicException('no driver configured'),
67+
},
68+
legacyModifiersMap: $legacyModifiersMap !== null
69+
? $legacyModifiersMap
70+
: LegacySupport::V2_TO_V3_MODIFIERS_MAP,
71+
);
72+
}
73+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Assets\ImageCreation\Intervention;
6+
7+
use Assets\Error\InvalidArgumentException;
8+
use Assets\Error\ModificationFailedException;
9+
use Assets\ImageCreation\ImageInterface;
10+
use Intervention\Image\MediaType;
11+
use Intervention\Image\Interfaces as Intervention;
12+
use Nette\Utils\FileSystem;
13+
14+
final class InterventionImageFacade implements ImageInterface
15+
{
16+
/**
17+
* @param array<string, string|callable> $legacyMdifiersMap
18+
*/
19+
public function __construct(
20+
private Intervention\ImageInterface $interventionImage,
21+
private array $legacyMdifiersMap,
22+
) {
23+
}
24+
25+
public function width(): int
26+
{
27+
return $this->interventionImage->width();
28+
}
29+
30+
public function height(): int
31+
{
32+
return $this->interventionImage->height();
33+
}
34+
35+
/**
36+
* @see MediaType
37+
*/
38+
public function mime(): string
39+
{
40+
return $this->interventionImage->origin()->mediaType();
41+
}
42+
43+
public function save(string $absolutePath, ?int $quality, ?string $format): void
44+
{
45+
if ($format !== null && !str_ends_with($absolutePath, '.' . $format)) {
46+
$absolutePath .= '.' . $format;
47+
}
48+
49+
$dir = dirname($absolutePath);
50+
51+
if (!is_dir($dir)) {
52+
FileSystem::createDir($dir);
53+
}
54+
55+
$this->interventionImage->save($absolutePath, quality: $quality);
56+
}
57+
58+
public function modify(string $modifier, array $params): ImageInterface
59+
{
60+
if ($params === []) {
61+
throw new ModificationFailedException('Empty params given');
62+
}
63+
64+
$modify = function (string $modifier, array $params, ?string $legacyModifier = null) {
65+
try {
66+
$this->interventionImage->{$modifier}(...$params);
67+
} catch (\Throwable $throwable) {
68+
69+
if ($legacyModifier === null) {
70+
$callback = $this->legacyMdifiersMap[$modifier] ?? null;
71+
if ($callback !== null && is_callable($callback)) {
72+
$callback($this->interventionImage, ...$params);
73+
return $this->interventionImage;
74+
}
75+
}
76+
77+
throw new ModificationFailedException(
78+
sprintf(
79+
'Modification `%s` failed with params: %s.%s',
80+
$modifier,
81+
var_export($params, true),
82+
$legacyModifier !== null ? ' Legacy: ' . $legacyModifier : '',
83+
),
84+
previous: $throwable,
85+
);
86+
}
87+
};
88+
89+
if (method_exists($this->interventionImage, $modifier)) {
90+
$modify($modifier, $params);
91+
return $this;
92+
}
93+
94+
$mappedFromLegacy = $this->legacyMdifiersMap[$modifier] ?? null;
95+
96+
if (
97+
$mappedFromLegacy !== null
98+
&& method_exists($this->interventionImage, $mappedFromLegacy)
99+
) {
100+
$modify($mappedFromLegacy, $params, $modifier);
101+
return $this;
102+
}
103+
104+
throw new ModificationFailedException(sprintf('Modifier `%s` does not exist', $modifier));
105+
}
106+
107+
public function getInterventionImage(): Intervention\ImageInterface
108+
{
109+
return $this->requireInterventionImage();
110+
}
111+
112+
public function requireInterventionImage(): Intervention\ImageInterface
113+
{
114+
return $this->interventionImage;
115+
}
116+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Assets\ImageCreation\Intervention;
6+
7+
use Assets\ImageCreation\ImageInterface;
8+
use Assets\ImageCreation\ImageManagerInterface;
9+
use Intervention\Image\Interfaces as Intervention;
10+
11+
final class InterventionImageManagerFacade implements ImageManagerInterface
12+
{
13+
/**
14+
* @param array<string, string|callable> $legacyModifiersMap
15+
*/
16+
public function __construct(
17+
private Intervention\ImageManagerInterface $interventionImageManager,
18+
private array $legacyModifiersMap,
19+
) {
20+
}
21+
22+
public function read(string $absolutePath): ImageInterface
23+
{
24+
return new InterventionImageFacade(
25+
$this->interventionImageManager->read($absolutePath),
26+
$this->legacyModifiersMap,
27+
);
28+
}
29+
30+
public function create(int $width, int $height): ImageInterface
31+
{
32+
return new InterventionImageFacade(
33+
$this->interventionImageManager->create($width, $height),
34+
$this->legacyModifiersMap,
35+
);
36+
}
37+
38+
public function canvas(int $width, int $height): ImageInterface
39+
{
40+
return $this->create($width, $height);
41+
}
42+
}

0 commit comments

Comments
 (0)