From 033cc7bb31b020fefcdfaac923fda2654d500129 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 11 Apr 2026 10:01:11 +0200 Subject: [PATCH 1/2] Migrate to Intervention Image v4 --- composer.json | 2 +- framework/core/composer.json | 2 +- .../core/src/Api/Controller/UploadFaviconController.php | 5 +++-- framework/core/src/Api/Controller/UploadLogoController.php | 7 ++++--- framework/core/src/Api/Resource/UserResource.php | 2 +- framework/core/src/User/AvatarUploader.php | 5 +++-- framework/core/src/User/AvatarValidator.php | 7 +++---- framework/core/src/User/Command/UploadAvatarHandler.php | 2 +- .../core/src/User/Console/ConvertAvatarsToWebpCommand.php | 3 ++- framework/core/tests/unit/User/AvatarUploaderTest.php | 5 +++-- 10 files changed, 22 insertions(+), 18 deletions(-) diff --git a/composer.json b/composer.json index 75c9b3f641..949ef23d54 100644 --- a/composer.json +++ b/composer.json @@ -141,7 +141,7 @@ "illuminate/support": "^13.0", "illuminate/validation": "^13.0", "illuminate/view": "^13.0", - "intervention/image": "^3.2", + "intervention/image": "^4.0", "jenssegers/agent": "^2.6", "laminas/laminas-diactoros": "^3.0", "laminas/laminas-httphandlerrunner": "^2.6", diff --git a/framework/core/composer.json b/framework/core/composer.json index a2b5bfdb78..b949ccf2f7 100644 --- a/framework/core/composer.json +++ b/framework/core/composer.json @@ -60,7 +60,7 @@ "illuminate/support": "^13.0", "illuminate/validation": "^13.0", "illuminate/view": "^13.0", - "intervention/image": "^3.2", + "intervention/image": "^4.0", "jenssegers/agent": "^2.6.4", "laminas/laminas-diactoros": "^3.0", "laminas/laminas-httphandlerrunner": "^2.6.1", diff --git a/framework/core/src/Api/Controller/UploadFaviconController.php b/framework/core/src/Api/Controller/UploadFaviconController.php index 1892b6ce1c..1d16cd4716 100644 --- a/framework/core/src/Api/Controller/UploadFaviconController.php +++ b/framework/core/src/Api/Controller/UploadFaviconController.php @@ -9,6 +9,7 @@ namespace Flarum\Api\Controller; +use Intervention\Image\Format; use Intervention\Image\Interfaces\EncodedImageInterface; use Psr\Http\Message\StreamInterface; use Psr\Http\Message\UploadedFileInterface; @@ -26,9 +27,9 @@ protected function makeImage(UploadedFileInterface $file): EncodedImageInterface return $file->getStream(); } - $encodedImage = $this->imageManager->read($file->getStream()->getMetadata('uri')) + $encodedImage = $this->imageManager->decode($file->getStream()->getMetadata('uri')) ->scale(64, 64) - ->toPng(); + ->encodeUsingFormat(Format::PNG); $this->fileExtension = 'png'; diff --git a/framework/core/src/Api/Controller/UploadLogoController.php b/framework/core/src/Api/Controller/UploadLogoController.php index 42c065d1e7..6f2be7014f 100644 --- a/framework/core/src/Api/Controller/UploadLogoController.php +++ b/framework/core/src/Api/Controller/UploadLogoController.php @@ -9,6 +9,7 @@ namespace Flarum\Api\Controller; +use Intervention\Image\Format; use Intervention\Image\Interfaces\EncodedImageInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\UploadedFileInterface; @@ -21,18 +22,18 @@ class UploadLogoController extends UploadImageController protected function makeImage(UploadedFileInterface $file): EncodedImageInterface { - $image = $this->imageManager->read($file->getStream()->getMetadata('uri')) + $image = $this->imageManager->decode($file->getStream()->getMetadata('uri')) ->scale(height: 60); if ($image->isAnimated()) { $this->resolvedExtension = 'gif'; - return $image->toGif(); + return $image->encodeUsingFormat(Format::GIF); } $this->resolvedExtension = 'webp'; - return $image->toWebp(); + return $image->encodeUsingFormat(Format::WEBP); } protected function fileExtension(ServerRequestInterface $request, UploadedFileInterface $file): string diff --git a/framework/core/src/Api/Resource/UserResource.php b/framework/core/src/Api/Resource/UserResource.php index 59785b4c88..6e076b7c31 100644 --- a/framework/core/src/Api/Resource/UserResource.php +++ b/framework/core/src/Api/Resource/UserResource.php @@ -424,7 +424,7 @@ private function uploadAvatarFromUrl(User $user, string $url): void $urlContents = $this->retrieveAvatarFromUrl($url); if ($urlContents !== null) { - $image = $this->imageManager->read($urlContents); + $image = $this->imageManager->decodeBinary($urlContents); $this->avatarUploader->upload($user, $image); } diff --git a/framework/core/src/User/AvatarUploader.php b/framework/core/src/User/AvatarUploader.php index ddf182a02e..6e22acec3d 100644 --- a/framework/core/src/User/AvatarUploader.php +++ b/framework/core/src/User/AvatarUploader.php @@ -12,6 +12,7 @@ use Illuminate\Contracts\Filesystem\Factory; use Illuminate\Contracts\Filesystem\Filesystem; use Illuminate\Support\Str; +use Intervention\Image\Format; use Intervention\Image\Interfaces\ImageInterface; class AvatarUploader @@ -29,10 +30,10 @@ public function upload(User $user, ImageInterface $image): void $avatarPath = Str::random(); if ($image->isAnimated()) { - $encodedImage = $image->toGif(); + $encodedImage = $image->encodeUsingFormat(Format::GIF); $avatarPath .= '.gif'; } else { - $encodedImage = $image->toWebp(); + $encodedImage = $image->encodeUsingFormat(Format::WEBP); $avatarPath .= '.webp'; } diff --git a/framework/core/src/User/AvatarValidator.php b/framework/core/src/User/AvatarValidator.php index ac8e91ece5..dbf56251e9 100644 --- a/framework/core/src/User/AvatarValidator.php +++ b/framework/core/src/User/AvatarValidator.php @@ -13,8 +13,7 @@ use Flarum\Foundation\ValidationException; use Flarum\Locale\TranslatorInterface; use Illuminate\Validation\Factory; -use Intervention\Gif\Exceptions\DecoderException as GifDecoderException; -use Intervention\Image\Exceptions\DecoderException; +use Intervention\Image\Exceptions\ImageException; use Intervention\Image\ImageManager; use Psr\Http\Message\UploadedFileInterface; use Symfony\Component\Mime\MimeTypes; @@ -74,8 +73,8 @@ protected function assertFileMimes(UploadedFileInterface $file): void } try { - $this->imageManager->read($file->getStream()->getMetadata('uri')); - } catch (DecoderException|GifDecoderException) { + $this->imageManager->decode($file->getStream()->getMetadata('uri')); + } catch (ImageException) { $this->raise('image'); } } diff --git a/framework/core/src/User/Command/UploadAvatarHandler.php b/framework/core/src/User/Command/UploadAvatarHandler.php index ec211e4e6a..2d524d2d73 100644 --- a/framework/core/src/User/Command/UploadAvatarHandler.php +++ b/framework/core/src/User/Command/UploadAvatarHandler.php @@ -43,7 +43,7 @@ public function handle(UploadAvatar $command): User $this->validator->assertValid(['avatar' => $command->file]); - $image = $this->imageManager->read($command->file->getStream()->getMetadata('uri')); + $image = $this->imageManager->decode($command->file->getStream()->getMetadata('uri')); $this->events->dispatch( new AvatarSaving($user, $actor, $image) diff --git a/framework/core/src/User/Console/ConvertAvatarsToWebpCommand.php b/framework/core/src/User/Console/ConvertAvatarsToWebpCommand.php index 3e2834d999..b552a7a660 100644 --- a/framework/core/src/User/Console/ConvertAvatarsToWebpCommand.php +++ b/framework/core/src/User/Console/ConvertAvatarsToWebpCommand.php @@ -13,6 +13,7 @@ use Flarum\User\User; use Illuminate\Contracts\Filesystem\Factory; use Illuminate\Contracts\Filesystem\Filesystem; +use Intervention\Image\Format; use Intervention\Image\ImageManager; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\ProgressBar; @@ -73,7 +74,7 @@ protected function fire(): int try { $contents = $this->uploadDir->get($oldPath); - $webpContents = $this->imageManager->read($contents)->toWebp(); + $webpContents = $this->imageManager->decodeBinary($contents)->encodeUsingFormat(Format::WEBP); $newPath = pathinfo($oldPath, PATHINFO_FILENAME).'.webp'; diff --git a/framework/core/tests/unit/User/AvatarUploaderTest.php b/framework/core/tests/unit/User/AvatarUploaderTest.php index 9a75b73fd8..2c1b9ce8ea 100644 --- a/framework/core/tests/unit/User/AvatarUploaderTest.php +++ b/framework/core/tests/unit/User/AvatarUploaderTest.php @@ -16,6 +16,7 @@ use Illuminate\Contracts\Filesystem\Factory; use Illuminate\Contracts\Filesystem\Filesystem; use Illuminate\Database\Eloquent\Model; +use Intervention\Image\Drivers\Gd\Driver; use Intervention\Image\ImageManager; use Mockery as m; @@ -93,7 +94,7 @@ public function test_changing_avatar_removes_file() $user->changeAvatarPath('ABCDEFGHabcdefgh.png'); $user->syncOriginal(); - $this->uploader->upload($user, ImageManager::gd()->create(50, 50)); + $this->uploader->upload($user, ImageManager::usingDriver(Driver::class)->createImage(50, 50)); // Simulate saving foreach ($user->releaseAfterSaveCallbacks() as $callback) { @@ -111,7 +112,7 @@ public function test_upload_stores_webp_for_static_images() $user = new User(); - $this->uploader->upload($user, ImageManager::gd()->create(50, 50)); + $this->uploader->upload($user, ImageManager::usingDriver(Driver::class)->createImage(50, 50)); // Simulate saving foreach ($user->releaseAfterSaveCallbacks() as $callback) { From 1cba227b59105c640821cfc23e46b20dd9f773b9 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 11 Apr 2026 10:41:50 +0200 Subject: [PATCH 2/2] Refactor to use path based encoding methods --- .../src/Api/Controller/UploadFaviconController.php | 7 +++---- .../core/src/Api/Controller/UploadLogoController.php | 11 ++--------- framework/core/src/User/AvatarUploader.php | 5 ++--- .../src/User/Console/ConvertAvatarsToWebpCommand.php | 5 ++--- 4 files changed, 9 insertions(+), 19 deletions(-) diff --git a/framework/core/src/Api/Controller/UploadFaviconController.php b/framework/core/src/Api/Controller/UploadFaviconController.php index 1d16cd4716..f58ea29112 100644 --- a/framework/core/src/Api/Controller/UploadFaviconController.php +++ b/framework/core/src/Api/Controller/UploadFaviconController.php @@ -9,7 +9,6 @@ namespace Flarum\Api\Controller; -use Intervention\Image\Format; use Intervention\Image\Interfaces\EncodedImageInterface; use Psr\Http\Message\StreamInterface; use Psr\Http\Message\UploadedFileInterface; @@ -27,11 +26,11 @@ protected function makeImage(UploadedFileInterface $file): EncodedImageInterface return $file->getStream(); } + $this->fileExtension = 'png'; + $encodedImage = $this->imageManager->decode($file->getStream()->getMetadata('uri')) ->scale(64, 64) - ->encodeUsingFormat(Format::PNG); - - $this->fileExtension = 'png'; + ->encodeUsingFileExtension($this->fileExtension); return $encodedImage; } diff --git a/framework/core/src/Api/Controller/UploadLogoController.php b/framework/core/src/Api/Controller/UploadLogoController.php index 6f2be7014f..13c76876f0 100644 --- a/framework/core/src/Api/Controller/UploadLogoController.php +++ b/framework/core/src/Api/Controller/UploadLogoController.php @@ -9,7 +9,6 @@ namespace Flarum\Api\Controller; -use Intervention\Image\Format; use Intervention\Image\Interfaces\EncodedImageInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\UploadedFileInterface; @@ -25,15 +24,9 @@ protected function makeImage(UploadedFileInterface $file): EncodedImageInterface $image = $this->imageManager->decode($file->getStream()->getMetadata('uri')) ->scale(height: 60); - if ($image->isAnimated()) { - $this->resolvedExtension = 'gif'; + $this->resolvedExtension = $image->isAnimated() ? 'gif' : 'webp'; - return $image->encodeUsingFormat(Format::GIF); - } - - $this->resolvedExtension = 'webp'; - - return $image->encodeUsingFormat(Format::WEBP); + return $image->encodeUsingFileExtension($this->resolvedExtension); } protected function fileExtension(ServerRequestInterface $request, UploadedFileInterface $file): string diff --git a/framework/core/src/User/AvatarUploader.php b/framework/core/src/User/AvatarUploader.php index 6e22acec3d..a4f7b4e131 100644 --- a/framework/core/src/User/AvatarUploader.php +++ b/framework/core/src/User/AvatarUploader.php @@ -12,7 +12,6 @@ use Illuminate\Contracts\Filesystem\Factory; use Illuminate\Contracts\Filesystem\Filesystem; use Illuminate\Support\Str; -use Intervention\Image\Format; use Intervention\Image\Interfaces\ImageInterface; class AvatarUploader @@ -30,13 +29,13 @@ public function upload(User $user, ImageInterface $image): void $avatarPath = Str::random(); if ($image->isAnimated()) { - $encodedImage = $image->encodeUsingFormat(Format::GIF); $avatarPath .= '.gif'; } else { - $encodedImage = $image->encodeUsingFormat(Format::WEBP); $avatarPath .= '.webp'; } + $encodedImage = $image->encodeUsingPath($avatarPath); + $this->removeFileAfterSave($user); $user->changeAvatarPath($avatarPath); diff --git a/framework/core/src/User/Console/ConvertAvatarsToWebpCommand.php b/framework/core/src/User/Console/ConvertAvatarsToWebpCommand.php index b552a7a660..46d4e7cf07 100644 --- a/framework/core/src/User/Console/ConvertAvatarsToWebpCommand.php +++ b/framework/core/src/User/Console/ConvertAvatarsToWebpCommand.php @@ -13,7 +13,6 @@ use Flarum\User\User; use Illuminate\Contracts\Filesystem\Factory; use Illuminate\Contracts\Filesystem\Filesystem; -use Intervention\Image\Format; use Intervention\Image\ImageManager; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\ProgressBar; @@ -74,10 +73,10 @@ protected function fire(): int try { $contents = $this->uploadDir->get($oldPath); - $webpContents = $this->imageManager->decodeBinary($contents)->encodeUsingFormat(Format::WEBP); - $newPath = pathinfo($oldPath, PATHINFO_FILENAME).'.webp'; + $webpContents = $this->imageManager->decodeBinary($contents)->encodeUsingPath($newPath); + $this->uploadDir->put($newPath, $webpContents); User::where('id', $user->id)->update(['avatar_url' => $newPath]);