github timnarr/kirby-imagex 0.5.0

6 hours ago

Full Changelog: 0.4.0...0.5.0

[0.5.0] - October 02, 2026

Fixed

  • Bug: The imagex-picture-json snippet crashed with Call to undefined function transformForJson(): the helper lives in the TimNarr namespace, but the snippet called it unqualified. It now imports the function.
  • Bug: Art-direction styles applied the wrong aspect-ratio/object-position when several media queries matched at once (e.g. (min-width: 1200px) followed by (min-width: 600px)). <picture> uses the first matching <source>, but CSS applies the last matching rule, so the later source's values won. Rules are now emitted in reverse order, and once a property is overridden by any source, every source sets it — including sources that keep the default, which previously inherited another source's value.
  • Bug: With customLazyloading: true, eager images (loading: 'eager') were rendered without a src attribute — and, combined with noSrcsetInImg: true, without any image URL at all. Eager images now always get src.
  • Bug: relativeUrls: true produced broken or absolute URLs on subfolder installs and multi-language sites. It stripped the full site()->url() — which includes the subfolder (/blog/media/… became /media/…) and, on multi-language sites, the language prefix (so nothing matched). Only the origin of Kirby's index URL is stripped now (new getUrlOrigin() helper). Hosts that merely share the prefix (example.com.cdn.net) are no longer mangled.
  • Bug: Kirby's shorthand srcset presets ('default' => [400, 800] or [400 => '1x', 800 => '2x']) crashed with Cannot use a scalar value as an array. They are now normalized via the new normalizeSrcsetPreset() helper, which also throws a descriptive error for empty presets or entries without a positive width.
  • ratio: 'intrinsic' on an image without readable dimensions (e.g. SVG) now throws a descriptive InvalidArgumentException naming the file instead of a bare DivisionByZeroError.
  • Both snippets now report a missing image with the plugin's own Missing required option: image error instead of a preceding PHP "undefined variable" warning.
  • Bug: Flat attributes on artDirection entries (e.g. ['data-x' => 'y']) were dropped silently with a PHP warning, and class/style strings threw a type mismatch error. They are now normalized like attributes.img/picture/sources, so flat and structured attributes as well as class/style strings work everywhere.
  • An invalid ratio like '0/9' threw a generic Kirby\Exception\Exception. It now throws InvalidArgumentException like every other validation error. Ratio errors include the offending value, and for artDirection entries also the entry index (e.g. artDirection[0]: Invalid ratio format '16:9'). Attribute errors on artDirection entries carry the index too.
  • Bug: A user-supplied <img> id starting with a digit (e.g. a UUID) or containing characters like . or : produced an invalid #id selector in the art-direction styles, which browsers silently dropped. The id is now escaped via the new escapeCssIdentifier() helper (CSSOM CSS.escape() semantics, plus hex-escaping </> so it can't close the <style> element).
  • Bug: For images that already are in one of the configured formats (e.g. a WebP upload with formats: ['avif', 'webp']), the <img> used the format preset (my-srcset-webp) instead of the base preset (my-srcset), including its quality settings. Srcset presets are now resolved by preset name, so the <img> always uses the base preset, as documented.

Added

  • New nonce snippet option for strict Content Security Policies. It is added to the generated <style> element, and the focus styles move from the <img>'s style attribute into that element as an #id rule (CSP nonces don't cover style attributes). New Imagex::getNonce() getter.
  • Integration tests for the Imagex class (tests/imagexTest.php), running against a real Kirby instance with generated fixture images. tests/bootstrap.php now registers the plugin once for the whole run.

Changed

  • BREAKING: Unknown options now throw an InvalidArgumentException listing the allowed ones. This covers top-level Imagex options (e.g. ration) and attributes keys other than img, picture and sources (e.g. image). Previously both were ignored silently. Migration: fix the misspelled key. A misspelled snippet option is still ignored, because the snippets only forward known options.
  • Invalid class/style attribute types (e.g. an integer) now throw when the Imagex instance is created rather than when its attributes are read.
  • The option defaults now live in the Imagex class only, so the snippets no longer repeat them. new Imagex(['image' => $file]) now works with the same defaults as the snippets, and options passed as null fall back to the default. Previously ratio, srcset and compareFormats were required when using the class directly.
  • The helper functions in the TimNarr namespace, as well as Imagex::getSmallestFormatForImage(), are now marked @internal. They are not part of the public API and may change in any release. The README has a new "Public API" section listing what is covered by semantic versioning.
  • BREAKING: Attributes that mix flat keys with shared/eager/lazy keys (e.g. ['class' => 'x', 'lazy' => [...]]) now throw an InvalidArgumentException. Previously the flat keys were dropped silently. Migration: move the flat keys into shared.
  • BREAKING: artDirection entries are validated at construction time. Each entry must have a non-empty media string (a <source> without media always matches, so the default image was never used), only the keys media, ratio, image and attributes are accepted, image must be a Kirby\Cms\File or null (still falls back to the main image), and ratio must be valid. Migration: add a media condition to every entry and fix typos in keys.
  • The main ratio is now validated at construction time instead of on first render.
  • BREAKING: The relativeUrls plugin option must be a boolean. A non-boolean value (e.g. 1 or a string from env()) was previously coerced silently and now throws an InvalidArgumentException. Migration: set it to true or false.
  • The plugin options formats, compareFormatsWeights, customLazyloading, addOriginalFormatAsSource, noSrcsetInImg and relativeUrls are type-checked at construction time, throwing a descriptive InvalidArgumentException instead of a TypeError.
  • All srcset preset errors (missing thumbs.srcsets, missing base or format preset, malformed preset) and the compareFormats "not enough formats" error are now thrown at construction time as InvalidArgumentException. Previously a missing thumbs.srcsets config crashed with a TypeError, and some errors only surfaced during rendering.
  • All PHP files declare strict_types, enforced by php-cs-fixer's declare_strict_types rule.

Performance

  • Ratio-based srcset presets are memoized per Imagex instance instead of going through the persistent cache. They were resolved for the <img>, every format and every art-directed source — each time re-validating the config, JSON-encoding and hashing it, and reading a cache file for what is plain arithmetic. Existing srcset-config-* cache entries are no longer used and can be removed by flushing the plugin cache.
  • The compareFormats result is memoized per image and ratio on top of the persistent cache; it was looked up once per format for every art-directed source.
  • Srcset presets are resolved, normalized and validated once in the constructor (previously checked twice and rebuilt on every call).

Removed

  • BREAKING: Imagex::getSmallestFormat(). It was an unused wrapper around getSmallestFormatForImage() without arguments. Migration: call getSmallestFormatForImage() instead (now @internal).
  • BREAKING: srcHandler() helper. Its only job — dropping src for custom lazy loading — is now a plain default attribute in Imagex::getImgAttributes(); user-supplied src overrides are handled by mergeHTMLAttributes() as for every other attribute. Migration: none for snippet users; if you called it directly, set src via attributes.img instead.

Migration Guide

From 0.4.x to 0.5.0

Most setups need no changes. 0.5.0 validates options up front and throws where 0.4.x silently ignored mistakes, so an upgrade surfaces misconfigurations that were already there. Go through these points; each error message names the offending option and the valid alternatives.

1. Add a media condition to every artDirection entry

An entry without media always matched, so the default image was never shown. It now throws.

// Before
'artDirection' => [
  ['ratio' => '21/9', 'image' => $wideImage],
],

// After
'artDirection' => [
  ['media' => '(min-width: 800px)', 'ratio' => '21/9', 'image' => $wideImage],
],

Only the keys media, ratio, image and attributes are accepted; a typo like ratios now throws instead of being ignored.

2. Don't mix flat and structured attributes

Flat keys next to shared/eager/lazy were dropped silently. Move them into shared:

// Before
'attributes' => [
  'img' => [
    'class' => 'hero',
    'lazy' => ['data-sizes' => 'auto'],
  ],
],

// After
'attributes' => [
  'img' => [
    'shared' => ['class' => 'hero'],
    'lazy' => ['data-sizes' => 'auto'],
  ],
],

3. Fix unknown option and attributes keys

attributes only accepts img, picture and sources (e.g. image throws). When you use the Imagex class directly, misspelled options like ration throw too. Misspelled snippet options are still ignored.

4. Set relativeUrls to a real boolean

// Before
'timnarr.imagex.relativeUrls' => env('RELATIVE_URLS'), // e.g. '1'

// After
'timnarr.imagex.relativeUrls' => env('RELATIVE_URLS') === '1',

5. Only if you call PHP internals directly

  • Imagex::getSmallestFormat() was removed. Use getSmallestFormatForImage().
  • The srcHandler() helper was removed. Set src via attributes.img instead.
  • The helper functions in the TimNarr namespace and Imagex::getSmallestFormatForImage() are now @internal and may change in any release. See "Public API" in the README for what is covered by semantic versioning.

Don't miss a new kirby-imagex release

NewReleases is sending notifications on new releases.