PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / 1.3.6.1
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF v1.3.6.1
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.6.1, at inc/classes/class-attachment.php

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