PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.7.1
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.7.1
2.7.1 2.7.2 2.7.0 2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 All 141 releases
← All changes | media-optimization/class-media-settings.php +54 -3 2.6.17 → 2.7.1 View file →
@@ -37,12 +37,24 @@
37 37 ];
38 38
39 39 /**
40 40 * Get merged settings with defaults.
41 + *
42 + * AVIF is coerced back to WebP when the runtime cannot support it. A value of
43 + * 'avif' can already be persisted (saved on a capable server, then the site
44 + * moved or downgraded), so the coercion has to happen on read as well as on
45 + * save — otherwise stored settings would keep driving AVIF conversion on a
46 + * runtime that mis-measures the result. See supports_avif().
41 47 */
42 48 public static function get_settings(): array {
43 - $saved = get_option(self::OPTION_KEY, []);
44 - return wp_parse_args($saved, self::$defaults);
49 + $saved = get_option(self::OPTION_KEY, []);
50 + $settings = wp_parse_args($saved, self::$defaults);
51 +
52 + if (($settings['conversion_format'] ?? '') === 'avif' && !self::supports_avif()) {
53 + $settings['conversion_format'] = 'webp';
54 + }
55 +
56 + return $settings;
45 57 }
46 58
47 59 /**
48 60 * Get default settings.
@@ -64,9 +76,9 @@
64 76 */
65 77 public static function sanitize(array $input): array {
66 78 return [
67 79 'enable_conversion' => !empty($input['enable_conversion']),
68 - 'conversion_format' => in_array($input['conversion_format'] ?? '', ['webp', 'avif'], true) ? $input['conversion_format'] : 'webp',
80 + 'conversion_format' => self::sanitize_conversion_format($input['conversion_format'] ?? ''),
69 81 'conversion_quality' => max(1, min(100, (int) ($input['conversion_quality'] ?? 82))),
70 82 'conversion_strategy' => in_array($input['conversion_strategy'] ?? '', ['replace', 'alongside'], true) ? $input['conversion_strategy'] : 'alongside',
71 83 'convert_existing_sizes' => !empty($input['convert_existing_sizes']),
72 84 'max_image_dimensions' => max(0, min(10000, (int) ($input['max_image_dimensions'] ?? 2560))),
@@ -79,11 +91,50 @@
79 91 ];
80 92 }
81 93
82 94 /**
95 + * Resolve the target conversion format, refusing AVIF the runtime can't measure.
96 + *
97 + * Anything unrecognised falls back to WebP, as does AVIF on a runtime or GD/
98 + * Imagick build that cannot support it. This keeps an unsupported value from
99 + * ever reaching the database.
100 + */
101 + private static function sanitize_conversion_format(string $format): string {
102 + if ($format === 'avif' && self::supports_avif()) {
103 + return 'avif';
104 + }
105 +
106 + return 'webp';
107 + }
108 +
109 + /**
110 + * Minimum PHP version at which AVIF output can be measured correctly.
111 + *
112 + * getimagesize() only learned to read real AVIF dimensions in PHP 8.2. On
113 + * 8.1 it returns 0x0 for a valid AVIF file (and a truthy [0,0] for a
114 + * truncated header, where 8.2 returns false). Every dimension read in this
115 + * module guards with `$info && $info[0] > 0 && $info[1] > 0`, so on 8.1 the
116 + * guard fails and:
117 + * - the converter cannot sync post-downscale dimensions onto the
118 + * attachment, leaving stale oversized width/height in metadata, and
119 + * - the dimension injector then emits those stale values into the
120 + * rendered HTML, which is worse than emitting none because the browser
121 + * trusts them (a Core Web Vitals / CLS regression).
122 + *
123 + * imageavif() exists from 8.1 onward, so capability detection alone would
124 + * pass and let an 8.1 site select AVIF. WebP is unaffected and is the
125 + * default, so 8.1 sites are held to WebP until the runtime can measure AVIF.
126 + */
127 + const AVIF_MIN_PHP_VERSION_ID = 80200;
128 +
129 + /**
83 130 * Check if the server supports AVIF conversion.
84 131 */
85 132 public static function supports_avif(): bool {
133 + // Encoding is not enough — the result also has to be measurable.
134 + if (PHP_VERSION_ID < self::AVIF_MIN_PHP_VERSION_ID) {
135 + return false;
136 + }
86 137 if (extension_loaded('imagick')) {
87 138 try {
88 139 $formats = \Imagick::queryFormats('AVIF');
89 140 return !empty($formats);