| @@ -1,37 +1,30 @@ | ||
| 1 | 1 | <?php |
| 2 | +declare(strict_types=1); | |
| 3 | + | |
| 2 | 4 | namespace Imagify\Optimization\Process; |
| 3 | 5 | |
| 6 | +use Imagify_Filesystem; | |
| 7 | +use Imagify\Deprecated\Traits\Optimization\Process\AbstractProcessDeprecatedTrait; | |
| 4 | 8 | use Imagify\Job\MediaOptimization; |
| 5 | 9 | use Imagify\Optimization\Data\DataInterface; |
| 6 | 10 | use Imagify\Optimization\File; |
| 11 | +use Imagify\Media\MediaInterface; | |
| 12 | +use WP_Error; | |
| 7 | 13 | |
| 8 | -defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' ); | |
| 9 | - | |
| 10 | 14 | /** |
| 11 | 15 | * Abstract class used to optimize medias. |
| 12 | 16 | * |
| 13 | - * @since 1.9 | |
| 14 | - * @author Grégory Viguier | |
| 17 | + * @since 1.9 | |
| 15 | 18 | */ |
| 16 | 19 | abstract class AbstractProcess implements ProcessInterface { |
| 17 | - use \Imagify\Deprecated\Traits\Optimization\Process\AbstractProcessDeprecatedTrait; | |
| 20 | + use AbstractProcessDeprecatedTrait; | |
| 18 | 21 | |
| 19 | 22 | /** |
| 20 | - * The suffix used in the thumbnail size name. | |
| 21 | - * | |
| 22 | - * @var string | |
| 23 | - * @since 1.9 | |
| 24 | - * @author Grégory Viguier | |
| 25 | - */ | |
| 26 | - const WEBP_SUFFIX = '@imagify-webp'; | |
| 27 | - | |
| 28 | - /** | |
| 29 | 23 | * The suffix used in file name to create a temporary copy of the full size. |
| 30 | 24 | * |
| 31 | - * @var string | |
| 32 | - * @since 1.9 | |
| 33 | - * @author Grégory Viguier | |
| 25 | + * @var string | |
| 26 | + * @since 1.9 | |
| 34 | 27 | */ |
| 35 | 28 | const TMP_SUFFIX = '@imagify-tmp'; |
| 36 | 29 | |
| 37 | 30 | /** |
| @@ -37,11 +30,10 @@ | ||
| 37 | 30 | /** |
| 38 | 31 | * Used for the name of the transient telling if a media is locked. |
| 39 | 32 | * %1$s is the context, %2$s is the media ID. |
| 40 | 33 | * |
| 41 | - * @var string | |
| 42 | - * @since 1.9 | |
| 43 | - * @author Grégory Viguier | |
| 34 | + * @var string | |
| 35 | + * @since 1.9 | |
| 44 | 36 | */ |
| 45 | 37 | const LOCK_NAME = 'imagify_%1$s_%2$s_process_locked'; |
| 46 | 38 | |
| 47 | 39 | /** |
| @@ -46,12 +38,10 @@ | ||
| 46 | 38 | |
| 47 | 39 | /** |
| 48 | 40 | * The data optimization object. |
| 49 | 41 | * |
| 50 | - * @var DataInterface | |
| 51 | - * @since 1.9 | |
| 52 | - * @access protected | |
| 53 | - * @author Grégory Viguier | |
| 42 | + * @var DataInterface | |
| 43 | + * @since 1.9 | |
| 54 | 44 | */ |
| 55 | 45 | protected $data; |
| 56 | 46 | |
| 57 | 47 | /** |
| @@ -56,12 +46,10 @@ | ||
| 56 | 46 | |
| 57 | 47 | /** |
| 58 | 48 | * The optimization data format. |
| 59 | 49 | * |
| 60 | - * @var array | |
| 61 | - * @since 1.9 | |
| 62 | - * @access protected | |
| 63 | - * @author Grégory Viguier | |
| 50 | + * @var array | |
| 51 | + * @since 1.9 | |
| 64 | 52 | */ |
| 65 | 53 | protected $data_format = [ |
| 66 | 54 | 'level' => null, |
| 67 | 55 | 'status' => null, |
| @@ -73,12 +61,10 @@ | ||
| 73 | 61 | |
| 74 | 62 | /** |
| 75 | 63 | * A File instance. |
| 76 | 64 | * |
| 77 | - * @var File | |
| 78 | - * @since 1.9 | |
| 79 | - * @access protected | |
| 80 | - * @author Grégory Viguier | |
| 65 | + * @var File | |
| 66 | + * @since 1.9 | |
| 81 | 67 | */ |
| 82 | 68 | protected $file; |
| 83 | 69 | |
| 84 | 70 | /** |
| @@ -83,12 +69,10 @@ | ||
| 83 | 69 | |
| 84 | 70 | /** |
| 85 | 71 | * Filesystem object. |
| 86 | 72 | * |
| 87 | - * @var Imagify_Filesystem | |
| 88 | - * @since 1.9 | |
| 89 | - * @access protected | |
| 90 | - * @author Grégory Viguier | |
| 73 | + * @var Imagify_Filesystem | |
| 74 | + * @since 1.9 | |
| 91 | 75 | */ |
| 92 | 76 | protected $filesystem; |
| 93 | 77 | |
| 94 | 78 | /** |
| @@ -93,22 +77,36 @@ | ||
| 93 | 77 | |
| 94 | 78 | /** |
| 95 | 79 | * Used to cache the plugin’s options. |
| 96 | 80 | * |
| 97 | - * @var array | |
| 98 | - * @since 1.9 | |
| 99 | - * @access protected | |
| 100 | - * @author Grégory Viguier | |
| 81 | + * @var array | |
| 82 | + * @since 1.9 | |
| 101 | 83 | */ |
| 102 | 84 | protected $options = []; |
| 103 | 85 | |
| 104 | 86 | /** |
| 87 | + * Tells the format we are currently processing | |
| 88 | + * | |
| 89 | + * @var string | |
| 90 | + * @since 2.2 | |
| 91 | + */ | |
| 92 | + protected $format; | |
| 93 | + | |
| 94 | + /** | |
| 95 | + * Array of image extensions processed. | |
| 96 | + * | |
| 97 | + * @var array | |
| 98 | + */ | |
| 99 | + protected $extensions = [ | |
| 100 | + 'webp', | |
| 101 | + 'avif', | |
| 102 | + ]; | |
| 103 | + | |
| 104 | + /** | |
| 105 | 105 | * The constructor. |
| 106 | 106 | * |
| 107 | - * @since 1.9 | |
| 108 | - * @access public | |
| 109 | - * @see self::constructor_accepts() | |
| 110 | - * @author Grégory Viguier | |
| 107 | + * @since 1.9 | |
| 108 | + * @see self::constructor_accepts() | |
| 111 | 109 | * |
| 112 | 110 | * @param mixed $id An ID, or whatever type the constructor accepts. |
| 113 | 111 | */ |
| 114 | 112 | public function __construct( $id ) { |
| @@ -122,18 +120,18 @@ | ||
| 122 | 120 | $this->data = false; |
| 123 | 121 | } |
| 124 | 122 | |
| 125 | 123 | $this->filesystem = \Imagify_Filesystem::get_instance(); |
| 124 | + $this->format = $this->get_current_format(); | |
| 126 | 125 | } |
| 127 | 126 | |
| 128 | 127 | /** |
| 129 | 128 | * Tell if the given entry can be accepted in the constructor. |
| 130 | 129 | * |
| 131 | - * @since 1.9 | |
| 132 | - * @access public | |
| 133 | - * @author Grégory Viguier | |
| 130 | + * @since 1.9 | |
| 134 | 131 | * |
| 135 | - * @param mixed $id Whatever. | |
| 132 | + * @param mixed $id Whatever. | |
| 133 | + * | |
| 136 | 134 | * @return bool |
| 137 | 135 | */ |
| 138 | 136 | public static function constructor_accepts( $id ) { |
| 139 | 137 | if ( $id instanceof DataInterface ) { |
| @@ -148,11 +146,9 @@ | ||
| 148 | 146 | |
| 149 | 147 | /** |
| 150 | 148 | * Get the data instance. |
| 151 | 149 | * |
| 152 | - * @since 1.9 | |
| 153 | - * @access public | |
| 154 | - * @author Grégory Viguier | |
| 150 | + * @since 1.9 | |
| 155 | 151 | * |
| 156 | 152 | * @return DataInterface|false |
| 157 | 153 | */ |
| 158 | 154 | public function get_data() { |
| @@ -161,11 +157,9 @@ | ||
| 161 | 157 | |
| 162 | 158 | /** |
| 163 | 159 | * Get the media instance. |
| 164 | 160 | * |
| 165 | - * @since 1.9 | |
| 166 | - * @access public | |
| 167 | - * @author Grégory Viguier | |
| 161 | + * @since 1.9 | |
| 168 | 162 | * |
| 169 | 163 | * @return MediaInterface|false |
| 170 | 164 | */ |
| 171 | 165 | public function get_media() { |
| @@ -178,11 +172,9 @@ | ||
| 178 | 172 | |
| 179 | 173 | /** |
| 180 | 174 | * Get the File instance of the original file. |
| 181 | 175 | * |
| 182 | - * @since 1.9.8 | |
| 183 | - * @access public | |
| 184 | - * @author Grégory Viguier | |
| 176 | + * @since 1.9.8 | |
| 185 | 177 | * |
| 186 | 178 | * @return File|false |
| 187 | 179 | */ |
| 188 | 180 | public function get_original_file() { |
| @@ -201,11 +193,9 @@ | ||
| 201 | 193 | |
| 202 | 194 | /** |
| 203 | 195 | * Get the File instance of the full size file. |
| 204 | 196 | * |
| 205 | - * @since 1.9.8 | |
| 206 | - * @access public | |
| 207 | - * @author Grégory Viguier | |
| 197 | + * @since 1.9.8 | |
| 208 | 198 | * |
| 209 | 199 | * @return File|false |
| 210 | 200 | */ |
| 211 | 201 | public function get_fullsize_file() { |
| @@ -224,11 +214,9 @@ | ||
| 224 | 214 | |
| 225 | 215 | /** |
| 226 | 216 | * Tell if the current media is valid. |
| 227 | 217 | * |
| 228 | - * @since 1.9 | |
| 229 | - * @access public | |
| 230 | - * @author Grégory Viguier | |
| 218 | + * @since 1.9 | |
| 231 | 219 | * |
| 232 | 220 | * @return bool |
| 233 | 221 | */ |
| 234 | 222 | public function is_valid() { |
| @@ -237,13 +225,11 @@ | ||
| 237 | 225 | |
| 238 | 226 | /** |
| 239 | 227 | * Tell if the current user is allowed to operate Imagify in this context. |
| 240 | 228 | * |
| 241 | - * @since 1.9 | |
| 242 | - * @access public | |
| 243 | - * @author Grégory Viguier | |
| 229 | + * @since 1.9 | |
| 244 | 230 | * |
| 245 | - * @param string $describer Capacity describer. See \Imagify\Context\ContextInterface->get_capacity() for possible values. Can also be a "real" user capacity. | |
| 231 | + * @param string $describer Capacity describer. See \Imagify\Context\ContextInterface->get_capacity() for possible values. Can also be a "real" user capacity. | |
| 246 | 232 | * @return bool |
| 247 | 233 | */ |
| 248 | 234 | public function current_user_can( $describer ) { |
| 249 | 235 | if ( ! $this->is_valid() ) { |
| @@ -254,48 +240,46 @@ | ||
| 254 | 240 | |
| 255 | 241 | return $media->get_context_instance()->current_user_can( $describer, $media->get_id() ); |
| 256 | 242 | } |
| 257 | 243 | |
| 258 | - | |
| 259 | - /** ----------------------------------------------------------------------------------------- */ | |
| 260 | - /** OPTIMIZATION ============================================================================ */ | |
| 261 | - /** ----------------------------------------------------------------------------------------- */ | |
| 262 | - | |
| 263 | 244 | /** |
| 264 | 245 | * Optimize a media files. |
| 265 | 246 | * |
| 266 | - * @since 1.9 | |
| 267 | - * @access public | |
| 268 | - * @author Grégory Viguier | |
| 247 | + * @since 1.9 | |
| 248 | + * @since 2.3.2 Added the $args parameter (e.g. 'bulk', 'priority'). See self::optimize_sizes() | |
| 249 | + * for the full list. 'bulk'/'priority' are extracted before the item is queued, | |
| 250 | + * any other key ends up in the queued item's 'data', forwarded verbatim to the | |
| 251 | + * 'imagify_before_*'/'imagify_after_*' hooks. | |
| 269 | 252 | * |
| 270 | - * @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra). | |
| 271 | - * @param array $args An array of optionnal arguments. | |
| 272 | - * @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure. | |
| 253 | + * @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra). | |
| 254 | + * @param array $args An array of optionnal arguments. | |
| 255 | + * | |
| 256 | + * @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure. | |
| 273 | 257 | */ |
| 274 | 258 | public function optimize( $optimization_level = null, $args = [] ) { |
| 275 | 259 | if ( ! $this->is_valid() ) { |
| 276 | - return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) ); | |
| 260 | + return new WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) ); | |
| 277 | 261 | } |
| 278 | 262 | |
| 279 | 263 | $media = $this->get_media(); |
| 280 | 264 | |
| 281 | 265 | if ( ! $media->is_supported() ) { |
| 282 | - return new \WP_Error( 'media_not_supported', __( 'This media is not supported.', 'imagify' ) ); | |
| 266 | + return new WP_Error( 'media_not_supported', __( 'This media is not supported.', 'imagify' ) ); | |
| 283 | 267 | } |
| 284 | 268 | |
| 285 | 269 | $data = $this->get_data(); |
| 286 | 270 | |
| 287 | 271 | if ( $data->is_optimized() ) { |
| 288 | - return new \WP_Error( 'optimized', __( 'This media has already been optimized by Imagify.', 'imagify' ) ); | |
| 272 | + return new WP_Error( 'optimized', __( 'This media has already been optimized by Imagify.', 'imagify' ) ); | |
| 289 | 273 | } |
| 290 | 274 | |
| 291 | - if ( $data->is_already_optimized() && $this->has_webp() ) { | |
| 292 | - // If already optimized but has WebP, delete WebP versions and optimization data. | |
| 275 | + if ( $data->is_already_optimized() && $this->has_next_gen() ) { | |
| 276 | + // If already optimized but has next-gen, delete next-gen versions and optimization data. | |
| 293 | 277 | $data->delete_optimization_data(); |
| 294 | - $deleted = $this->delete_webp_files(); | |
| 278 | + $deleted = $this->delete_nextgen_files(); | |
| 295 | 279 | |
| 296 | 280 | if ( is_wp_error( $deleted ) ) { |
| 297 | - return new \WP_Error( 'webp_not_deleted', __( 'Previous WebP files could not be deleted.', 'imagify' ) ); | |
| 281 | + return new WP_Error( 'next_gen_not_deleted', __( 'Previous Next-Gen files could not be deleted.', 'imagify' ) ); | |
| 298 | 282 | } |
| 299 | 283 | } |
| 300 | 284 | |
| 301 | 285 | $sizes = $media->get_media_files(); |
| @@ -309,37 +293,40 @@ | ||
| 309 | 293 | |
| 310 | 294 | /** |
| 311 | 295 | * Re-optimize a media files with a different level. |
| 312 | 296 | * |
| 313 | - * @since 1.9 | |
| 314 | - * @access public | |
| 315 | - * @author Grégory Viguier | |
| 297 | + * @since 1.9 | |
| 298 | + * @since 2.3.2 Added the $args parameter (e.g. 'bulk', 'priority'). See self::optimize_sizes() | |
| 299 | + * for the full list. 'bulk'/'priority' are extracted before the item is queued, | |
| 300 | + * any other key ends up in the queued item's 'data', forwarded verbatim to the | |
| 301 | + * 'imagify_before_*'/'imagify_after_*' hooks. | |
| 316 | 302 | * |
| 317 | - * @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra). | |
| 318 | - * @param array $args An array of optionnal arguments. | |
| 319 | - * @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure. | |
| 303 | + * @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra). | |
| 304 | + * @param array $args An array of optionnal arguments. | |
| 305 | + * | |
| 306 | + * @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure. | |
| 320 | 307 | */ |
| 321 | 308 | public function reoptimize( $optimization_level = null, $args = [] ) { |
| 322 | 309 | if ( ! $this->is_valid() ) { |
| 323 | - return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) ); | |
| 310 | + return new WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) ); | |
| 324 | 311 | } |
| 325 | 312 | |
| 326 | 313 | $media = $this->get_media(); |
| 327 | 314 | |
| 328 | 315 | if ( ! $media->is_supported() ) { |
| 329 | - return new \WP_Error( 'media_not_supported', __( 'This media is not supported.', 'imagify' ) ); | |
| 316 | + return new WP_Error( 'media_not_supported', __( 'This media is not supported.', 'imagify' ) ); | |
| 330 | 317 | } |
| 331 | 318 | |
| 332 | 319 | $data = $this->get_data(); |
| 333 | 320 | |
| 334 | 321 | if ( ! $data->get_optimization_status() ) { |
| 335 | - return new \WP_Error( 'not_processed_yet', __( 'This media has not been processed yet.', 'imagify' ) ); | |
| 322 | + return new WP_Error( 'not_processed_yet', __( 'This media has not been processed yet.', 'imagify' ) ); | |
| 336 | 323 | } |
| 337 | 324 | |
| 338 | 325 | $optimization_level = $this->sanitize_optimization_level( $optimization_level ); |
| 339 | 326 | |
| 340 | 327 | if ( $data->get_optimization_level() === $optimization_level ) { |
| 341 | - return new \WP_Error( 'identical_optimization_level', __( 'This media is already optimized with this level.', 'imagify' ) ); | |
| 328 | + return new WP_Error( 'identical_optimization_level', __( 'This media is already optimized with this level.', 'imagify' ) ); | |
| 342 | 329 | } |
| 343 | 330 | |
| 344 | 331 | $this->restore(); |
| 345 | 332 | |
| @@ -354,41 +341,45 @@ | ||
| 354 | 341 | |
| 355 | 342 | /** |
| 356 | 343 | * Optimize several file sizes by pushing tasks into the queue. |
| 357 | 344 | * |
| 358 | - * @since 1.9 | |
| 359 | - * @access public | |
| 360 | - * @see MediaOptimization->task_before() | |
| 361 | - * @see MediaOptimization->task_after() | |
| 362 | - * @author Grégory Viguier | |
| 345 | + * @since 1.9 | |
| 346 | + * @see MediaOptimization->task_before() | |
| 347 | + * @see MediaOptimization->task_after() | |
| 363 | 348 | * |
| 364 | - * @param array $sizes An array of media sizes (strings). Use "full" for the size of the main file. | |
| 365 | - * @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra). | |
| 366 | - * @param array $args { | |
| 367 | - * An array of optionnal arguments. | |
| 349 | + * @since 2.2 | |
| 350 | + * Addition of the image format | |
| 368 | 351 | * |
| 352 | + * @param array $sizes An array of media sizes (strings). Use "full" for the size of the main file. | |
| 353 | + * @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra). | |
| 354 | + * @param array $args { | |
| 355 | + * An array of optionnal arguments. | |
| 356 | + * | |
| 369 | 357 | * @type string $hook_suffix Suffix used to trigger hooks before and after optimization. |
| 358 | + * @type bool $priority Whether this optimization should jump ahead of the queue (e.g. new uploads, manual clicks). Default false. Extracted before queueing: not part of the queued item's 'data'. | |
| 359 | + * @type bool $bulk Whether this optimization was triggered by a bulk run (see Bulk::force_optimize()). Default false. Kept in the queued item's 'data', so it is forwarded verbatim to the 'imagify_before_*'/'imagify_after_*' hook callbacks. | |
| 370 | 360 | * } |
| 371 | - * @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure. | |
| 361 | + * | |
| 362 | + * @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure. | |
| 372 | 363 | */ |
| 373 | 364 | public function optimize_sizes( $sizes, $optimization_level = null, $args = [] ) { |
| 374 | 365 | if ( ! $this->is_valid() ) { |
| 375 | - return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) ); | |
| 366 | + return new WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) ); | |
| 376 | 367 | } |
| 377 | 368 | |
| 378 | 369 | $media = $this->get_media(); |
| 379 | 370 | |
| 380 | 371 | if ( ! $media->is_supported() ) { |
| 381 | - return new \WP_Error( 'media_not_supported', __( 'This media is not supported.', 'imagify' ) ); | |
| 372 | + return new WP_Error( 'media_not_supported', __( 'This media is not supported.', 'imagify' ) ); | |
| 382 | 373 | } |
| 383 | 374 | |
| 384 | 375 | if ( ! $sizes ) { |
| 385 | - return new \WP_Error( 'no_sizes', __( 'No sizes given to be optimized.', 'imagify' ) ); | |
| 376 | + return new WP_Error( 'no_sizes', __( 'No sizes given to be optimized.', 'imagify' ) ); | |
| 386 | 377 | } |
| 387 | 378 | |
| 388 | 379 | if ( empty( $args['locked'] ) ) { |
| 389 | 380 | if ( $this->is_locked() ) { |
| 390 | - return new \WP_Error( 'media_locked', __( 'This media is already being processed.', 'imagify' ) ); | |
| 381 | + return new WP_Error( 'media_locked', __( 'This media is already being processed.', 'imagify' ) ); | |
| 391 | 382 | } |
| 392 | 383 | |
| 393 | 384 | $this->lock(); |
| 394 | 385 | } |
| @@ -393,10 +384,18 @@ | ||
| 393 | 384 | $this->lock(); |
| 394 | 385 | } |
| 395 | 386 | |
| 396 | 387 | if ( $media->is_image() ) { |
| 397 | - if ( $this->get_option( 'convert_to_webp' ) ) { | |
| 398 | - // Add WebP convertion. | |
| 388 | + // Add Next-Gen conversion. | |
| 389 | + $formats = imagify_nextgen_images_formats(); | |
| 390 | + | |
| 391 | + foreach ( $formats as $format ) { | |
| 392 | + if ( 'avif' === $format ) { | |
| 393 | + $format_suffix = static::AVIF_SUFFIX; | |
| 394 | + } elseif ( 'webp' === $format ) { | |
| 395 | + $format_suffix = static::WEBP_SUFFIX; | |
| 396 | + } | |
| 397 | + | |
| 399 | 398 | $files = $media->get_media_files(); |
| 400 | 399 | |
| 401 | 400 | foreach ( $sizes as $size_name ) { |
| 402 | 401 | if ( empty( $files[ $size_name ] ) ) { |
| @@ -401,16 +400,18 @@ | ||
| 401 | 400 | foreach ( $sizes as $size_name ) { |
| 402 | 401 | if ( empty( $files[ $size_name ] ) ) { |
| 403 | 402 | continue; |
| 404 | 403 | } |
| 405 | - if ( 'image/webp' === $files[ $size_name ]['mime-type'] ) { | |
| 404 | + | |
| 405 | + if ( $this->get_mime_type( $format ) === $files[ $size_name ]['mime-type'] ) { | |
| 406 | 406 | continue; |
| 407 | 407 | } |
| 408 | - if ( in_array( $size_name . static::WEBP_SUFFIX, $sizes, true ) ) { | |
| 408 | + | |
| 409 | + if ( in_array( $size_name . $format_suffix, $sizes, true ) ) { | |
| 409 | 410 | continue; |
| 410 | 411 | } |
| 411 | 412 | |
| 412 | - array_unshift( $sizes, $size_name . static::WEBP_SUFFIX ); | |
| 413 | + array_unshift( $sizes, $size_name . $format_suffix ); | |
| 413 | 414 | } |
| 414 | 415 | } |
| 415 | 416 | |
| 416 | 417 | if ( ! $media->get_context_instance()->can_backup() && ! $media->get_backup_path() && ! $this->get_data()->get_size_data( 'full', 'success' ) ) { |
| @@ -415,25 +416,24 @@ | ||
| 415 | 416 | |
| 416 | 417 | if ( ! $media->get_context_instance()->can_backup() && ! $media->get_backup_path() && ! $this->get_data()->get_size_data( 'full', 'success' ) ) { |
| 417 | 418 | /** |
| 418 | 419 | * Backup is NOT activated, and a backup file does NOT exist yet, and the full size is NOT optimized yet. |
| 419 | - * WebP conversion needs a backup file, even a temporary one: we’ll create one. | |
| 420 | + * Next-Gen conversion needs a backup file, even a temporary one: we’ll create one. | |
| 420 | 421 | */ |
| 421 | - $webp = false; | |
| 422 | + $next_gen = false; | |
| 422 | 423 | |
| 423 | 424 | foreach ( $sizes as $size_name ) { |
| 424 | - if ( $this->is_size_webp( $size_name ) ) { | |
| 425 | - $webp = true; | |
| 425 | + if ( $this->is_size_next_gen( $size_name ) ) { | |
| 426 | + $next_gen = true; | |
| 426 | 427 | break; |
| 427 | 428 | } |
| 428 | 429 | } |
| 429 | 430 | |
| 430 | - if ( $webp ) { | |
| 431 | - // We have at least one WebP conversion to do: create a temporary backup. | |
| 431 | + if ( $next_gen ) { | |
| 432 | + // We have at least one next-gen conversion to do: create a temporary backup. | |
| 432 | 433 | $backuped = $this->get_original_file()->backup( $media->get_raw_backup_path() ); |
| 433 | 434 | |
| 434 | 435 | if ( $backuped ) { |
| 435 | - // See \Imagify\Job\MediaOptimization->delete_backup(). | |
| 436 | 436 | $args['delete_backup'] = true; |
| 437 | 437 | } |
| 438 | 438 | } |
| 439 | 439 | } |
| @@ -444,10 +444,9 @@ | ||
| 444 | 444 | |
| 445 | 445 | /** |
| 446 | 446 | * Filter the data sent to the optimization process. |
| 447 | 447 | * |
| 448 | - * @since 1.9 | |
| 449 | - * @author Grégory Viguier | |
| 448 | + * @since 1.9 | |
| 450 | 449 | * |
| 451 | 450 | * @param array $new_args Additional data to send to the optimization process. |
| 452 | 451 | * @param array $args Current data sent to the process. |
| 453 | 452 | * @param ProcessInterface $process The current optimization process. |
| @@ -459,20 +458,32 @@ | ||
| 459 | 458 | if ( $new_args && is_array( $new_args ) ) { |
| 460 | 459 | $args = array_merge( $new_args, $args ); |
| 461 | 460 | } |
| 462 | 461 | |
| 462 | + // 'priority' is a queue-mechanics flag: it must be a sibling key of 'id'/'sizes'/etc., | |
| 463 | + // not nested inside 'data' (which is opaque payload forwarded verbatim to the | |
| 464 | + // 'imagify_before_*'/'imagify_after_*' hook callbacks). Extract it before building 'data'. | |
| 465 | + $is_priority = ! empty( $args['priority'] ); | |
| 466 | + unset( $args['priority'] ); | |
| 467 | + | |
| 463 | 468 | /** |
| 464 | 469 | * Push the item to the queue, save the queue in the DB, empty the queue. |
| 465 | 470 | * A "batch" is then created in the DB with this unique item, it is then free to loop through its steps (files) without another item interfering (each media optimization has its own dedicated batch/queue). |
| 466 | 471 | */ |
| 467 | - MediaOptimization::get_instance()->push_to_queue( [ | |
| 472 | + $item = [ | |
| 468 | 473 | 'id' => $media->get_id(), |
| 469 | 474 | 'sizes' => $sizes, |
| 470 | 475 | 'optimization_level' => $optimization_level, |
| 471 | 476 | 'process_class' => get_class( $this ), |
| 472 | 477 | 'data' => $args, |
| 473 | - ] )->save(); | |
| 478 | + ]; | |
| 474 | 479 | |
| 480 | + if ( $is_priority ) { | |
| 481 | + $item['priority'] = true; | |
| 482 | + } | |
| 483 | + | |
| 484 | + MediaOptimization::get_instance()->push_to_queue( $item )->save(); | |
| 485 | + | |
| 475 | 486 | return true; |
| 476 | 487 | } |
| 477 | 488 | |
| 478 | 489 | /** |
| @@ -477,39 +488,38 @@ | ||
| 477 | 488 | |
| 478 | 489 | /** |
| 479 | 490 | * Optimize one file with Imagify directly. |
| 480 | 491 | * |
| 481 | - * @since 1.9 | |
| 482 | - * @access public | |
| 483 | - * @author Grégory Viguier | |
| 492 | + * @since 1.9 | |
| 484 | 493 | * |
| 485 | - * @param string $size The media size. | |
| 486 | - * @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra). | |
| 487 | - * @return array|\WP_Error Optimized image data. A \WP_Error object on error. | |
| 494 | + * @param string $size The media size. | |
| 495 | + * @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra). | |
| 496 | + * | |
| 497 | + * @return array|WP_Error Optimized image data. A WP_Error object on error. | |
| 488 | 498 | */ |
| 489 | 499 | public function optimize_size( $size, $optimization_level = null ) { |
| 490 | 500 | if ( ! $this->is_valid() ) { // Bail out. |
| 491 | - return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) ); | |
| 501 | + return new WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) ); | |
| 492 | 502 | } |
| 493 | 503 | |
| 494 | 504 | $media = $this->get_media(); |
| 495 | 505 | $sizes = $media->get_media_files(); |
| 496 | 506 | $thumb_size = $size; |
| 497 | - $webp = $this->is_size_webp( $size ); | |
| 507 | + $next_gen = $this->is_size_next_gen( $size ); | |
| 498 | 508 | $path_is_temp = false; |
| 499 | 509 | |
| 500 | - if ( $webp ) { | |
| 510 | + if ( $next_gen ) { | |
| 501 | 511 | // We'll make sure the file is an image later. |
| 502 | - $thumb_size = $webp; // Contains the name of the non-WebP size. | |
| 503 | - $webp = true; | |
| 512 | + $thumb_size = $next_gen; // Contains the name of the non-next-gen size. | |
| 513 | + $next_gen = true; | |
| 504 | 514 | } |
| 505 | 515 | |
| 506 | 516 | if ( empty( $sizes[ $thumb_size ]['path'] ) ) { // Bail out. |
| 507 | 517 | // This size is not in our list. |
| 508 | - return new \WP_Error( | |
| 518 | + return new WP_Error( | |
| 509 | 519 | 'unknown_size', |
| 510 | 520 | sprintf( |
| 511 | - /* translators: %s is a size name. */ | |
| 521 | + /* translators: %s is a size name. */ | |
| 512 | 522 | __( 'The size %s is unknown.', 'imagify' ), |
| 513 | 523 | '<code>' . esc_html( $thumb_size ) . '</code>' |
| 514 | 524 | ) |
| 515 | 525 | ); |
| @@ -514,28 +524,38 @@ | ||
| 514 | 524 | ) |
| 515 | 525 | ); |
| 516 | 526 | } |
| 517 | 527 | |
| 518 | - if ( $this->get_data()->get_size_data( $size, 'success' ) ) { // Bail out. | |
| 519 | - // This size is already optimized with Imagify, and must not be optimized again. | |
| 520 | - if ( $webp ) { | |
| 521 | - return new \WP_Error( | |
| 522 | - 'size_is_successfully_optimized', | |
| 523 | - sprintf( | |
| 528 | + if ( $this->get_data()->get_size_data( $size, 'success' ) ) { | |
| 529 | + /* | |
| 530 | + * The data says this size is already optimized. For a next-gen size, this can be | |
| 531 | + * stale: an API response carrying a `message` (see File::optimize()) could have | |
| 532 | + * been recorded as `success` without any next-gen file ever being written. In that | |
| 533 | + * case, don't bail out: fall through and let the file be (re)generated. | |
| 534 | + */ | |
| 535 | + $next_gen_file_is_missing = $next_gen && $this->is_next_gen_file_missing( $size, $sizes[ $thumb_size ]['path'] ); | |
| 536 | + | |
| 537 | + if ( ! $next_gen_file_is_missing ) { // Bail out. | |
| 538 | + // This size is already optimized with Imagify, and must not be optimized again. | |
| 539 | + if ( $next_gen ) { | |
| 540 | + return new WP_Error( | |
| 541 | + 'size_is_successfully_optimized', | |
| 542 | + sprintf( | |
| 524 | 543 | /* translators: %s is a size name. */ |
| 525 | - __( 'The WebP format for the size %s already exists.', 'imagify' ), | |
| 526 | - '<code>' . esc_html( $thumb_size ) . '</code>' | |
| 527 | - ) | |
| 528 | - ); | |
| 529 | - } else { | |
| 530 | - return new \WP_Error( | |
| 531 | - 'size_is_successfully_optimized', | |
| 532 | - sprintf( | |
| 544 | + __( 'The Next-Gen format for the size %s already exists.', 'imagify' ), | |
| 545 | + '<code>' . esc_html( $thumb_size ) . '</code>' | |
| 546 | + ) | |
| 547 | + ); | |
| 548 | + } else { | |
| 549 | + return new WP_Error( | |
| 550 | + 'size_is_successfully_optimized', | |
| 551 | + sprintf( | |
| 533 | 552 | /* translators: %s is a size name. */ |
| 534 | - __( 'The size %s is already optimized by Imagify.', 'imagify' ), | |
| 535 | - '<code>' . esc_html( $thumb_size ) . '</code>' | |
| 536 | - ) | |
| 537 | - ); | |
| 553 | + __( 'The size %s is already optimized by Imagify.', 'imagify' ), | |
| 554 | + '<code>' . esc_html( $thumb_size ) . '</code>' | |
| 555 | + ) | |
| 556 | + ); | |
| 557 | + } | |
| 538 | 558 | } |
| 539 | 559 | } |
| 540 | 560 | |
| 541 | 561 | /** |
| @@ -544,18 +564,18 @@ | ||
| 544 | 564 | $path = $sizes[ $thumb_size ]['path']; |
| 545 | 565 | |
| 546 | 566 | $optimization_level = $this->sanitize_optimization_level( $optimization_level ); |
| 547 | 567 | |
| 548 | - if ( $webp && $this->get_data()->get_size_data( $thumb_size, 'success' ) ) { | |
| 549 | - // We want a WebP version but the source file is already optimized by Imagify. | |
| 568 | + if ( $next_gen && $this->get_data()->get_size_data( $thumb_size, 'success' ) ) { | |
| 569 | + // We want a next-gen version but the source file is already optimized by Imagify. | |
| 550 | 570 | $result = $this->create_temporary_copy( $thumb_size, $sizes ); |
| 551 | 571 | |
| 552 | 572 | if ( ! $result ) { // Bail out. |
| 553 | - // Could not create a copy of the non-WebP version. | |
| 554 | - $response = new \WP_Error( | |
| 555 | - 'non_webp_copy_failed', | |
| 573 | + // Could not create a copy of the non-next-gen version. | |
| 574 | + $response = new WP_Error( | |
| 575 | + 'non_next_gen_copy_failed', | |
| 556 | 576 | sprintf( |
| 557 | - /* translators: %s is a size name. */ | |
| 577 | + /* translators: %s is a size name. */ | |
| 558 | 578 | __( 'Could not create an unoptimized copy of the size %s.', 'imagify' ), |
| 559 | 579 | '<code>' . esc_html( $thumb_size ) . '</code>' |
| 560 | 580 | ) |
| 561 | 581 | ); |
| @@ -577,18 +597,28 @@ | ||
| 577 | 597 | if ( ! $file->is_supported( $media->get_allowed_mime_types() ) ) { // Bail out. |
| 578 | 598 | // This file type is not supported. |
| 579 | 599 | $extension = $file->get_extension(); |
| 580 | 600 | |
| 581 | - if ( '' === $extension ) { | |
| 582 | - $response = new \WP_Error( | |
| 601 | + if ( ! $extension ) { | |
| 602 | + $response = new WP_Error( | |
| 603 | + 'extension_not_mime', | |
| 604 | + __( 'This file has an extension that does not match a mime type.', 'imagify' ) | |
| 605 | + ); | |
| 606 | + } elseif ( '' === $extension ) { | |
| 607 | + $response = new WP_Error( | |
| 583 | 608 | 'no_extension', |
| 584 | 609 | __( 'With no extension, this file cannot be optimized.', 'imagify' ) |
| 585 | 610 | ); |
| 611 | + } elseif ( ! $extension ) { | |
| 612 | + $response = new WP_Error( | |
| 613 | + 'extension_not_mime', | |
| 614 | + __( 'This file has an extension that does not match a mime type.', 'imagify' ) | |
| 615 | + ); | |
| 586 | 616 | } else { |
| 587 | - $response = new \WP_Error( | |
| 617 | + $response = new WP_Error( | |
| 588 | 618 | 'extension_not_supported', |
| 589 | 619 | sprintf( |
| 590 | - /* translators: %s is a file extension. */ | |
| 620 | + /* translators: %s is a file extension. */ | |
| 591 | 621 | __( '%s cannot be optimized.', 'imagify' ), |
| 592 | 622 | '<code>' . esc_html( strtolower( $extension ) ) . '</code>' |
| 593 | 623 | ) |
| 594 | 624 | ); |
| @@ -602,16 +632,16 @@ | ||
| 602 | 632 | |
| 603 | 633 | return $response; |
| 604 | 634 | } |
| 605 | 635 | |
| 606 | - if ( $webp && ! $file->is_image() ) { // Bail out. | |
| 636 | + if ( $next_gen && ! $file->is_image() ) { // Bail out. | |
| 607 | 637 | if ( $path_is_temp ) { |
| 608 | 638 | $this->filesystem->delete( $path ); |
| 609 | 639 | } |
| 610 | 640 | |
| 611 | - $response = new \WP_Error( | |
| 612 | - 'no_webp', | |
| 613 | - __( 'This file is not an image and cannot be converted to WebP format.', 'imagify' ) | |
| 641 | + $response = new WP_Error( | |
| 642 | + 'no_next_gen', | |
| 643 | + __( 'This file is not an image and cannot be converted to Next-Gen format.', 'imagify' ) | |
| 614 | 644 | ); |
| 615 | 645 | |
| 616 | 646 | $this->update_size_optimization_data( $response, $size, $optimization_level ); |
| 617 | 647 | |
| @@ -621,14 +651,13 @@ | ||
| 621 | 651 | $is_disabled = ! empty( $sizes[ $thumb_size ]['disabled'] ); |
| 622 | 652 | |
| 623 | 653 | /** |
| 624 | 654 | * Fires before optimizing a file. |
| 625 | - * Return a \WP_Error object to prevent the optimization. | |
| 655 | + * Return a WP_Error object to prevent the optimization. | |
| 626 | 656 | * |
| 627 | - * @since 1.9 | |
| 628 | - * @author Grégory Viguier | |
| 657 | + * @since 1.9 | |
| 629 | 658 | * |
| 630 | - * @param null|\WP_Error $response Null by default. Return a \WP_Error object to prevent optimization. | |
| 659 | + * @param null|WP_Error $response Null by default. Return a WP_Error object to prevent optimization. | |
| 631 | 660 | * @param ProcessInterface $process The optimization process instance. |
| 632 | 661 | * @param File $file The file instance. If $webp is true, $file references the non-WebP file. |
| 633 | 662 | * @param string $thumb_size The media size. |
| 634 | 663 | * @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra). |
| @@ -634,40 +663,40 @@ | ||
| 634 | 663 | * @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra). |
| 635 | 664 | * @param bool $webp The image will be converted to WebP. |
| 636 | 665 | * @param bool $is_disabled Tell if this size is disabled from optimization. |
| 637 | 666 | */ |
| 638 | - $response = apply_filters( 'imagify_before_optimize_size', null, $this, $file, $thumb_size, $optimization_level, $webp, $is_disabled ); | |
| 667 | + $response = apply_filters( 'imagify_before_optimize_size', null, $this, $file, $thumb_size, $optimization_level, $next_gen, $is_disabled ); | |
| 639 | 668 | |
| 640 | 669 | if ( ! is_wp_error( $response ) ) { |
| 641 | 670 | if ( $is_disabled ) { |
| 642 | 671 | // This size must not be optimized. |
| 643 | - $response = new \WP_Error( | |
| 672 | + $response = new WP_Error( | |
| 644 | 673 | 'unauthorized_size', |
| 645 | 674 | sprintf( |
| 646 | - /* translators: %s is a size name. */ | |
| 675 | + /* translators: %s is a size name. */ | |
| 647 | 676 | __( 'The size %s is not authorized to be optimized. Update your Imagify settings if you want to optimize it.', 'imagify' ), |
| 648 | 677 | '<code>' . esc_html( $thumb_size ) . '</code>' |
| 649 | 678 | ) |
| 650 | 679 | ); |
| 651 | 680 | } elseif ( ! $this->filesystem->exists( $file->get_path() ) ) { |
| 652 | - $response = new \WP_Error( | |
| 681 | + $response = new WP_Error( | |
| 653 | 682 | 'file_not_exists', |
| 654 | 683 | sprintf( |
| 655 | - /* translators: %s is a file path. */ | |
| 684 | + /* translators: %s is a file path. */ | |
| 656 | 685 | __( 'The file %s does not seem to exist.', 'imagify' ), |
| 657 | 686 | '<code>' . esc_html( $this->filesystem->make_path_relative( $file->get_path() ) ) . '</code>' |
| 658 | 687 | ) |
| 659 | 688 | ); |
| 660 | - } elseif ( $webp && ! $this->can_create_webp_version( $file->get_path() ) ) { | |
| 661 | - $response = new \WP_Error( | |
| 689 | + } elseif ( $next_gen && ! $this->can_create_next_gen_version( $file->get_path() ) ) { | |
| 690 | + $response = new WP_Error( | |
| 662 | 691 | 'is_animated_gif', |
| 663 | - __( 'This file is an animated gif: since Imagify does not support animated WebP, WebP creation for animated gif is disabled.', 'imagify' ) | |
| 692 | + __( 'This file is an animated gif: since Imagify does not support animated WebP/AVIF, WebP/AVIF creation for animated gif is disabled.', 'imagify' ) | |
| 664 | 693 | ); |
| 665 | 694 | } elseif ( ! $this->filesystem->is_writable( $file->get_path() ) ) { |
| 666 | - $response = new \WP_Error( | |
| 695 | + $response = new WP_Error( | |
| 667 | 696 | 'file_not_writable', |
| 668 | 697 | sprintf( |
| 669 | - /* translators: %s is a file path. */ | |
| 698 | + /* translators: %s is a file path. */ | |
| 670 | 699 | __( 'The file %s does not seem to be writable.', 'imagify' ), |
| 671 | 700 | '<code>' . esc_html( $this->filesystem->make_path_relative( $file->get_path() ) ) . '</code>' |
| 672 | 701 | ) |
| 673 | 702 | ); |
| @@ -674,29 +703,52 @@ | ||
| 674 | 703 | } else { |
| 675 | 704 | // Maybe resize the file. |
| 676 | 705 | $response = $this->maybe_resize( $thumb_size, $file ); |
| 677 | 706 | |
| 707 | + $convert = ''; | |
| 708 | + | |
| 709 | + if ( $next_gen ) { | |
| 710 | + if ( strpos( $size, static::AVIF_SUFFIX ) ) { | |
| 711 | + $convert = 'avif'; | |
| 712 | + } elseif ( strpos( $size, static::WEBP_SUFFIX ) ) { | |
| 713 | + $convert = 'webp'; | |
| 714 | + } | |
| 715 | + } | |
| 716 | + | |
| 678 | 717 | if ( ! is_wp_error( $response ) ) { |
| 679 | 718 | // Resizing succeeded: optimize the file. |
| 680 | - $response = $file->optimize( [ | |
| 681 | - 'backup' => ! $response['backuped'] && $this->can_backup( $size ), | |
| 682 | - 'backup_path' => $media->get_raw_backup_path(), | |
| 683 | - 'backup_source' => 'full' === $thumb_size ? $media->get_original_path() : null, | |
| 684 | - 'optimization_level' => $optimization_level, | |
| 685 | - 'convert' => $webp ? 'webp' : '', | |
| 686 | - 'keep_exif' => $this->can_keep_exif( $size ), | |
| 687 | - 'context' => $media->get_context(), | |
| 688 | - 'original_size' => $response['file_size'], | |
| 689 | - ] ); | |
| 719 | + $response = $file->optimize( | |
| 720 | + [ | |
| 721 | + 'backup' => ! $response['backuped'] && $this->can_backup( $size ), | |
| 722 | + 'backup_path' => $media->get_raw_backup_path(), | |
| 723 | + 'backup_source' => 'full' === $thumb_size ? $media->get_original_path() : null, | |
| 724 | + 'optimization_level' => $optimization_level, | |
| 725 | + 'convert' => $convert, | |
| 726 | + 'keep_exif' => true, | |
| 727 | + 'context' => $media->get_context(), | |
| 728 | + 'original_size' => $response['file_size'], | |
| 729 | + ] | |
| 730 | + ); | |
| 690 | 731 | |
| 691 | - $response = $this->compare_webp_file_size( [ | |
| 692 | - 'response' => $response, | |
| 693 | - 'file' => $file, | |
| 694 | - 'is_webp' => $webp, | |
| 695 | - 'non_webp_thumb_size' => $thumb_size, | |
| 696 | - 'non_webp_file_path' => $sizes[ $thumb_size ]['path'], // Don't use $path nor $file->get_path(), it may return the path to a temporary file. | |
| 697 | - 'optimization_level' => $optimization_level, | |
| 698 | - ] ); | |
| 732 | + $response = $this->compare_next_gen_file_size( | |
| 733 | + [ | |
| 734 | + 'response' => $response, | |
| 735 | + 'file' => $file, | |
| 736 | + 'is_next_gen' => $next_gen, | |
| 737 | + 'next_gen_format' => $convert, | |
| 738 | + 'non_next_gen_thumb_size' => $thumb_size, | |
| 739 | + 'non_next_gen_file_path' => $sizes[ $thumb_size ]['path'], // Don't use $path nor $file->get_path(), it may return the path to a temporary file. | |
| 740 | + 'optimization_level' => $optimization_level, | |
| 741 | + ] | |
| 742 | + ); | |
| 743 | + | |
| 744 | + if ( property_exists( $response, 'message' ) ) { | |
| 745 | + $path_is_temp = false; | |
| 746 | + if ( $path !== $sizes[ $thumb_size ]['path'] ) { | |
| 747 | + $this->filesystem->delete( $path ); | |
| 748 | + } | |
| 749 | + $path = $sizes[ $thumb_size ]['path']; | |
| 750 | + } | |
| 699 | 751 | } |
| 700 | 752 | } |
| 701 | 753 | } |
| 702 | 754 | |
| @@ -704,10 +756,9 @@ | ||
| 704 | 756 | |
| 705 | 757 | /** |
| 706 | 758 | * Fires after optimizing a file. |
| 707 | 759 | * |
| 708 | - * @since 1.9 | |
| 709 | - * @author Grégory Viguier | |
| 760 | + * @since 1.9 | |
| 710 | 761 | * |
| 711 | 762 | * @param ProcessInterface $process The optimization process instance. |
| 712 | 763 | * @param File $file The file instance. |
| 713 | 764 | * @param string $thumb_size The media size. |
| @@ -714,9 +765,9 @@ | ||
| 714 | 765 | * @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra). |
| 715 | 766 | * @param bool $webp The image was supposed to be converted to WebP. |
| 716 | 767 | * @param bool $is_disabled Tell if this size is disabled from optimization. |
| 717 | 768 | */ |
| 718 | - do_action( 'imagify_after_optimize_size', $this, $file, $thumb_size, $optimization_level, $webp, $is_disabled ); | |
| 769 | + do_action( 'imagify_after_optimize_size', $this, $file, $thumb_size, $optimization_level, $next_gen, $is_disabled ); | |
| 719 | 770 | |
| 720 | 771 | if ( ! $path_is_temp ) { |
| 721 | 772 | return $data; |
| 722 | 773 | } |
| @@ -736,113 +787,111 @@ | ||
| 736 | 787 | return $data; |
| 737 | 788 | } |
| 738 | 789 | |
| 739 | 790 | /** |
| 740 | - * Compare the file size of a file and its WebP version: if the WebP version is heavier than the non-WebP file, delete it. | |
| 791 | + * Compare the file size of a file and its Next-Gen version: if the Next-Gen version is heavier than the non-next-gen file, delete it. | |
| 741 | 792 | * |
| 742 | - * @since 1.9.4 | |
| 743 | - * @access protected | |
| 744 | - * @author Grégory Viguier | |
| 793 | + * @since 2.2 | |
| 745 | 794 | * |
| 746 | - * @param array $args { | |
| 795 | + * @param array $args { | |
| 747 | 796 | * A list of mandatory arguments. |
| 748 | 797 | * |
| 749 | - * @type \sdtClass|\WP_Error $response Optimized image data. A \WP_Error object on error. | |
| 798 | + * @type \sdtClass|WP_Error $response Optimized image data. A WP_Error object on error. | |
| 750 | 799 | * @type File $file The File instance of the file currently being optimized. |
| 751 | - * @type bool $is_webp Tell if we're requesting a WebP file. | |
| 752 | - * @type string $non_webp_thumb_size Name of the corresponding non-WebP thumbnail size. If we're not creating a WebP file, this corresponds to the current thumbnail size. | |
| 753 | - * @type string $non_webp_file_path Path to the corresponding non-WebP file. If we're not creating a WebP file, this corresponds to the current file path. | |
| 800 | + * @type bool $is_next_gen Tell if we're requesting a next-gen file. | |
| 801 | + * @type string $non_next_gen_thumb_size Name of the corresponding non-next-gen thumbnail size. If we're not creating a Next-Gen file, this corresponds to the current thumbnail size. | |
| 802 | + * @type string $non_next_gen_file_path Path to the corresponding non-next-gen file. If we're not creating a Next-Gen file, this corresponds to the current file path. | |
| 754 | 803 | * @type string $optimization_level The optimization level. |
| 755 | 804 | * } |
| 756 | - * @return \sdtClass|\WP_Error Optimized image data. A \WP_Error object on error. | |
| 805 | + * | |
| 806 | + * @return \sdtClass|WP_Error Optimized image data. A WP_Error object on error. | |
| 757 | 807 | */ |
| 758 | - protected function compare_webp_file_size( $args ) { | |
| 759 | - static $keep_large_webp; | |
| 808 | + protected function compare_next_gen_file_size( $args ) { | |
| 809 | + static $keep_large_next_gen; | |
| 760 | 810 | |
| 761 | - if ( ! isset( $keep_large_webp ) ) { | |
| 811 | + if ( ! isset( $keep_large_next_gen ) ) { | |
| 762 | 812 | /** |
| 763 | - * Allow to not store WebP images that are larger than their non-WebP version. | |
| 813 | + * Allow to not store next-gen images that are larger than their non-next-gen version. | |
| 764 | 814 | * |
| 765 | - * @since 1.9.4 | |
| 766 | - * @author Grégory Viguier | |
| 815 | + * @since 1.9.4 | |
| 767 | 816 | * |
| 768 | - * @param bool $keep_large_webp Set to false if you prefer your visitors over your Pagespeed score. Default value is true. | |
| 817 | + * @param bool $keep_large_next-gen Set to false if you prefer your visitors over your Pagespeed score. Default value is true. | |
| 769 | 818 | */ |
| 770 | - $keep_large_webp = apply_filters( 'imagify_keep_large_webp', true ); | |
| 819 | + $keep_large_next_gen = apply_filters( 'imagify_keep_large_next_gen', true ); | |
| 771 | 820 | } |
| 772 | 821 | |
| 773 | - if ( $keep_large_webp || is_wp_error( $args['response'] ) || ! $args['file']->is_image() ) { | |
| 822 | + if ( $keep_large_next_gen || is_wp_error( $args['response'] ) || ! $args['file']->is_image() ) { | |
| 774 | 823 | return $args['response']; |
| 775 | 824 | } |
| 776 | 825 | |
| 777 | 826 | // Optimization succeeded. |
| 778 | - if ( $args['is_webp'] ) { | |
| 827 | + if ( ! property_exists( $args['response'], 'message' ) && $args['is_next_gen'] ) { | |
| 779 | 828 | /** |
| 780 | - * We just created a WebP version: | |
| 781 | - * Check if it is lighter than the (maybe optimized) non-WebP file. | |
| 829 | + * We just created a next-gen version: | |
| 830 | + * Check if it is lighter than the (maybe optimized) non-next-gen file. | |
| 782 | 831 | */ |
| 783 | - $data = $this->get_data()->get_size_data( $args['non_webp_thumb_size'] ); | |
| 832 | + $data = $this->get_data()->get_size_data( $args['non_next_gen_thumb_size'] ); | |
| 784 | 833 | |
| 785 | 834 | if ( ! $data ) { |
| 786 | - // We haven’t tried to optimize the non-WebP size yet. | |
| 835 | + // We haven’t tried to optimize the non-next-gen size yet. | |
| 787 | 836 | return $args['response']; |
| 788 | 837 | } |
| 789 | 838 | |
| 790 | 839 | if ( ! empty( $data['optimized_size'] ) ) { |
| 791 | - // The non-WebP size is optimized, we know the file size. | |
| 792 | - $non_webp_file_size = $data['optimized_size']; | |
| 840 | + // The non-next-gen size is optimized, we know the file size. | |
| 841 | + $non_next_gen_file_size = $data['optimized_size']; | |
| 793 | 842 | } else { |
| 794 | - // The non-WebP size is "already optimized" or "error": grab the file size directly from the file. | |
| 795 | - $non_webp_file_size = $this->filesystem->size( $args['non_webp_file_path'] ); | |
| 843 | + // The non-next-gen size is "already optimized" or "error": grab the file size directly from the file. | |
| 844 | + $non_next_gen_file_size = $this->filesystem->size( $args['non_next_gen_file_path'] ); | |
| 796 | 845 | } |
| 797 | 846 | |
| 798 | - if ( ! $non_webp_file_size || $non_webp_file_size > $args['response']->new_size ) { | |
| 799 | - // The new WebP file is lighter. | |
| 847 | + if ( ! $non_next_gen_file_size || $non_next_gen_file_size > $args['response']->new_size ) { | |
| 848 | + // The new next-gen file is lighter. | |
| 800 | 849 | return $args['response']; |
| 801 | 850 | } |
| 802 | 851 | |
| 803 | - // The new WebP file is heavier than the non-WebP file: delete it and return an error. | |
| 852 | + // The new next-gen file is heavier than the non-next-gen file: delete it and return an error. | |
| 804 | 853 | $this->filesystem->delete( $args['file']->get_path() ); |
| 805 | 854 | |
| 806 | - return new \WP_Error( | |
| 807 | - 'webp_heavy', | |
| 855 | + return new WP_Error( | |
| 856 | + 'next_gen_heavy', | |
| 808 | 857 | sprintf( |
| 809 | - /* translators: %s is a size name. */ | |
| 810 | - __( 'The WebP version of the size %s is heavier than its non-WebP version.', 'imagify' ), | |
| 811 | - '<code>' . esc_html( $args['non_webp_thumb_size'] ) . '</code>' | |
| 858 | + /* translators: %s is a size name. */ | |
| 859 | + __( 'The Next-Gen version of the size %s is heavier than its non-next-gen version.', 'imagify' ), | |
| 860 | + '<code>' . esc_html( $args['non_next_gen_thumb_size'] ) . '</code>' | |
| 812 | 861 | ) |
| 813 | 862 | ); |
| 814 | 863 | } |
| 815 | 864 | |
| 816 | 865 | /** |
| 817 | - * We just created a non-WebP version: | |
| 818 | - * Check if its WebP version file is lighter than this one. | |
| 866 | + * We just created a non-next-gen version: | |
| 867 | + * Check if its next-gen version file is lighter than this one. | |
| 819 | 868 | */ |
| 820 | - $webp_size = $args['non_webp_thumb_size'] . static::WEBP_SUFFIX; | |
| 821 | - $webp_file_size = $this->get_data()->get_size_data( $webp_size, 'optimized_size' ); | |
| 869 | + $next_gen_size = $args['non_next_gen_thumb_size'] . $args['next_gen_format']; | |
| 870 | + $next_gen_file_size = $this->get_data()->get_size_data( $next_gen_size, 'optimized_size' ); | |
| 822 | 871 | |
| 823 | - if ( ! $webp_file_size || $webp_file_size < $args['response']->new_size ) { | |
| 824 | - // The WebP file is lighter than this one. | |
| 872 | + if ( property_exists( $args['response'], 'message' ) || ! $next_gen_file_size || $next_gen_file_size < $args['response']->new_size ) { | |
| 873 | + // The next-gen file is lighter than this one. | |
| 825 | 874 | return $args['response']; |
| 826 | 875 | } |
| 827 | 876 | |
| 828 | - // The new optimized file is lighter than the WebP file: delete the WebP file and store an error. | |
| 829 | - $webp_path = $args['file']->get_path_to_webp(); | |
| 877 | + // The new optimized file is lighter than the next-gen file: delete the next-gen file and store an error. | |
| 878 | + $next_gen_path = $args['file']->get_path_to_nextgen( $args['next_gen_format'] ); | |
| 830 | 879 | |
| 831 | - if ( $webp_path && $this->filesystem->is_writable( $webp_path ) ) { | |
| 832 | - $this->filesystem->delete( $webp_path ); | |
| 880 | + if ( $next_gen_path && $this->filesystem->is_writable( $next_gen_path ) ) { | |
| 881 | + $this->filesystem->delete( $next_gen_path ); | |
| 833 | 882 | } |
| 834 | 883 | |
| 835 | - $webp_response = new \WP_Error( | |
| 836 | - 'webp_heavy', | |
| 884 | + $next_gen_response = new WP_Error( | |
| 885 | + 'next_gen_heavy', | |
| 837 | 886 | sprintf( |
| 838 | - /* translators: %s is a size name. */ | |
| 839 | - __( 'The WebP version of the size %s is heavier than its non-WebP version.', 'imagify' ), | |
| 840 | - '<code>' . esc_html( $args['non_webp_thumb_size'] ) . '</code>' | |
| 887 | + /* translators: %s is a size name. */ | |
| 888 | + __( 'The Next-Gen version of the size %s is heavier than its non-next-gen version.', 'imagify' ), | |
| 889 | + '<code>' . esc_html( $args['non_next_gen_thumb_size'] ) . '</code>' | |
| 841 | 890 | ) |
| 842 | 891 | ); |
| 843 | 892 | |
| 844 | - $this->update_size_optimization_data( $webp_response, $webp_size, $args['optimization_level'] ); | |
| 893 | + $this->update_size_optimization_data( $next_gen_response, $next_gen_size, $args['optimization_level'] ); | |
| 845 | 894 | |
| 846 | 895 | return $args['response']; |
| 847 | 896 | } |
| 848 | 897 | |
| @@ -848,31 +897,29 @@ | ||
| 848 | 897 | |
| 849 | 898 | /** |
| 850 | 899 | * Restore the media files from the backup file. |
| 851 | 900 | * |
| 852 | - * @since 1.9 | |
| 853 | - * @access public | |
| 854 | - * @author Grégory Viguier | |
| 901 | + * @since 1.9 | |
| 855 | 902 | * |
| 856 | - * @return bool|WP_Error True on success. A \WP_Error instance on failure. | |
| 903 | + * @return bool|WP_Error True on success. A WP_Error instance on failure. | |
| 857 | 904 | */ |
| 858 | 905 | public function restore() { |
| 859 | 906 | if ( ! $this->is_valid() ) { |
| 860 | - return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) ); | |
| 907 | + return new WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) ); | |
| 861 | 908 | } |
| 862 | 909 | |
| 863 | 910 | $media = $this->get_media(); |
| 864 | 911 | |
| 865 | 912 | if ( ! $media->is_supported() ) { |
| 866 | - return new \WP_Error( 'media_not_supported', __( 'This media is not supported.', 'imagify' ) ); | |
| 913 | + return new WP_Error( 'media_not_supported', __( 'This media is not supported.', 'imagify' ) ); | |
| 867 | 914 | } |
| 868 | 915 | |
| 869 | 916 | if ( ! $media->has_backup() ) { |
| 870 | - return new \WP_Error( 'no_backup', __( 'This media has no backup file.', 'imagify' ) ); | |
| 917 | + return new WP_Error( 'no_backup', __( 'This media has no backup file.', 'imagify' ) ); | |
| 871 | 918 | } |
| 872 | 919 | |
| 873 | 920 | if ( $this->is_locked() ) { |
| 874 | - return new \WP_Error( 'media_locked', __( 'This media is already being processed.', 'imagify' ) ); | |
| 921 | + return new WP_Error( 'media_locked', __( 'This media is already being processed.', 'imagify' ) ); | |
| 875 | 922 | } |
| 876 | 923 | |
| 877 | 924 | $this->lock( 'restoring' ); |
| 878 | 925 | |
| @@ -879,11 +926,10 @@ | ||
| 879 | 926 | $backup_path = $media->get_backup_path(); |
| 880 | 927 | $original_path = $media->get_raw_original_path(); |
| 881 | 928 | |
| 882 | 929 | if ( $backup_path === $original_path ) { |
| 883 | - // Uh?! | |
| 884 | 930 | $this->unlock(); |
| 885 | - return new \WP_Error( 'same_path', __( 'Image path and backup path are identical.', 'imagify' ) ); | |
| 931 | + return new WP_Error( 'same_path', __( 'Image path and backup path are identical.', 'imagify' ) ); | |
| 886 | 932 | } |
| 887 | 933 | |
| 888 | 934 | $dest_dir = $this->filesystem->dir_path( $original_path ); |
| 889 | 935 | |
| @@ -894,9 +940,9 @@ | ||
| 894 | 940 | $dest_file_is_writable = ! $this->filesystem->exists( $original_path ) || $this->filesystem->is_writable( $original_path ); |
| 895 | 941 | |
| 896 | 942 | if ( ! $dest_file_is_writable || ! $this->filesystem->is_writable( $dest_dir ) ) { |
| 897 | 943 | $this->unlock(); |
| 898 | - return new \WP_Error( 'destination_not_writable', __( 'The image to replace is not writable.', 'imagify' ) ); | |
| 944 | + return new WP_Error( 'destination_not_writable', __( 'The image to replace is not writable.', 'imagify' ) ); | |
| 899 | 945 | } |
| 900 | 946 | |
| 901 | 947 | // Get some data before doing anything. |
| 902 | 948 | $data = $this->get_data()->get_optimization_data(); |
| @@ -903,14 +949,13 @@ | ||
| 903 | 949 | $files = $media->get_media_files(); |
| 904 | 950 | |
| 905 | 951 | /** |
| 906 | 952 | * Fires before restoring a media. |
| 907 | - * Return a \WP_Error object to prevent the restoration. | |
| 953 | + * Return a WP_Error object to prevent the restoration. | |
| 908 | 954 | * |
| 909 | - * @since 1.9 | |
| 910 | - * @author Grégory Viguier | |
| 955 | + * @since 1.9 | |
| 911 | 956 | * |
| 912 | - * @param null|\WP_Error $response Null by default. Return a \WP_Error object to prevent optimization. | |
| 957 | + * @param null|WP_Error $response Null by default. Return a WP_Error object to prevent optimization. | |
| 913 | 958 | * @param ProcessInterface $process Instance of this process. |
| 914 | 959 | */ |
| 915 | 960 | $response = apply_filters( 'imagify_before_restore_media', null, $this ); |
| 916 | 961 | |
| @@ -919,9 +964,9 @@ | ||
| 919 | 964 | $response = $this->filesystem->copy( $backup_path, $original_path, true ); |
| 920 | 965 | |
| 921 | 966 | if ( ! $response ) { |
| 922 | 967 | // Failure. |
| 923 | - $response = new \WP_Error( 'copy_failed', __( 'The backup file could not be copied over the optimized one.', 'imagify' ) ); | |
| 968 | + $response = new WP_Error( 'copy_failed', __( 'The backup file could not be copied over the optimized one.', 'imagify' ) ); | |
| 924 | 969 | } else { |
| 925 | 970 | // Backup successfully copied. |
| 926 | 971 | $this->filesystem->chmod_file( $original_path ); |
| 927 | 972 | |
| @@ -932,9 +977,9 @@ | ||
| 932 | 977 | // Restore the original dimensions in the database. |
| 933 | 978 | $media->update_dimensions(); |
| 934 | 979 | |
| 935 | 980 | // Delete the WebP version. |
| 936 | - $this->delete_webp_file( $original_path ); | |
| 981 | + $this->delete_nextgen_file( $original_path, true ); | |
| 937 | 982 | |
| 938 | 983 | // Restore the thumbnails. |
| 939 | 984 | $response = $this->restore_thumbnails(); |
| 940 | 985 | } |
| @@ -943,10 +988,9 @@ | ||
| 943 | 988 | |
| 944 | 989 | /** |
| 945 | 990 | * Fires after restoring a media. |
| 946 | 991 | * |
| 947 | - * @since 1.9 | |
| 948 | - * @author Grégory Viguier | |
| 992 | + * @since 1.9 | |
| 949 | 993 | * |
| 950 | 994 | * @param ProcessInterface $process Instance of this process. |
| 951 | 995 | * @param bool|WP_Error $response The result of the operation: true on success, a WP_Error object on failure. |
| 952 | 996 | * @param array $files The list of files, before restoring them. |
| @@ -961,40 +1005,31 @@ | ||
| 961 | 1005 | |
| 962 | 1006 | /** |
| 963 | 1007 | * Restore the thumbnails. |
| 964 | 1008 | * |
| 965 | - * @since 1.9 | |
| 966 | - * @access protected | |
| 967 | - * @author Grégory Viguier | |
| 1009 | + * @since 1.9 | |
| 968 | 1010 | * |
| 969 | - * @return bool|WP_Error True on success. A \WP_Error instance on failure. | |
| 1011 | + * @return bool|WP_Error True on success. A WP_Error instance on failure. | |
| 970 | 1012 | */ |
| 971 | 1013 | protected function restore_thumbnails() { |
| 972 | 1014 | $media = $this->get_media(); |
| 973 | 1015 | |
| 974 | 1016 | /** |
| 975 | - * Delete the WebP versions. | |
| 1017 | + * Delete the next-gen versions. | |
| 976 | 1018 | * If the full size file and the original file are not the same, the full size is considered like a thumbnail. |
| 977 | - * In that case we must also delete the WebP file associated to the full size. | |
| 1019 | + * In that case we must also delete the next-gen file associated to the full size. | |
| 978 | 1020 | */ |
| 979 | - $keep_full_webp = $media->get_raw_original_path() === $media->get_raw_fullsize_path(); | |
| 980 | - $this->delete_webp_files( $keep_full_webp ); | |
| 1021 | + $keep_full_next_gen = $media->get_raw_original_path() === $media->get_raw_fullsize_path(); | |
| 1022 | + $this->delete_nextgen_files( $keep_full_next_gen, true ); | |
| 981 | 1023 | |
| 982 | 1024 | // Generate new thumbnails. |
| 983 | 1025 | return $media->generate_thumbnails(); |
| 984 | 1026 | } |
| 985 | 1027 | |
| 986 | - | |
| 987 | - /** ----------------------------------------------------------------------------------------- */ | |
| 988 | - /** BACKUP FILE ============================================================================= */ | |
| 989 | - /** ----------------------------------------------------------------------------------------- */ | |
| 990 | - | |
| 991 | 1028 | /** |
| 992 | 1029 | * Delete the backup file. |
| 993 | 1030 | * |
| 994 | - * @since 1.9 | |
| 995 | - * @access public | |
| 996 | - * @author Grégory Viguier | |
| 1031 | + * @since 1.9 | |
| 997 | 1032 | */ |
| 998 | 1033 | public function delete_backup() { |
| 999 | 1034 | if ( ! $this->is_valid() ) { |
| 1000 | 1035 | return; |
| @@ -1003,32 +1038,31 @@ | ||
| 1003 | 1038 | $backup_path = $this->get_media()->get_backup_path(); |
| 1004 | 1039 | |
| 1005 | 1040 | if ( $backup_path ) { |
| 1006 | 1041 | $this->filesystem->delete( $backup_path ); |
| 1042 | + | |
| 1043 | + // Check for the -scaled version in the backup. | |
| 1044 | + $scaled_backup_path = preg_replace( '/(\.)([^\.]+)$/', '-scaled.$2', $backup_path ); | |
| 1045 | + if ( $this->filesystem->exists( $scaled_backup_path ) ) { | |
| 1046 | + // Delete the -scaled version from the backup. | |
| 1047 | + $this->filesystem->delete( $scaled_backup_path ); | |
| 1048 | + } | |
| 1007 | 1049 | } |
| 1008 | 1050 | } |
| 1009 | 1051 | |
| 1010 | - | |
| 1011 | - /** ----------------------------------------------------------------------------------------- */ | |
| 1012 | - /** TEMPORARY COPY OF A SIZE FILE =========================================================== */ | |
| 1013 | - /** ----------------------------------------------------------------------------------------- */ | |
| 1014 | - | |
| 1015 | 1052 | /** |
| 1016 | - * If we need to create a WebP version, we must create it from an unoptimized image. | |
| 1017 | - * The full size is always optimized before the WebP version creation, and in some cases it’s the same for the thumbnails. | |
| 1053 | + * Create a temporary copy of a size file. | |
| 1054 | + * | |
| 1055 | + * If we need to create a next-gen version, we must create it from an unoptimized image. | |
| 1056 | + * The full size is always optimized before the next-gen version creation, and in some cases it’s the same for the thumbnails. | |
| 1018 | 1057 | * Then we use the backup file to create temporary files. |
| 1019 | - */ | |
| 1020 | - | |
| 1021 | - /** | |
| 1022 | - * Create a temporary copy of a size file. | |
| 1023 | 1058 | * |
| 1024 | - * @since 1.9 | |
| 1025 | - * @access protected | |
| 1026 | - * @author Grégory Viguier | |
| 1059 | + * @since 1.9 | |
| 1027 | 1060 | * |
| 1028 | - * @param string $size The image size name. | |
| 1029 | - * @param array $sizes A list of thumbnail sizes being optimized. | |
| 1030 | - * @return bool True if the file exists/is created. False on failure. | |
| 1061 | + * @param string $size The image size name. | |
| 1062 | + * @param array $sizes A list of thumbnail sizes being optimized. | |
| 1063 | + * | |
| 1064 | + * @return bool True if the file exists/is created. False on failure. | |
| 1031 | 1065 | */ |
| 1032 | 1066 | protected function create_temporary_copy( $size, $sizes = null ) { |
| 1033 | 1067 | $media = $this->get_media(); |
| 1034 | 1068 | |
| @@ -1079,11 +1113,20 @@ | ||
| 1079 | 1113 | if ( ! $copied ) { |
| 1080 | 1114 | return false; |
| 1081 | 1115 | } |
| 1082 | 1116 | |
| 1117 | + /** | |
| 1118 | + * The backup file may be the original, un-rotated JPEG that WordPress auto-rotated on | |
| 1119 | + * upload (WordPress resets the orientation on the rotated file, but keeps the original | |
| 1120 | + * orientation in the backup). Correct the orientation of this disposable temporary copy | |
| 1121 | + * so that a Next-Gen version (or a thumbnail) generated from it isn't mis-oriented. | |
| 1122 | + * The backup file itself is never touched. | |
| 1123 | + */ | |
| 1124 | + $tmp_file->maybe_correct_exif_orientation(); | |
| 1125 | + | |
| 1083 | 1126 | if ( 'full' === $size ) { |
| 1084 | 1127 | /** |
| 1085 | - * We create a copy of the backup to be able to create a WebP version from it. | |
| 1128 | + * We create a copy of the backup to be able to create a next-gen version from it. | |
| 1086 | 1129 | * That means the optimization process will resize the file if needed, so there is nothing more to do here. |
| 1087 | 1130 | */ |
| 1088 | 1131 | return true; |
| 1089 | 1132 | } |
| @@ -1105,10 +1148,9 @@ | ||
| 1105 | 1148 | if ( ! isset( $size_data['crop'] ) ) { |
| 1106 | 1149 | /** |
| 1107 | 1150 | * In case of a dynamic thumbnail we don’t know if the image must be croped or resized. |
| 1108 | 1151 | * |
| 1109 | - * @since 1.9 | |
| 1110 | - * @author Grégory Viguier | |
| 1152 | + * @since 1.9 | |
| 1111 | 1153 | * |
| 1112 | 1154 | * @param bool $crop True to crop the thumbnail, false to resize. Null by default. |
| 1113 | 1155 | * @param string $size Name of the thumbnail size. |
| 1114 | 1156 | * @param array $size_data Data of the thumbnail being processed. Contains at least 'width', 'height', and 'path'. |
| @@ -1134,9 +1176,8 @@ | ||
| 1134 | 1176 | |
| 1135 | 1177 | $thumb_dimensions = $this->filesystem->get_image_size( $size_data['path'] ); |
| 1136 | 1178 | |
| 1137 | 1179 | if ( ! $thumb_dimensions || ! $thumb_dimensions['width'] || ! $thumb_dimensions['height'] ) { |
| 1138 | - // ( ; Д ; ) | |
| 1139 | 1180 | return false; |
| 1140 | 1181 | } |
| 1141 | 1182 | |
| 1142 | 1183 | // Compare dimensions. |
| @@ -1145,15 +1186,17 @@ | ||
| 1145 | 1186 | $size_data['crop'] = abs( $thumb_dimensions['height'] - $new_height ) > 1; |
| 1146 | 1187 | } |
| 1147 | 1188 | } |
| 1148 | 1189 | |
| 1149 | - $resized = $tmp_file->create_thumbnail( [ | |
| 1150 | - 'path' => $tmp_path, | |
| 1151 | - 'width' => $size_data['width'], | |
| 1152 | - 'height' => $size_data['height'], | |
| 1153 | - 'crop' => $size_data['crop'], | |
| 1154 | - 'adjust_filename' => false, | |
| 1155 | - ] ); | |
| 1190 | + $resized = $tmp_file->create_thumbnail( | |
| 1191 | + [ | |
| 1192 | + 'path' => $tmp_path, | |
| 1193 | + 'width' => $size_data['width'], | |
| 1194 | + 'height' => $size_data['height'], | |
| 1195 | + 'crop' => $size_data['crop'], | |
| 1196 | + 'adjust_filename' => false, | |
| 1197 | + ] | |
| 1198 | + ); | |
| 1156 | 1199 | |
| 1157 | 1200 | if ( is_wp_error( $resized ) ) { |
| 1158 | 1201 | return false; |
| 1159 | 1202 | } |
| @@ -1170,14 +1213,13 @@ | ||
| 1170 | 1213 | |
| 1171 | 1214 | /** |
| 1172 | 1215 | * Get the path to a temporary copy of a size file. |
| 1173 | 1216 | * |
| 1174 | - * @since 1.9 | |
| 1175 | - * @access protected | |
| 1176 | - * @author Grégory Viguier | |
| 1217 | + * @since 1.9 | |
| 1177 | 1218 | * |
| 1178 | - * @param string $size The image size name. | |
| 1179 | - * @param array $sizes A list of thumbnail sizes being optimized. | |
| 1219 | + * @param string $size The image size name. | |
| 1220 | + * @param array $sizes A list of thumbnail sizes being optimized. | |
| 1221 | + * | |
| 1180 | 1222 | * @return string|bool An image path. False on failure. |
| 1181 | 1223 | */ |
| 1182 | 1224 | protected function get_temporary_copy_path( $size, $sizes = null ) { |
| 1183 | 1225 | if ( 'full' === $size ) { |
| @@ -1202,23 +1244,17 @@ | ||
| 1202 | 1244 | |
| 1203 | 1245 | return $info['dir_path'] . $info['file_base'] . static::TMP_SUFFIX . '.' . $info['extension']; |
| 1204 | 1246 | } |
| 1205 | 1247 | |
| 1206 | - | |
| 1207 | - /** ----------------------------------------------------------------------------------------- */ | |
| 1208 | - /** RESIZE FILE ============================================================================= */ | |
| 1209 | - /** ----------------------------------------------------------------------------------------- */ | |
| 1210 | - | |
| 1211 | 1248 | /** |
| 1212 | 1249 | * Maybe resize an image. |
| 1213 | 1250 | * |
| 1214 | - * @since 1.9 | |
| 1215 | - * @access public | |
| 1216 | - * @author Grégory Viguier | |
| 1251 | + * @since 1.9 | |
| 1217 | 1252 | * |
| 1218 | - * @param string $size The size name. | |
| 1219 | - * @param File $file A File instance. | |
| 1220 | - * @return array|WP_Error A \WP_Error instance on failure, an array on success as follow: { | |
| 1253 | + * @param string $size The size name. | |
| 1254 | + * @param File $file A File instance. | |
| 1255 | + * | |
| 1256 | + * @return array|WP_Error A WP_Error instance on failure, an array on success as follow: { | |
| 1221 | 1257 | * @type bool $resized True when the image has been resized. |
| 1222 | 1258 | * @type bool $backuped True when the image has been backuped. |
| 1223 | 1259 | * @type int $file_size The file size in bytes. |
| 1224 | 1260 | * } |
| @@ -1232,22 +1268,16 @@ | ||
| 1232 | 1268 | 'file_size' => 0, |
| 1233 | 1269 | ]; |
| 1234 | 1270 | } |
| 1235 | 1271 | |
| 1236 | - $media = $this->get_media(); | |
| 1237 | - $dimensions = $media->get_dimensions(); | |
| 1272 | + $dimensions = $file->get_dimensions(); | |
| 1238 | 1273 | |
| 1239 | 1274 | if ( ! $dimensions['width'] ) { |
| 1240 | - // The dimensions don't seem to be in the database anymore: try to get them directly from the file. | |
| 1241 | - $dimensions = $file->get_dimensions(); | |
| 1242 | - } | |
| 1243 | - | |
| 1244 | - if ( ! $dimensions['width'] ) { | |
| 1245 | 1275 | // Could not get the image dimensions. |
| 1246 | - return new \WP_Error( | |
| 1276 | + return new WP_Error( | |
| 1247 | 1277 | 'no_dimensions', |
| 1248 | 1278 | sprintf( |
| 1249 | - /* translators: %s is an error message. */ | |
| 1279 | + /* translators: %s is an error message. */ | |
| 1250 | 1280 | __( 'Resizing failed: %s', 'imagify' ), |
| 1251 | 1281 | __( 'Imagify could not get the image dimensions.', 'imagify' ) |
| 1252 | 1282 | ) |
| 1253 | 1283 | ); |
| @@ -1252,8 +1282,9 @@ | ||
| 1252 | 1282 | ) |
| 1253 | 1283 | ); |
| 1254 | 1284 | } |
| 1255 | 1285 | |
| 1286 | + $media = $this->get_media(); | |
| 1256 | 1287 | $resize_width = $media->get_context_instance()->get_resizing_threshold(); |
| 1257 | 1288 | |
| 1258 | 1289 | if ( $resize_width >= $dimensions['width'] ) { |
| 1259 | 1290 | // No need to resize. |
| @@ -1267,12 +1298,12 @@ | ||
| 1267 | 1298 | $resized_path = $file->resize( $dimensions, $resize_width ); |
| 1268 | 1299 | |
| 1269 | 1300 | if ( is_wp_error( $resized_path ) ) { |
| 1270 | 1301 | // The resizement failed. |
| 1271 | - return new \WP_Error( | |
| 1302 | + return new WP_Error( | |
| 1272 | 1303 | 'resize_failure', |
| 1273 | 1304 | sprintf( |
| 1274 | - /* translators: %s is an error message. */ | |
| 1305 | + /* translators: %s is an error message. */ | |
| 1275 | 1306 | __( 'Resizing failed: %s', 'imagify' ), |
| 1276 | 1307 | $resized_path->get_error_message() |
| 1277 | 1308 | ) |
| 1278 | 1309 | ); |
| @@ -1283,12 +1314,12 @@ | ||
| 1283 | 1314 | $backuped = $file->backup( $media->get_raw_backup_path(), $source ); |
| 1284 | 1315 | |
| 1285 | 1316 | if ( is_wp_error( $backuped ) ) { |
| 1286 | 1317 | // The backup failed. |
| 1287 | - return new \WP_Error( | |
| 1318 | + return new WP_Error( | |
| 1288 | 1319 | 'backup_failure', |
| 1289 | 1320 | sprintf( |
| 1290 | - /* translators: %s is an error message. */ | |
| 1321 | + /* translators: %s is an error message. */ | |
| 1291 | 1322 | __( 'Backup failed: %s', 'imagify' ), |
| 1292 | 1323 | $backuped->get_error_message() |
| 1293 | 1324 | ) |
| 1294 | 1325 | ); |
| @@ -1301,9 +1332,9 @@ | ||
| 1301 | 1332 | $resized = $this->filesystem->move( $resized_path, $file->get_path(), true ); |
| 1302 | 1333 | |
| 1303 | 1334 | if ( ! $resized ) { |
| 1304 | 1335 | // The resizement failed. |
| 1305 | - return new \WP_Error( | |
| 1336 | + return new WP_Error( | |
| 1306 | 1337 | 'resize_move_failure', |
| 1307 | 1338 | __( 'The image could not be replaced by the resized one.', 'imagify' ) |
| 1308 | 1339 | ); |
| 1309 | 1340 | } |
| @@ -1320,14 +1351,13 @@ | ||
| 1320 | 1351 | |
| 1321 | 1352 | /** |
| 1322 | 1353 | * Tell if a size should be resized. |
| 1323 | 1354 | * |
| 1324 | - * @since 1.9 | |
| 1325 | - * @access protected | |
| 1326 | - * @author Grégory Viguier | |
| 1355 | + * @since 1.9 | |
| 1327 | 1356 | * |
| 1328 | - * @param string $size The size name. | |
| 1329 | - * @param File $file A File instance. | |
| 1357 | + * @param string $size The size name. | |
| 1358 | + * @param File $file A File instance. | |
| 1359 | + * | |
| 1330 | 1360 | * @return bool |
| 1331 | 1361 | */ |
| 1332 | 1362 | protected function can_resize( $size, $file ) { |
| 1333 | 1363 | if ( ! $this->is_valid() ) { |
| @@ -1333,10 +1363,18 @@ | ||
| 1333 | 1363 | if ( ! $this->is_valid() ) { |
| 1334 | 1364 | return false; |
| 1335 | 1365 | } |
| 1336 | 1366 | |
| 1337 | - if ( 'full' !== $size && 'full' . static::WEBP_SUFFIX !== $size ) { | |
| 1338 | - // We resize only the main file and its WebP version. | |
| 1367 | + if ( | |
| 1368 | + 'full' !== $size | |
| 1369 | + && | |
| 1370 | + ( | |
| 1371 | + 'full' . static::WEBP_SUFFIX !== $size | |
| 1372 | + || | |
| 1373 | + 'full' . static::AVIF_SUFFIX !== $size | |
| 1374 | + ) | |
| 1375 | + ) { | |
| 1376 | + // We resize only the main file and its next-gen version. | |
| 1339 | 1377 | return false; |
| 1340 | 1378 | } |
| 1341 | 1379 | |
| 1342 | 1380 | if ( ! $file->is_image() ) { |
| @@ -1348,13 +1386,12 @@ | ||
| 1348 | 1386 | |
| 1349 | 1387 | /** |
| 1350 | 1388 | * Tell if a size should be backuped. |
| 1351 | 1389 | * |
| 1352 | - * @since 1.9 | |
| 1353 | - * @access protected | |
| 1354 | - * @author Grégory Viguier | |
| 1390 | + * @since 1.9 | |
| 1355 | 1391 | * |
| 1356 | - * @param string $size The size name. | |
| 1392 | + * @param string $size The size name. | |
| 1393 | + * | |
| 1357 | 1394 | * @return bool |
| 1358 | 1395 | */ |
| 1359 | 1396 | protected function can_backup( $size ) { |
| 1360 | 1397 | if ( ! $this->is_valid() ) { |
| @@ -1369,112 +1406,41 @@ | ||
| 1369 | 1406 | return $this->get_media()->get_context_instance()->can_backup(); |
| 1370 | 1407 | } |
| 1371 | 1408 | |
| 1372 | 1409 | /** |
| 1373 | - * Tell if a size should keep exif. | |
| 1410 | + * Get mime type | |
| 1374 | 1411 | * |
| 1375 | - * @since 1.9 | |
| 1376 | - * @access protected | |
| 1377 | - * @author Grégory Viguier | |
| 1378 | - * | |
| 1379 | - * @param string $size The size name. | |
| 1380 | - * @return bool | |
| 1412 | + * @param string $format nextgen image format. | |
| 1381 | 1413 | */ |
| 1382 | - protected function can_keep_exif( $size ) { | |
| 1383 | - if ( ! $this->is_valid() ) { | |
| 1384 | - return false; | |
| 1385 | - } | |
| 1386 | - | |
| 1387 | - if ( 'full' !== $size && 'full' . static::WEBP_SUFFIX !== $size ) { | |
| 1388 | - // We keep exif only on the main file and its WebP version. | |
| 1389 | - return false; | |
| 1390 | - } | |
| 1391 | - | |
| 1392 | - return $this->get_media()->get_context_instance()->can_keep_exif(); | |
| 1393 | - } | |
| 1394 | - | |
| 1395 | - | |
| 1396 | - /** ----------------------------------------------------------------------------------------- */ | |
| 1397 | - /** WEBP ==================================================================================== */ | |
| 1398 | - /** ----------------------------------------------------------------------------------------- */ | |
| 1399 | - | |
| 1400 | - /** | |
| 1401 | - * Generate WebP images if they are missing. | |
| 1402 | - * | |
| 1403 | - * @since 1.9 | |
| 1404 | - * @access public | |
| 1405 | - * @author Grégory Viguier | |
| 1406 | - * | |
| 1407 | - * @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure. | |
| 1408 | - */ | |
| 1409 | - public function generate_webp_versions() { | |
| 1410 | - if ( ! $this->is_valid() ) { | |
| 1411 | - return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) ); | |
| 1412 | - } | |
| 1413 | - | |
| 1414 | - $media = $this->get_media(); | |
| 1415 | - | |
| 1416 | - if ( ! $media->is_image() ) { | |
| 1417 | - return new \WP_Error( 'no_webp', __( 'This media is not an image and cannot be converted to WebP format.', 'imagify' ) ); | |
| 1418 | - } | |
| 1419 | - | |
| 1420 | - if ( ! $media->has_backup() ) { | |
| 1421 | - return new \WP_Error( 'no_backup', __( 'This media has no backup file.', 'imagify' ) ); | |
| 1422 | - } | |
| 1423 | - | |
| 1424 | - $data = $this->get_data(); | |
| 1425 | - | |
| 1426 | - if ( ! $data->is_optimized() && ! $data->is_already_optimized() ) { | |
| 1427 | - return new \WP_Error( 'not_optimized', __( 'This media has not been optimized by Imagify yet.', 'imagify' ) ); | |
| 1428 | - } | |
| 1429 | - | |
| 1430 | - if ( $this->has_webp() ) { | |
| 1431 | - return new \WP_Error( 'has_webp', __( 'This media already has WebP versions.', 'imagify' ) ); | |
| 1432 | - } | |
| 1433 | - | |
| 1434 | - $files = $media->get_media_files(); | |
| 1435 | - $sizes = []; | |
| 1436 | - $args = [ | |
| 1437 | - 'hook_suffix' => 'generate_webp_versions', | |
| 1414 | + private function get_mime_type( $format ) { | |
| 1415 | + $mime_types = [ | |
| 1416 | + 'avif' => 'image/avif', | |
| 1417 | + 'webp' => 'image/webp', | |
| 1438 | 1418 | ]; |
| 1439 | 1419 | |
| 1440 | - foreach ( $files as $size_name => $file ) { | |
| 1441 | - if ( 'image/webp' !== $files[ $size_name ]['mime-type'] ) { | |
| 1442 | - array_unshift( $sizes, $size_name . static::WEBP_SUFFIX ); | |
| 1443 | - } | |
| 1444 | - } | |
| 1445 | - | |
| 1446 | - if ( ! $sizes ) { | |
| 1447 | - return new \WP_Error( 'no_sizes', __( 'This media does not have files that can be converted to WebP format.', 'imagify' ) ); | |
| 1448 | - } | |
| 1449 | - | |
| 1450 | - $optimization_level = $data->get_optimization_level(); | |
| 1451 | - | |
| 1452 | - // Optimize. | |
| 1453 | - return $this->optimize_sizes( $sizes, $optimization_level, $args ); | |
| 1420 | + return isset( $mime_types[ $format ] ) ? $mime_types[ $format ] : false; | |
| 1454 | 1421 | } |
| 1455 | 1422 | |
| 1456 | 1423 | /** |
| 1457 | - * Delete the WebP images. | |
| 1424 | + * Delete the next gen format images. | |
| 1458 | 1425 | * This doesn't delete the related optimization data. |
| 1459 | 1426 | * |
| 1460 | - * @since 1.9 | |
| 1461 | - * @since 1.9.6 Return WP_Error or true. | |
| 1462 | - * @access public | |
| 1463 | - * @author Grégory Viguier | |
| 1427 | + * @since 2.2 | |
| 1464 | 1428 | * |
| 1465 | - * @param bool $keep_full Set to true to keep the full size. | |
| 1466 | - * @return bool|\WP_Error True on success. A \WP_Error object on failure. | |
| 1429 | + * @param bool $keep_full Set to true to keep the full size. | |
| 1430 | + * @param bool $all_next_gen True: will delete every next-gen format. False: will delete only the current enabled format. | |
| 1431 | + * | |
| 1432 | + * @return bool|WP_Error True on success. A WP_Error object on failure. | |
| 1467 | 1433 | */ |
| 1468 | - public function delete_webp_files( $keep_full = false ) { | |
| 1434 | + public function delete_nextgen_files( $keep_full = false, $all_next_gen = false ) { | |
| 1469 | 1435 | if ( ! $this->is_valid() ) { |
| 1470 | - return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) ); | |
| 1436 | + return new WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) ); | |
| 1471 | 1437 | } |
| 1472 | 1438 | |
| 1473 | 1439 | $media = $this->get_media(); |
| 1474 | 1440 | |
| 1475 | 1441 | if ( ! $media->is_image() ) { |
| 1476 | - return new \WP_Error( 'media_not_an_image', __( 'This media is not an image.', 'imagify' ) ); | |
| 1442 | + return new WP_Error( 'media_not_an_image', __( 'This media is not an image.', 'imagify' ) ); | |
| 1477 | 1443 | } |
| 1478 | 1444 | |
| 1479 | 1445 | $files = $media->get_media_files(); |
| 1480 | 1446 | |
| @@ -1489,9 +1455,9 @@ | ||
| 1489 | 1455 | $error_count = 0; |
| 1490 | 1456 | |
| 1491 | 1457 | foreach ( $files as $file ) { |
| 1492 | 1458 | if ( 0 === strpos( $file['mime-type'], 'image/' ) ) { |
| 1493 | - $deleted = $this->delete_webp_file( $file['path'] ); | |
| 1459 | + $deleted = $this->delete_nextgen_file( $file['path'], $all_next_gen ); | |
| 1494 | 1460 | |
| 1495 | 1461 | if ( is_wp_error( $deleted ) ) { |
| 1496 | 1462 | ++$error_count; |
| 1497 | 1463 | } |
| @@ -1498,12 +1464,12 @@ | ||
| 1498 | 1464 | } |
| 1499 | 1465 | } |
| 1500 | 1466 | |
| 1501 | 1467 | if ( $error_count ) { |
| 1502 | - return new \WP_Error( | |
| 1468 | + return new WP_Error( | |
| 1503 | 1469 | 'files_not_deleted', |
| 1504 | 1470 | sprintf( |
| 1505 | - /* translators: %s is a formatted number, don’t use %d. */ | |
| 1471 | + /* translators: %s is a formatted number, don’t use %d. */ | |
| 1506 | 1472 | _n( '%s file could not be deleted.', '%s files could not be deleted.', $error_count, 'imagify' ), |
| 1507 | 1473 | number_format_i18n( $error_count ) |
| 1508 | 1474 | ) |
| 1509 | 1475 | ); |
| @@ -1512,66 +1478,88 @@ | ||
| 1512 | 1478 | return true; |
| 1513 | 1479 | } |
| 1514 | 1480 | |
| 1515 | 1481 | /** |
| 1516 | - * Delete a WebP image, given its non-WebP version's path. | |
| 1482 | + * Delete a next gen format image, given its non-next-gen version's path. | |
| 1517 | 1483 | * This doesn't delete the related optimization data. |
| 1518 | 1484 | * |
| 1519 | - * @since 1.9 | |
| 1520 | - * @since 1.9.6 Return WP_Error or true. | |
| 1521 | - * @access protected | |
| 1522 | - * @author Grégory Viguier | |
| 1485 | + * @since 2.2 | |
| 1523 | 1486 | * |
| 1524 | - * @param string $file_path Path to the non-WebP file. | |
| 1525 | - * @return bool|\WP_Error True on success. A \WP_Error object on failure. | |
| 1487 | + * @param string $file_path Path to the non-next-gen file. | |
| 1488 | + * @param bool $all_next_gen True: will delete every next-gen format. False: will delete only the current enabled format. | |
| 1489 | + * | |
| 1490 | + * @return void|WP_Error A \WP_Error object on failure. | |
| 1526 | 1491 | */ |
| 1527 | - protected function delete_webp_file( $file_path ) { | |
| 1492 | + protected function delete_nextgen_file( $file_path, $all_next_gen = false ) { | |
| 1528 | 1493 | if ( ! $file_path ) { |
| 1529 | - return new \WP_Error( 'no_path', __( 'Path to non-WebP file not provided.', 'imagify' ) ); | |
| 1494 | + return new WP_Error( 'no_path', __( 'Path to non-next-gen file not provided.', 'imagify' ) ); | |
| 1530 | 1495 | } |
| 1531 | 1496 | |
| 1532 | - $webp_file = new File( $file_path ); | |
| 1533 | - $webp_path = $webp_file->get_path_to_webp(); | |
| 1497 | + $next_gen_file = new File( $file_path ); | |
| 1498 | + $formats = $this->extensions; | |
| 1534 | 1499 | |
| 1535 | - if ( ! $webp_path ) { | |
| 1536 | - return new \WP_Error( 'no_webp_path', __( 'Could not get the path to the WebP file.', 'imagify' ) ); | |
| 1500 | + if ( ! $all_next_gen ) { | |
| 1501 | + $formats = imagify_nextgen_images_formats(); | |
| 1537 | 1502 | } |
| 1503 | + // Delete next-gen images. | |
| 1504 | + foreach ( $formats as $extension ) { | |
| 1505 | + $path = $next_gen_file->get_path_to_nextgen( $extension ); | |
| 1538 | 1506 | |
| 1539 | - if ( ! $this->filesystem->exists( $webp_path ) ) { | |
| 1507 | + if ( ! $path ) { | |
| 1508 | + continue; | |
| 1509 | + } | |
| 1510 | + | |
| 1511 | + $this->delete_file( $path ); | |
| 1512 | + } | |
| 1513 | + } | |
| 1514 | + | |
| 1515 | + /** | |
| 1516 | + * Delete a next gen format image, given its non-next-gen version's path. | |
| 1517 | + * | |
| 1518 | + * @param string $next_gen_path Path to the non-next-gen file. | |
| 1519 | + * | |
| 1520 | + * @return bool|WP_Error True on success. A WP_Error object on failure. | |
| 1521 | + */ | |
| 1522 | + protected function delete_file( string $next_gen_path ) { | |
| 1523 | + if ( empty( $next_gen_path ) ) { | |
| 1524 | + return new WP_Error( 'no_$next_gen_path', __( 'Could not get the path to the Next-Gen format file.', 'imagify' ) ); | |
| 1525 | + } | |
| 1526 | + | |
| 1527 | + if ( ! $this->filesystem->exists( $next_gen_path ) ) { | |
| 1540 | 1528 | return true; |
| 1541 | 1529 | } |
| 1542 | 1530 | |
| 1543 | - if ( ! $this->filesystem->is_writable( $webp_path ) ) { | |
| 1544 | - return new \WP_Error( | |
| 1531 | + if ( ! $this->filesystem->is_writable( $next_gen_path ) ) { | |
| 1532 | + return new WP_Error( | |
| 1545 | 1533 | 'file_not_writable', |
| 1546 | 1534 | sprintf( |
| 1547 | - /* translators: %s is a file path. */ | |
| 1535 | + /* translators: %s is a file path. */ | |
| 1548 | 1536 | __( 'The file %s does not seem to be writable.', 'imagify' ), |
| 1549 | - '<code>' . esc_html( $this->filesystem->make_path_relative( $webp_path ) ) . '</code>' | |
| 1537 | + '<code>' . esc_html( $this->filesystem->make_path_relative( $next_gen_path ) ) . '</code>' | |
| 1550 | 1538 | ) |
| 1551 | 1539 | ); |
| 1552 | 1540 | } |
| 1553 | 1541 | |
| 1554 | - if ( ! $this->filesystem->is_file( $webp_path ) ) { | |
| 1555 | - return new \WP_Error( | |
| 1542 | + if ( ! $this->filesystem->is_file( $next_gen_path ) ) { | |
| 1543 | + return new WP_Error( | |
| 1556 | 1544 | 'not_a_file', |
| 1557 | 1545 | sprintf( |
| 1558 | - /* translators: %s is a file path. */ | |
| 1546 | + /* translators: %s is a file path. */ | |
| 1559 | 1547 | __( 'This does not seem to be a file: %s.', 'imagify' ), |
| 1560 | - '<code>' . esc_html( $this->filesystem->make_path_relative( $webp_path ) ) . '</code>' | |
| 1548 | + '<code>' . esc_html( $this->filesystem->make_path_relative( $next_gen_path ) ) . '</code>' | |
| 1561 | 1549 | ) |
| 1562 | 1550 | ); |
| 1563 | 1551 | } |
| 1564 | 1552 | |
| 1565 | - $deleted = $this->filesystem->delete( $webp_path, false, 'f' ); | |
| 1553 | + $deleted = $this->filesystem->delete( $next_gen_path, false, 'f' ); | |
| 1566 | 1554 | |
| 1567 | 1555 | if ( ! $deleted ) { |
| 1568 | - return new \WP_Error( | |
| 1556 | + return new WP_Error( | |
| 1569 | 1557 | 'file_not_deleted', |
| 1570 | 1558 | sprintf( |
| 1571 | - /* translators: %s is a file path. */ | |
| 1559 | + /* translators: %s is a file path. */ | |
| 1572 | 1560 | __( 'The file %s could not be deleted.', 'imagify' ), |
| 1573 | - '<code>' . esc_html( $this->filesystem->make_path_relative( $webp_path ) ) . '</code>' | |
| 1561 | + '<code>' . esc_html( $this->filesystem->make_path_relative( $next_gen_path ) ) . '</code>' | |
| 1574 | 1562 | ) |
| 1575 | 1563 | ); |
| 1576 | 1564 | } |
| 1577 | 1565 | |
| @@ -1578,26 +1566,37 @@ | ||
| 1578 | 1566 | return true; |
| 1579 | 1567 | } |
| 1580 | 1568 | |
| 1581 | 1569 | /** |
| 1582 | - * Tell if a thumbnail size is an "Imagify WebP" size. | |
| 1570 | + * Gives the next-gen image format we are processing. | |
| 1583 | 1571 | * |
| 1584 | - * @since 1.9 | |
| 1585 | - * @access public | |
| 1586 | - * @author Grégory Viguier | |
| 1572 | + * @return string Current format we are targeting. | |
| 1573 | + */ | |
| 1574 | + public function get_current_format() { | |
| 1575 | + $format = get_imagify_option( 'optimization_format' ); | |
| 1576 | + | |
| 1577 | + return ( 'avif' === $format ) ? static::AVIF_SUFFIX : static::WEBP_SUFFIX; | |
| 1578 | + } | |
| 1579 | + | |
| 1580 | + /** | |
| 1581 | + * Tell if a thumbnail size is an "Imagify Next-Gen" size. | |
| 1587 | 1582 | * |
| 1588 | - * @param string $size_name The size name. | |
| 1589 | - * @return string|bool The unsuffixed name of the size if WebP. False if not WebP. | |
| 1583 | + * @since 1.9 | |
| 1584 | + * @since 2.2 addition of the format parameter. | |
| 1585 | + * | |
| 1586 | + * @param string $size_name The size name. | |
| 1587 | + * | |
| 1588 | + * @return string|bool The unsuffixed name of the size if next-gen. False if not next-gen. | |
| 1590 | 1589 | */ |
| 1591 | - public function is_size_webp( $size_name ) { | |
| 1592 | - static $suffix; | |
| 1590 | + public function is_size_next_gen( $size_name ) { | |
| 1591 | + $formats = imagify_nextgen_images_formats(); | |
| 1593 | 1592 | |
| 1594 | - if ( ! isset( $suffix ) ) { | |
| 1595 | - $suffix = preg_quote( static::WEBP_SUFFIX, '/' ); | |
| 1596 | - } | |
| 1593 | + foreach ( $formats as $format ) { | |
| 1594 | + $suffix = preg_quote( $this->get_suffix_from_format( $format ), '/' ); | |
| 1597 | 1595 | |
| 1598 | - if ( preg_match( '/^(?<size>.+)' . $suffix . '$/', $size_name, $matches ) ) { | |
| 1599 | - return $matches['size']; | |
| 1596 | + if ( preg_match( '/^(?<size>.+)' . $suffix . '$/', (string) $size_name, $matches ) ) { | |
| 1597 | + return $matches['size']; | |
| 1598 | + } | |
| 1600 | 1599 | } |
| 1601 | 1600 | |
| 1602 | 1601 | return false; |
| 1603 | 1602 | } |
| @@ -1602,17 +1601,46 @@ | ||
| 1602 | 1601 | return false; |
| 1603 | 1602 | } |
| 1604 | 1603 | |
| 1605 | 1604 | /** |
| 1606 | - * Tell if the media has WebP versions. | |
| 1605 | + * Tell if the next-gen file for a size is missing from disk. | |
| 1607 | 1606 | * |
| 1608 | - * @since 1.9 | |
| 1609 | - * @access public | |
| 1610 | - * @author Grégory Viguier | |
| 1607 | + * @since 2.3.2 | |
| 1611 | 1608 | * |
| 1609 | + * @param string $size The (next-gen) size name, e.g. "thumbnail@imagify-avif". | |
| 1610 | + * @param string $original_path Path to the non-next-gen version of that size. | |
| 1612 | 1611 | * @return bool |
| 1613 | 1612 | */ |
| 1614 | - public function has_webp() { | |
| 1613 | + protected function is_next_gen_file_missing( string $size, string $original_path ): bool { | |
| 1614 | + $format = strpos( $size, static::AVIF_SUFFIX ) ? 'avif' : 'webp'; | |
| 1615 | + $next_gen_path = imagify_path_to_nextgen( $original_path, $format ); | |
| 1616 | + | |
| 1617 | + return ! $next_gen_path || ! $this->filesystem->exists( $next_gen_path ); | |
| 1618 | + } | |
| 1619 | + | |
| 1620 | + /** | |
| 1621 | + * Get suffix from format. | |
| 1622 | + * | |
| 1623 | + * @param string $format Format extension of next-gen image. | |
| 1624 | + * @return string | |
| 1625 | + */ | |
| 1626 | + private function get_suffix_from_format( string $format ): string { | |
| 1627 | + $suffixes = [ | |
| 1628 | + 'avif' => static::AVIF_SUFFIX, | |
| 1629 | + 'webp' => static::WEBP_SUFFIX, | |
| 1630 | + ]; | |
| 1631 | + | |
| 1632 | + return $suffixes[ $format ]; | |
| 1633 | + } | |
| 1634 | + | |
| 1635 | + /** | |
| 1636 | + * Tell if the media has a next gen format. | |
| 1637 | + * | |
| 1638 | + * @since 2.2 | |
| 1639 | + * | |
| 1640 | + * @return bool | |
| 1641 | + */ | |
| 1642 | + public function has_next_gen() { | |
| 1615 | 1643 | if ( ! $this->is_valid() ) { |
| 1616 | 1644 | return false; |
| 1617 | 1645 | } |
| 1618 | 1646 | |
| @@ -1625,9 +1653,9 @@ | ||
| 1625 | 1653 | if ( empty( $data['sizes'] ) ) { |
| 1626 | 1654 | return false; |
| 1627 | 1655 | } |
| 1628 | 1656 | |
| 1629 | - $needle = static::WEBP_SUFFIX . '";a:4:{s:7:"success";b:1;'; | |
| 1657 | + $needle = $this->format . '";a:4:{s:7:"success";b:1;'; | |
| 1630 | 1658 | $data = maybe_serialize( $data['sizes'] ); |
| 1631 | 1659 | |
| 1632 | 1660 | return is_string( $data ) && strpos( $data, $needle ); |
| 1633 | 1661 | } |
| @@ -1632,34 +1660,76 @@ | ||
| 1632 | 1660 | return is_string( $data ) && strpos( $data, $needle ); |
| 1633 | 1661 | } |
| 1634 | 1662 | |
| 1635 | 1663 | /** |
| 1636 | - * Tell if a WebP version can be created for the given file. | |
| 1664 | + * Tell if the media has all Next-Gen versions. | |
| 1665 | + * | |
| 1666 | + * @return bool | |
| 1667 | + */ | |
| 1668 | + public function is_full_next_gen() { | |
| 1669 | + if ( ! $this->is_valid() ) { | |
| 1670 | + return false; | |
| 1671 | + } | |
| 1672 | + | |
| 1673 | + if ( ! $this->get_media()->is_image() ) { | |
| 1674 | + return false; | |
| 1675 | + } | |
| 1676 | + | |
| 1677 | + $data = $this->get_data()->get_optimization_data(); | |
| 1678 | + | |
| 1679 | + $sizes = $data['sizes']; | |
| 1680 | + | |
| 1681 | + if ( empty( $sizes ) ) { | |
| 1682 | + return false; | |
| 1683 | + } | |
| 1684 | + | |
| 1685 | + $keys = array_keys( $sizes ); | |
| 1686 | + | |
| 1687 | + $non_next_gen_keys = array_values( | |
| 1688 | + array_filter( | |
| 1689 | + $keys, | |
| 1690 | + function ( $key ) { | |
| 1691 | + return strpos( (string) $key, $this->format ) === false; | |
| 1692 | + } | |
| 1693 | + ) | |
| 1694 | + ); | |
| 1695 | + | |
| 1696 | + return array_reduce( | |
| 1697 | + $non_next_gen_keys, | |
| 1698 | + function ( $is_fully, $key ) use ( $sizes ) { | |
| 1699 | + return key_exists( $key . $this->format, $sizes ) && $is_fully; | |
| 1700 | + }, | |
| 1701 | + true | |
| 1702 | + ); | |
| 1703 | + } | |
| 1704 | + | |
| 1705 | + /** | |
| 1706 | + * Tell if a Next-Gen version can be created for the given file. | |
| 1637 | 1707 | * Make sure the file is an image before using this method. |
| 1638 | 1708 | * |
| 1639 | - * @since 1.9.5 | |
| 1640 | - * @access public | |
| 1641 | - * @author Grégory Viguier | |
| 1709 | + * @since 1.9.5 | |
| 1642 | 1710 | * |
| 1643 | 1711 | * @param string $file_path Path to the file. |
| 1712 | + * | |
| 1644 | 1713 | * @return bool |
| 1645 | 1714 | */ |
| 1646 | - public function can_create_webp_version( $file_path ) { | |
| 1715 | + public function can_create_next_gen_version( $file_path ) { | |
| 1647 | 1716 | if ( ! $file_path ) { |
| 1648 | 1717 | return false; |
| 1649 | 1718 | } |
| 1650 | 1719 | |
| 1720 | + $can = apply_filters_deprecated( 'imagify_pre_can_create_webp_version', [ null, $file_path ], '2.2', 'imagify_pre_can_create_next_gen_version' ); | |
| 1721 | + | |
| 1651 | 1722 | /** |
| 1652 | - * Tell if a WebP version can be created for the given file. | |
| 1723 | + * Tell if a next-gen version can be created for the given file. | |
| 1653 | 1724 | * The file is an image. |
| 1654 | 1725 | * |
| 1655 | - * @since 1.9.5 | |
| 1656 | - * @author Grégory Viguier | |
| 1726 | + * @since 1.9.5 | |
| 1657 | 1727 | * |
| 1658 | - * @param bool $can True to create a WebP version, false otherwise. Null by default. | |
| 1728 | + * @param bool $can True to create a next-gen version, false otherwise. Null by default. | |
| 1659 | 1729 | * @param string $file_path Path to the file. |
| 1660 | 1730 | */ |
| 1661 | - $can = apply_filters( 'imagify_pre_can_create_webp_version', null, $file_path ); | |
| 1731 | + $can = apply_filters( 'imagify_pre_can_create_next_gen_version', $can, $file_path ); | |
| 1662 | 1732 | |
| 1663 | 1733 | if ( isset( $can ) ) { |
| 1664 | 1734 | return (bool) $can; |
| 1665 | 1735 | } |
| @@ -1674,19 +1744,78 @@ | ||
| 1674 | 1744 | // At this point $is_animated_gif is null, which means the file cannot be read (yet). |
| 1675 | 1745 | return true; |
| 1676 | 1746 | } |
| 1677 | 1747 | |
| 1748 | + /** | |
| 1749 | + * Generate next-gen images if they are missing. | |
| 1750 | + * | |
| 1751 | + * @since 1.9 | |
| 1752 | + * | |
| 1753 | + * @return bool|WP_Error True if successfully launched. A WP_Error instance on failure. | |
| 1754 | + */ | |
| 1755 | + public function generate_nextgen_versions() { | |
| 1756 | + if ( ! $this->is_valid() ) { | |
| 1757 | + return new WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) ); | |
| 1758 | + } | |
| 1678 | 1759 | |
| 1679 | - /** ----------------------------------------------------------------------------------------- */ | |
| 1680 | - /** PROCESS STATUS ========================================================================== */ | |
| 1681 | - /** ----------------------------------------------------------------------------------------- */ | |
| 1760 | + $media = $this->get_media(); | |
| 1682 | 1761 | |
| 1762 | + if ( ! $media->is_image() ) { | |
| 1763 | + return new WP_Error( 'no_next_gen', __( 'This media is not an image and cannot be converted to next-gen format.', 'imagify' ) ); | |
| 1764 | + } | |
| 1765 | + | |
| 1766 | + if ( ! $media->has_backup() ) { | |
| 1767 | + return new WP_Error( 'no_backup', __( 'This media has no backup file.', 'imagify' ) ); | |
| 1768 | + } | |
| 1769 | + | |
| 1770 | + $data = $this->get_data(); | |
| 1771 | + | |
| 1772 | + if ( ! $data->is_optimized() && ! $data->is_already_optimized() ) { | |
| 1773 | + return new WP_Error( 'not_optimized', __( 'This media has not been optimized by Imagify yet.', 'imagify' ) ); | |
| 1774 | + } | |
| 1775 | + | |
| 1776 | + if ( $this->has_next_gen() ) { | |
| 1777 | + return new WP_Error( 'has_next_gen', __( 'This media already has next-gen versions.', 'imagify' ) ); | |
| 1778 | + } | |
| 1779 | + | |
| 1780 | + $files = $media->get_media_files(); | |
| 1781 | + $sizes = []; | |
| 1782 | + $args = [ | |
| 1783 | + 'hook_suffix' => 'generate_nextgen_versions', | |
| 1784 | + ]; | |
| 1785 | + | |
| 1786 | + foreach ( $files as $size_name => $file ) { | |
| 1787 | + $formats = imagify_nextgen_images_formats(); | |
| 1788 | + | |
| 1789 | + foreach ( $formats as $format ) { | |
| 1790 | + if ( 'avif' === $format ) { | |
| 1791 | + $format_suffix = static::AVIF_SUFFIX; | |
| 1792 | + } elseif ( 'webp' === $format ) { | |
| 1793 | + $format_suffix = static::WEBP_SUFFIX; | |
| 1794 | + } | |
| 1795 | + | |
| 1796 | + if ( $this->get_mime_type( $format ) === $files[ $size_name ]['mime-type'] ) { | |
| 1797 | + continue; | |
| 1798 | + } | |
| 1799 | + | |
| 1800 | + array_unshift( $sizes, $size_name . $format_suffix ); | |
| 1801 | + } | |
| 1802 | + } | |
| 1803 | + | |
| 1804 | + if ( ! $sizes ) { | |
| 1805 | + return new WP_Error( 'no_sizes', __( 'This media does not have files that can be converted to next-gen format.', 'imagify' ) ); | |
| 1806 | + } | |
| 1807 | + | |
| 1808 | + $optimization_level = $data->get_optimization_level(); | |
| 1809 | + | |
| 1810 | + // Optimize. | |
| 1811 | + return $this->optimize_sizes( $sizes, $optimization_level, $args ); | |
| 1812 | + } | |
| 1813 | + | |
| 1683 | 1814 | /** |
| 1684 | 1815 | * Tell if a process is running for this media. |
| 1685 | 1816 | * |
| 1686 | - * @since 1.9 | |
| 1687 | - * @access public | |
| 1688 | - * @author Grégory Viguier | |
| 1817 | + * @since 1.9 | |
| 1689 | 1818 | * |
| 1690 | 1819 | * @return string|bool The action if locked ('optimizing' or 'restoring'). False if not locked. |
| 1691 | 1820 | */ |
| 1692 | 1821 | public function is_locked() { |
| @@ -1708,11 +1837,9 @@ | ||
| 1708 | 1837 | |
| 1709 | 1838 | /** |
| 1710 | 1839 | * Set the running status to "running" for 10 minutes. |
| 1711 | 1840 | * |
| 1712 | - * @since 1.9 | |
| 1713 | - * @access public | |
| 1714 | - * @author Grégory Viguier | |
| 1841 | + * @since 1.9 | |
| 1715 | 1842 | * |
| 1716 | 1843 | * @param string $action The action performed behind this lock: 'optimizing' or 'restoring'. |
| 1717 | 1844 | */ |
| 1718 | 1845 | public function lock( $action = 'optimizing' ) { |
| @@ -1731,11 +1858,9 @@ | ||
| 1731 | 1858 | |
| 1732 | 1859 | /** |
| 1733 | 1860 | * Unset the running status. |
| 1734 | 1861 | * |
| 1735 | - * @since 1.9 | |
| 1736 | - * @access public | |
| 1737 | - * @author Grégory Viguier | |
| 1862 | + * @since 1.9 | |
| 1738 | 1863 | */ |
| 1739 | 1864 | public function unlock() { |
| 1740 | 1865 | $name = $this->get_lock_name(); |
| 1741 | 1866 | |
| @@ -1750,11 +1875,9 @@ | ||
| 1750 | 1875 | |
| 1751 | 1876 | /** |
| 1752 | 1877 | * Get the name of the transient that stores the lock status. |
| 1753 | 1878 | * |
| 1754 | - * @since 1.9 | |
| 1755 | - * @access protected | |
| 1756 | - * @author Grégory Viguier | |
| 1879 | + * @since 1.9 | |
| 1757 | 1880 | * |
| 1758 | 1881 | * @return string|bool The name on success. False on failure. |
| 1759 | 1882 | */ |
| 1760 | 1883 | protected function get_lock_name() { |
| @@ -1773,11 +1896,9 @@ | ||
| 1773 | 1896 | |
| 1774 | 1897 | /** |
| 1775 | 1898 | * Validate the lock action. |
| 1776 | 1899 | * |
| 1777 | - * @since 1.9 | |
| 1778 | - * @access protected | |
| 1779 | - * @author Grégory Viguier | |
| 1900 | + * @since 1.9 | |
| 1780 | 1901 | * |
| 1781 | 1902 | * @param string $action The action performed behind this lock: 'optimizing' or 'restoring'. |
| 1782 | 1903 | * @return string The valid action. |
| 1783 | 1904 | */ |
| @@ -1794,19 +1915,12 @@ | ||
| 1794 | 1915 | |
| 1795 | 1916 | return $action; |
| 1796 | 1917 | } |
| 1797 | 1918 | |
| 1798 | - | |
| 1799 | - /** ----------------------------------------------------------------------------------------- */ | |
| 1800 | - /** DATA ==================================================================================== */ | |
| 1801 | - /** ----------------------------------------------------------------------------------------- */ | |
| 1802 | - | |
| 1803 | 1919 | /** |
| 1804 | 1920 | * Tell if a size already has optimization data. |
| 1805 | 1921 | * |
| 1806 | - * @since 1.9 | |
| 1807 | - * @access public | |
| 1808 | - * @author Grégory Viguier | |
| 1922 | + * @since 1.9 | |
| 1809 | 1923 | * |
| 1810 | 1924 | * @param string $size The size name. |
| 1811 | 1925 | * @return bool |
| 1812 | 1926 | */ |
| @@ -1818,15 +1932,15 @@ | ||
| 1818 | 1932 | |
| 1819 | 1933 | /** |
| 1820 | 1934 | * Update the optimization data for a size. |
| 1821 | 1935 | * |
| 1822 | - * @since 1.9 | |
| 1823 | - * @access public | |
| 1824 | - * @author Grégory Viguier | |
| 1936 | + * @since 1.9 | |
| 1937 | + * @since 2.2 - Addition of the format in the call. | |
| 1825 | 1938 | * |
| 1826 | - * @param object $response The API response. | |
| 1827 | - * @param string $size The size name. | |
| 1828 | - * @param int $level The optimization level (0=normal, 1=aggressive, 2=ultra). | |
| 1939 | + * @param object $response The API response. | |
| 1940 | + * @param string $size The size name. | |
| 1941 | + * @param int $level The optimization level (0=normal, 1=aggressive, 2=ultra). | |
| 1942 | + * | |
| 1829 | 1943 | * @return array { |
| 1830 | 1944 | * The optimization data. |
| 1831 | 1945 | * |
| 1832 | 1946 | * @type int $level The optimization level. |
| @@ -1862,13 +1976,16 @@ | ||
| 1862 | 1976 | } else { |
| 1863 | 1977 | /** |
| 1864 | 1978 | * Success. |
| 1865 | 1979 | */ |
| 1866 | - $response = (object) array_merge( [ | |
| 1867 | - 'original_size' => 0, | |
| 1868 | - 'new_size' => 0, | |
| 1869 | - 'percent' => 0, | |
| 1870 | - ], (array) $response ); | |
| 1980 | + $response = (object) array_merge( | |
| 1981 | + [ | |
| 1982 | + 'original_size' => 0, | |
| 1983 | + 'new_size' => 0, | |
| 1984 | + 'percent' => 0, | |
| 1985 | + ], | |
| 1986 | + (array) $response | |
| 1987 | + ); | |
| 1871 | 1988 | |
| 1872 | 1989 | // Status. |
| 1873 | 1990 | $data['status'] = 'success'; |
| 1874 | 1991 | $data['error'] = null; |
| @@ -1873,9 +1990,14 @@ | ||
| 1873 | 1990 | $data['status'] = 'success'; |
| 1874 | 1991 | $data['error'] = null; |
| 1875 | 1992 | |
| 1876 | 1993 | // Size data. |
| 1877 | - $data['success'] = true; | |
| 1994 | + $data['success'] = true; | |
| 1995 | + | |
| 1996 | + if ( property_exists( $response, 'message' ) ) { | |
| 1997 | + $data['message'] = imagify_translate_api_message( $response->message ); | |
| 1998 | + } | |
| 1999 | + | |
| 1878 | 2000 | $data['original_size'] = $response->original_size; |
| 1879 | 2001 | $data['optimized_size'] = $response->new_size; |
| 1880 | 2002 | } |
| 1881 | 2003 | |
| @@ -1883,10 +2005,9 @@ | ||
| 1883 | 2005 | |
| 1884 | 2006 | /** |
| 1885 | 2007 | * Filter the optimization data. |
| 1886 | 2008 | * |
| 1887 | - * @since 1.9 | |
| 1888 | - * @author Grégory Viguier | |
| 2009 | + * @since 1.9 | |
| 1889 | 2010 | * |
| 1890 | 2011 | * @param array $data { |
| 1891 | 2012 | * The optimization data. |
| 1892 | 2013 | * |
| @@ -1903,8 +2024,24 @@ | ||
| 1903 | 2024 | * @param object $media_data The DataInterface instance of the media. |
| 1904 | 2025 | */ |
| 1905 | 2026 | $data = (array) apply_filters( "imagify{$_unauthorized}_file_optimization_data", $data, $response, $size, $level, $this->get_data() ); |
| 1906 | 2027 | |
| 2028 | + if ( property_exists( $response, 'message' ) && $this->is_next_gen_size( $size ) ) { | |
| 2029 | + /** | |
| 2030 | + * The API answered with a 200 and a message instead of a converted file: it declined the | |
| 2031 | + * conversion, because the next-gen version would be heavier than the original or the file is | |
| 2032 | + * already compressed. No next-gen file is written to disk in that case (see | |
| 2033 | + * Imagify\Optimization\File::optimize()), so recording a success here would make the plugin | |
| 2034 | + * report a next-gen version that does not exist. | |
| 2035 | + * | |
| 2036 | + * Store a terminal entry instead, flagged as a permanent refusal so the bulk queries can skip | |
| 2037 | + * the media without re-sending it to the API on every run. Transient failures go through the | |
| 2038 | + * WP_Error branch above and are not flagged, so they keep being retried. | |
| 2039 | + */ | |
| 2040 | + $data['success'] = false; | |
| 2041 | + $data['error'] = $data['message']; | |
| 2042 | + $data['permanent_error'] = true; | |
| 2043 | + } | |
| 1907 | 2044 | // Store. |
| 1908 | 2045 | $this->get_data()->update_size_optimization_data( $size, $data ); |
| 1909 | 2046 | |
| 1910 | 2047 | return $data; |
| @@ -1909,21 +2046,34 @@ | ||
| 1909 | 2046 | |
| 1910 | 2047 | return $data; |
| 1911 | 2048 | } |
| 1912 | 2049 | |
| 2050 | + /** | |
| 2051 | + * Tell if a size name refers to a next-gen (AVIF or WebP) version. | |
| 2052 | + * | |
| 2053 | + * @param string $size The size name. | |
| 2054 | + * | |
| 2055 | + * @return bool | |
| 2056 | + */ | |
| 2057 | + private function is_next_gen_size( $size ) { | |
| 2058 | + $size = (string) $size; | |
| 1913 | 2059 | |
| 1914 | - /** ----------------------------------------------------------------------------------------- */ | |
| 1915 | - /** VARIOUS TOOLS =========================================================================== */ | |
| 1916 | - /** ----------------------------------------------------------------------------------------- */ | |
| 2060 | + foreach ( [ static::AVIF_SUFFIX, static::WEBP_SUFFIX ] as $suffix ) { | |
| 2061 | + if ( '' !== $suffix && substr( $size, - strlen( $suffix ) ) === $suffix ) { | |
| 2062 | + return true; | |
| 2063 | + } | |
| 2064 | + } | |
| 1917 | 2065 | |
| 2066 | + return false; | |
| 2067 | + } | |
| 2068 | + | |
| 1918 | 2069 | /** |
| 1919 | 2070 | * Get a plugin’s option. |
| 1920 | 2071 | * |
| 1921 | - * @since 1.9 | |
| 1922 | - * @access protected | |
| 1923 | - * @author Grégory Viguier | |
| 2072 | + * @since 1.9 | |
| 1924 | 2073 | * |
| 1925 | - * @param string $option_name The option nme. | |
| 2074 | + * @param string $option_name The option name. | |
| 2075 | + * | |
| 1926 | 2076 | * @return mixed |
| 1927 | 2077 | */ |
| 1928 | 2078 | protected function get_option( $option_name ) { |
| 1929 | 2079 | if ( isset( $this->options[ $option_name ] ) ) { |
| @@ -1938,19 +2088,50 @@ | ||
| 1938 | 2088 | /** |
| 1939 | 2089 | * Sanitize and validate an optimization level. |
| 1940 | 2090 | * If not provided (false, null), fallback to the level set in the plugin's settings. |
| 1941 | 2091 | * |
| 1942 | - * @since 1.9 | |
| 1943 | - * @access protected | |
| 1944 | - * @author Grégory Viguier | |
| 2092 | + * @since 1.9 | |
| 1945 | 2093 | * |
| 1946 | - * @param mixed $optimization_level The optimization level. | |
| 2094 | + * @param mixed $optimization_level The optimization level. | |
| 2095 | + * | |
| 1947 | 2096 | * @return int |
| 1948 | 2097 | */ |
| 1949 | 2098 | protected function sanitize_optimization_level( $optimization_level ) { |
| 1950 | 2099 | if ( ! is_numeric( $optimization_level ) ) { |
| 2100 | + if ( $this->get_option( 'lossless' ) ) { | |
| 2101 | + return 0; | |
| 2102 | + } | |
| 2103 | + | |
| 1951 | 2104 | return $this->get_option( 'optimization_level' ); |
| 1952 | 2105 | } |
| 1953 | 2106 | |
| 1954 | 2107 | return \Imagify_Options::get_instance()->sanitize_and_validate( 'optimization_level', $optimization_level ); |
| 2108 | + } | |
| 2109 | + | |
| 2110 | + /** | |
| 2111 | + * Tell if the media has AVIF versions. | |
| 2112 | + * | |
| 2113 | + * @since 2.2 | |
| 2114 | + * | |
| 2115 | + * @return bool | |
| 2116 | + */ | |
| 2117 | + public function has_avif() { | |
| 2118 | + if ( ! $this->is_valid() ) { | |
| 2119 | + return false; | |
| 2120 | + } | |
| 2121 | + | |
| 2122 | + if ( ! $this->get_media()->is_image() ) { | |
| 2123 | + return false; | |
| 2124 | + } | |
| 2125 | + | |
| 2126 | + $data = $this->get_data()->get_optimization_data(); | |
| 2127 | + | |
| 2128 | + if ( empty( $data['sizes'] ) ) { | |
| 2129 | + return false; | |
| 2130 | + } | |
| 2131 | + | |
| 2132 | + $needle = static::AVIF_SUFFIX . '";a:4:{s:7:"success";b:1;'; | |
| 2133 | + $data = maybe_serialize( $data['sizes'] ); | |
| 2134 | + | |
| 2135 | + return is_string( $data ) && strpos( $data, $needle ); | |
| 1955 | 2136 | } |
| 1956 | 2137 | } |