Auto-gegenereerd via Reflection — blijft altijd in sync met src/.
GdBackend
Framework\Image\Backend\GdBackendGD-implementatie van {@see ImageBackendInterface}.
GD is gebouwd in PHP en bijna overal aanwezig — handig als fallback
wanneer ext-imagick ontbreekt. Ondersteunt jpg/png/webp; AVIF alleen
als PHP gebouwd is tegen libavif (vaak niet — gooit dan een
ImageBackendException).
Voor productie met avif-support en betere kleur-management is
{@see ImagickBackend} sterker. De keuze gebeurt in bootstrap.
__construct()1 public method
transform(string $imageBytes, \TransformSpec $spec): stringImageBackendException
Framework\Image\Backend\ImageBackendExceptionBackend kon niet decoden / encoden / transformeren — meestal corrupte
input of een format-mismatch. Mapt naar HTTP 422 in de handler.
__construct(string $message = '', int $code = 0, ?Throwable $previous = NULL)ImageBackendInterface
Framework\Image\Backend\ImageBackendInterfacePure transform-laag: image-bytes in, image-bytes uit.
Geen IO, geen cache, geen network — alleen de berekening. De Transformer
en Handler zijn verantwoordelijk voor source-IO en cache-write.
V1: alleen {@see ImagickBackend}. libvips/GD-backends kunnen later
worden toegevoegd via dezelfde interface.
1 public method
transform(string $imageBytes, \TransformSpec $spec): stringImagickBackend
Framework\Image\Backend\ImagickBackendImagick-implementatie van {@see ImageBackendInterface}.
Mapping van fit-modes naar Imagick-API:
- Cover → cropThumbnailImage (vult target, snijdt af)
- Contain → thumbnailImage met `bestfit=true` + zwarte/transparante padding
- Fit → scaleImage met aspect-ratio behouden, geen padding
- Crop → exact cropImage centered (vereist W én H)
Output-format via {@see Format} → setImageFormat. AVIF/WebP vereisen
een Imagick gebouwd tegen libheif/libwebp — vrijwel alle moderne
distros hebben dit.
__construct()1 public method
transform(string $imageBytes, \TransformSpec $spec): stringVariantCache
Framework\Image\Cache\VariantCacheSchrijft / leest bytes onder een relative path binnen een root-directory.
Wordt twee keer gebruikt:
- Voor de getransformeerde varianten (`data/cache/img/`)
- Voor geëxtraheerde video-frames (`data/cache/img/frames/`)
Schrijven gaat atomair (tmp-file + rename) zodat een crash midden in de
write geen halve files achterlaat — een halve file zou nginx als geldige
cache-hit serveren en de browser zou corrupt content krijgen.
Path-traversal guard: relative path mag geen `..` of absolute prefix bevatten.
__construct(string $root)4 public methods
exists(string $relativePath): boolpath(string $relativePath): stringread(string $relativePath): ?stringwrite(string $relativePath, string $bytes): voidFitMode
Framework\Image\FitModeHoe een afbeelding in z'n target-box past. Spiegelt CSS object-fit
voor consistentie met Framework\Media\DisplayMode.
5 public methods
static cases(): arraystatic from(string|int $value): staticstatic fromToken(string $token): selftoken(): stringstatic tryFrom(string|int $value): ?staticCover, Contain, Fit, CropFormat
Framework\Image\FormatOutput-formaat voor de transform.
Format::Auto is een marker — UrlBuilder weigert 'm. Image::url() resolved
Auto naar een concreet formaat op basis van de Accept-header van de
huidige request, vóór 'ie de URL bouwt.
6 public methods
static cases(): arrayextension(): stringstatic from(string|int $value): staticstatic fromExtension(string $ext): selfmimeType(): stringstatic tryFrom(string|int $value): ?staticWebp, Avif, Jpeg, Png, AutoHmacSigner
Framework\Image\HmacSignerHMAC-SHA256 signer met rotation-support.
`sign()` gebruikt altijd het huidige secret. `verify()` probeert eerst
het huidige, dan (als gezet) het vorige — zodat tijdens een rotation-
grace-period URLs gegenereerd onder het oude secret nog geldig zijn.
Beide vergelijkingen via `hash_equals()` om timing-attacks te voorkomen.
__construct(string $currentSecret, ?string $previousSecret = NULL)2 public methods
sign(string $payload): stringverify(string $payload, string $signature): boolImageHandler
Framework\Image\Http\ImageHandlerHTTP-handler voor `/img/...`-routes.
Flow:
1. Parse URL → ParsedUrl (intern hash óf extern URL + sig-verify)
2. Variant-cache check → hit: serve direct, miss: stap 3
3. Resolve source-bytes (FileStorage óf remote-fetch+ingest)
4. StillFrame-extractie (passthrough / GIF frame 0 / video frame via ffmpeg)
Voor video: tussencache zodat ffmpeg niet per maat opnieuw runt
5. Transform via backend
6. Schrijf naar variant-cache zodat nginx volgende requests direct serveert
__construct(\UrlParser $urlParser, \FileStorageSource $internalSource, \RemoteUrlSource $remoteSource, \StillFrameRegistry $stillFrames, \Transformer $transformer, \VariantCache $variantCache, \VariantCache $frameCache)Image
Framework\Image\ImagePublieke facade voor URL-generatie.
Source mag zijn:
- {@see FileRecord} — gebruikt `contentHash` voor lokale, of `externalUrl`
voor externe (YouTube/Vimeo etc. zijn doorgaans niet relevant voor
image-transform, maar Realworks-style "extern via URL" werkt zo)
- `string` met sha256-hash (64 hex chars) — direct internal
- `string` met http(s) URL — extern, met HMAC-signing
Format::Auto resolveert op aanroep-tijd op basis van de huidige
Accept-header. Dat gebeurt via de `acceptHeaderProvider`-closure
zodat we op één Image-instance per request blijven (singleton in
de container) en de URL toch context-bewust is.
__construct(\UrlBuilder $builder, Closure $acceptHeaderProvider)1 public method
url(\FileRecord|string $source, ?int $width = NULL, ?int $height = NULL, \FitMode $fit = \Framework\Image\FitMode::Cover, \Format $format = \Framework\Image\Format::Auto, int $quality = 75, int $dpr = 1): stringFileStorageSource
Framework\Image\Source\FileStorageSourceResolveert een sha256-hash naar bytes via de bestaande FileStorage.
Mime-type komt uit `finfo_buffer()` op de eerste paar KB — zo werkt 't
ook voor files die zonder extensie in storage zijn gezet (b.v. via
Realworks-import).
__construct(\FileStorage $storage)1 public method
resolve(string $identifier): \SourceResultRemoteFetchException
Framework\Image\Source\RemoteFetchExceptionExterne fetch faalde (timeout, non-2xx, te groot, niet-toegestaan content-type).
Mapt naar HTTP 502 / 415 in de handler afhankelijk van de oorzaak.
__construct(string $message, string $reason, ?Throwable $previous = NULL)RemoteUrlSource
Framework\Image\Source\RemoteUrlSourceHaalt een externe URL op, valideert content-type + grootte, en ingest
de bytes in de gedeelde FileStorage. Volgende calls met dezelfde URL
(en dus dezelfde bytes/hash) zijn dedupe-gratis.
Gooit {@see RemoteFetchException} bij netwerkfouten, niet-2xx-status,
verboden content-type of een body groter dan `maxBytes`.
__construct(\Client $client, \FileStorage $storage, int $maxBytes = 20971520, float $timeoutSeconds = 5.0, array $allowedMimePrefixes = array (
0 => 'image/',
1 => 'video/',
), string $userAgent = 'FrameworkImage/1.0 (+https://framework.multiminded.nl/image)')1 public method
resolve(string $identifier): \SourceResultSourceNotFoundException
Framework\Image\Source\SourceNotFoundExceptionSource-resolver kon de identifier niet vinden.
Mapt naar HTTP 404 in de handler.
__construct(string $message = '', int $code = 0, ?Throwable $previous = NULL)SourceResolverInterface
Framework\Image\Source\SourceResolverInterfaceLevert ruwe bytes voor een transform-pijplijn.
Identifier-vorm verschilt per implementatie:
- {@see FileStorageSource}: een sha256-hash
- {@see RemoteUrlSource}: een http(s) URL
Implementaties moeten {@see SourceNotFoundException} gooien als de
identifier niet leidt tot bytes.
1 public method
resolve(string $identifier): \SourceResultSourceResult
Framework\Image\Source\SourceResultResultaat van een SourceResolver: ruwe bytes + mime-type + content-hash.
Bytes kunnen image, gif of video zijn — dat bepaalt de StillFrameExtractor.
`sourceHash` is sha256 van de bytes; gebruikt als cache-key voor
geëxtraheerde frames (zodat twee URLs die naar dezelfde video wijzen
hetzelfde frame delen).
__construct(string $rawBytes, string $mimeType, string $sourceHash)SpecCodec
Framework\Image\SpecCodecCodeert / decodeert een TransformSpec van/naar het canonieke URL-token.
Format: {w}x{h}-{fit}-q{quality}[-dpr{n}]
- w of h mag ontbreken (bv. "800" of "x600")
- fit en quality zijn altijd aanwezig (URLs blijven leesbaar)
- dpr verschijnt alleen als > 1
Encoder is strikt (canonieke volgorde, voorspelbare cache-key).
Decoder is permissief: tokens mogen in willekeurige volgorde — handig
bij handmatig getypte URLs of toekomstige migraties.
2 public methods
decode(string $token, \Format $format): \TransformSpecencode(\TransformSpec $spec): stringGifFrameExtractor
Framework\Image\StillFrame\GifFrameExtractorExtract het eerste frame van een (animated) GIF via Imagick.
Output is een **PNG** — lossless intermediate, want we willen geen
kwaliteitsverlies vóór de Transformer er wat mee doet. De Transformer
kiest het uiteindelijke output-format.
Vereist `ext-imagick`. Als de extensie ontbreekt gooit de constructor
een StillFrameException — bootstrap moet 'm dan niet binden.
__construct()1 public method
extract(string $rawBytes, string $mimeType): stringPassthroughExtractor
Framework\Image\StillFrame\PassthroughExtractorNo-op extractor voor formaten die de ImageBackend rechtstreeks aankan
(jpg/png/webp/avif/svg). Geeft de bytes ongewijzigd door.
1 public method
extract(string $rawBytes, string $mimeType): stringStillFrameException
Framework\Image\StillFrame\StillFrameExceptionFout bij frame-extractie (gif/video). Mapt naar HTTP 422 / 500 in de handler.
__construct(string $message = '', int $code = 0, ?Throwable $previous = NULL)StillFrameExtractor
Framework\Image\StillFrame\StillFrameExtractorVertaalt ruwe source-bytes naar **image-bytes** die de Transformer kan
verwerken. Implementaties zijn responsible voor:
- jpg/png/webp/avif/svg → bytes ongewijzigd doorgeven
- gif → frame 0 extracten als losse image
- mp4/mov/webm → frame extractie via ffmpeg
De selectie gebeurt in {@see StillFrameRegistry} op basis van mime-type.
1 public method
extract(string $rawBytes, string $mimeType): stringStillFrameRegistry
Framework\Image\StillFrame\StillFrameRegistryKiest een {@see StillFrameExtractor} op basis van het mime-type.
Default-mapping:
- image/gif → GifFrameExtractor
- video/* → VideoFrameExtractor
- alles anders → PassthroughExtractor (jpg/png/webp/avif/svg)
Bootstrap kan via de constructor extractors weglaten als de
vereiste deps ontbreken (bv. geen Imagick → geen Gif-handler).
__construct(\StillFrameExtractor $passthrough, ?\StillFrameExtractor $gif = NULL, ?\StillFrameExtractor $video = NULL)2 public methods
extract(string $rawBytes, string $mimeType): stringpick(string $mimeType): \StillFrameExtractorVideoFrameExtractor
Framework\Image\StillFrame\VideoFrameExtractorExtract een frame uit een video via een ffmpeg subprocess.
Werkt via stdin/stdout pipes — geen temp-files nodig:
ffmpeg -i pipe:0 -ss <seconds> -frames:v 1 -f image2 -vcodec mjpeg pipe:1
Output is JPEG (lossy maar compact); de Transformer re-encodeert toch
naar het uiteindelijke formaat. Default frame-time is 1 seconde — dat
vermijdt zwarte/intro-frames die vaak op t=0 staan.
`$ffmpegBinary` is configureerbaar (settings.php) zodat servers met
een non-standard pad (bv. `/usr/local/bin/ffmpeg`) ook werken.
__construct(string $ffmpegBinary = 'ffmpeg', float $frameTimeSeconds = 1.0, int $timeoutSeconds = 10)2 public methods
extract(string $rawBytes, string $mimeType): stringstatic isFfmpegAvailable(string $binary = 'ffmpeg'): boolCheap check — caller (bootstrap) kan beslissen of de extractor
geregistreerd moet worden.
TransformBounds
Framework\Image\TransformBoundsProject-bounds voor TransformSpec — voorkomt dat een geldige URL een
50000×50000-render kan triggeren die de RAM opvreet.
Config in config/settings.php onder de 'image'-sleutel.
__construct(int $maxWidth = 2600, int $maxHeight = 2600, int $maxDpr = 3)1 public method
validate(\TransformSpec $spec): voidTransformSpec
Framework\Image\TransformSpecWat we met een afbeelding willen doen — onafhankelijk van waar 'ie vandaan komt.
Validatie hier is intrinsiek (logische correctheid). Project-bounds
(max-w/h/dpr) leven in {@see TransformBounds} omdat die per project
configureerbaar zijn (config/settings.php).
__construct(?int $width = NULL, ?int $height = NULL, \FitMode $fit = \Framework\Image\FitMode::Cover, \Format $format = \Framework\Image\Format::Webp, int $quality = 75, int $dpr = 1)3 public methods
effectiveHeight(): ?inteffectiveWidth(): ?intwithFormat(\Format $format): selfTransformer
Framework\Image\TransformerOrchestreert: bounds-validate → backend transform.
Geen IO. Caller (Handler) is verantwoordelijk voor source-IO en
cache-write. Deze klasse is dunne glue tussen TransformBounds en
de backend zodat beide testbaar blijven zonder elkaar.
__construct(\ImageBackendInterface $backend, \TransformBounds $bounds)1 public method
transform(string $imageBytes, \TransformSpec $spec): stringInvalidUrlException
Framework\Image\Url\InvalidUrlException__construct(string $message = '', int $code = 0, ?Throwable $previous = NULL)ParsedUrl
Framework\Image\Url\ParsedUrlResultaat van UrlParser. Discriminate via isInternal() / isExternal().
4 public methods
static external(string $sourceUrl, \TransformSpec $spec): selfstatic internal(string $fileHash, \TransformSpec $spec): selfisExternal(): boolisInternal(): boolUrlBuilder
Framework\Image\Url\UrlBuilderBouwt URLs voor de image-handler.
- Intern: /img/{prefix}/{file-hash}/{spec}.{ext}
- Extern: /img/extern/{prefix}/{url-hash}/{spec}.{ext}?u={base64url}&sig={hmac}
Format::Auto wordt door deze klasse niet geresolved — caller moet
eerst een concreet formaat kiezen (Image-facade doet dat op basis van
Accept-header).
__construct(\SpecCodec $codec, \HmacSigner $signer)3 public methods
buildExternal(string $sourceUrl, \TransformSpec $spec): stringbuildInternal(string $fileHash, \TransformSpec $spec): stringsignaturePayload(string $sourceUrl, string $specToken, string $extension): stringUrlParser
Framework\Image\Url\UrlParserParseert URLs gegenereerd door UrlBuilder en verifieert HMAC voor extern.
__construct(\SpecCodec $codec, \HmacSigner $signer)1 public method
parse(string $path, string $queryString): \ParsedUrlImage
Framework\Media\Source\ImageImage bron — rendert een `<img>`-element. Ondersteunt `loading="lazy"`,
`fetchpriority`, `width`/`height`, `alt`. Geen poster (heeft geen video).
__construct(string $url, ?int $width = NULL, ?int $height = NULL)1 public method
render(\Variant $variant, \RenderOptions $options): \El