File Storage Image Processing
April 29, 2026 · View on GitHub
Image Processing for the File Storage Library.
Built on top of Intervention Image v4.
Features
- Generates and stores variants of an uploaded image (thumbnails, avatars, hero crops, …)
- 24+ first-class image operations: resize, scale, cover, crop, rotate, flip, sharpen, orient (EXIF auto-rotate), brightness, contrast, grayscale, colorize, blur, pixelate, trim, resizeCanvas, padding, place (watermark), convert (format swap), …
- Per-format encoder quality, EXIF/metadata strip toggle, ICC color-profile preservation
- Pluggable operation registry — add custom operations without forking
- Optional file-size optimization via spatie/image-optimizer
- Works with League Flysystem for flexible storage backends
- Fluent API for chaining operations, including repeating the same operation type multiple times
Requirements
- GD or Imagick extension
- Intervention Image v4
Installation
composer require php-collective/file-storage-image-processor
Quick Example
use PhpCollective\Infrastructure\Storage\Processor\Image\Driver;
use PhpCollective\Infrastructure\Storage\Processor\Image\Format;
use PhpCollective\Infrastructure\Storage\Processor\Image\ImageProcessor;
use PhpCollective\Infrastructure\Storage\Processor\Image\ImageVariantCollection;
use PhpCollective\Infrastructure\Storage\Processor\Image\Position;
// Driver::Auto picks Imagick when the extension is loaded and falls
// back to GD; use Driver::Gd or Driver::Imagick to choose explicitly.
$imageProcessor = ImageProcessor::create(Driver::Auto, $fileStorage, $pathBuilder);
$collection = ImageVariantCollection::create();
// Create a thumbnail with aspect ratio preserved
$collection->addNew('thumbnail')
->scale(300, 300)
->optimize();
// Create an avatar that fills exact dimensions
$collection->addNew('avatar')
->cover(150, 150, Position::TopCenter)
->optimize();
// Re-encode a JPEG source as WebP — Format enum or string both work
$collection->addNew('webp')
->scale(800, 600)
->convert(Format::Webp);
// Repeat the same operation type without losing earlier steps
$collection->addNew('effects')
->blur(1)
->blur(6);
$file = $file->withVariants($collection->toArray());
$file = $imageProcessor->process($file);
Tuning the encoder
$imageProcessor
->setQuality(['webp' => 80, 'jpg' => 90, 'avif' => 70]) // per-format
->setStripExif(true) // privacy + smaller files (default)
->setPreserveProfile(true) // keep wide-gamut color rendering (default)
->setPreserveAnimation(true); // animated GIF/WebP keep all frames (default)
setPreserveAnimation(false) flattens animated sources to a single frame — useful for static thumbnail variants or when converting to a non-animated format like JPEG.
Processing a subset of variants
// Per-call filter — does not leak into subsequent process() calls.
$file = $imageProcessor->process($file, ['thumbnail']);
Custom operations
use PhpCollective\Infrastructure\Storage\Processor\Image\Operation\Operation;
use PhpCollective\Infrastructure\Storage\Processor\Image\Operation\OperationRegistry;
$registry = OperationRegistry::default()
->register('myFilter', static fn (array $args): Operation => new MyFilter(...$args));
$processor = new ImageProcessor($storage, $pathBuilder, $imageManager, urlBuilder: null, operationRegistry: $registry);
Repeated operations of the same name are preserved in order when you serialize variants with toArray() and rebuild them later with ImageVariantCollection::fromArray().
Documentation
Please start by reading the documentation in the docs/ directory:
- Installation & Setup
- Processing Images
- Available Operations — complete reference of all image operations
Upgrading from 1.x to 2.x
This major release migrates to Intervention Image v4 and replaces the stringly-typed dispatcher with a typed Operation class hierarchy. See the CHANGELOG for the full list; the headline changes:
- PHP 8.3+ required (was 8.1)
- Intervention Image v4 required (was v3)
processOnlyTheseVariants()/processAll()removed — use the newprocess($file, ['thumbnail'])per-call filter insteadOperations::POSITION_*string constants replaced by thePositionenum (Position::Center,Position::TopCenter, …)ImageVariant::FLIP_HORIZONTAL/FLIP_VERTICALconstants replaced by theFlipDirectionenumOperationsclass is gone — operations are now individual classes undersrc/Operation/resolved viaOperationRegistryflip()now acceptsFlipDirection|string(string form still works for config-driven setups)cover()no longer takes a (always-ignored)$callbackparameter