tobento/service-picture-generator

Responsive picture generation workflow for PHP applications, including queueing, storage, and fallback handling.

Installs: 3

Dependents: 1

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/tobento/service-picture-generator

2.0.1 2026-02-20 09:23 UTC

This package is auto-updated.

Last update: 2026-02-20 09:24:15 UTC


README

The Picture Generator Service provides a powerful, framework-agnostic system for generating responsive images and <picture> elements in PHP applications.
It enables developers to define reusable picture configurations, generate optimized image variants, and store metadata for fast retrieval - all without relying on URL-based transformations.

The service is designed to be secure, predictable, and fully decoupled from HTTP.
It supports synchronous and queued generation, integrates with any file-storage backend, and outputs standard HTML <picture> elements that work in any PHP framework or templating system.

Key Features

  • Definition-based picture generation Create named picture definitions that describe breakpoints, formats, crops, and responsive strategies - reusable across your entire application.
  • Secure, URL-free architecture No query parameters, no runtime transformations, and no exposure of private storage. All images are generated server-side using controlled definitions.
  • Pre-generation & regeneration Generate images on upload, on demand, or via CLI commands. Regenerate all images when definitions change.
  • Queue support Offload heavy image processing to background workers using a PictureQueueHandlerInterface.
  • Metadata repository Store and retrieve picture metadata (JSON) for fast rendering of <picture> tags.
  • Storage-agnostic Works with any StoragesInterface implementation (local, cloud, Flysystem, etc.).
  • SEO-friendly static URLs All generated images use clean, human-readable paths without query strings or signatures. This results in stable, shareable URLs that search engines can index reliably and that remain consistent over time.
  • PSR-compliant Uses PSR-7 streams and PSR-3 logging for maximum interoperability.

Table of Contents

Getting Started

Add the latest version of the picture generator project running this command.

composer require tobento/service-picture-generator

Requirements

  • PHP 8.4 or above

Documentation

Workflow

When you display a picture, the service checks whether the required images for the given path and definition already exist.

  • If the images are not yet generated and queueing is enabled (default),
    a picture job is dispatched to the configured queue.
    The view immediately receives a lightweight fallback picture based on the original resource.

  • If queueing is disabled (queue: false),
    the images are generated synchronously during the request and the final <picture> or <img> tag is returned immediately.

  • Once the queued job finishes, all defined image variants are created and stored.
    Subsequent requests automatically render the fully generated picture.

This workflow ensures fast responses when using background processing, while still allowing on-demand synchronous generation when needed.

Fallback Images

If a picture cannot be generated immediately (for example, when queueing is enabled), a fallback image is returned:

  • If the file has a public URL, the fallback uses that URL.
  • If no public URL is available, a base64-encoded data URI is returned.
  • If fallback creation fails (e.g., the file is not a valid image), a NullPictureTag is returned and a warning is logged.

Basic Usage

Once you have created the Picture Generator, you can use it to display responsive pictures based on a path, resource, and definition.

Within your view file, call the generate method and render the returned picture tag:

Example Using a Named Definition

Named definitions allow you to reuse picture configurations across your application.
See the Picture Definitions section for how to register them.

<?= $pictureGenerator->generate(
    // The path to the original image within the storage:
    path: 'path/to/image.jpg',

    // The storage name where the image is located:
    resource: 'storage-name',

    // The named picture definition to use:
    definition: 'name',

    // Whether to queue image generation (recommended):
    queue: true, // default

    // Whether private storages may be read:
    allowPrivateStorage: false, // default
)->imgAttr('alt', 'Alt Text') ?>

About allowPrivateStorage

Use this option when the image source is a private storage.
(For most cases, using a public storage as the image source is recommended.)

  • false (default): private storages are not read and a NullPictureTag is returned.
  • true: allow reading from private storage (useful for backend-only sources such as uploads or protected files).

Depending on your definition, the output may look like:

<picture>
  <source srcset="https://example.com/path/to/image.webp" type="image/webp">
  <source srcset="https://example.com/path/to/image.jpg" type="image/jpeg">
  <img src="https://example.com/path/to/image.jpg" alt="Alt Text">
</picture>

Using named definitions provides several advantages:

  • You can crop images based on the definition
  • You can define different picture sets per view theme

Example Using a Definition Instance

use Tobento\Service\Picture\Definition\ArrayDefinition;

<?= $pictureGenerator->generate(
    path: 'path/to/image.jpg',
    resource: 'storage-name',
    definition: new ArrayDefinition('product-main', [
        'img' => [
            'src' => [600],
            'alt' => 'Alternative Text',
            'loading' => 'lazy',
        ],
        // You may define any sources:
        'sources' => [
            [
                'media' => '(min-width: 800px)',
                'srcset' => [
                    '' => [1200, 500],
                ],
                'type' => 'image/webp',
            ],
            [
                'media' => '(max-width: 600px)',
                'srcset' => [
                    '' => [600, 400],
                ],
                'type' => 'image/webp',
            ],
        ],
    ]),
) ?>

See the Picture Definition section for more details on defining picture configurations.

Example Using an Imager Resource

use Tobento\Service\Imager\ResourceInterface;

<?= $pictureGenerator->generate(
    path: 'path/to/image.jpg',
    resource: $resource, // ResourceInterface
    definition: 'name',
)->imgAttr('alt', 'Alt Text') ?>

See Picture Creating - Create Picture From Resource for more information.

Creating Picture Generator

To use the picture generator, you first need to create the required components:
a PictureRepository, an optional PictureQueueHandler, and finally the PictureGenerator itself.

use Psr\Log\LoggerInterface;
use Tobento\Service\FileStorage\StoragesInterface;
use Tobento\Service\Picture\DefinitionsInterface;
use Tobento\Service\Picture\Generator\PictureGenerator;
use Tobento\Service\Picture\Generator\PictureGeneratorInterface;
use Tobento\Service\Picture\Generator\PictureRepository;
use Tobento\Service\Picture\Generator\PictureRepositoryInterface;
use Tobento\Service\Picture\Generator\Queue\PictureQueueHandler;
use Tobento\Service\Picture\Generator\Queue\PictureQueueHandlerInterface;
use Tobento\Service\Picture\PictureCreatorInterface;
use Tobento\Service\Queue\QueueInterface;

// The repository stores picture metadata and generated image paths:
$pictureRepository = new PictureRepository(
    pictureStorageName: 'generated-picture-data', // Storage for picture metadata
    imageStorageName: 'generated-images', // Storage for generated images
    storages: $storages, // StoragesInterface
);

// Optional queue handler for background image generation:
$queueHandler = new PictureQueueHandler(
    queue: $queue, // QueueInterface
    queueName: 'name', // Queue name to dispatch picture jobs to
);

// Create the picture generator:
$pictureGenerator = new PictureGenerator(
    pictureRepository: $pictureRepository, // PictureRepositoryInterface
    storages: $storages, // StoragesInterface
    definitions: $definitions, // DefinitionsInterface
    queueHandler: $queueHandler, // Or null for no queueing at all
    uniqueJob: true, // Whether queued jobs should be unique
    pictureCreator: null, // Custom PictureCreatorInterface or null (default)
    logger: $logger, // LoggerInterface or null
);

var_dump($pictureGenerator instanceof PictureGeneratorInterface);
// bool(true)

Private Storage Behavior

When generating pictures from private file storage, the behavior depends on the allowPrivateStorage and queue parameters:

Condition Behavior
allowPrivateStorage = false Returns a NullPictureTag and does not queue a job
allowPrivateStorage = true and queue = false Generates the picture synchronously
allowPrivateStorage = true and queue = true Queues a job and returns a fallback image

Related Documentation

Queue Worker

If you use background image generation via the queue handler,
you may install the Console Service to run the queue worker:

composer require tobento/service-console

See the Queue Service - Console for details on running the worker.

Unique Jobs

The queue handler uses unique jobs by default.
To enable unique job locking, the Queue Service requires a PSR-16 CacheInterface implementation.

You may use any PSR-16 compatible cache. For example, if you use a container and want a simple drop-in implementation:

composer require tobento/service-cache

See the Queue Service - Unique Parameter
for details on how unique job locking works and how to provide your own cache.

Warning:
If unique jobs are disabled or no cache is available, duplicate picture generation jobs may be queued and processed. This can lead to redundant work, increased queue load, and multiple workers generating the same images.

Logging

If a LoggerInterface is provided, the picture generator may log useful information such as picture generation steps, queue dispatching, or errors that occur during processing. Logging is optional and depends on the logger implementation you supply.

Picture Definitions

Picture definitions can be stored wherever you prefer.
You are free to organize them in any directory structure that fits your application.

A common convention is to place JSON definition files inside a dedicated folder, for example:

app/
    views/
        picture-definitions/
            product-main.json
            ...

For a detailed explanation of how JSON-based definitions work, how they are structured, and how to load them, see the
Json Files Definitions section of the Picture Service documentation.

Clearing Generated Pictures

You can clear (delete) generated pictures either via the console command or directly through the PictureGenerator.

Using Console Command

See Picture Clear Command section.

Using Picture Generator

use Tobento\App\Media\Picture\PictureGeneratorInterface;

class SomeService
{
    public function __construct(
        protected PictureGeneratorInterface $pictureGenerator,
    ) {}

    private function deleteGeneratedPictures(): void
    {
        $repository = $this->pictureGenerator->pictureRepository();

        // Delete the generated picture (all created images) for a specific path and definition:
        $repository->delete(
            path: 'foo/image.jpg',
            definition: 'product-main',
        );

        // Delete all generated pictures for a specific definition:
        $repository->deleteAll(
            definition: 'product-main',
        );

        // Delete all generated pictures for a specific original path:
        $repository->deleteAllByPath(
            path: 'foo/image.jpg',
        );
    }
}

Console

Picture Clear Command

This package integrates with the Console Service to provide a command for clearing generated pictures.

Requirements

composer require tobento/service-console

Example

To clear all generated pictures, run:

php ap picture:clear

To clear generated pictures for specific definitions only:

php ap picture:clear --def=product-main --def=post

Using the App Ecosystem

If you are building on the Tobento App Ecosystem, you may use the app-media package, which automatically configures the picture generator, queue worker, cache, and storages. In that case, no manual setup is required.

Framework Integration Notes

This package is framework-agnostic.
If you integrate it into a framework that provides its own queue or filesystem (e.g. Laravel, Symfony, etc.), you may need to implement small adapters:

  • Queue Adapter Queue Service
    Implement Tobento\Service\Queue\QueueInterface to forward jobs to your framework's queue system.

  • File Storage Adapter File Storage Service
    Implement Tobento\Service\FileStorage\StorageInterface to map your framework's filesystem to the storage abstraction used by this package.

These adapters are typically thin wrappers, as both interfaces are intentionally minimal.

Credits