PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / 1.3.4
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF v1.3.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-attachment.php

class-attachment.php in Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF 1.3.4, at inc/classes/class-attachment.php

606 lines 14.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 defined( 'ABSPATH' ) or die( 'Cheatin\' uh?' );
3
4 class Imagify_Attachment {
5 /**
6 * The attachment ID
7 *
8 * @since 1.0
9 *
10 * @var int
11 * @access public
12 */
13 public $id;
14
15 /**
16 * The constructor
17 *
18 * @since 1.0
19 *
20 * @return void
21 **/
22 function __construct( $id = 0 ) {
23 global $post;
24
25 if ( is_object( $post ) && ! $id ) {
26 $this->id = $post->ID;
27 } else {
28 $this->id = (int) $id;
29 }
30 }
31
32 /**
33 * Get the attachment backup filepath.
34 *
35 * @since 1.0
36 *
37 * @access public
38 * @return string|false
39 */
40 public function get_backup_path() {
41 $file_path = $this->get_original_path();
42 $backup_path = get_imagify_attachment_backup_path( $file_path );
43
44 if( file_exists( $backup_path ) ) {
45 return $backup_path;
46 }
47
48 return false;
49 }
50
51 /**
52 * Get the attachment optimization data.
53 *
54 * @since 1.0
55 *
56 * @access public
57 * @return array
58 */
59 public function get_data() {
60 return get_post_meta( $this->id, '_imagify_data', true );
61 }
62
63 /**
64 * Get the attachment extension.
65 *
66 * @since 1.0
67 *
68 * @access public
69 * @return string
70 */
71 public function get_extension() {
72 $fullsize_path = $this->get_original_path();
73 return pathinfo( $fullsize_path, PATHINFO_EXTENSION );
74 }
75
76 /**
77 * Get the attachment error if there is one.
78 *
79 * @since 1.1.5
80 *
81 * @access public
82 * @return string The message error
83 */
84 public function get_optimized_error() {
85 $error = $this->get_size_data( 'full', 'error' );
86
87 if ( is_string( $error ) ) {
88 return $error;
89 }
90
91 return false;
92 }
93
94 /**
95 * Get the attachment optimization level.
96 *
97 * @since 1.0
98 *
99 * @access public
100 * @return int
101 */
102 public function get_optimization_level() {
103 return get_post_meta( $this->id, '_imagify_optimization_level', true );
104 }
105
106 /**
107 * Get the attachment optimization level label.
108 *
109 * @since 1.2
110 *
111 * @access public
112 * @return string
113 */
114 public function get_optimization_level_label() {
115 $label = '';
116 $level = $this->get_optimization_level();
117
118 switch( $level ) {
119 case 2:
120 $label = __( 'Ultra', 'imagify' );
121 break;
122 case 1:
123 $label = __( 'Aggressive', 'imagify' );
124 break;
125 case 0:
126 $label = __( 'Normal', 'imagify' );
127 break;
128 }
129
130 return $label;
131 }
132
133 /**
134 * Count number of optimized sizes.
135 *
136 * @since 1.0
137 *
138 * @access public
139 * @return int
140 */
141 public function get_optimized_sizes_count() {
142 $data = $this->get_data();
143 $sizes = (array) $data['sizes'];
144 $count = 0;
145
146 if ( isset( $sizes['full'] ) ) {
147 unset( $sizes['full'] );
148 }
149
150 foreach ( $sizes as $size ) {
151 if ( $size['success'] ) {
152 $count++;
153 }
154 }
155
156 return (int) $count;
157 }
158
159 /**
160 * Get the attachment optimization status (success or error).
161 *
162 * @since 1.0
163 *
164 * @access public
165 * @return string
166 */
167 public function get_status() {
168 return get_post_meta( $this->id, '_imagify_status', true );
169 }
170
171 /**
172 * Get the original attachment path.
173 *
174 * @since 1.0
175 *
176 * @access public
177 * @return string
178 */
179 public function get_original_path() {
180 return get_attached_file( $this->id );
181 }
182
183 /**
184 * Get the original attachment size.
185 *
186 * @since 1.0
187 *
188 * @access public
189 * @return string
190 */
191 public function get_original_size() {
192 $original_size = $this->get_size_data( 'full', 'original_size' );
193 $original_size = ( empty( $original_size ) ) ? @filesize( $this->get_original_path() ) : $original_size;
194 $original_size = size_format( $original_size, 2 );
195
196 return $original_size;
197 }
198
199 /**
200 * Get the original attachment URL.
201 *
202 * @since 1.0
203 *
204 * @access public
205 * @return string
206 */
207 public function get_original_url() {
208 return wp_get_attachment_url( $this->id );
209 }
210
211 /*
212 * Get the statistics of a specific size.
213 *
214 * @since 1.0
215 *
216 * @access public
217 * @param string $size The thumbnail slug.
218 * @param string $key The specific data slug.
219 * @return array|string
220 */
221 public function get_size_data( $size = 'full', $key = '' ) {
222 $data = $this->get_data();
223 $stats = array();
224
225 if ( isset( $data['sizes'][ $size ] ) ) {
226 $stats = $data['sizes'][ $size ];
227 }
228
229 if ( isset( $stats[ $key ] ) ) {
230 $stats = $stats[ $key ];
231 }
232
233 return $stats;
234 }
235
236 /**
237 * Get the global statistics data or a specific one.
238 *
239 * @since 1.0
240 *
241 * @access public
242 * @param string $key The specific data slug.
243 * @return array|string
244 */
245 public function get_stats_data( $key = '' ) {
246 $data = $this->get_data();
247 $stats = '';
248
249 if ( isset( $data['stats'] ) ) {
250 $stats = $data['stats'];
251 }
252
253 if ( isset( $stats[ $key ] ) ) {
254 $stats = $stats[ $key ];
255 }
256
257 return $stats;
258 }
259
260 /**
261 * Check if the attachment is already optimized (before Imagify).
262 *
263 * @since 1.1.6
264 *
265 * @access public
266 * @return bool True if the attachment is optimized.
267 */
268 public function is_already_optimized() {
269 return ( 'already_optimized' === $this->get_status() ) > 0;
270 }
271
272 /**
273 * Check if the attachment is optimized.
274 *
275 * @since 1.0
276 *
277 * @access public
278 * @return bool True if the attachment is optimized.
279 */
280 public function is_optimized() {
281 return ( 'success' === $this->get_status() ) > 0;
282 }
283
284 /**
285 * Check if the attachment exceeding the limit size (> 5mo).
286 *
287 * @since 1.0
288 *
289 * @access public
290 * @return bool True if the attachment is skipped.
291 */
292 public function is_exceeded() {
293 $filepath = $this->get_original_path();
294 $size = 0;
295
296 if ( file_exists( $filepath ) ) {
297 $size = filesize( $filepath );
298 }
299
300 return ( $size > IMAGIFY_MAX_BYTES ) > 0;
301 }
302
303 /**
304 * Check if the attachment has a backup of the original size.
305 *
306 * @since 1.0
307 *
308 * @access public
309 * @return bool True if the attachment has a backup.
310 */
311 public function has_backup() {
312 return (bool) $this->get_backup_path();
313 }
314
315 /**
316 * Check if the attachment has an error.
317 *
318 * @since 1.0
319 *
320 * @access public
321 * @return bool True if the attachment has an error.
322 */
323 public function has_error() {
324 $has_error = $this->get_size_data( 'full', 'error' );
325 return ( is_string( $has_error ) ) > 0;
326 }
327
328 /**
329 * Update the metadata size of the attachment
330 *
331 * @since 1.2
332 *
333 * @access public
334 * @return void
335 */
336 public function update_metadata_size() {
337 if ( ! wp_attachment_is_image( $this->id ) ) {
338 return false;
339 }
340
341 $size = @getimagesize( $this->get_original_path() );
342
343 if ( isset( $size[0], $size[1] ) ) {
344 $metadata = wp_get_attachment_metadata( $this->id );
345 $metadata['width'] = $size[0];
346 $metadata['height'] = $size[1];
347
348 wp_update_attachment_metadata( $this->id, $metadata );
349 }
350 }
351
352 /**
353 * Delete the backup file.
354 *
355 * @since 1.0
356 *
357 * @access public
358 * @return void
359 */
360 public function delete_backup() {
361 $backup_path = $this->get_backup_path();
362
363 if ( ! empty( $backup_path ) ) {
364 @unlink( $backup_path );
365 }
366 }
367
368 /**
369 * Fills statistics data with values from $data array
370 *
371 * @since 1.0
372 *
373 * @access public
374 * @static
375 * @param array $data The statistics data
376 * @param object $response The API response
377 * @param int $id The attachment ID
378 * @param int $url The attachment URL
379 * @param string $size The attachment size key
380 * @return bool|array False if the original size has an error or an array contains the data for other result
381 */
382 static public function fill_data( $data, $response, $id, $url, $size = 'full' ) {
383 if ( is_wp_error( $response ) ) {
384 $error = $response->get_error_message();
385 $error_status = 'error';
386
387 $data['sizes'][ $size ] = array(
388 'success' => false,
389 'error' => $error
390 );
391
392 // Update the error status for the original size
393 if ( 'full' === $size ) {
394 update_post_meta( $id, '_imagify_data', $data );
395
396 if ( false !== strpos( $error, 'This image is already compressed' ) ) {
397 $error_status = 'already_optimized';
398 }
399
400 update_post_meta( $id, '_imagify_status', $error_status );
401
402 return false;
403 }
404 } else {
405 $data['sizes'][ $size ] = array(
406 'success' => true,
407 'file_url' => $url,
408 'original_size' => $response->original_size,
409 'optimized_size' => $response->new_size,
410 'percent' => $response->percent
411 );
412
413 $data['stats']['original_size'] += ( isset( $response->original_size ) ) ? $response->original_size : 0;
414 $data['stats']['optimized_size'] += ( isset( $response->new_size ) ) ? $response->new_size : 0;
415 }
416
417 return $data;
418 }
419
420 /**
421 * Optimize all sizes with Imagify.
422 *
423 * @since 1.0
424 *
425 * @access public
426 * @param int $optimization_level The optimization level (2=ultra, 1=aggressive, 0=normal)
427 * @param array $metadata The attachment meta data
428 * @return array $optimized_data The optimization data
429 */
430 public function optimize( $optimization_level = null, $metadata = array() ) {
431 $optimization_level = ( is_null( $optimization_level ) ) ? (int) get_imagify_option( 'optimization_level', 1 ) : (int) $optimization_level;
432
433 $id = $this->id;
434 $metadata = ( (bool) $metadata ) ? $metadata : wp_get_attachment_metadata( $id );
435 $sizes = ( isset( $metadata['sizes'] ) ) ? (array) $metadata['sizes'] : array();
436 $data = array(
437 'stats' => array(
438 'original_size' => 0,
439 'optimized_size' => 0,
440 'percent' => 0,
441 )
442 );
443
444 // Get file path & URL for original image
445 $attachment_path = $this->get_original_path();
446 $attachment_url = $this->get_original_url();
447
448 // Check if the attachment extension is allowed
449 if ( ! $id || ! wp_attachment_is_image( $id ) ) {
450 return;
451 }
452
453 // Check if the full size is already optimized
454 if ( $this->is_optimized() && ( $this->get_optimization_level() == $optimization_level ) ) {
455 return;
456 }
457
458 /**
459 * Fires before optimizing an attachment.
460 *
461 * @since 1.0
462 *
463 * @param int $id The attachment ID
464 */
465 do_action( 'before_imagify_optimize_attachment', $id );
466
467 // Get the resize values for the original size
468 $resize = array();
469 $do_resize = get_imagify_option( 'resize_larger', false );
470 $resize_width = get_imagify_option( 'resize_larger_w' );
471 $attachment_size = @getimagesize( $attachment_path );
472
473 if ( $do_resize && isset( $attachment_size[0] ) && $resize_width < $attachment_size[0] ) {
474 $resize['width'] = $resize_width;
475 }
476
477 // Optimize the original size
478 $response = do_imagify( $attachment_path, get_imagify_option( 'backup', false ), $optimization_level, $resize, get_imagify_option( 'exif', false ) );
479 $data = $this->fill_data( $data, $response, $id, $attachment_url );
480
481 // Save the optimization level
482 update_post_meta( $id, '_imagify_optimization_level', $optimization_level );
483
484 if( (bool) ! $data ) {
485 return;
486 }
487
488 // If we resized the original with success, we have to update the attachment metadata
489 // If not, WordPress keeps the old attachment size.
490 if ( $do_resize && isset( $resize['width'] ) ) {
491 $this->update_metadata_size();
492 }
493
494 // Optimize all thumbnails
495 if ( (bool) $sizes ) {
496 foreach ( $sizes as $size_key => $size_data ) {
497 // Check if this size has to be optimized
498 if ( array_key_exists( $size_key, get_imagify_option( 'disallowed-sizes', array() ) ) && ! imagify_is_active_for_network() ) {
499 $data['sizes'][ $size_key ] = array(
500 'success' => false,
501 'error' => __( 'This size isn\'t authorized to be optimized. Update your Imagify settings if you want to optimize it.', 'imagify' )
502 );
503 continue;
504 }
505
506 $thumbnail_path = trailingslashit( dirname( $attachment_path ) ) . $size_data['file'];
507 $thumbnail_url = trailingslashit( dirname( $attachment_url ) ) . $size_data['file'];
508
509 // Optimize the thumbnail size
510 $response = do_imagify( $thumbnail_path, false, $optimization_level );
511 $data = $this->fill_data( $data, $response, $id, $thumbnail_url, $size_key );
512
513 /**
514 * Filter the optimization data of a specific thumbnail
515 *
516 * @since 1.0
517 *
518 * @param array $data The statistics data
519 * @param object $response The API response
520 * @param int $id The attachment ID
521 * @param string $thumbnail_path The attachment path
522 * @param string $thumbnail_url The attachment URL
523 * @param string $size_key The attachment size key
524 * @param bool $is_aggressive The optimization level
525 * @return array $data The new optimization data
526 */
527 $data = apply_filters( 'imagify_fill_thumbnail_data', $data, $response, $id, $thumbnail_path, $thumbnail_url, $size_key, $optimization_level );
528 }
529 }
530
531 $data['stats']['percent'] = round( ( ( $data['stats']['original_size'] - $data['stats']['optimized_size'] ) / $data['stats']['original_size'] ) * 100, 2 );
532
533 update_post_meta( $id, '_imagify_data', $data );
534 update_post_meta( $id, '_imagify_status', 'success' );
535
536 $optimized_data = $this->get_data();
537
538 /**
539 * Fires after optimizing an attachment.
540 *
541 * @since 1.0
542 *
543 * @param int $id The attachment ID
544 * @param array $optimized_data The optimization data
545 */
546 do_action( 'after_imagify_optimize_attachment', $id, $optimized_data );
547
548 return $optimized_data;
549 }
550
551 /**
552 * Process an attachment restoration from the backup file.
553 *
554 * @since 1.0
555 *
556 * @access public
557 * @return void
558 */
559 public function restore() {
560 // Stop the process if there is no backup file to restore
561 if ( ! $this->has_backup() ) {
562 return;
563 }
564
565 $id = $this->id;
566 $backup_path = $this->get_backup_path();
567 $attachment_path = $this->get_original_path();
568
569 /**
570 * Fires before restoring an attachment.
571 *
572 * @since 1.0
573 *
574 * @param int $id The attachment ID
575 */
576 do_action( 'before_imagify_restore_attachment', $id );
577
578 // Create the original image from the backup
579 @copy( $backup_path, $attachment_path );
580 imagify_chmod_file( $attachment_path );
581
582 if ( ! function_exists( 'wp_generate_attachment_metadata' ) ) {
583 require_once( ABSPATH . 'wp-admin/includes/image.php' );
584 }
585
586 remove_filter( 'wp_generate_attachment_metadata', '_imagify_optimize_attachment', PHP_INT_MAX );
587 wp_generate_attachment_metadata( $id, $attachment_path );
588
589 // Remove old optimization data
590 delete_post_meta( $id, '_imagify_data' );
591 delete_post_meta( $id, '_imagify_status' );
592 delete_post_meta( $id, '_imagify_optimization_level' );
593
594 // Restore the original size in the metadata
595 $this->update_metadata_size();
596
597 /**
598 * Fires after restoring an attachment.
599 *
600 * @since 1.0
601 *
602 * @param int $id The attachment ID
603 */
604 do_action( 'after_imagify_restore_attachment', $id );
605 }
606 }