Developer reference 

Architecture 

The extension has three entry points that share the same optimization backend:

  1. ProcessingMiddleware intercepts frontend requests matching the /processed/ path and asks the Processor to create a variant on demand.
  2. OptimizeOnUploadListener reacts to PSR-14 file events (AfterFileAddedEvent and AfterFileReplacedEvent) and delegates to the shared ImageOptimizer service for in-place lossless compression.
  3. OptimizeImagesCommand and AnalyzeImagesCommand iterate the FAL index from the CLI. Both extend AbstractImageCommand, which provides shared database iteration, progress rendering, and option parsing.

The MaintenanceController provides the backend module for statistics, cleanup, and system-requirement checks.

Directory structure 

  • Classes/

    • Command/

      • AbstractImageCommand.php
      • AnalyzeImagesCommand.php
      • OptimizeImagesCommand.php
    • Controller/

      • MaintenanceController.php
    • Event/

      • ImageProcessedEvent.php
      • VariantServedEvent.php
    • EventListener/

      • OptimizeOnUploadListener.php
    • Middleware/

      • ProcessingMiddleware.php
    • Service/

      • ImageManagerAdapter.php
      • ImageManagerFactory.php
      • ImageOptimizer.php
      • ImageReaderInterface.php
      • SystemRequirementsService.php
    • Processor.php
    • ProcessorInterface.php
    • ViewHelpers/

      • SourceSetViewHelper.php
  • Configuration/

    • Backend/

      • Modules.php
    • Icons.php
    • RequestMiddlewares.php
    • Services.yaml

Processing middleware 

class ProcessingMiddleware
Fully qualified name
\Netresearch\NrImageOptimize\Middleware\ProcessingMiddleware

Frontend PSR-15 middleware registered before typo3/cms-frontend/site (see Middleware registration). Intercepts every request whose path starts with /processed/ and delegates to the \Netresearch\NrImageOptimize\ProcessorInterface implementation. Any other request is passed to the next handler unchanged.

Processor 

class ProcessorInterface
Fully qualified name
\Netresearch\NrImageOptimize\ProcessorInterface

Contract for the on-demand processor -- declares generateAndSend(ServerRequestInterface): ResponseInterface. The middleware depends on this interface, so integrators can replace the implementation via a Services.yaml alias.

class Processor
Fully qualified name
\Netresearch\NrImageOptimize\Processor

Default implementation. Parses the requested URL (see Variant URL format) to extract path, dimensions, quality, and mode. Loads the original via the ImageReaderInterface adapter (see ImageManager abstraction), generates the primary variant plus optional WebP / AVIF sidecars, and streams a PSR-7 response back with long-lived cache headers.

Key behaviors:

  • Rejects path-traversal (..) sequences at URL parse time, returning HTTP 400.
  • Clamps w / h to 1--8192 and q to 1--100.
  • Uses TYPO3's LockFactory to serialize concurrent requests for the same variant; a 503 is returned if the lock can't be acquired within  1 second.
  • Respects the Accept header plus the skipWebP and skipAvif query parameters when choosing which cached sidecar to serve.
  • Dispatches \Netresearch\NrImageOptimize\Event\ImageProcessedEvent after a new variant is written and \Netresearch\NrImageOptimize\Event\VariantServedEvent before the response body is sent.

PSR-14 events 

Integrators can subscribe to the processor's lifecycle to collect metrics, trigger cache warming, or apply additional post-processing.

class ImageProcessedEvent
Fully qualified name
\Netresearch\NrImageOptimize\Event\ImageProcessedEvent

Dispatched after the processor has written a new variant to disk. Properties (all public readonly):

pathOriginal (string)
Absolute path of the source image.
pathVariant (string)
Absolute path of the variant -- the actual served file may be a .webp or .avif sidecar of this path.
extension (string)
Lowercased extension of the variant (jpg, png, webp, avif ...).
targetWidth / targetHeight (?int)
Final clamped dimensions in pixels, or null if the URL omitted the value.
targetQuality (int)
Output quality (1--100).
processingMode (int)
0 = cover, 1 = scale / fit.
webpGenerated / avifGenerated (bool)
Whether the respective sidecar was written alongside.
class VariantServedEvent
Fully qualified name
\Netresearch\NrImageOptimize\Event\VariantServedEvent

Dispatched immediately before the response body is streamed to the client -- both for fresh variants and for cache hits. Properties:

pathVariant (string)
Absolute base path of the variant being served.
extension (string)
Lowercased source extension.
responseStatusCode (int)
HTTP status code (always 200 in practice).
fromCache (bool)
true when the response reuses a previously generated file on disk; false when the variant was produced in this request.

Register listeners via Configuration/Services.yaml or the per-class #[AsEventListener] attribute:

Configuration/Services.yaml
services:
  Acme\Example\Listener\MetricsListener:
    tags:
      - name: event.listener
        identifier: 'acme.metrics.variant_served'
        event: Netresearch\NrImageOptimize\Event\VariantServedEvent
Copied!

Upload event listener 

class OptimizeOnUploadListener
Fully qualified name
\Netresearch\NrImageOptimize\EventListener\OptimizeOnUploadListener

PSR-14 listener registered for AfterFileAddedEvent and AfterFileReplacedEvent. Normalizes the file extension, short-circuits unsupported extensions and offline storages, and delegates to \Netresearch\NrImageOptimize\Service\ImageOptimizer.

Re-entrancy guard: keyed by storageUid . ':' . identifier so two storages sharing an identifier (/image.jpg in different driver roots) don't block each other, while still catching the replaceFile() loop that the optimizer's own write would otherwise trigger.

Storage state: setEvaluatePermissions(false) is applied before delegation and restored to its previous value in a finally block so long-running CLI runs don't leak permission state across iterations.

ImageOptimizer service 

class ImageOptimizer
Fully qualified name
\Netresearch\NrImageOptimize\Service\ImageOptimizer

Shared backend used by both the event listener and the CLI commands. Resolves optimizer binaries via $PATH, with env overrides (OPTIPNG_BIN, GIFSICLE_BIN, JPEGOPTIM_BIN). A set-but-invalid override is authoritative -- the tool is reported unavailable rather than silently falling back to $PATH. $PATH lookups also verify is_executable().

Public API:

optimize(FileInterface $file, bool $stripMetadata = false, ?int $jpegQuality = null, bool $dryRun = false): array
Run the appropriate tool in place and replace the FAL file when the result shrinks.
analyze(FileInterface $file, bool $stripMetadata = false, ?int $jpegQuality = null): array
Same pipeline, but never writes back.
analyzeHeuristic(FileInterface $file, int $maxWidth = 2560, int $maxHeight = 1440, int $minSize = 512000): array
Fast estimation from size and resolution. No binary is invoked.
resolveToolFor(string $extension): ?array
Tool discovery for callers that want to pre-check.
hasAnyTool(): bool
Quick check whether at least one optimizer is installed.
supportedExtensions(): list<string>
Returns ['png', 'gif', 'jpg', 'jpeg'].

ImageManager abstraction 

class ImageManagerFactory
Fully qualified name
\Netresearch\NrImageOptimize\Service\ImageManagerFactory

Selects the best available Intervention Image driver (Imagick over GD). See Image driver selection for behavior and failure modes.

class ImageReaderInterface
Fully qualified name
\Netresearch\NrImageOptimize\Service\ImageReaderInterface

Version-agnostic abstraction around Intervention Image's loading API. v3 uses ImageManager::read(), v4 uses ImageManager::decode(); this interface hides that difference so consumers and static analysis see a single stable contract.

class ImageManagerAdapter
Fully qualified name
\Netresearch\NrImageOptimize\Service\ImageManagerAdapter

Default implementation. Dispatches to whichever method is present on the installed ImageManager, letting the extension support Intervention Image ^3 || ^4 simultaneously without version-conditional code paths.

SystemRequirementsService 

class SystemRequirementsService
Fully qualified name
\Netresearch\NrImageOptimize\Service\SystemRequirementsService

Produces the data shown on the System requirements tab of the backend module. Inspects:

  • PHP version and loaded extensions (imagick, gd, fileinfo).
  • ImageMagick / GraphicsMagick capabilities as reported by Imagick (WebP, AVIF support).
  • Intervention Image installed version and the active driver (Imagick or GD).
  • TYPO3 version.
  • Availability of optional CLI tools (magick, convert, identify, gm).

Returns a structured array the controller renders into a Fluid template.

CLI commands 

class AbstractImageCommand
Fully qualified name
\Netresearch\NrImageOptimize\Command\AbstractImageCommand

Base class for the image CLI commands. Exposes parseStorageUidsOption, getIntOption, extractUid, countImages, iterateViaIndex (Generator over sys_file rows -- constant memory), createProgress, buildLabel, shortenLabel, and formatMbGb.

class OptimizeImagesCommand
Fully qualified name
\Netresearch\NrImageOptimize\Command\OptimizeImagesCommand

Implements nr:image:optimize. Delegates to \Netresearch\NrImageOptimize\Service\ImageOptimizer per file and renders a Symfony progress bar with cumulative savings. See Bulk optimize images.

class AnalyzeImagesCommand
Fully qualified name
\Netresearch\NrImageOptimize\Command\AnalyzeImagesCommand

Implements nr:image:analyze. Uses ImageOptimizer::analyzeHeuristic() -- no external tool is invoked. See Analyze optimization potential.

SourceSetViewHelper 

class SourceSetViewHelper
Fully qualified name
\Netresearch\NrImageOptimize\ViewHelpers\SourceSetViewHelper

Fluid ViewHelper that generates <img> tags with srcset attributes for responsive image delivery. Supports both density-based (2x) and width-based responsive srcset modes. See Usage for examples and SourceSetViewHelper for the parameter reference.

MaintenanceController 

class MaintenanceController
Fully qualified name
\Netresearch\NrImageOptimize\Controller\MaintenanceController

Extbase controller powering the backend module. Three actions:

indexAction
Overview of the processed-images directory: file count, total size, largest files, type distribution.
systemRequirementsAction
Renders the data produced by \Netresearch\NrImageOptimize\Service\SystemRequirementsService.
clearProcessedImagesAction
Recursively deletes every file under the configured processed-images directory. A flash message reports the freed space.

Module registration (access limited to systemMaintainer) lives in Configuration/Backend/Modules.php.

Testing 

Run the full test suite
composer ci:test
Copied!

Individual test commands:

Available test commands
composer ci:test:php:cgl      # Code style
composer ci:test:php:lint     # PHP syntax
composer ci:test:php:phpstan  # Static analysis
composer ci:test:php:unit     # PHPUnit tests
composer ci:test:php:rector   # Code quality
Copied!
Docker-based matrix runner (also used by CI)
Build/Scripts/runTests.sh -s unit -p 8.4
Build/Scripts/runTests.sh -s phpstan -p 8.4
Copied!