Image Optimization for TYPO3 

Extension key

nr_image_optimize

Package name

netresearch/nr-image-optimize

Version

main

Language

en


The netresearch/nr-image-optimize extension provides on-demand image optimization for TYPO3. Images are processed lazily via middleware when first requested, with support for modern formats (WebP, AVIF), responsive srcset generation, and automatic format negotiation.

Introduction 

Learn what the extension does, its features, and system requirements.

Installation 

Install via Composer or the Extension Manager.

Configuration 

Configure the SourceSetViewHelper, render modes, responsive srcset, and lazy loading.

Usage 

Integrate responsive images into your Fluid templates with practical examples.

Maintenance 

Manage processed images and check system requirements in the backend module.

Developer reference 

Architecture overview and PHP API reference.

Introduction 

What does it do? 

The Image Optimization for TYPO3 extension (nr_image_optimize) compresses images in TYPO3 on three independent layers:

On upload
A PSR-14 event listener runs lossless optimization whenever a file is added to or replaced in a FAL storage (AfterFileAddedEvent / AfterFileReplacedEvent). The listener delegates to the installed optipng / gifsicle / jpegoptim binaries.
On demand in the frontend
A PSR-15 middleware intercepts every request that starts with /processed/ and delegates to the \Netresearch\NrImageOptimize\Processor. The processor parses the URL, loads the original via Intervention Image, produces a resized/recropped variant, optionally writes a matching WebP and AVIF sidecar, and streams the best result back to the client. Variants are cached on disk and served with long-lived HTTP caching headers.
In bulk from the CLI
Two Symfony Console commands iterate the FAL index: nr:image:optimize compresses every eligible file, nr:image:analyze reports optimization potential as a fast heuristic without touching any file.

All three layers share a common \Netresearch\NrImageOptimize\Service\ImageOptimizer service for tool resolution and process orchestration.

Features 

  • Automatic optimization on upload. In-place lossless compression without re-encoding. Unsupported extensions, offline storages, and missing binaries are handled transparently -- the listener never raises.
  • Bulk CLI commands. Streaming iteration over sys_file keeps memory usage flat on large installations. Progress bar with cumulative savings.
  • On-demand variant generation. Variants are produced only when a visitor first requests them through the /processed/ URL.
  • Next-gen format support. Automatic WebP and AVIF sidecar generation with Accept-header-driven content negotiation and skipWebP / skipAvif opt-outs.
  • Responsive images. \Netresearch\NrImageOptimize\ViewHelpers\SourceSetViewHelper emits <img> tags with density-based or width-based srcset + sizes.
  • Render modes. Choose between cover and fit resize strategies per URL.
  • Fetch priority. Native fetchpriority attribute support for Core Web Vitals (LCP) tuning.
  • PSR-14 extension points. \Netresearch\NrImageOptimize\Event\ImageProcessedEvent and \Netresearch\NrImageOptimize\Event\VariantServedEvent let integrators observe the pipeline.
  • Driver abstraction. Imagick is preferred when the extension is loaded; GD is used as a fallback. The ImageReaderInterface adapter (see ImageManager abstraction) hides the Intervention Image v3/v4 API difference.
  • Backend maintenance module. View statistics about processed images, check prerequisites, and clear the cache from the TYPO3 backend.
  • Security. Path traversal is blocked at URL parsing time, quality and dimension values are clamped to safe ranges, PSR-7 responses replace direct header() / exit calls.

Requirements 

  • PHP 8.2, 8.3, 8.4, or 8.5.
  • TYPO3 13.4 or 14.
  • Imagick or GD PHP extension.
  • Intervention Image 3.11+ (installed automatically via Composer).

Optional optimizer binaries 

The on-upload listener and the CLI commands only compress files when a matching binary is available in $PATH:

optipng
Lossless PNG compression.
gifsicle
Lossless GIF compression.
jpegoptim
Lossless (default) or lossy JPEG compression.

Paths can be pinned per binary via the OPTIPNG_BIN, GIFSICLE_BIN, and JPEGOPTIM_BIN environment variables. A set-but-invalid override is treated as authoritative: the tool is reported unavailable rather than silently falling back to $PATH. $PATH lookups also verify is_executable().

See Optional: install optimizer binaries for package-manager snippets.

Installation 

Manual installation 

  1. Download the extension from the TYPO3 Extension Repository.
  2. Upload to typo3conf/ext/.
  3. Activate the extension in the Extension Manager.

Setup 

The extension works out of the box after installation:

  • The frontend middleware picks up any request to the /processed/ path and produces a variant on demand.
  • The PSR-14 event listener for AfterFileAddedEvent and AfterFileReplacedEvent runs on every new or replaced image.

No additional configuration is required.

Optional: install optimizer binaries 

The automatic on-upload compression and the nr:image:optimize CLI command call out to optipng, gifsicle, and jpegoptim. The nr:image:analyze command is purely heuristic and does not require these binaries.

Install whichever tools you need and make them available in $PATH:

Debian/Ubuntu
sudo apt-get install optipng gifsicle jpegoptim
Copied!
macOS (Homebrew)
brew install optipng gifsicle jpegoptim
Copied!

To pin a specific path (useful in containerized environments) set one or more of the following environment variables:

OPTIPNG_BIN
Absolute path to optipng.
GIFSICLE_BIN
Absolute path to gifsicle.
JPEGOPTIM_BIN
Absolute path to jpegoptim.

A set-but-invalid override is authoritative: the tool is reported unavailable. There is no silent fallback to $PATH.

Configuration 

The extension works out of the box with sensible defaults. All three operating modes -- on-demand frontend processing, on-upload compression, and bulk CLI -- activate automatically after installation. This page documents the extension points that can be tweaked.

SourceSetViewHelper 

The SourceSetViewHelper generates responsive <img> tags with srcset attributes.

Basic ViewHelper usage
{namespace nr=Netresearch\NrImageOptimize\ViewHelpers}

<nr:sourceSet path="{f:uri.image(image: image)}"
              width="1200"
              height="800"
              alt="{image.properties.alternative}"
              sizes="(max-width: 768px) 100vw, 50vw"
              responsiveSrcset="1"
/>
Copied!

Parameters 

path

path
Type
string
Required

true

Public path to the source image (for example /fileadmin/foo.jpg), typically generated via f:uri.image().

width

width
Type
int|float
Default
0

Base width in pixels for the rendered <img>. 0 resolves automatically from the source file. Clamped by the processor to 1--8192 when baked into the variant URL.

height

height
Type
int|float
Default
0

Base height in pixels. 0 preserves aspect ratio relative to width. Clamped by the processor to 1--8192.

set

set
Type
array
Default
[]

Responsive set in the form {maxWidth: {width: int, height: int}}. Each entry becomes a <source media="(max-width: <maxWidth>px)"> tag.

alt

alt
Type
string
Default
empty string

Alternative text for the image. Always rendered (even when empty) to keep assistive-tech compatibility.

title

title
Type
string
Default
empty string

HTML-escaped title attribute.

class

class
Type
string
Default
empty string

CSS classes for the <img> tag. Include lazyload to switch from native to JS-based lazy loading (see Lazy loading).

mode

mode
Type
string
Default
cover

Render mode. cover resizes images to fully cover the given dimensions (crop/fill). fit resizes images to fit within the given dimensions.

lazyload

lazyload
Type
boolean
Default
false

Add loading="lazy" (native lazy loading).

responsiveSrcset

responsiveSrcset
Type
boolean
Default
false

Enable width-based responsive srcset instead of the density-based 2x output (preserved for backward compatibility).

widthVariants

widthVariants
Type
string|array
Default
480, 576, 640, 768, 992, 1200, 1800

Width variants for responsive srcset (comma-separated string or array). Only honored when responsiveSrcset is enabled.

sizes

sizes
Type
string
Default
auto, (min-width: 992px) 991px, 100vw

Responsive sizes attribute for the generated <img> tag.

fetchpriority

fetchpriority
Type
string
Default
empty string

Native HTML fetchpriority attribute. Allowed values: high, low, auto. Omitted when empty.

attributes

attributes
Type
array
Default
[]

Extra HTML attributes merged into the rendered tag.

Source set configuration 

Define source sets per media breakpoint via the set attribute:

Source set with breakpoint-specific dimensions
<nr:sourceSet
    path="{f:uri.image(
        image: image,
        width: '960',
        height: '690',
        cropVariant: 'default'
    )}"
    set="{
        480:{width: 160, height: 90},
        800:{width: 400, height: 300}
    }"
/>
Copied!

Render modes 

cover
Default. Resizes images to fully cover the provided width and height.
fit
Resizes images so they fit within the provided width and height.
Using fit mode
<nr:sourceSet
    path="{f:uri.image(
        image: image,
        width: '960',
        height: '690',
        cropVariant: 'default'
    )}"
    width="960"
    height="690"
    mode="fit"
/>
Copied!

Lazy loading 

Both modes support lazy loading via the native loading="lazy" attribute. When using JS-based lazy loading (class="lazyload"), the data-srcset attribute is added automatically.

Backward compatibility 

By default responsiveSrcset is false, preserving the existing 2x density-based srcset behavior. All existing templates continue to work without modifications.

Variant URL format 

Processed variants are served from a dedicated URL path. The ViewHelper generates these URLs automatically, but any markup that writes a URL of this form will be intercepted by the ProcessingMiddleware:

URL template
/processed/<original-path>.<mode-config>.<ext>[?<query>]
Copied!
<original-path>
Public path of the source image, including the /fileadmin/ (or other storage) prefix. Path traversal sequences (..) are rejected at URL-parsing time.
<mode-config>

Concatenation of one or more of:

w<n>
Target width in pixels.
h<n>
Target height in pixels.
q<n>
Quality (1--100).
m<n>
Processing mode (0 = cover, 1 = scale/fit).
<ext>
Source image extension. The processor decides at response time whether to serve the original, the .webp sidecar, or the .avif sidecar based on the Accept header and the query flags below.
Example URL
/processed/fileadmin/photos/hero.w1200h800m0q85.jpg
Copied!

Variant negotiation 

When the processor generates a variant, it writes the original file to disk and additionally produces a .webp and an .avif sidecar (same base name). On each request it inspects the Accept header and returns the best match the client supports, preferring AVIF over WebP over the original format.

Two query parameters let callers opt out of sidecar generation for individual URLs:

skipWebP=1
Do not produce or serve a WebP variant for this URL. The Content-Type always matches the source extension.
skipAvif=1
Do not produce or serve an AVIF variant for this URL. If WebP is still allowed and the client supports it, WebP is served.

These flags are useful when specific consumers (for example e-mail clients or legacy RSS renderers) cannot handle modern formats.

Cache headers 

Processed variant URLs are effectively content-addressed -- any change to dimensions, quality, or format produces a different URL. The processor therefore responds with an immutable, long-lived cache header:

Cache-Control: public, max-age=31536000, immutable
Copied!

This value is a compile-time constant and not user-configurable.

Image driver selection 

Intervention Image is instantiated through \Netresearch\NrImageOptimize\Service\ImageManagerFactory, which selects the best available driver at runtime:

  1. Imagick when the imagick PHP extension is loaded (preferred -- supports AVIF natively if the underlying ImageMagick build does).
  2. GD when imagick is unavailable and the gd extension is loaded.

If neither extension is present, the factory throws a RuntimeException with a descriptive message. Use the backend maintenance module to verify driver availability on your host.

Middleware registration 

Configuration/RequestMiddlewares.php registers the ProcessingMiddleware on the frontend pipeline before typo3/cms-frontend/site. This ordering is required so the middleware can intercept /processed/ URLs before TYPO3's frontend routing claims them. The registration has no user-configurable options.

Processor limits 

The processor enforces the following bounds when parsing a URL:

MAX_DIMENSION
Width and height are clamped to 1--8192 pixels to prevent denial-of-service via excessive memory allocation.
MIN_QUALITY / MAX_QUALITY
Quality is clamped to 1--100.
LOCK_MAX_RETRIES
Up to 10 attempts (at 100 ms intervals) to acquire the per-variant processing lock before returning HTTP 503. Prevents duplicate work when multiple clients hit the same uncached variant simultaneously.

Usage 

This chapter shows practical examples for integrating responsive images into your Fluid templates and for operating the command-line tools that ship with the extension.

Register the namespace 

Add the ViewHelper namespace at the top of your Fluid template or register it globally:

Inline namespace declaration
{namespace nr=Netresearch\NrImageOptimize\ViewHelpers}
Copied!

Basic responsive image 

Simple responsive image with quality setting
<nr:sourceSet file="{image}"
              width="1200"
              height="800"
              quality="85"
              sizes="(max-width: 768px) 100vw, 50vw"
/>
Copied!

Responsive width-based srcset 

Enable width-based srcset generation with a sizes attribute for improved responsive image handling. This is opt-in per usage.

Enable responsive srcset with default variants
<nr:sourceSet
    path="{f:uri.image(
        image: image,
        maxWidth: size,
        cropVariant: 'default'
    )}"
    width="{size}"
    height="{size * ratio}"
    alt="{image.properties.alternative}"
    lazyload="1"
    mode="fit"
    responsiveSrcset="1"
/>
Copied!

Custom width variants 

Specify custom breakpoints for srcset
<nr:sourceSet
    path="{f:uri.image(
        image: image,
        maxWidth: size,
        cropVariant: 'default'
    )}"
    width="{size}"
    height="{size * ratio}"
    responsiveSrcset="1"
    widthVariants="320,640,1024,1920,2560"
    sizes="(max-width: 640px) 100vw,
           (max-width: 1024px) 75vw, 50vw"
/>
Copied!

Output comparison 

Legacy mode (responsiveSrcset=false or not set):

Density-based 2x srcset output
<img src="/processed/fileadmin/image.w625h250m1q100.jpg"
     srcset="/processed/fileadmin/image.w1250h500m1q100.jpg x2"
     width="625"
     height="250"
     loading="lazy">
Copied!

Responsive mode (responsiveSrcset=true):

Width-based srcset output
<img src="/processed/fileadmin/image.w1250h1250m1q100.png"
     srcset="/processed/fileadmin/image.w480h480m1q100.png 480w,
             /processed/fileadmin/image.w576h576m1q100.png 576w,
             /processed/fileadmin/image.w640h640m1q100.png 640w,
             /processed/fileadmin/image.w768h768m1q100.png 768w,
             /processed/fileadmin/image.w992h992m1q100.png 992w,
             /processed/fileadmin/image.w1200h1200m1q100.png 1200w,
             /processed/fileadmin/image.w1800h1800m1q100.png 1800w"
     sizes="auto, (min-width: 992px) 991px, 100vw"
     width="991"
     loading="lazy"
     alt="Image">
Copied!

Fetch priority for Core Web Vitals 

Use the fetchpriority attribute to hint the browser about resource prioritization, improving Largest Contentful Paint (LCP) scores:

High priority for above-the-fold hero image
<nr:sourceSet file="{heroImage}"
              width="1920"
              height="1080"
              fetchpriority="high"
/>
Copied!

Command-line tools 

Both commands read the FAL index directly and process eligible image files (image/jpeg, image/gif, image/png) on online storages. Per-file storage permission evaluation is temporarily disabled and restored in a finally block so long-running CLI runs don't leak state across iterations or require a BE user context.

Bulk optimize images 

The nr:image:optimize command compresses every eligible PNG, GIF, and JPEG file across all storages (or a restricted subset) using the installed optimizer binaries. The original file is replaced in place only when the tool produces a smaller result.

Preview what would be processed
vendor/bin/typo3 nr:image:optimize --dry-run
Copied!
Compress storage 1 with lossy JPEG quality 85
vendor/bin/typo3 nr:image:optimize \
    --storages=1 \
    --jpeg-quality=85 \
    --strip-metadata
Copied!

Options:

--dry-run
Only analyze; do not modify files.
--storages
Restrict to specific storage UIDs. Accepts repeated occurrences or a comma-separated list.
--jpeg-quality
Lossy JPEG quality 0--100. Omit for lossless JPEG optimization.
--strip-metadata
Remove EXIF and comments when the tool supports it.

Analyze optimization potential 

The nr:image:analyze command estimates how much disk space could be saved by running nr:image:optimize or by downscaling oversized originals. It is purely heuristic -- no external binaries are invoked, so it runs quickly even on large installations.

Report potential for storage 1
vendor/bin/typo3 nr:image:analyze --storages=1
Copied!

Options:

--storages
Restrict to specific storage UIDs.
--max-width / --max-height
Target display box (default 2560 x 1440). Images larger than this box are assumed to be downscaled and the estimate factors in the area reduction.
--min-size
Skip files smaller than this many bytes (default 512000). Prevents noise from already-tiny images.

Maintenance 

The extension ships with both a backend module and two CLI commands. The backend module is reachable via Admin Tools > Processed Images Maintenance (access restricted to users with the systemMaintainer role). The CLI commands are documented in Command-line tools.

Access control 

The backend module is registered under the Admin Tools parent with access: systemMaintainer. Only TYPO3 users in the systemMaintainer group (configured in LocalConfiguration.php) see it in the backend navigation.

Overview 

The Overview tab (MaintenanceController::indexAction) reports statistics about the processed-images directory:

  • File count and total disk usage.
  • Number of directories under /processed/.
  • The five largest files (name, size, last-modified timestamp).
  • File-type distribution (JPEG / PNG / GIF / WebP / AVIF / other).

The stats are computed on demand by recursively walking the filesystem; no metadata is persisted to the database.

System requirements check 

The System requirements tab (MaintenanceController::systemRequirementsAction) verifies all technical prerequisites needed for successful variant generation:

  • PHP version and loaded extensions (imagick, gd, fileinfo).
  • ImageMagick / GraphicsMagick capabilities reported by Imagick, in particular the ability to decode/encode WebP and AVIF. If Imagick is not installed, the page falls back to GD capabilities.
  • Intervention Image installed version and the driver selected at runtime.
  • TYPO3 version.
  • Availability of optional CLI helpers (magick, convert, identify, gm) -- none are strictly required but may be useful for debugging.

Each check is rendered as a pass/warning/fail row with a short explanation so an administrator can fix a misconfigured host in minutes.

Clear processed images 

The Clear processed images tab (MaintenanceController::clearProcessedImagesAction) recursively deletes every file under the configured processed-images directory. A flash message reports the number of files removed and the disk space freed.

Command-line maintenance 

Two console commands complement the backend module:

nr:image:optimize
Lossless (or optionally lossy) bulk compression of every PNG, GIF, and JPEG file in the FAL index.
nr:image:analyze
Fast heuristic report that estimates optimization potential without touching files or running external tools.

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!

Changelog 

Unreleased 

New in version next

Automatic optimization on upload and bulk CLI commands for optimizing or analyzing the FAL image inventory.

  • Added OptimizeOnUploadListener -- PSR-14 listener that runs optipng / gifsicle / jpegoptim on AfterFileAddedEvent and AfterFileReplacedEvent. Keyed by storageUid . ':' . identifier to avoid cross-storage re-entrancy collisions; restores setEvaluatePermissions in a finally block.
  • Added nr:image:optimize -- bulk optimization command with --dry-run, --storages, --jpeg-quality, and --strip-metadata options. Uses a streaming Generator over sys_file so large installations don't load the full index into memory.
  • Added nr:image:analyze -- heuristic analysis command that estimates optimization potential without invoking any binary. Fast even on large installations.
  • Added ImageOptimizer service -- shared backend used by the listener and both CLI commands. Env overrides (OPTIPNG_BIN, GIFSICLE_BIN, JPEGOPTIM_BIN) are authoritative: a set-but-invalid override is reported as unavailable rather than silently falling back to $PATH. $PATH lookups also verify is_executable().

2.2.1 

  • Adjusted author information in ext_emconf.php.

2.2.0 

  • Fixed: always render alt attribute on generated <img> tags.
  • Expanded unit test coverage for Processor and SourceSetViewHelper.

2.1.0 

New in version 2.1.0

Width-based responsive srcset with sizes attribute, configurable width variants, and fetchpriority support.

  • Added responsive width-based srcset generation as opt-in feature.
  • Added widthVariants parameter for custom breakpoints.
  • Added sizes parameter for responsive image sizing.
  • Added fetchpriority attribute for resource hints.
  • Optimized default sizes attribute values.

2.0.1 

  • Fixed declare statement issue preventing TER publishing via GitHub Actions.

2.0.0 

New in version 2.0.0

TYPO3 13 compatibility with PHP 8.2--8.4 support.

  • Added TYPO3 13 compatibility.
  • Added PHP 8.2, 8.3, and 8.4 support.
  • Dropped support for older TYPO3 versions.
  • Switched to Intervention Image 3.x.
  • Removed obsolete system binary checks.

1.0.1 

  • Added ext_emconf.php for classic installation.

1.0.0 

  • Initial stable release.
  • GitHub Actions CI workflows.

0.1.5 

  • Fixed strtolower() null argument error.
  • Fixed array offset access on boolean value.
  • Allowed numeric characters in file extensions.
  • Added extension icon.
  • Corrected crop variant examples.
  • Improved lazy loading behavior.