Standalone GD-based image component for PHP 8.1+.
This package provides image loading, resizing, cropping, generated image caching, fallback image generation and WebP output support. It is designed as a reusable Lemonade component and can also be integrated into other PHP projects when the expected storage layout is provided.
- PHP 8.1+
- GD extension
- mbstring extension
This package is not published on Packagist. Install it directly from the public GitHub repository by adding it as a Composer VCS repository in your project.
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/johnnyxlemonade/component_image.git"
}
],
"require": {
"lemonade/component_image": "dev-master"
}
}Then run:
composer update lemonade/component_imageAlternatively, configure the repository from the command line:
composer config repositories.lemonade-component-image vcs https://github.com/johnnyxlemonade/component_image.git
composer require lemonade/component_image:dev-master- JPEG, PNG, GIF and WebP support
- Image loading from file or string
- Blank image creation
- Resize, crop, fit and canvas-based transformations
- Generated image cache
- WebP support detection
- Browser cache validation through
If-Modified-Since - Filesystem cache validation against source image modification time
- HTTP cache headers
- Filesystem abstraction for cache writes
- Typed component exceptions
- Immutable request and response DTOs
- PHPUnit test suite
- PHPStan level 10 configuration
- Memory-efficient streaming from cache in 8kB chunks with output buffer flushing
The public entrypoint is Lemonade\Image\AppImage::emit() with an immutable ImageRequest.
<?php
use Lemonade\Image\AppImage;
use Lemonade\Image\Generator\ImageRequest;
AppImage::emit(
request: ImageRequest::create(
level: 6,
storageTypeId: 'gallery',
moduleId: 12,
artId: 345,
baseName: 'example.jpg',
args: 'w800-h600-z3-cffffff-q85-e1',
),
);The component resolves the source file, checks browser and filesystem cache, generates a resized variant when needed, stores the generated image in cache and emits the HTTP image response.
| Parameter | Type | Description |
|---|---|---|
level |
int |
Directory split depth used when building the object storage path from artId. |
storageTypeId |
`string | int |
moduleId |
`string | int |
artId |
`string | int |
baseName |
`string | null` |
args |
`string | null` |
Image options are passed as a dash-separated string:
w800-h600-z3-cffffff-q85-e1Supported tokens:
| Token | Example | Description |
|---|---|---|
w |
w800 |
Target width in pixels. |
h |
h600 |
Target height in pixels. |
q |
q85 |
Output quality. Invalid values are ignored. Values are normalized to the supported range by the parser. |
c |
cffffff |
Canvas color as a 6-character hexadecimal value without #. |
e |
e1 |
Enables or disables missing-image fallback handling. Supported values are 0 and 1. |
z |
z3 |
Resize mode. See the resize mode table below. |
Unknown or invalid tokens are ignored. If the same option is passed multiple times, the last valid value wins.
Examples:
w800
w800-h600
w800-h600-z3
w800-h600-z1-cf5f5f5
w800-h600-q85Size presets are provided by ImageSizePresetConfig. The default configuration includes:
| Preset | Size |
|---|---|
xss |
32 × 32 |
xs |
48 × 48 |
sm |
96 × 96 |
md |
160 × 160 |
lg |
320 × 320 |
xl |
640 × 640 |
Presets can be scaled by appending a numeric suffix:
md2With the default configuration, md2 resolves to 320 × 320 and md3 resolves to 480 × 480.
Preset scale suffixes are supported up to the configured maximum scale. The default maximum scale is intentionally limited to prevent excessive generated image sizes.
The special option original preserves the source dimensions:
originalOriginal mode resets width and height and bypasses the normal fallback dimensions and size limits.
Resize mode is controlled by the z token.
| Mode | Enum | Meaning |
|---|---|---|
z0 |
ImageResizeMode::Shrink |
Shrink only. |
z1 |
ImageResizeMode::FitWithCanvas |
Fit into a canvas using the normal canvas scale. |
z2 |
ImageResizeMode::Exact |
Exact resize. |
z3 |
ImageResizeMode::Fit |
Fit into the requested box. |
z4 |
ImageResizeMode::FitWithBiggerCanvas |
Fit into a canvas using a larger canvas scale. |
z5 |
ImageResizeMode::FitWithMaxCanvas |
Fit into a canvas using the maximum canvas scale. |
The internal implementation represents these modes through ImageResizeMode, while the public option string remains backward-compatible with the numeric z values.
The original token is represented internally as ImageResizeMode::Original. It is intentionally not exposed through the numeric z token.
By default, ImageStorageConfig::createDefault() uses the following base paths:
./storage
./storage/0/cache
./storage/0/cache/0/0
./themes/frontend/error.pngMeaning:
| Config value | Default |
|---|---|
| Storage base | ./storage |
| Cache base | ./storage/0/cache |
| Fallback cache directory | ./storage/0/cache/0/0 |
| Placeholder image file | ./themes/frontend/error.png |
Source images are resolved under:
{storageBase}/{moduleId}/{storageTypeId}/{splitArtId}/{baseName}Generated cache variants are stored under:
{cacheBase}/{moduleId}/{storageTypeId}/{splitArtId}/Fallback cache images are stored under:
{fallbackCacheDirectory}/{optionsHash}.png
{fallbackCacheDirectory}/{optionsHash}.webpThe directory resolver supports these storage type aliases:
| Alias | Resolved storage type |
|---|---|
template |
template |
thumbnail |
1 |
gallery |
2 |
editor |
5 |
Numeric or string storage type identifiers can also be passed directly.
The artId is converted to hexadecimal and split into nested directory segments according to level.
For example, with level: 6, the component creates a deterministic nested path from the object identifier. This keeps large image collections distributed across multiple directories instead of storing everything in a single flat folder.
You can create a custom storage configuration:
<?php
use Lemonade\Image\ImageStorageConfig;
$config = new ImageStorageConfig(
storageRoot: '/var/www/project',
storageDirectory: 'storage',
cacheDirectory: 'cache',
fallbackModuleId: '0',
fallbackStorageTypeId: '0',
placeholderImageFile: '/var/www/project/themes/frontend/error.png',
);AppImageFactory::createDefault() uses ImageStorageConfig::createDefault(). For custom integration, instantiate AppImageFactory directly with a custom ImageStorageConfig.
<?php
use Lemonade\Image\AppImageFactory;
use Lemonade\Image\Fallback\ImageFallbackConfig;
use Lemonade\Image\Generator\ImageRequest;
use Lemonade\Image\Http\ImageResponseEmitter;
use Lemonade\Image\ImageStorageConfig;
use Lemonade\Image\Utils\FileSystem;
$request = ImageRequest::create(
level: 6,
storageTypeId: 'gallery',
moduleId: 12,
artId: 345,
baseName: 'example.jpg',
args: 'w800-h600-z3-q85',
);
$factory = new AppImageFactory(
filesystem: new FileSystem(),
storageConfig: new ImageStorageConfig(
storageRoot: '/var/www/project',
),
fallbackConfig: new ImageFallbackConfig(
defaultWidth: 600,
defaultHeight: 600,
),
);
$response = $factory
->createApplication($request)
->handle();
(new ImageResponseEmitter())->emit(
response: $response,
);This split allows advanced integrations to inspect or test the prepared ImageHttpResponse before emitting it.
ImageOptionsParser converts the compact argument string into an immutable ImageOptionsDTO snapshot:
<?php
use Lemonade\Image\Options\ImageOptionsParser;
use Lemonade\Image\Options\ImageResizeMode;
$options = (new ImageOptionsParser(
args: 'w800-h600-z3-q85',
))->toDTO();
$options->getWidth(); // 800
$options->getHeight(); // 600
$options->getResizeMode(); // ImageResizeMode::Fit
$options->getQuality(); // 85
$options->getCanvasColor(); // ffffff
$options->isMissing(); // true
$options->getHash(); // deterministic options hashRuntime image processing should consume the DTO rather than depending on parser internals.
ImageOptionsParser uses a default parser configuration out of the box. Applications can provide their own immutable config objects to override size presets, preset scaling, default canvas color, quality limits and dimension limits.
<?php
use Lemonade\Image\Options\Config\ImageCanvasConfig;
use Lemonade\Image\Options\Config\ImageDimensionConfig;
use Lemonade\Image\Options\Config\ImageOptionsParserConfig;
use Lemonade\Image\Options\Config\ImageQualityConfig;
use Lemonade\Image\Options\Config\ImageSizePreset;
use Lemonade\Image\Options\Config\ImageSizePresetCollection;
use Lemonade\Image\Options\Config\ImageSizePresetConfig;
use Lemonade\Image\Options\ImageOptionsParser;
use Lemonade\Image\Options\ImageResizeMode;
$config = new ImageOptionsParserConfig(
sizePresets: new ImageSizePresetConfig(
presets: new ImageSizePresetCollection([
ImageSizePreset::create(code: 'card', width: 600, height: 400),
ImageSizePreset::create(code: 'hero', width: 1600, height: 900),
]),
maxScale: 4,
),
canvas: new ImageCanvasConfig(
defaultColor: 'f5f5f5',
),
quality: new ImageQualityConfig(
defaultQuality: 85,
minQuality: 10,
maxQuality: 95,
),
dimensions: new ImageDimensionConfig(
minWidth: 50,
minHeight: 50,
maxWidth: 3840,
maxHeight: 2160,
),
);
$options = (new ImageOptionsParser(
args: 'hero2-z3-q100',
config: $config,
))->toDTO();
$options->getWidth(); // 3200
$options->getHeight(); // 1800
$options->getResizeMode(); // ImageResizeMode::Fit
$options->getQuality(); // 95
$options->getCanvasColor(); // f5f5f5The image workflow checks cache in this order:
- Browser cache through
If-Modified-Since. - Existing filesystem cache variant.
- Source image generation.
- Missing-image fallback generation.
Generated variants are saved into the cache directory. WebP variants are generated when WebP is supported by the installed GD extension.
A cached variant is reused only when it is at least as fresh as the source image. If the source image is newer than the cache variant, the image is regenerated.
When the source image is missing, only the current generated variant cache is deleted. Other cached files in the same cache directory are preserved.
The runtime workflow produces an immutable ImageHttpResponse.
The default facade emits the response immediately:
AppImage::emit(
request: $request,
);Custom integrations can handle the response manually:
$response = $factory
->createApplication($request)
->handle();
$response->isBinary();
$response->isFile();
$response->isNotModified();If the source image does not exist or generation fails, the component generates a fallback image.
If the configured placeholder image exists, it is used as the fallback source. Otherwise, the component creates an internal transparent placeholder image and renders it into the requested fallback canvas.
Fallback image dimensions are configurable through ImageFallbackConfig. These dimensions are used only as a final safety net when the request options do not provide usable width or height values.
<?php
use Lemonade\Image\Fallback\ImageFallbackConfig;
$fallbackConfig = new ImageFallbackConfig(
defaultWidth: 600,
defaultHeight: 600,
);Fallback images are cached separately by option hash.
The component emits image responses through ImageResponseEmitter.
Cached image files are streamed directly from disk in 8kB chunks with active output buffer flushing, ensuring a near-zero memory footprint even under heavy load. Generated images are rendered to binary output and emitted with cache headers, content type, and content length metadata.
Supported output MIME types:
| Type | MIME type |
|---|---|
| JPEG | image/jpeg |
| PNG | image/png |
| GIF | image/gif |
| WebP | image/webp |
Install dependencies:
composer updateRun PHP syntax lint:
composer lintRun static analysis:
composer stanRun tests:
composer testRun tests in CI mode:
composer test:ciCheck coding standards:
composer cs:checkFix coding standards:
composer cs:fixRun smoke test:
composer smokeRun all checks:
composer check| Script | Description |
|---|---|
composer lint |
Run PHP syntax lint over source files. |
composer smoke |
Run smoke checks for basic component wiring. |
composer composer:validate |
Validate composer.json in strict mode. |
composer stan |
Run PHPStan static analysis. |
composer stan:ci |
Run PHPStan static analysis with GitHub Actions output format. |
composer cs:check |
Check coding standards using PHP-CS-Fixer. |
composer cs:fix |
Fix coding standards using PHP-CS-Fixer. |
composer test |
Run PHPUnit tests. |
composer test:ci |
Run PHPUnit tests without colors. |
composer check:platform |
Check installed platform requirements. |
composer check |
Run Composer validation, platform checks, lint, coding standards, static analysis and tests. |
MIT