From 8c49eb21a6d2572532d1bc425964264f3e496846 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 30 Jul 2025 15:13:19 +0200 Subject: [PATCH] Allow all driver namespaces --- src/Drivers/AbstractDriver.php | 16 ++++-- src/Interfaces/DriverInterface.php | 2 +- src/Traits/CanBeDriverSpecialized.php | 11 ++-- tests/Unit/Drivers/Gd/DriverTest.php | 63 +++++++++++++++++++++++ tests/Unit/Drivers/Imagick/DriverTest.php | 63 +++++++++++++++++++++++ 5 files changed, 144 insertions(+), 11 deletions(-) diff --git a/src/Drivers/AbstractDriver.php b/src/Drivers/AbstractDriver.php index 36b2ab3e..f5d70240 100644 --- a/src/Drivers/AbstractDriver.php +++ b/src/Drivers/AbstractDriver.php @@ -77,13 +77,21 @@ abstract class AbstractDriver implements DriverInterface } // resolve classname for specializable object - $driver_namespace = (new ReflectionClass($this))->getNamespaceName(); - $object_path = substr($object::class, strlen("Intervention\\Image\\")); - $specialized_classname = $driver_namespace . "\\" . $object_path; + $specialized_classname = implode("\\", [ + (new ReflectionClass($this))->getNamespaceName(), // driver's namespace + match (true) { + $object instanceof ModifierInterface => 'Modifiers', + $object instanceof AnalyzerInterface => 'Analyzers', + $object instanceof EncoderInterface => 'Encoders', + $object instanceof DecoderInterface => 'Decoders', + }, + $object_shortname = (new ReflectionClass($object))->getShortName(), + ]); + // fail if driver specialized classname does not exists if (!class_exists($specialized_classname)) { throw new NotSupportedException( - "Class '" . $object_path . "' is not supported by " . $this->id() . " driver." + "Class '" . $object_shortname . "' is not supported by " . $this->id() . " driver." ); } diff --git a/src/Interfaces/DriverInterface.php b/src/Interfaces/DriverInterface.php index 95a715cf..6b746674 100644 --- a/src/Interfaces/DriverInterface.php +++ b/src/Interfaces/DriverInterface.php @@ -25,7 +25,7 @@ interface DriverInterface public function config(): Config; /** - * Resolve given object into a specialized version for the current driver + * Resolve given (generic) object into a specialized version for the current driver * * @throws NotSupportedException * @throws DriverException diff --git a/src/Traits/CanBeDriverSpecialized.php b/src/Traits/CanBeDriverSpecialized.php index 5671c63d..efa5622a 100644 --- a/src/Traits/CanBeDriverSpecialized.php +++ b/src/Traits/CanBeDriverSpecialized.php @@ -64,15 +64,14 @@ trait CanBeDriverSpecialized } /** - * Determine if the given object belongs to the driver's namespace + * Determine if the current object belongs to the given driver's namespace */ - protected function belongsToDriver(object $object): bool + protected function belongsToDriver(object $driver): bool { - $driverId = function (object $object): string|bool { - $id = substr($object::class, 27); - return strstr($id, "\\", true); + $namespace = function (object $object): string { + return (new ReflectionClass($object))->getNamespaceName(); }; - return $driverId($this) === $driverId($object); + return str_starts_with($namespace($this), $namespace($driver)); } } diff --git a/tests/Unit/Drivers/Gd/DriverTest.php b/tests/Unit/Drivers/Gd/DriverTest.php index 366653c5..1b23ed2b 100644 --- a/tests/Unit/Drivers/Gd/DriverTest.php +++ b/tests/Unit/Drivers/Gd/DriverTest.php @@ -5,15 +5,27 @@ declare(strict_types=1); namespace Intervention\Image\Tests\Unit\Drivers\Gd; use Generator; +use Intervention\Image\Analyzers\WidthAnalyzer as GenericWidthAnalyzer; use Intervention\Image\Colors\Rgb\Colorspace; use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder; +use Intervention\Image\Decoders\FilePathImageDecoder as GenericFilePathImageDecoder; +use Intervention\Image\Drivers\Gd\Analyzers\WidthAnalyzer; +use Intervention\Image\Drivers\Gd\Decoders\FilePathImageDecoder; use Intervention\Image\Drivers\Gd\Driver; +use Intervention\Image\Drivers\Gd\Encoders\PngEncoder; +use Intervention\Image\Drivers\Gd\Modifiers\ResizeModifier; +use Intervention\Image\Encoders\PngEncoder as GenericPngEncoder; +use Intervention\Image\Exceptions\NotSupportedException; use Intervention\Image\FileExtension; use Intervention\Image\Format; +use Intervention\Image\Interfaces\AnalyzerInterface; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorProcessorInterface; +use Intervention\Image\Interfaces\DriverInterface; use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Interfaces\SpecializableInterface; use Intervention\Image\MediaType; +use Intervention\Image\Modifiers\ResizeModifier as GenericResizeModifier; use Intervention\Image\Tests\BaseTestCase; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; @@ -211,4 +223,55 @@ final class DriverTest extends BaseTestCase { $this->assertTrue(is_string($this->driver->version())); } + + #[DataProvider('spezializeDataProvider')] + public function testSpecialize(string $inputClassname, string $outputClassname): void + { + $this->assertInstanceOf($outputClassname, $this->driver->specialize(new $inputClassname())); + } + + public static function spezializeDataProvider(): Generator + { + // specializing possible + yield [GenericResizeModifier::class, ResizeModifier::class]; + yield [GenericWidthAnalyzer::class, WidthAnalyzer::class]; + yield [GenericPngEncoder::class, PngEncoder::class]; + yield [GenericFilePathImageDecoder::class, FilePathImageDecoder::class]; + + // already specialized + yield [ResizeModifier::class, ResizeModifier::class]; + yield [WidthAnalyzer::class, WidthAnalyzer::class]; + yield [PngEncoder::class, PngEncoder::class]; + yield [FilePathImageDecoder::class, FilePathImageDecoder::class]; + } + + public function testSpecializeFailure(): void + { + $this->expectException(NotSupportedException::class); + $this->driver->specialize(new class () implements AnalyzerInterface, SpecializableInterface + { + protected DriverInterface $driver; + + public function analyze(ImageInterface $image): mixed + { + return true; + } + + /** @return array **/ + public function specializable(): array + { + return []; + } + + public function setDriver(DriverInterface $driver): SpecializableInterface + { + return $this; + } + + public function driver(): DriverInterface + { + return $this->driver; + } + }); + } } diff --git a/tests/Unit/Drivers/Imagick/DriverTest.php b/tests/Unit/Drivers/Imagick/DriverTest.php index f69a5056..ee7d53d7 100644 --- a/tests/Unit/Drivers/Imagick/DriverTest.php +++ b/tests/Unit/Drivers/Imagick/DriverTest.php @@ -5,14 +5,26 @@ declare(strict_types=1); namespace Intervention\Image\Tests\Unit\Drivers\Imagick; use Generator; +use Intervention\Image\Analyzers\WidthAnalyzer as GenericWidthAnalyzer; +use Intervention\Image\Decoders\FilePathImageDecoder as GenericFilePathImageDecoder; +use Intervention\Image\Encoders\PngEncoder as GenericPngEncoder; +use Intervention\Image\Modifiers\ResizeModifier as GenericResizeModifier; use Intervention\Image\Colors\Rgb\Colorspace; use Intervention\Image\Colors\Rgb\Decoders\HexColorDecoder; +use Intervention\Image\Drivers\Imagick\Analyzers\WidthAnalyzer; +use Intervention\Image\Drivers\Imagick\Decoders\FilePathImageDecoder; use Intervention\Image\Drivers\Imagick\Driver; +use Intervention\Image\Drivers\Imagick\Encoders\PngEncoder; +use Intervention\Image\Drivers\Imagick\Modifiers\ResizeModifier; +use Intervention\Image\Exceptions\NotSupportedException; use Intervention\Image\FileExtension; use Intervention\Image\Format; +use Intervention\Image\Interfaces\AnalyzerInterface; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ColorProcessorInterface; +use Intervention\Image\Interfaces\DriverInterface; use Intervention\Image\Interfaces\ImageInterface; +use Intervention\Image\Interfaces\SpecializableInterface; use Intervention\Image\MediaType; use Intervention\Image\Tests\BaseTestCase; use PHPUnit\Framework\Attributes\CoversClass; @@ -211,4 +223,55 @@ final class DriverTest extends BaseTestCase { $this->assertTrue(is_string($this->driver->version())); } + + #[DataProvider('spezializeDataProvider')] + public function testSpecialize(string $inputClassname, string $outputClassname): void + { + $this->assertInstanceOf($outputClassname, $this->driver->specialize(new $inputClassname())); + } + + public static function spezializeDataProvider(): Generator + { + // specializing possible + yield [GenericResizeModifier::class, ResizeModifier::class]; + yield [GenericWidthAnalyzer::class, WidthAnalyzer::class]; + yield [GenericPngEncoder::class, PngEncoder::class]; + yield [GenericFilePathImageDecoder::class, FilePathImageDecoder::class]; + + // already specialized + yield [ResizeModifier::class, ResizeModifier::class]; + yield [WidthAnalyzer::class, WidthAnalyzer::class]; + yield [PngEncoder::class, PngEncoder::class]; + yield [FilePathImageDecoder::class, FilePathImageDecoder::class]; + } + + public function testSpecializeFailure(): void + { + $this->expectException(NotSupportedException::class); + $this->driver->specialize(new class () implements AnalyzerInterface, SpecializableInterface + { + protected DriverInterface $driver; + + public function analyze(ImageInterface $image): mixed + { + return true; + } + + /** @return array **/ + public function specializable(): array + { + return []; + } + + public function setDriver(DriverInterface $driver): SpecializableInterface + { + return $this; + } + + public function driver(): DriverInterface + { + return $this->driver; + } + }); + } }