Developer reference
Architecture
The extension has three entry points that share the same optimization backend:
- ProcessingMiddleware intercepts frontend requests
matching the
/processed/path and asks the Processor to create a variant on demand. - OptimizeOnUploadListener reacts to PSR-14 file events
(
AfterFileAddedEventandAfterFileReplacedEvent) and delegates to the shared ImageOptimizer service for in-place lossless compression. - 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
-
-
-
AbstractImageCommand.php
-
AnalyzeImagesCommand.php
-
OptimizeImagesCommand.php
-
-
-
MaintenanceController.php
-
-
-
ImageProcessedEvent.php
-
VariantServedEvent.php
-
-
-
OptimizeOnUploadListener.php
-
-
-
ProcessingMiddleware.php
-
-
-
ImageManagerAdapter.php
-
ImageManagerFactory.php
-
ImageOptimizer.php
-
ImageReaderInterface.php
-
SystemRequirementsService.php
-
-
Processor.php
-
ProcessorInterface.php
-
-
SourceSetViewHelper.php
-
-
-
-
-
Modules.php
-
-
Icons.php
-
RequestMiddlewares.php
-
Services.yaml
-
Processing middleware
- class ProcessingMiddleware
-
- Fully qualified name
-
\Netresearch\
Nr Image Optimize\ Middleware\ Processing Middleware
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\
Nr Image Optimize\ Processor Interface
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\
Nr Image Optimize\ Processor
Default implementation. Parses the requested URL (see Variant URL format) to extract path, dimensions, quality, and mode. Loads the original via the
ImageReaderInterfaceadapter (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/hto 1--8192 andqto 1--100. - Uses TYPO3's
LockFactoryto serialize concurrent requests for the same variant; a 503 is returned if the lock can't be acquired within 1 second. - Respects the
Acceptheader plus theskipWebPandskipAvifquery 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\
Nr Image Optimize\ Event\ Image Processed Event
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
.webpor.avifsidecar of this path. extension(string)- Lowercased extension of the variant (
jpg,png,webp,avif...). targetWidth/targetHeight(?int)- Final clamped dimensions in pixels, or
nullif 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\
Nr Image Optimize\ Event\ Variant Served Event
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)truewhen the response reuses a previously generated file on disk;falsewhen the variant was produced in this request.
Register listeners via Configuration/Services.yaml or the
per-class #[AsEventListener] attribute:
services:
Acme\Example\Listener\MetricsListener:
tags:
- name: event.listener
identifier: 'acme.metrics.variant_served'
event: Netresearch\NrImageOptimize\Event\VariantServedEvent
Upload event listener
- class OptimizeOnUploadListener
-
- Fully qualified name
-
\Netresearch\
Nr Image Optimize\ Event Listener\ Optimize On Upload Listener
PSR-14 listener registered for
AfterFileAddedEventandAfterFileReplacedEvent. Normalizes the file extension, short-circuits unsupported extensions and offline storages, and delegates to \Netresearch\NrImageOptimize\Service\ImageOptimizer.Re-entrancy guard: keyed by
storageUid . ':' . identifierso two storages sharing an identifier (/image.jpgin different driver roots) don't block each other, while still catching thereplaceFile()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 afinallyblock so long-running CLI runs don't leak permission state across iterations.
ImageOptimizer service
- class ImageOptimizer
-
- Fully qualified name
-
\Netresearch\
Nr Image Optimize\ Service\ Image Optimizer
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.$PATHlookups also verifyis_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\
Nr Image Optimize\ Service\ Image Manager Factory
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\
Nr Image Optimize\ Service\ Image Reader Interface
Version-agnostic abstraction around Intervention Image's loading API. v3 uses
ImageManager::read(), v4 usesImageManager::decode(); this interface hides that difference so consumers and static analysis see a single stable contract.
- class ImageManagerAdapter
-
- Fully qualified name
-
\Netresearch\
Nr Image Optimize\ Service\ Image Manager Adapter
Default implementation. Dispatches to whichever method is present on the installed
ImageManager, letting the extension support Intervention Image^3 || ^4simultaneously without version-conditional code paths.
SystemRequirementsService
- class SystemRequirementsService
-
- Fully qualified name
-
\Netresearch\
Nr Image Optimize\ Service\ System Requirements Service
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\
Nr Image Optimize\ Command\ Abstract Image Command
Base class for the image CLI commands. Exposes
parseStorageUidsOption,getIntOption,extractUid,countImages,iterateViaIndex(Generator oversys_filerows -- constant memory),createProgress,buildLabel,shortenLabel, andformatMbGb.
- class OptimizeImagesCommand
-
- Fully qualified name
-
\Netresearch\
Nr Image Optimize\ Command\ Optimize Images Command
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\
Nr Image Optimize\ Command\ Analyze Images Command
Implements
nr:image:analyze. UsesImageOptimizer::analyzeHeuristic()-- no external tool is invoked. See Analyze optimization potential.
SourceSetViewHelper
- class SourceSetViewHelper
-
- Fully qualified name
-
\Netresearch\
Nr Image Optimize\ View Helpers\ Source Set View Helper
Fluid ViewHelper that generates
<img>tags withsrcsetattributes 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\
Nr Image Optimize\ Controller\ Maintenance Controller
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 inConfiguration/.Backend/ Modules. php
Testing
composer ci:test
Individual 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
Build/Scripts/runTests.sh -s unit -p 8.4
Build/Scripts/runTests.sh -s phpstan -p 8.4