PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / trunk
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF vtrunk
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 trunk, at inc/classes/class-imagify-auto-optimization.php

790 lines 24.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use Imagify\Traits\InstanceGetterTrait;
4
5 /**
6 * Class that handles the auto-optimization process.
7 * This occurs when a new image is uploaded, and when an optimized image is worked with (resized, etc).
8 * The process will work only if wp_generate_attachment_metadata() and wp_update_attachment_metadata() are used.
9 *
10 * @since 1.8.4
11 */
12 class Imagify_Auto_Optimization extends Imagify_Auto_Optimization_Deprecated {
13 use InstanceGetterTrait;
14
15 /**
16 * An array containing all the "steps" an attachment is going through.
17 * This is used to decide the behavior of the automatic optimization.
18 *
19 * @var array {
20 * An array of arrays with attachment ID as keys.
21 * Each array can contain the following:
22 *
23 * @type $upload int Set to 1 if the attachment is a new upload.
24 * @type $generate int Set to 1 when going though wp_generate_attachment_metadata().
25 * @type $update int Set to 1 when going though wp_update_attachment_metadata().
26 * }
27 * @since 1.8.4
28 * @since 1.9.10 Private.
29 * @since 1.9.10 Items are arrays instead of 1s.
30 */
31 private $attachments = [];
32
33 /**
34 * Tell if we’re using WP 5.3+.
35 *
36 * @var bool
37 * @since 1.9.10
38 */
39 private $is_wp_53;
40
41 /**
42 * Attachment IDs whose optimization must wait for the metadata to be stored.
43 *
44 * @var array
45 * @since 2.3.3
46 */
47 private $deferred = [];
48
49 /**
50 * The ID of the attachment that failed to be uploaded.
51 *
52 * @var int
53 * @since 1.9.8
54 */
55 protected $upload_failure_id = 0;
56
57 /**
58 * Used to prevent an auto-optimization locally.
59 *
60 * @var array
61 * @since 1.8.4
62 */
63 private static $prevented = [];
64
65 /**
66 * Used to prevent an auto-optimization internally.
67 *
68 * @var array
69 * @since 1.9.8
70 */
71 private static $prevented_internally = [];
72
73 /**
74 * Init.
75 *
76 * @since 1.8.4
77 */
78 public function init() {
79 global $wp_version;
80
81 $priority = IMAGIFY_INT_MAX - 30;
82 $this->is_wp_53 = version_compare( $wp_version, '5.3-alpha1' ) >= 0;
83
84 // Automatic optimization tunel.
85 add_action( 'add_attachment', [ $this, 'store_upload_ids' ], $priority );
86 add_action( 'rest_after_insert_attachment', [ $this, 'flag_awaiting_client_side_subsizes' ], $priority, 3 );
87 add_filter( 'wp_generate_attachment_metadata', [ $this, 'maybe_store_generate_step' ], $priority, 3 );
88 add_filter( 'wp_update_attachment_metadata', [ $this, 'store_ids_to_optimize' ], $priority, 2 );
89
90 if ( $this->is_wp_53 ) {
91 // WP 5.3+.
92 add_action( 'imagify_after_auto_optimization_init', [ $this, 'do_auto_optimization' ], $priority, 2 );
93 // Upload failure recovering.
94 add_action( 'wp_ajax_media-create-image-subsizes', [ $this, 'prevent_auto_optimization_when_recovering_from_upload_failure' ], -5 ); // Before WP’s hook (priority 1).
95 }
96
97 /**
98 * Also used on WP 5.3+ to optimize once the metadata is stored, which the client side
99 * upload of WP 7.1 needs: it stores every sub size at once, so reading them before the
100 * write would only ever see the full size. Harmless the rest of the time, since an
101 * optimization that already ran has cleared its steps by then.
102 *
103 * The trade-off is that these two hooks now fire for every post meta write on the site
104 * rather than only on old WordPress versions. The callback returns on anything that is
105 * not '_wp_attachment_metadata', which is the first thing it checks, so the cost is one
106 * string comparison. Carrying the state on the attachment instead would mean an extra
107 * read on every upload, for a narrower guarantee.
108 */
109 add_action( 'updated_post_meta', [ $this, 'do_auto_optimization_after_meta_update' ], $priority, 4 );
110 add_action( 'added_post_meta', [ $this, 'do_auto_optimization_after_meta_update' ], $priority, 4 );
111
112 add_action( 'deleted_post_meta', [ $this, 'unset_optimization' ], $priority, 3 );
113
114 // Prevent to re-optimize when updating the image width and height (when resizing the full image).
115 add_action( 'imagify_before_update_wp_media_data_dimensions', [ __CLASS__, 'prevent_optimization' ], 5 );
116 add_action( 'imagify_after_update_wp_media_data_dimensions', [ __CLASS__, 'allow_optimization' ], 5 );
117 }
118
119 /**
120 * Remove the hooks.
121 *
122 * @since 1.8.4
123 */
124 public function remove_hooks() {
125 $priority = IMAGIFY_INT_MAX - 30;
126
127 // Automatic optimization tunel.
128 remove_action( 'add_attachment', [ $this, 'store_upload_ids' ], $priority );
129 remove_action( 'rest_after_insert_attachment', [ $this, 'flag_awaiting_client_side_subsizes' ], $priority );
130 remove_filter( 'wp_generate_attachment_metadata', [ $this, 'maybe_store_generate_step' ], $priority );
131 remove_filter( 'wp_update_attachment_metadata', [ $this, 'store_ids_to_optimize' ], $priority );
132
133 if ( $this->is_wp_53 ) {
134 // WP 5.3+.
135 remove_action( 'imagify_after_auto_optimization_init', [ $this, 'do_auto_optimization' ], $priority );
136 // Upload failure recovering.
137 remove_action( 'wp_ajax_media-create-image-subsizes', [ $this, 'prevent_auto_optimization_when_recovering_from_upload_failure' ], -5 );
138 }
139
140 remove_action( 'updated_post_meta', [ $this, 'do_auto_optimization_after_meta_update' ], $priority );
141 remove_action( 'added_post_meta', [ $this, 'do_auto_optimization_after_meta_update' ], $priority );
142
143 remove_action( 'deleted_post_meta', [ $this, 'unset_optimization' ], $priority );
144
145 // Prevent to re-optimize when updating the image width and height (when resizing the full image).
146 remove_action( 'imagify_before_update_wp_media_data_dimensions', [ __CLASS__, 'prevent_optimization' ], 5 );
147 remove_action( 'imagify_after_update_wp_media_data_dimensions', [ __CLASS__, 'allow_optimization' ], 5 );
148 }
149
150
151 /** ----------------------------------------------------------------------------------------- */
152 /** HOOKS =================================================================================== */
153 /** ----------------------------------------------------------------------------------------- */
154
155 /**
156 * Store the "upload step" when an attachment has just been uploaded.
157 *
158 * @since 1.8.4
159 * @see $this->store_ids_to_optimize()
160 *
161 * @param int $attachment_id Current attachment ID.
162 */
163 public function store_upload_ids( $attachment_id ) {
164 if ( ! self::is_optimization_prevented( $attachment_id ) && imagify_is_attachment_mime_type_supported( $attachment_id ) ) {
165 $this->set_step( $attachment_id, 'upload' );
166 }
167 }
168
169 /**
170 * Remember that the browser will send the sub sizes of an attachment separately.
171 *
172 * WordPress 7.1 can let the browser process an upload. The attachment is then created
173 * with no sub sizes at all, each one is sent afterwards through the sideload endpoint,
174 * and a last request finalizes the metadata. Optimizing when the attachment is created
175 * would only ever cover the full size.
176 *
177 * This runs before the metadata is first generated, and the flag is stored because the
178 * sub sizes arrive in later requests.
179 *
180 * @since 2.3.3
181 *
182 * @param object $attachment Inserted or updated attachment object. A \WP_Post when WordPress fires this.
183 * @param object $request Request object. A \WP_REST_Request when WordPress fires this.
184 * @param bool $creating True when creating an attachment, false when updating.
185 */
186 public function flag_awaiting_client_side_subsizes( $attachment, $request, $creating ) {
187 if ( ! $creating || ! is_object( $attachment ) || ! isset( $attachment->ID ) ) {
188 return;
189 }
190
191 if ( ! is_object( $request ) || ! is_callable( [ $request, 'get_param' ] ) ) {
192 return;
193 }
194
195 if ( false !== $request->get_param( 'generate_sub_sizes' ) ) {
196 return;
197 }
198
199 if ( ! imagify_is_attachment_mime_type_supported( $attachment->ID ) ) {
200 // Nothing would ever read the flag for this attachment.
201 return;
202 }
203
204 set_transient( $this->get_awaiting_subsizes_transient_name( $attachment->ID ), 1, HOUR_IN_SECONDS );
205 }
206
207 /**
208 * Store the "generate step" when wp_generate_attachment_metadata() is used.
209 *
210 * @since 1.9.10
211 * @since 2.3.3 Added the $context parameter.
212 *
213 * @param array $metadata An array of attachment meta data.
214 * @param int $attachment_id Current attachment ID.
215 * @param string $context Can be 'create' when the metadata was initially created for a
216 * new attachment, or 'update' when it was updated. Passed by WordPress
217 * since 5.3, so only null if something else applies the filter.
218 * @return array
219 */
220 public function maybe_store_generate_step( $metadata, $attachment_id, $context = null ) {
221 if ( self::is_optimization_prevented( $attachment_id ) ) {
222 return $metadata;
223 }
224
225 if ( empty( $metadata ) || ! imagify_is_attachment_mime_type_supported( $attachment_id ) ) {
226 $this->unset_steps( $attachment_id );
227 return $metadata;
228 }
229
230 if ( $this->is_awaiting_client_side_subsizes( $attachment_id ) ) {
231 if ( 'create' === $context ) {
232 /**
233 * The browser has not sent its sub sizes yet. Optimizing now would cover the
234 * full size only, and leave every thumbnail untouched.
235 */
236 return $metadata;
237 }
238
239 /**
240 * The sub sizes are in. The upload step was set in the request that created the
241 * attachment and did not outlive it, so set it again: as far as Imagify is
242 * concerned this still is a brand new upload.
243 */
244 $this->clear_awaiting_client_side_subsizes( $attachment_id );
245 $this->set_step( $attachment_id, 'upload' );
246
247 /**
248 * The sub sizes are all stored in one go, right after this, so the optimization
249 * has to wait for that write. Reading them now would only see the full size.
250 */
251 $this->deferred[ $attachment_id ] = 1;
252 }
253
254 $this->set_step( $attachment_id, 'generate' );
255
256 return $metadata;
257 }
258
259 /**
260 * Tell if the browser is still to send the sub sizes of an attachment.
261 *
262 * @since 2.3.3
263 *
264 * @param int $attachment_id Current attachment ID.
265 * @return bool
266 */
267 public function is_awaiting_client_side_subsizes( $attachment_id ) {
268 return (bool) get_transient( $this->get_awaiting_subsizes_transient_name( $attachment_id ) );
269 }
270
271 /**
272 * Forget that the browser was to send the sub sizes of an attachment.
273 *
274 * @since 2.3.3
275 *
276 * @param int $attachment_id Current attachment ID.
277 */
278 public function clear_awaiting_client_side_subsizes( $attachment_id ) {
279 delete_transient( $this->get_awaiting_subsizes_transient_name( $attachment_id ) );
280 }
281
282 /**
283 * Get the transient name used to await the sub sizes of an attachment.
284 *
285 * @since 2.3.3
286 *
287 * @param int $attachment_id Current attachment ID.
288 * @return string
289 */
290 private function get_awaiting_subsizes_transient_name( $attachment_id ) {
291 return 'imagify_awaiting_subsizes_' . (int) $attachment_id;
292 }
293
294 /**
295 * After the attachment meta data has been generated (partially, since WP 5.3), init the auto-optimization.
296 * Two cases are possible to trigger the optimization:
297 * - It's a new upload and auto-optimization is enabled.
298 * - It's not a new upload (it is regenerated) and the attachment is already optimized.
299 *
300 * @since 1.8.4
301 *
302 * @param array $metadata An array of attachment meta data.
303 * @param int $attachment_id Current attachment ID.
304 * @return array
305 */
306 public function store_ids_to_optimize( $metadata, $attachment_id ) {
307 static $auto_optimize;
308
309 if ( self::is_optimization_prevented( $attachment_id ) ) {
310 return $metadata;
311 }
312
313 if ( empty( $metadata ) || ! imagify_is_attachment_mime_type_supported( $attachment_id ) ) {
314 $this->unset_steps( $attachment_id );
315 return $metadata;
316 }
317
318 if ( ! $this->has_step( $attachment_id, 'generate' ) ) {
319 return $metadata;
320 }
321
322 $is_new_upload = $this->has_step( $attachment_id, 'upload' );
323
324 if ( $is_new_upload ) {
325 // It's a new upload.
326 if ( ! isset( $auto_optimize ) ) {
327 $auto_optimize = get_imagify_option( 'auto_optimize' );
328 }
329
330 if ( ! $auto_optimize ) {
331 /**
332 * Fires when a new attachment is uploaded but auto-optimization is disabled.
333 *
334 * @since 1.8.4
335 *
336 * @param int $attachment_id Attachment ID.
337 * @param array $metadata An array of attachment meta data.
338 */
339 do_action( 'imagify_new_attachment_auto_optimization_disabled', $attachment_id, $metadata );
340
341 return $metadata;
342 }
343
344 /**
345 * Allow to prevent automatic optimization for a specific attachment.
346 *
347 * @since 1.6.12
348 *
349 * @param bool $optimize True to optimize, false otherwise.
350 * @param int $attachment_id Attachment ID.
351 * @param array $metadata An array of attachment meta data.
352 */
353 $optimize = apply_filters( 'imagify_auto_optimize_attachment', true, $attachment_id, $metadata );
354
355 if ( ! $optimize ) {
356 return $metadata;
357 }
358
359 /**
360 * It's a new upload and auto-optimization is enabled.
361 */
362 }
363
364 if ( ! $is_new_upload ) {
365 // An existing attachment being regenerated (or something).
366 $process = imagify_get_optimization_process( $attachment_id, 'wp' );
367
368 if ( ! $process->is_valid() ) {
369 // Uh?
370 return $metadata;
371 }
372
373 if ( ! $process->get_data()->get_optimization_status() ) {
374 /**
375 * Fires when an attachment is updated but not optimized yet.
376 *
377 * @since 1.8.4
378 *
379 * @param int $attachment_id Attachment ID.
380 * @param array $metadata An array of attachment meta data.
381 */
382 do_action( 'imagify_not_optimized_attachment_updated', $attachment_id, $metadata );
383
384 return $metadata;
385 }
386
387 /**
388 * Allow to prevent automatic reoptimization for a specific attachment.
389 *
390 * @since 1.8.4
391 *
392 * @param bool $optimize True to optimize, false otherwise.
393 * @param int $attachment_id Attachment ID.
394 * @param array $metadata An array of attachment meta data.
395 */
396 $optimize = apply_filters( 'imagify_auto_optimize_optimized_attachment', true, $attachment_id, $metadata );
397
398 if ( ! $optimize ) {
399 return $metadata;
400 }
401
402 /**
403 * The attachment already exists and was already optimized.
404 */
405 }
406
407 // Ready for the next step.
408 $this->set_step( $attachment_id, 'update' );
409
410 if ( ! empty( $this->deferred[ $attachment_id ] ) ) {
411 /**
412 * The metadata is not stored yet, so the sizes to optimize cannot be read.
413 * $this->do_auto_optimization_after_meta_update() takes over once it is.
414 */
415 unset( $this->deferred[ $attachment_id ] );
416
417 return $metadata;
418 }
419
420 /**
421 * Triggered after a media auto-optimization init.
422 *
423 * @since 1.9.8
424 *
425 * @param int $attachment_id The media ID.
426 * @param bool $is_new_upload True if it's a new upload. False otherwize.
427 */
428 do_action( 'imagify_after_auto_optimization_init', $attachment_id, $is_new_upload );
429
430 return $metadata;
431 }
432
433 /**
434 * Launch auto optimization immediately after the post meta '_wp_attachment_metadata' is added or updated.
435 *
436 * @since 1.9
437 * @since 1.9 Previously named do_auto_optimization().
438 *
439 * @param int $meta_id ID of the metadata entry.
440 * @param int $attachment_id Current attachment ID.
441 * @param string $meta_key Meta key.
442 * @param mixed $metadata Meta value.
443 */
444 public function do_auto_optimization_after_meta_update( $meta_id, $attachment_id, $meta_key, $metadata ) {
445 if ( '_wp_attachment_metadata' !== $meta_key ) {
446 return;
447 }
448
449 if ( self::is_optimization_prevented( $attachment_id ) ) {
450 return;
451 }
452
453 if ( ! $this->has_step( $attachment_id, 'update' ) ) {
454 return;
455 }
456
457 $this->do_auto_optimization( $attachment_id, $this->has_step( $attachment_id, 'upload' ) );
458 }
459
460 /**
461 * Launch auto optimization immediately after the post meta '_wp_attachment_metadata' is added or updated.
462 *
463 * @since 1.8.4
464 * @since 1.9.8 Changed signature.
465 *
466 * @param int $attachment_id The media ID.
467 * @param bool $is_new_upload True if it's a new upload. False otherwize.
468 */
469 public function do_auto_optimization( $attachment_id, $is_new_upload ) {
470 $this->unset_steps( $attachment_id );
471
472 $process = imagify_get_optimization_process( $attachment_id, 'wp' );
473
474 /**
475 * Fires before an attachment auto-optimization is triggered.
476 *
477 * @since 1.8.4
478 *
479 * @param int $attachment_id The attachment ID.
480 * @param bool $is_new_upload True if it's a new upload. False otherwize.
481 */
482 do_action_deprecated( 'imagify_before_auto_optimization_launch', [ $attachment_id, $is_new_upload ], '1.9', 'imagify_before_auto_optimization' );
483
484 /**
485 * Triggered before a media is auto-optimized.
486 *
487 * @since 1.8.4
488 *
489 * @param int $attachment_id The media ID.
490 * @param bool $is_new_upload True if it's a new upload. False otherwize.
491 */
492 do_action( 'imagify_before_auto_optimization', $attachment_id, $is_new_upload );
493
494 if ( $is_new_upload ) {
495 /**
496 * It's a new upload.
497 */
498 // Optimize. Flag as priority so it jumps ahead of any bulk optimization queue.
499 $process->optimize(
500 null,
501 [
502 'is_new_upload' => 1,
503 'priority' => true,
504 ]
505 );
506 } else {
507 /**
508 * The media has already been optimized (or at least it has been tried).
509 */
510 $process_data = $process->get_data();
511
512 // Get the optimization level before deleting the optimization data.
513 $optimization_level = $process_data->get_optimization_level();
514
515 // Some specifics for the image editor.
516 if ( isset( $_POST['action'], $_POST['do'], $_POST['postid'] ) && 'image-editor' === $_POST['action'] && (int) $_POST['postid'] === $attachment_id ) { // WPCS: CSRF ok.
517 check_ajax_referer( 'image_editor-' . $attachment_id );
518
519 if ( ! current_user_can( 'edit_post', $attachment_id ) ) {
520 imagify_die();
521 }
522
523 // Restore the backup file.
524 $result = $process->restore();
525
526 if ( is_wp_error( $result ) ) {
527 // Restoration failed, there is no good way to handle this case.
528 $process_data->delete_optimization_data();
529 }
530 } else {
531 // Remove old optimization data.
532 $process_data->delete_optimization_data();
533 }
534
535 // Optimize.
536 $process->optimize( $optimization_level );
537 }
538
539 /**
540 * Triggered after a media auto-optimization is launched.
541 *
542 * @since 1.8.4
543 *
544 * @param int $attachment_id The media ID.
545 * @param bool $is_new_upload True if it's a new upload. False otherwize.
546 */
547 do_action( 'imagify_after_auto_optimization', $attachment_id, $is_new_upload );
548 }
549
550 /**
551 * Remove the attachment ID from the $attachments property if the post meta '_wp_attachment_metadata' is deleted.
552 *
553 * @since 1.8.4
554 *
555 * @param int $meta_ids An array of deleted metadata entry IDs.
556 * @param int $attachment_id Current attachment ID.
557 * @param string $meta_key Meta key.
558 */
559 public function unset_optimization( $meta_ids, $attachment_id, $meta_key ) {
560 if ( '_wp_attachment_metadata' !== $meta_key ) {
561 return;
562 }
563
564 $this->unset_steps( $attachment_id );
565 }
566
567
568 /** ----------------------------------------------------------------------------------------- */
569 /** HOOKS FOR WP 5.3+’S UPLOAD FAILURE RECOVERING =========================================== */
570 /** ----------------------------------------------------------------------------------------- */
571
572 /**
573 * With WP 5.3+, prevent auto-optimization when WP tries to create thumbnails after an upload error, because it triggers wp_update_attachment_metadata() for each thumbnail size.
574 *
575 * @since 1.9.8
576 * @see wp_ajax_media_create_image_subsizes()
577 * @see wp_update_image_subsizes()
578 */
579 public function prevent_auto_optimization_when_recovering_from_upload_failure() {
580 if ( ! check_ajax_referer( 'media-form', false, false ) ) {
581 return;
582 }
583
584 if ( ! current_user_can( 'upload_files' ) ) {
585 return;
586 }
587
588 if ( ! imagify_get_context( 'wp' )->current_user_can( 'auto-optimize' ) ) {
589 return;
590 }
591
592 $attachment_id = ! empty( $_POST['attachment_id'] ) ? (int) $_POST['attachment_id'] : 0;
593
594 if ( empty( $attachment_id ) ) {
595 return;
596 }
597
598 if ( ! imagify_is_attachment_mime_type_supported( $attachment_id ) ) {
599 return;
600 }
601
602 $this->upload_failure_id = $attachment_id;
603
604 // Auto-optimization will be done on shutdown.
605 ob_start( [ $this, 'maybe_do_auto_optimization_after_recovering_from_upload_failure' ] );
606 }
607
608 /**
609 * Maybe launch auto-optimization after recovering from an upload failure, when all thumbnails are created.
610 *
611 * @since 1.9.8
612 * @see wp_ajax_media_create_image_subsizes()
613 *
614 * @param string $content Buffer’s content.
615 * @return string Buffer’s content.
616 */
617 public function maybe_do_auto_optimization_after_recovering_from_upload_failure( $content ) {
618 if ( empty( $content ) ) {
619 return $content;
620 }
621
622 if ( empty( $this->upload_failure_id ) ) {
623 // Uh?
624 return $content;
625 }
626
627 if ( ! get_post( $this->upload_failure_id ) ) {
628 return $content;
629 }
630
631 $json = json_decode( $content );
632
633 if ( empty( $json->success ) ) {
634 return $content;
635 }
636
637 $attachment_id = $this->upload_failure_id;
638 $metadata = wp_get_attachment_metadata( $attachment_id );
639
640 // Launch the process.
641 $this->upload_failure_id = 0;
642 $this->set_step( $attachment_id, 'generate' );
643 $this->store_ids_to_optimize( $metadata, $attachment_id );
644
645 return $content;
646 }
647
648
649 /** ----------------------------------------------------------------------------------------- */
650 /** TOOLS =================================================================================== */
651 /** ----------------------------------------------------------------------------------------- */
652
653 /**
654 * Set a "step" for an attachment.
655 *
656 * @since 1.9.10
657 * @see $this->attachments
658 *
659 * @param int $attachment_id Current attachment ID.
660 * @param string $step The step to add.
661 */
662 public function set_step( $attachment_id, $step ) {
663 if ( empty( $this->attachments[ $attachment_id ] ) ) {
664 $this->attachments[ $attachment_id ] = [];
665 }
666
667 $this->attachments[ $attachment_id ][ $step ] = 1;
668 }
669
670 /**
671 * Unset a "step" for an attachment.
672 *
673 * @since 1.9.10
674 * @see $this->attachments
675 *
676 * @param int $attachment_id Current attachment ID.
677 * @param string $step The step to add.
678 */
679 public function unset_step( $attachment_id, $step ) {
680 unset( $this->attachments[ $attachment_id ][ $step ] );
681
682 if ( empty( $this->attachments[ $attachment_id ] ) ) {
683 $this->unset_steps( $attachment_id );
684 }
685 }
686
687 /**
688 * Unset all "steps" for an attachment.
689 *
690 * @since 1.9.10
691 * @see $this->attachments
692 *
693 * @param int $attachment_id Current attachment ID.
694 */
695 public function unset_steps( $attachment_id ) {
696 unset( $this->attachments[ $attachment_id ] );
697 }
698
699 /**
700 * Tell if a "step" for an attachment exists.
701 *
702 * @since 1.9.10
703 * @see $this->attachments
704 *
705 * @param int $attachment_id Current attachment ID.
706 * @param string $step The step to add.
707 * @return bool
708 */
709 public function has_step( $attachment_id, $step ) {
710 return ! empty( $this->attachments[ $attachment_id ][ $step ] );
711 }
712
713 /**
714 * Prevent an auto-optimization locally.
715 * How to use it:
716 * Imagify_Auto_Optimization::prevent_optimization( $attachment_id );
717 * wp_update_attachment_metadata( $attachment_id );
718 * Imagify_Auto_Optimization::allow_optimization( $attachment_id );
719 *
720 * @since 1.8.4
721 * @since 1.9.8 Prevents/Allows can stack.
722 *
723 * @param int $attachment_id Current attachment ID.
724 */
725 public static function prevent_optimization( $attachment_id ) {
726 if ( ! isset( self::$prevented[ $attachment_id ] ) ) {
727 self::$prevented[ $attachment_id ] = 1;
728 } else {
729 ++self::$prevented[ $attachment_id ];
730 }
731 }
732
733 /**
734 * Allow an auto-optimization locally.
735 * How to use it:
736 * Imagify_Auto_Optimization::prevent_optimization( $attachment_id );
737 * wp_update_attachment_metadata( $attachment_id );
738 * Imagify_Auto_Optimization::allow_optimization( $attachment_id );
739 *
740 * @since 1.8.4
741 * @since 1.9.8 Prevents/Allows can stack.
742 *
743 * @param int $attachment_id Current attachment ID.
744 */
745 public static function allow_optimization( $attachment_id ) {
746 if ( ! isset( self::$prevented[ $attachment_id ] ) ) {
747 return;
748 }
749 --self::$prevented[ $attachment_id ];
750
751 if ( self::$prevented[ $attachment_id ] <= 0 ) {
752 unset( self::$prevented[ $attachment_id ] );
753 }
754 }
755
756 /**
757 * Tell if an auto-optimization is prevented locally.
758 *
759 * @since 1.8.4
760 *
761 * @param int $attachment_id Current attachment ID.
762 * @return bool
763 */
764 public static function is_optimization_prevented( $attachment_id ) {
765 return ! empty( self::$prevented[ $attachment_id ] ) || ! empty( self::$prevented_internally[ $attachment_id ] );
766 }
767
768 /**
769 * Prevent an auto-optimization internally.
770 *
771 * @since 1.9.8
772 *
773 * @param int $attachment_id Current attachment ID.
774 */
775 protected static function prevent_optimization_internally( $attachment_id ) {
776 self::$prevented_internally[ $attachment_id ] = 1;
777 }
778
779 /**
780 * Allow an auto-optimization internally.
781 *
782 * @since 1.9.8
783 *
784 * @param int $attachment_id Current attachment ID.
785 */
786 protected static function allow_optimization_internally( $attachment_id ) {
787 unset( self::$prevented_internally[ $attachment_id ] );
788 }
789 }
790