From f09616961a82e1d777d579bc9308af24d508a31f Mon Sep 17 00:00:00 2001 From: Arnaud Ligny Date: Fri, 19 Jul 2024 17:41:51 +0200 Subject: [PATCH] refactor: migration to Intervention/Image v3 (#2013) --- composer.json | 2 +- composer.lock | 384 ++++++++++--------------------------------- src/Assets/Asset.php | 1 - src/Assets/Image.php | 68 ++++---- 4 files changed, 118 insertions(+), 337 deletions(-) diff --git a/composer.json b/composer.json index 68ac4a69e..33db557b5 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ "cocur/slugify": "^4.5", "dflydev/dot-access-data": "^3.0", "erusev/parsedown-extra": "^0.8", - "intervention/image": "^2.7", + "intervention/image": "^3.7", "laravel-zero/phar-updater": "^1.4", "matthiasmullie/minify": "^1.3", "psr/log": "^3.0", diff --git a/composer.lock b/composer.lock index 2a35839af..49514e95f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e8dbdc34a2393043fb37d929032a476b", + "content-hash": "b88ef287374af0de711b516e89d58e7c", "packages": [ { "name": "amphp/amp", @@ -1398,122 +1398,6 @@ ], "time": "2023-12-10T13:29:09+00:00" }, - { - "name": "guzzlehttp/psr7", - "version": "2.6.2", - "source": { - "type": "git", - "url": "https://github.com/guzzle/psr7.git", - "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221", - "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221", - "shasum": "" - }, - "require": { - "php": "^7.2.5 || ^8.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.1 || ^2.0", - "ralouphie/getallheaders": "^3.0" - }, - "provide": { - "psr/http-factory-implementation": "1.0", - "psr/http-message-implementation": "1.0" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8.2", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.36 || ^9.6.15" - }, - "suggest": { - "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" - }, - "type": "library", - "extra": { - "bamarni-bin": { - "bin-links": true, - "forward-command": false - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Graham Campbell", - "email": "hello@gjcampbell.co.uk", - "homepage": "https://github.com/GrahamCampbell" - }, - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "George Mponos", - "email": "gmponos@gmail.com", - "homepage": "https://github.com/gmponos" - }, - { - "name": "Tobias Nyholm", - "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/Nyholm" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://github.com/sagikazarmark" - }, - { - "name": "Tobias Schultze", - "email": "webmaster@tubo-world.de", - "homepage": "https://github.com/Tobion" - }, - { - "name": "Márk Sági-Kazár", - "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" - } - ], - "description": "PSR-7 message implementation that also provides common utility methods", - "keywords": [ - "http", - "message", - "psr-7", - "request", - "response", - "stream", - "uri", - "url" - ], - "support": { - "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.6.2" - }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", - "type": "tidelift" - } - ], - "time": "2023-12-03T20:05:35+00:00" - }, { "name": "humbug/box", "version": "4.5.1", @@ -1714,51 +1598,103 @@ }, "time": "2023-11-04T18:01:12+00:00" }, + { + "name": "intervention/gif", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/Intervention/gif.git", + "reference": "3a2b5f8a8856e8877cdab5c47e51aab2d4cb23a3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Intervention/gif/zipball/3a2b5f8a8856e8877cdab5c47e51aab2d4cb23a3", + "reference": "3a2b5f8a8856e8877cdab5c47e51aab2d4cb23a3", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "phpstan/phpstan": "^1", + "phpunit/phpunit": "^10.0", + "slevomat/coding-standard": "~8.0", + "squizlabs/php_codesniffer": "^3.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Intervention\\Gif\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oliver Vogel", + "email": "oliver@intervention.io", + "homepage": "https://intervention.io/" + } + ], + "description": "Native PHP GIF Encoder/Decoder", + "homepage": "https://github.com/intervention/gif", + "keywords": [ + "animation", + "gd", + "gif", + "image" + ], + "support": { + "issues": "https://github.com/Intervention/gif/issues", + "source": "https://github.com/Intervention/gif/tree/4.1.0" + }, + "funding": [ + { + "url": "https://paypal.me/interventionio", + "type": "custom" + }, + { + "url": "https://github.com/Intervention", + "type": "github" + } + ], + "time": "2024-03-26T17:23:47+00:00" + }, { "name": "intervention/image", - "version": "2.7.2", + "version": "3.7.2", "source": { "type": "git", "url": "https://github.com/Intervention/image.git", - "reference": "04be355f8d6734c826045d02a1079ad658322dad" + "reference": "5451ff9f909c2fc836722e5ed6831b9f9a6db68c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Intervention/image/zipball/04be355f8d6734c826045d02a1079ad658322dad", - "reference": "04be355f8d6734c826045d02a1079ad658322dad", + "url": "https://api.github.com/repos/Intervention/image/zipball/5451ff9f909c2fc836722e5ed6831b9f9a6db68c", + "reference": "5451ff9f909c2fc836722e5ed6831b9f9a6db68c", "shasum": "" }, "require": { - "ext-fileinfo": "*", - "guzzlehttp/psr7": "~1.1 || ^2.0", - "php": ">=5.4.0" + "ext-mbstring": "*", + "intervention/gif": "^4.1", + "php": "^8.1" }, "require-dev": { - "mockery/mockery": "~0.9.2", - "phpunit/phpunit": "^4.8 || ^5.7 || ^7.5.15" + "mockery/mockery": "^1.6", + "phpstan/phpstan": "^1", + "phpunit/phpunit": "^10.0", + "slevomat/coding-standard": "~8.0", + "squizlabs/php_codesniffer": "^3.8" }, "suggest": { - "ext-gd": "to use GD library based image processing.", - "ext-imagick": "to use Imagick based image processing.", - "intervention/imagecache": "Caching extension for the Intervention Image library" + "ext-exif": "Recommended to be able to read EXIF data properly." }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - }, - "laravel": { - "providers": [ - "Intervention\\Image\\ImageServiceProvider" - ], - "aliases": { - "Image": "Intervention\\Image\\Facades\\Image" - } - } - }, "autoload": { "psr-4": { - "Intervention\\Image\\": "src/Intervention/Image" + "Intervention\\Image\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1772,19 +1708,19 @@ "homepage": "https://intervention.io/" } ], - "description": "Image handling and manipulation library with support for Laravel integration", - "homepage": "http://image.intervention.io/", + "description": "PHP image manipulation", + "homepage": "https://image.intervention.io/", "keywords": [ "gd", "image", "imagick", - "laravel", + "resize", "thumbnail", "watermark" ], "support": { "issues": "https://github.com/Intervention/image/issues", - "source": "https://github.com/Intervention/image/tree/2.7.2" + "source": "https://github.com/Intervention/image/tree/3.7.2" }, "funding": [ { @@ -1796,7 +1732,7 @@ "type": "github" } ], - "time": "2022-05-21T17:30:32+00:00" + "time": "2024-07-05T13:35:01+00:00" }, { "name": "jetbrains/phpstorm-stubs", @@ -2656,114 +2592,6 @@ }, "time": "2019-01-08T18:20:26+00:00" }, - { - "name": "psr/http-factory", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-factory.git", - "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", - "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", - "shasum": "" - }, - "require": { - "php": ">=7.1", - "psr/http-message": "^1.0 || ^2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", - "keywords": [ - "factory", - "http", - "message", - "psr", - "psr-17", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-factory" - }, - "time": "2024-04-15T12:06:14+00:00" - }, - { - "name": "psr/http-message", - "version": "2.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-message/tree/2.0" - }, - "time": "2023-04-04T09:54:51+00:00" - }, { "name": "psr/log", "version": "3.0.0", @@ -2865,50 +2693,6 @@ }, "time": "2021-10-29T13:26:27+00:00" }, - { - "name": "ralouphie/getallheaders", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" - }, - "type": "library", - "autoload": { - "files": [ - "src/getallheaders.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } - ], - "description": "A polyfill for getallheaders.", - "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" - }, - "time": "2019-03-08T08:55:37+00:00" - }, { "name": "scrivo/highlight.php", "version": "v9.18.1.10", diff --git a/src/Assets/Asset.php b/src/Assets/Asset.php index 9a4e802ef..55c3db6b2 100644 --- a/src/Assets/Asset.php +++ b/src/Assets/Asset.php @@ -20,7 +20,6 @@ use Cecil\Exception\ConfigException; use Cecil\Exception\RuntimeException; use Cecil\Util; -use Intervention\Image\ImageManagerStatic as ImageManager; use MatthiasMullie\Minify; use ScssPhp\ScssPhp\Compiler; use wapmorgan\Mp3Info\Mp3Info; diff --git a/src/Assets/Image.php b/src/Assets/Image.php index 5fdc1effb..cab398384 100644 --- a/src/Assets/Image.php +++ b/src/Assets/Image.php @@ -14,10 +14,26 @@ namespace Cecil\Assets; use Cecil\Exception\RuntimeException; -use Intervention\Image\ImageManagerStatic as ImageManager; +use Intervention\Image\Encoders\AutoEncoder; +use Intervention\Image\ImageManager; class Image { + /** + * Create new manager instance with desired driver. + */ + private static function manager(): ImageManager + { + if (\extension_loaded('gd') && \function_exists('gd_info')) { + return ImageManager::gd(); + } + if (\extension_loaded('imagick') && class_exists('Imagick')) { + return ImageManager::imagick(); + } + + throw new RuntimeException('PHP GD or Imagick extension is required.'); + } + /** * Resize an image Asset. * @@ -30,25 +46,12 @@ public static function resize(Asset $asset, int $width, int $quality): string if ($asset['type'] !== 'image') { throw new RuntimeException(sprintf('Not an image.')); } - // is GD is installed - if (!\extension_loaded('gd')) { - throw new RuntimeException('GD extension is required.'); - } // creates image object from source - $image = ImageManager::make($asset['content_source']); + $image = self::manager()->read($asset['content_source']); // resizes to $width with constraint the aspect-ratio and unwanted upsizing - $image->resize($width, null, function (\Intervention\Image\Constraint $constraint) { - $constraint->aspectRatio(); - $constraint->upsize(); - }); - // interlaces (PNG) or progressives (JPEG) image - $image->interlace(); - // save image in extension format and given quality - $imageAsString = (string) $image->encode($asset['ext'], $quality); - // destroy image object - $image->destroy(); - - return $imageAsString; + $image->scaleDown(width: $width); + // return image data + return (string) $image->encodeByMediaType($asset['subtype'], /** @scrutinizer ignore-type */ progressive: true, /** @scrutinizer ignore-type */ interlaced: true, quality: $quality); } catch (\Exception $e) { throw new RuntimeException(sprintf('Not able to resize "%s": %s', $asset['path'], $e->getMessage())); } @@ -65,11 +68,13 @@ public static function convert(Asset $asset, string $format, int $quality): stri if ($asset['type'] !== 'image') { throw new RuntimeException(sprintf('Not an image.')); } - $image = ImageManager::make($asset['content']); - $imageAsString = (string) $image->encode($format, $quality); - $image->destroy(); + $image = self::manager()->read($asset['content']); - return $imageAsString; + if (!\function_exists("image$format")) { + throw new RuntimeException(sprintf('Function "image%s" is not available.', $format)); + } + + return (string) $image->encodeByExtension($format, /** @scrutinizer ignore-type */ progressive: true, /** @scrutinizer ignore-type */ interlaced: true, quality: $quality); } catch (\Exception $e) { throw new RuntimeException(sprintf('Not able to convert "%s": %s', $asset['path'], $e->getMessage())); } @@ -86,11 +91,9 @@ public static function getDataUrl(Asset $asset, int $quality): string if ($asset['type'] != 'image' || self::isSVG($asset)) { throw new RuntimeException(sprintf('Not an image.')); } - $image = ImageManager::make($asset['content']); - $imageAsDataUrl = (string) $image->encode('data-url', $quality); - $image->destroy(); + $image = self::manager()->read($asset['content']); - return $imageAsDataUrl; + return (string) $image->encode(new AutoEncoder(quality: $quality))->toDataUri(); } catch (\Exception $e) { throw new RuntimeException(sprintf('Can\'t get Data URL of "%s": %s', $asset['path'], $e->getMessage())); } @@ -107,14 +110,11 @@ public static function getDominantColor(Asset $asset): string if ($asset['type'] != 'image' || self::isSVG($asset)) { throw new RuntimeException(sprintf('Not an image.')); } - $assetColor = clone $asset; $assetColor = $assetColor->resize(100); - $image = ImageManager::make($assetColor['content']); - $color = $image->limitColors(1)->pickColor(0, 0, 'hex'); - $image->destroy(); + $image = self::manager()->read($assetColor['content']); - return $color; + return $image->reduceColors(1)->pickColor(0, 0)->toHex(); } catch (\Exception $e) { throw new RuntimeException(sprintf('Can\'t get dominant color of "%s": %s', $asset['path'], $e->getMessage())); } @@ -133,11 +133,9 @@ public static function getLqip(Asset $asset): string } $assetLqip = clone $asset; $assetLqip = $assetLqip->resize(100); - $image = ImageManager::make($assetLqip['content']); - $imageAsString = (string) $image->blur(50)->encode('data-url'); - $image->destroy(); + $image = self::manager()->read($assetLqip['content']); - return $imageAsString; + return (string) $image->blur(50)->encode()->toDataUri(); } catch (\Exception $e) { throw new RuntimeException(sprintf('can\'t create LQIP of "%s": %s', $asset['path'], $e->getMessage())); }