PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / 1.9.4
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF v1.9.4
2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.9 2.2.8 trunk 1.10 1.3.3 1.3.4 1.3.5 1.3.5.1 1.3.5.2 1.3.6 1.3.6.1 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.5 All 103 releases
imagify / inc / classes / class-imagify-auto-optimization.php

class-imagify-auto-optimization.php in Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF 1.9.4, at inc/classes/class-imagify-auto-optimization.php

447 lines 13.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
3
4 /**
5 * Class that handles the auto-optimization process.
6 * This occurs when a new image is uploaded, and when an optimized image is worked with (resized, etc).
7 * The process will work only if wp_update_attachment_metadata() is used.
8 *
9 * @since 1.8.4
10 * @author Grégory Viguier
11 */
12 class Imagify_Auto_Optimization {
13
14 /**
15 * Class version.
16 *
17 * @var string
18 * @since 1.8.4
19 * @author Grégory Viguier
20 */
21 const VERSION = '1.1';
22
23 /**
24 * An array containing the IDs (as keys) of attachments just being uploaded.
25 *
26 * @var array
27 * @since 1.8.4
28 * @access protected
29 * @author Grégory Viguier
30 */
31 protected $uploads = [];
32
33 /**
34 * An array containing the IDs (as keys) of attachments that must be optimized automatically.
35 * The values tell if the attachment is a new upload.
36 *
37 * @var array
38 * @since 1.8.4
39 * @access protected
40 * @author Grégory Viguier
41 */
42 protected $attachments = [];
43
44 /**
45 * The single instance of the class.
46 *
47 * @var object
48 * @since 1.8.4
49 * @access protected
50 * @author Grégory Viguier
51 */
52 protected static $instance;
53
54 /**
55 * Used to prevent an auto-optimization locally.
56 *
57 * @var array
58 * @since 1.8.4
59 * @access private
60 * @author Grégory Viguier
61 */
62 private static $prevented = [];
63
64 /**
65 * Get the main Instance.
66 * Ensures only one instance of class is loaded or can be loaded.
67 *
68 * @since 1.8.4
69 * @access public
70 * @author Grégory Viguier
71 *
72 * @return object Main instance.
73 */
74 public static function get_instance() {
75 if ( ! isset( self::$instance ) ) {
76 self::$instance = new self();
77 }
78
79 return self::$instance;
80 }
81
82 /**
83 * The class constructor.
84 *
85 * @since 1.8.4
86 * @access protected
87 * @author Grégory Viguier
88 */
89 protected function __construct() {}
90
91 /**
92 * Init.
93 *
94 * @since 1.8.4
95 * @access public
96 * @author Grégory Viguier
97 */
98 public function init() {
99 $prio = IMAGIFY_INT_MAX - 30;
100
101 // Automatic optimization tunel.
102 add_action( 'add_attachment', [ $this, 'store_upload_ids' ], $prio );
103 add_filter( 'wp_update_attachment_metadata', [ $this, 'store_ids_to_optimize' ], $prio, 2 );
104 add_action( 'updated_post_meta', [ $this, 'do_auto_optimization' ], $prio, 4 );
105 add_action( 'added_post_meta', [ $this, 'do_auto_optimization' ], $prio, 4 );
106 add_action( 'deleted_post_meta', [ $this, 'unset_optimization' ], $prio, 3 );
107
108 // Prevent to re-optimize when updating the image width and height (when resizing the full image).
109 add_action( 'imagify_before_update_wp_media_data_dimensions', [ __CLASS__, 'prevent_optimization' ], 5 );
110 add_action( 'imagify_after_update_wp_media_data_dimensions', [ __CLASS__, 'allow_optimization' ], 5 );
111 }
112
113 /**
114 * Remove the hooks.
115 *
116 * @since 1.8.4
117 * @access public
118 * @author Grégory Viguier
119 */
120 public function remove_hooks() {
121 $prio = IMAGIFY_INT_MAX - 30;
122
123 remove_action( 'add_attachment', [ $this, 'store_upload_ids' ], $prio );
124 remove_filter( 'wp_update_attachment_metadata', [ $this, 'store_ids_to_optimize' ], $prio );
125 remove_action( 'updated_post_meta', [ $this, 'do_auto_optimization' ], $prio );
126 remove_action( 'added_post_meta', [ $this, 'do_auto_optimization' ], $prio );
127 remove_action( 'deleted_post_meta', [ $this, 'unset_optimization' ], $prio );
128 remove_action( 'imagify_before_update_wp_media_data_dimensions', [ __CLASS__, 'prevent_optimization' ], 5 );
129 remove_action( 'imagify_after_update_wp_media_data_dimensions', [ __CLASS__, 'allow_optimization' ], 5 );
130 }
131
132
133 /** ----------------------------------------------------------------------------------------- */
134 /** HOOKS =================================================================================== */
135 /** ----------------------------------------------------------------------------------------- */
136
137 /**
138 * Store the ID of attachments that just have been uploaded.
139 * We use those IDs to tell the difference later in `wp_update_attachment_metadata()`.
140 *
141 * @since 1.8.4
142 * @access public
143 * @see $this->store_ids_to_optimize()
144 * @author Grégory Viguier
145 *
146 * @param int $attachment_id Current attachment ID.
147 */
148 public function store_upload_ids( $attachment_id ) {
149 if ( ! self::is_optimization_prevented( $attachment_id ) && imagify_is_attachment_mime_type_supported( $attachment_id ) ) {
150 $this->uploads[ $attachment_id ] = 1;
151 }
152 }
153
154 /**
155 * After the attachment meta data has been generated, launch an async optimization.
156 * Two cases are possible to trigger the optimization:
157 * - It's a new upload and auto-optimization is enabled.
158 * - It's not a new upload (it is regenerated) and the attachment is already optimized.
159 *
160 * @since 1.8.4
161 * @access public
162 * @see $this->store_upload_ids()
163 * @author Grégory Viguier
164 *
165 * @param array $metadata An array of attachment meta data.
166 * @param int $attachment_id Current attachment ID.
167 * @return array
168 */
169 public function store_ids_to_optimize( $metadata, $attachment_id ) {
170 static $auto_optimize;
171
172 if ( self::is_optimization_prevented( $attachment_id ) ) {
173 return $metadata;
174 }
175
176 $is_new_upload = ! empty( $this->uploads[ $attachment_id ] );
177 unset( $this->uploads[ $attachment_id ] );
178
179 if ( ! $metadata || ! imagify_is_attachment_mime_type_supported( $attachment_id ) ) {
180 return $metadata;
181 }
182
183 if ( $is_new_upload ) {
184 // It's a new upload.
185 if ( ! isset( $auto_optimize ) ) {
186 $auto_optimize = get_imagify_option( 'auto_optimize' );
187 }
188
189 if ( ! $auto_optimize ) {
190 /**
191 * Fires when a new attachment is uploaded but auto-optimization is disabled.
192 *
193 * @since 1.8.4
194 * @author Grégory Viguier
195 *
196 * @param int $attachment_id Attachment ID.
197 * @param array $metadata An array of attachment meta data.
198 */
199 do_action( 'imagify_new_attachment_auto_optimization_disabled', $attachment_id, $metadata );
200
201 return $metadata;
202 }
203
204 /**
205 * Allow to prevent automatic optimization for a specific attachment.
206 *
207 * @since 1.6.12
208 * @author Grégory Viguier
209 *
210 * @param bool $optimize True to optimize, false otherwise.
211 * @param int $attachment_id Attachment ID.
212 * @param array $metadata An array of attachment meta data.
213 */
214 $optimize = apply_filters( 'imagify_auto_optimize_attachment', true, $attachment_id, $metadata );
215
216 if ( ! $optimize ) {
217 return $metadata;
218 }
219
220 /**
221 * It's a new upload and auto-optimization is enabled.
222 */
223 }
224
225 if ( ! $is_new_upload ) {
226 // An existing attachment being regenerated (or something).
227 $process = imagify_get_optimization_process( $attachment_id, 'wp' );
228
229 if ( ! $process->is_valid() ) {
230 // Uh?
231 return $metadata;
232 }
233
234 if ( ! $process->get_data()->get_optimization_status() ) {
235 /**
236 * Fires when an attachment is updated but not optimized yet.
237 *
238 * @since 1.8.4
239 * @author Grégory Viguier
240 *
241 * @param int $attachment_id Attachment ID.
242 * @param array $metadata An array of attachment meta data.
243 */
244 do_action( 'imagify_not_optimized_attachment_updated', $attachment_id, $metadata );
245
246 return $metadata;
247 }
248
249 /**
250 * Allow to prevent automatic reoptimization for a specific attachment.
251 *
252 * @since 1.8.4
253 * @author Grégory Viguier
254 *
255 * @param bool $optimize True to optimize, false otherwise.
256 * @param int $attachment_id Attachment ID.
257 * @param array $metadata An array of attachment meta data.
258 */
259 $optimize = apply_filters( 'imagify_auto_optimize_optimized_attachment', true, $attachment_id, $metadata );
260
261 if ( ! $optimize ) {
262 return $metadata;
263 }
264
265 /**
266 * The attachment already exists and was already optimized.
267 */
268 }
269
270 // Ready for the next step.
271 $this->attachments[ $attachment_id ] = $is_new_upload;
272
273 return $metadata;
274 }
275
276 /**
277 * Launch auto optimization immediately after the post meta '_wp_attachment_metadata' is added or updated.
278 *
279 * @since 1.8.4
280 * @access public
281 * @author Grégory Viguier
282 *
283 * @param int $meta_id ID of the metadata entry.
284 * @param int $attachment_id Current attachment ID.
285 * @param string $meta_key Meta key.
286 * @param mixed $metadata Meta value.
287 */
288 public function do_auto_optimization( $meta_id, $attachment_id, $meta_key, $metadata ) {
289 if ( '_wp_attachment_metadata' !== $meta_key || ! isset( $this->attachments[ $attachment_id ] ) ) {
290 return;
291 }
292
293 if ( self::is_optimization_prevented( $attachment_id ) ) {
294 return;
295 }
296
297 $is_new_upload = $this->attachments[ $attachment_id ];
298 unset( $this->attachments[ $attachment_id ] );
299
300 $process = imagify_get_optimization_process( $attachment_id, 'wp' );
301
302 /**
303 * Fires before an attachment auto-optimization is triggered.
304 *
305 * @since 1.8.4
306 * @author Grégory Viguier
307 *
308 * @param int $attachment_id The attachment ID.
309 * @param bool $is_new_upload True if it's a new upload. False otherwize.
310 */
311 do_action_deprecated( 'imagify_before_auto_optimization_launch', [ $attachment_id, $is_new_upload ], '1.9', 'imagify_before_auto_optimization' );
312
313 /**
314 * Triggered before a media is auto-optimized.
315 *
316 * @since 1.8.4
317 * @author Grégory Viguier
318 *
319 * @param int $attachment_id The media ID.
320 * @param bool $is_new_upload True if it's a new upload. False otherwize.
321 */
322 do_action( 'imagify_before_auto_optimization', $attachment_id, $is_new_upload );
323
324 if ( $is_new_upload ) {
325 /**
326 * It's a new upload.
327 */
328 // Optimize.
329 $process->optimize( null, [ 'is_new_upload' => 1 ] );
330 } else {
331 /**
332 * The media has already been optimized (or at least it has been tried).
333 */
334 $process_data = $process->get_data();
335
336 // Get the optimization level before deleting the optimization data.
337 $optimization_level = $process_data->get_optimization_level();
338
339 // Some specifics for the image editor.
340 if ( isset( $_POST['action'], $_POST['do'], $_POST['postid'] ) && 'image-editor' === $_POST['action'] && (int) $_POST['postid'] === $attachment_id ) { // WPCS: CSRF ok.
341 check_ajax_referer( 'image_editor-' . $attachment_id );
342
343 if ( ! current_user_can( 'edit_post', $attachment_id ) ) {
344 imagify_die();
345 }
346
347 // Restore the backup file.
348 $result = $process->restore();
349
350 if ( is_wp_error( $result ) ) {
351 // Restoration failed, there is no good way to handle this case.
352 $process_data->delete_optimization_data();
353 }
354 } else {
355 // Remove old optimization data.
356 $process_data->delete_optimization_data();
357 }
358
359 // Optimize.
360 $process->optimize( $optimization_level );
361 }
362
363 /**
364 * Triggered after a media auto-optimization is launched.
365 *
366 * @since 1.8.4
367 * @author Grégory Viguier
368 *
369 * @param int $attachment_id The media ID.
370 * @param bool $is_new_upload True if it's a new upload. False otherwize.
371 */
372 do_action( 'imagify_after_auto_optimization', $attachment_id, $is_new_upload );
373 }
374
375 /**
376 * Remove the attachment ID from the $attachments property if the post meta '_wp_attachment_metadata' is deleted.
377 *
378 * @since 1.8.4
379 * @access public
380 * @author Grégory Viguier
381 *
382 * @param int $meta_ids An array of deleted metadata entry IDs.
383 * @param int $attachment_id Current attachment ID.
384 * @param string $meta_key Meta key.
385 */
386 public function unset_optimization( $meta_ids, $attachment_id, $meta_key ) {
387 if ( '_wp_attachment_metadata' !== $meta_key || ! isset( $this->attachments[ $attachment_id ] ) ) {
388 return;
389 }
390
391 unset( $this->attachments[ $attachment_id ] );
392 }
393
394
395 /** ----------------------------------------------------------------------------------------- */
396 /** TOOLS =================================================================================== */
397 /** ----------------------------------------------------------------------------------------- */
398
399 /**
400 * Prevent an auto-optimization locally.
401 * How to use it:
402 * Imagify_Auto_Optimization::prevent_optimization( $attachment_id );
403 * wp_update_attachment_metadata( $attachment_id );
404 * Imagify_Auto_Optimization::allow_optimization( $attachment_id );
405 *
406 * @since 1.8.4
407 * @access public
408 * @author Grégory Viguier
409 *
410 * @param int $attachment_id Current attachment ID.
411 */
412 public static function prevent_optimization( $attachment_id ) {
413 self::$prevented[ $attachment_id ] = 1;
414 }
415
416 /**
417 * Allow an auto-optimization locally.
418 * How to use it:
419 * Imagify_Auto_Optimization::prevent_optimization( $attachment_id );
420 * wp_update_attachment_metadata( $attachment_id );
421 * Imagify_Auto_Optimization::allow_optimization( $attachment_id );
422 *
423 * @since 1.8.4
424 * @access public
425 * @author Grégory Viguier
426 *
427 * @param int $attachment_id Current attachment ID.
428 */
429 public static function allow_optimization( $attachment_id ) {
430 unset( self::$prevented[ $attachment_id ] );
431 }
432
433 /**
434 * Tell if an auto-optimization is prevented locally.
435 *
436 * @since 1.8.4
437 * @access public
438 * @author Grégory Viguier
439 *
440 * @param int $attachment_id Current attachment ID.
441 * @return bool
442 */
443 public static function is_optimization_prevented( $attachment_id ) {
444 return ! empty( self::$prevented[ $attachment_id ] );
445 }
446 }
447