Replaces Smush Pro's optimisation pipeline without the grey-wash bug. CLI commands working: wp h4b-img status wp h4b-img optimise --id=<n> wp h4b-img bulk wp h4b-img rescue Verified on dev.rds.ink: - ICC profile preservation works (the Smush-bug fix) - Bulk: 20 attachments → 487 KB saved (10.4%), 0 errors - Rescue: end-to-end mechanism verified on WorkingAsOne_horse fixture - WebP synchronous, AVIF queued via WP-Cron - Originals backed up to wp-content/h4b-img-originals/ See CHANGELOG.md for details + ../DESIGN-h4b-image-optim.md for architecture. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
69 lines
1.6 KiB
PHP
69 lines
1.6 KiB
PHP
<?php
|
|
/**
|
|
* Detects + caches paths to the external image binaries.
|
|
*
|
|
* @package H4B\ImageOptim
|
|
*/
|
|
|
|
namespace H4B\ImageOptim;
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
final class Tools {
|
|
|
|
private const REQUIRED = [ 'cwebp', 'cjpeg', 'djpeg', 'jpegtran', 'jpegoptim' ];
|
|
private const OPTIONAL = [ 'avifenc', 'avifdec', 'pngquant', 'optipng' ];
|
|
|
|
/**
|
|
* @return array<string,string|null> Map of tool name → absolute path or null.
|
|
*/
|
|
public static function detect(): array {
|
|
static $cache = null;
|
|
if ( null !== $cache ) {
|
|
return $cache;
|
|
}
|
|
$cache = [];
|
|
foreach ( array_merge( self::REQUIRED, self::OPTIONAL ) as $bin ) {
|
|
$cache[ $bin ] = self::which( $bin );
|
|
}
|
|
return $cache;
|
|
}
|
|
|
|
public static function path( string $bin ): ?string {
|
|
$tools = self::detect();
|
|
return $tools[ $bin ] ?? null;
|
|
}
|
|
|
|
public static function missing_required(): array {
|
|
$tools = self::detect();
|
|
$missing = [];
|
|
foreach ( self::REQUIRED as $bin ) {
|
|
if ( empty( $tools[ $bin ] ) ) {
|
|
$missing[] = $bin;
|
|
}
|
|
}
|
|
return $missing;
|
|
}
|
|
|
|
public static function has_avif(): bool {
|
|
return ! empty( self::path( 'avifenc' ) );
|
|
}
|
|
|
|
private static function which( string $bin ): ?string {
|
|
// Common locations first, then fall back to PATH lookup.
|
|
foreach ( [ '/usr/bin/', '/usr/local/bin/', '/opt/local/bin/' ] as $prefix ) {
|
|
$candidate = $prefix . $bin;
|
|
if ( is_executable( $candidate ) ) {
|
|
return $candidate;
|
|
}
|
|
}
|
|
if ( ! function_exists( 'shell_exec' ) ) {
|
|
return null;
|
|
}
|
|
$out = trim( (string) @shell_exec( 'command -v ' . escapeshellarg( $bin ) . ' 2>/dev/null' ) );
|
|
return $out !== '' && is_executable( $out ) ? $out : null;
|
|
}
|
|
}
|