amitdugar/img-opt

PHP image optimizer/delivery helper with AVIF/WebP support and CLI batch tool.

Installs: 19

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/amitdugar/img-opt

0.1.5 2026-01-09 11:43 UTC

This package is auto-updated.

Last update: 2026-01-09 11:44:24 UTC


README

Reusable PHP helpers for image delivery and batch conversion with AVIF/WebP support. Includes:

  • In-app service to generate cached variants (resize + format negotiation)
  • <picture> helper for srcset generation
  • CLI tool for one-time/bulk conversion
  • Cloudflare helper for /cdn-cgi/image URLs and dev fallback

Namespace: ImgOpt. Dependencies: PHP 8.2+, ext-imagick, Symfony Console/Filesystem/Finder. Versioning: Semantic Versioning (SemVer) via git tags (e.g., 0.0.1).

Install

composer require amitdugar/img-opt

Quick start (PHP)

use ImgOpt\Config;
use ImgOpt\ImgOpt;

$config = Config::fromArray([
    'cache_root' => __DIR__ . '/public/_imgcache',
    'public_root' => __DIR__ . '/public',
    'cdn_base' => getenv('CDN_BASE') ?: '',
    'max_width'  => 0, // keep original unless a smaller width is requested
    'quality'    => ['avif' => 42, 'webp' => 80, 'jpeg' => 82],
]);

$imgopt = ImgOpt::fromConfig(
    $config,
    publicPath: __DIR__ . '/public',
    useCloudflare: getenv('CF_ENABLED') === '1'
);

// Generate variants and emit a <picture> tag with srcset
echo $imgopt->picture(
    __DIR__ . '/public/img/photo.jpg',
    [480, 768, 1080, 1600],
    $_SERVER['HTTP_ACCEPT'] ?? '',
    '100vw',
    ['alt' => 'Sample photo']
);
  • The helper emits a <picture> with AVIF/WebP sources when Imagick supports them (and the Accept header allows them), then falls back to the original format. Variants are generated on demand under cache_root.
  • ImageService::ensureVariant($source, $width, $acceptHeader, $forceFormat) is available if you just need the cached file path.
  • Set cdn_base to rewrite URLs to a CDN host (e.g., https://cdn.example.com).

Cloudflare Image Resizing helper

If useCloudflare is enabled in ImgOpt::fromConfig, the same $imgopt->picture() call will emit Cloudflare URLs instead of local variants.

use ImgOpt\CloudflareImage;

$cf = new CloudflareImage(
    publicPath: __DIR__ . '/public',           // local public root (for size checks)
    cacheFile: __DIR__ . '/storage/image-sizes.json', // intrinsic-size cache (optional)
    defaultQuality: 85,
    enabled: true                               // set false in dev to bypass CF
);

echo $cf->img(
    '/img/photo.jpg',
    [480, 768, 1080, 1600],                     // widths for srcset (will clamp to intrinsic)
    ['alt' => 'Sample photo', 'class' => 'img-fluid'],
    default: 800
);
  • Builds /cdn-cgi/image/... URLs when enabled. In dev/disabled mode, falls back to a local <picture> and will use adjacent .avif/.webp files if they exist.
  • Caches intrinsic sizes in a small JSON file to avoid repeated getimagesize calls.
  • Helper avoids upscaling and auto-adds width/height for layout stability.

CLI (bulk conversion)

php bin/img-opt <folder> [--max-width N] [--q-avif N] [--q-webp N] [--formats avif,webp] [--force] [--dry-run] [--cache-dir DIR]
php vendor/bin/img-opt <folder> [--max-width N] [--q-avif N] [--q-webp N] [--formats avif,webp] [--force] [--dry-run] [--cache-dir DIR]
  • Converts PNG/JPG recursively, writing AVIF/WebP variants into cache-dir (defaults to <folder>/_imgcache).
  • Skips fresh outputs unless --force is set. Use --dry-run to preview.

Design notes

  • AVIF/WebP capability is auto-detected from Imagick. If AVIF is missing, it falls back to WebP → JPEG/PNG, and if the Accept header is empty it still prefers AVIF/WebP when available.
  • Cache keys include the source path + mtime + width + format + quality, so updates to the original regenerate variants.
  • Width requests are clamped to avoid upscaling and can be capped via max_width.
  • Minimal, maintained dependencies: Symfony Console/Filesystem/Finder; image work uses ext-imagick.

Troubleshooting

  • Ensure Imagick is built with WebP/AVIF: php -r "var_dump((new Imagick())->queryFormats('WEBP'));"
  • If AVIF is unavailable, install libheif + rebuild Imagick (varies by distro). The library will automatically downgrade formats.***

SVG optimization (optional)

If you want smaller SVGs, consider running SVGO as a separate step (SVGs are served as-is and not converted by ImgOpt).

Example (one-off):

npx svgo -f public/assets/img -r