PluginProbe
QuickWebP – WebP & AVIF Image Optimizer, Compression & SEO for WordPress / 4.1.2
QuickWebP – WebP & AVIF Image Optimizer, Compression & SEO for WordPress v4.1.2
4.1.2 4.1.1 4.1.0 4.0.1 4.0.0 3.3.1 3.3.0 trunk 1.0.0 2.0.0 2.1.0 3.0.0 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8
← All changes | admin/class-image-optimizer.php +342 -13 4.0.14.1.2 View file →
@@ -53,22 +53,30 @@
53 53 */
54 54 public function image_optimization( $file ) {
55 55 global $quickwebp_surecart_client;
56 56
57 + quickwebp_debug_log( 'image_optimization:start', array(
58 + 'file_name' => $file['name'] ?? '',
59 + 'file_type' => $file['type'] ?? '',
60 + ) );
61 +
57 62 $settings = $this->get_settings();
58 63 $image_file = $this->file_is_image( $file, $settings );
59 64
60 65 if ( ! $image_file ) {
66 + quickwebp_debug_log( 'image_optimization:not_image' );
61 67 return $file;
62 68 }
63 69
64 70 $mode_enabled = $settings['quickwebp_settings_conversion'];
65 71 if ( $mode_enabled === '0' ) {
72 + quickwebp_debug_log( 'image_optimization:conversion_disabled' );
66 73 return $image_file;
67 74 }
68 75
69 76 $save_original = is_array( $settings['quickwebp_settings_conversion_save_original'] ) ? $settings['quickwebp_settings_conversion_save_original'] : array();
70 77 if ( in_array( 'checked', $save_original ) ) {
78 + quickwebp_debug_log( 'image_optimization:save_original_enabled' );
71 79 return $image_file;
72 80 }
73 81
74 82 $ignore_same_format = is_array( $settings['quickwebp_settings_conversion_ignore_webp'] ) ? $settings['quickwebp_settings_conversion_ignore_webp'] : array();
@@ -74,10 +82,12 @@
74 82 $ignore_same_format = is_array( $settings['quickwebp_settings_conversion_ignore_webp'] ) ? $settings['quickwebp_settings_conversion_ignore_webp'] : array();
75 83 $mime_type = wp_get_image_mime( $image_file['tmp_name'] );
76 84 if ( in_array( 'checked', $ignore_same_format ) ) {
77 85 if ( '1' == $mode_enabled && 'image/webp' == $mime_type ) {
86 + quickwebp_debug_log( 'image_optimization:skip_same_format', array( 'mime_type' => $mime_type ) );
78 87 return $image_file;
79 88 } elseif( '2' == $mode_enabled && 'image/avif' == $mime_type ) {
89 + quickwebp_debug_log( 'image_optimization:skip_same_format', array( 'mime_type' => $mime_type ) );
80 90 return $image_file;
81 91 }
82 92 }
83 93
@@ -90,22 +100,28 @@
90 100 $image_file['new_size'] = $image_file['size'];
91 101 $image_file['new_type'] = $image_file['type'];
92 102
93 103 if ( '2' === $mode_enabled && $is_pro ) {
104 + quickwebp_debug_log( 'image_optimization:try_avif', array( 'mime_type' => $mime_type, 'quality' => $quality ) );
94 105 $avif_image = $this->create_avif_image( $image_file['tmp_name'], $image_file['tmp_name'], $quality );
95 106 if ( $avif_image ) {
96 107 $image_file['size'] = filesize( $image_file['tmp_name'] );
97 108 $image_file['type'] = 'image/avif';
98 109 $image_file['quickwebp'] = 'optimized';
110 + $this->record_optimization_stats( $image_file['new_size'], $image_file['size'] );
111 + quickwebp_debug_log( 'image_optimization:avif_success', array( 'new_size' => $image_file['size'] ) );
99 112 }
100 113 }
101 114
102 115 if ( '1' === $mode_enabled ) {
116 + quickwebp_debug_log( 'image_optimization:try_webp', array( 'mime_type' => $mime_type, 'quality' => $quality ) );
103 117 $webp_image = $this->create_webp_image( $image_file['tmp_name'], $image_file['tmp_name'], $quality );
104 118 if ( $webp_image ) {
105 119 $image_file['size'] = filesize( $image_file['tmp_name'] );
106 120 $image_file['type'] = 'image/webp';
107 121 $image_file['quickwebp'] = 'optimized';
122 + $this->record_optimization_stats( $image_file['new_size'], $image_file['size'] );
123 + quickwebp_debug_log( 'image_optimization:webp_success', array( 'new_size' => $image_file['size'] ) );
108 124 }
109 125 }
110 126
111 127 return $image_file;
@@ -175,8 +191,10 @@
175 191 }
176 192 }
177 193
178 194 if ( ! empty( $new_sizes ) ) {
195 + $this->record_attachment_optimization_stats( $new_sizes );
196 +
179 197 if ( '1' == $mode_enabled ) {
180 198 update_post_meta( $attachment_id, 'quickwebp_already_optimized', '1' );
181 199 } elseif ( '2' == $mode_enabled ) {
182 200 update_post_meta( $attachment_id, 'quickwebp_already_optimized', '2' );
@@ -278,8 +296,10 @@
278 296 */
279 297 public function image_optimization_ajax() {
280 298 global $quickwebp_surecart_client;
281 299
300 + quickwebp_debug_log( 'image_optimization_ajax:start' );
301 +
282 302 if ( ! current_user_can( 'upload_files' ) ) {
283 303 wp_send_json_error( __( 'You are not allowed to upload files.', 'quickwebp' ), 403 );
284 304 }
285 305
@@ -291,8 +311,9 @@
291 311
292 312 // Get the file
293 313 $file = count($_FILES) > 0 ? array_shift($_FILES) : array();
294 314 if ( empty($file) ) {
315 + quickwebp_debug_log( 'image_optimization_ajax:missing_file' );
295 316 wp_send_json_error( __( 'No image uploaded, try again.', 'quickwebp' ) );
296 317 }
297 318
298 319 $settings = $this->get_ajax_settings();
@@ -298,13 +319,15 @@
298 319 $settings = $this->get_ajax_settings();
299 320 $image_file = $this->file_is_image( $file, $settings );
300 321
301 322 if ( ! $image_file ) {
323 + quickwebp_debug_log( 'image_optimization_ajax:not_image' );
302 324 wp_send_json_error( __( 'No image uploaded, try again.', 'quickwebp' ) );
303 325 }
304 326
305 327 $mode_enabled = $settings['quickwebp_settings_conversion'];
306 328 if ( $mode_enabled === '0' ) {
329 + quickwebp_debug_log( 'image_optimization_ajax:conversion_disabled' );
307 330 $image_file['new_size'] = $image_file['size'];
308 331 $image_file['new_type'] = $image_file['type'];
309 332 $this->return_ajax_data( $image_file );
310 333 }
@@ -318,8 +341,9 @@
318 341 $image_file['new_size'] = $image_file['size'];
319 342 $image_file['new_type'] = $image_file['type'];
320 343
321 344 if ( '2' === $mode_enabled && $is_pro ) {
345 + quickwebp_debug_log( 'image_optimization_ajax:try_avif', array( 'quality' => $quality ) );
322 346 $avif_image = $this->create_avif_image( $image_file['tmp_name'], $image_file['tmp_name'], $quality );
323 347 if ( $avif_image ) {
324 348 $image_file['new_size'] = filesize( $image_file['tmp_name'] );
325 349 $image_file['new_type'] = 'image/avif';
@@ -326,8 +350,9 @@
326 350 }
327 351 }
328 352
329 353 if ( '1' === $mode_enabled ) {
354 + quickwebp_debug_log( 'image_optimization_ajax:try_webp', array( 'quality' => $quality ) );
330 355 $webp_image = $this->create_webp_image( $image_file['tmp_name'], $image_file['tmp_name'], $quality );
331 356 if ( $webp_image ) {
332 357 $image_file['new_size'] = filesize( $image_file['tmp_name'] );
333 358 $image_file['new_type'] = 'image/webp';
@@ -340,8 +365,9 @@
340 365 /**
341 366 * Optimize a single media
342 367 */
343 368 public function single_optimization_ajax() {
369 + quickwebp_debug_log( 'single_optimization_ajax:start' );
344 370
345 371 // verify the nonce.
346 372 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
347 373 if( !wp_verify_nonce( $nonce, 'quickwebp_admin_attachment' ) ) {
@@ -351,8 +377,9 @@
351 377 // Sanitize data
352 378 $attachment_id = isset( $_POST['attachment_id'] ) ? absint( wp_unslash( $_POST['attachment_id'] ) ) : 0;
353 379
354 380 if ( ! $attachment_id ) {
381 + quickwebp_debug_log( 'single_optimization_ajax:missing_attachment_id' );
355 382 wp_send_json_error( __( 'No attachment id.', 'quickwebp' ) );
356 383 }
357 384
358 385 $settings = $this->get_settings();
@@ -379,8 +406,9 @@
379 406 $sizes = $this->get_media_files( $attachment_id );
380 407 $new_sizes = array();
381 408
382 409 foreach ( $sizes as $key => $size ) {
410 + quickwebp_debug_log( 'single_optimization_ajax:optimize_size', array( 'size_key' => $key, 'path' => $size['path'] ?? '' ) );
383 411 $result = $this->optimize_local_file( $size );
384 412
385 413 if ( $result ) {
386 414 $new_sizes[$key] = $result;
@@ -387,8 +415,9 @@
387 415 }
388 416 }
389 417
390 418 if ( ! empty( $new_sizes ) ) {
419 + $this->record_attachment_optimization_stats( $new_sizes );
391 420
392 421 $data = get_post_meta( $attachment_id, 'quickwebp_data', true );
393 422 if ( ! empty( $data ) ) {
394 423 $this->remove_related_files( $data );
@@ -459,8 +488,9 @@
459 488 /**
460 489 * Get the image data for the preview in the settings page
461 490 */
462 491 public function get_image_data_for_preview_ajax() {
492 + quickwebp_debug_log( 'get_image_data_for_preview_ajax:start' );
463 493
464 494 // verify the nonce.
465 495 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
466 496 if( !wp_verify_nonce( $nonce, 'image_optimize_nonce' ) ) {
@@ -469,13 +499,15 @@
469 499
470 500 // Get the file
471 501 $file = count($_FILES) > 0 ? array_shift($_FILES) : array();
472 502 if ( empty($file) ) {
503 + quickwebp_debug_log( 'get_image_data_for_preview_ajax:missing_file' );
473 504 wp_send_json_error( __( 'No image uploaded, try again.', 'quickwebp' ) );
474 505 }
475 506
476 507 $image_file = $this->file_is_image( $file );
477 508 if ( ! $image_file ) {
509 + quickwebp_debug_log( 'get_image_data_for_preview_ajax:not_image' );
478 510 wp_send_json_error( __( 'No image uploaded, try again.', 'quickwebp' ) );
479 511 }
480 512
481 513 $preview_image_data = array(
@@ -488,8 +520,9 @@
488 520 $qualities = array( 'low', 'medium', 'high', 'extra_high' );
489 521
490 522 foreach ( $conversions as $conversion ) {
491 523 foreach ( $qualities as $quality ) {
524 + quickwebp_debug_log( 'get_image_data_for_preview_ajax:preview_variant', array( 'conversion' => $conversion, 'quality' => $quality ) );
492 525
493 526 $size_after = 0;
494 527
495 528 if ( '2' == $conversion ) {
@@ -526,11 +559,43 @@
526 559 }
527 560
528 561 /**
529 562 * Get the unoptimized media ids
563 + *
564 + * @param int $limit Maximum number of media IDs to retrieve.
565 + * @return int[]
530 566 */
531 - public function get_unoptimized_media_ids() {
567 + public function get_unoptimized_media_ids( $limit = 5 ) {
568 + $query_args = $this->get_unoptimized_media_query_args();
569 + $query_args['posts_per_page'] = max( 1, absint( $limit ) );
570 + $query_args['fields'] = 'ids';
571 + $query_args['no_found_rows'] = true;
532 572
573 + return get_posts( $query_args );
574 + }
575 +
576 + /**
577 + * Count unoptimized media.
578 + *
579 + * @return int
580 + */
581 + public function get_unoptimized_media_count() {
582 + $query_args = $this->get_unoptimized_media_query_args();
583 + $query_args['posts_per_page'] = 1;
584 + $query_args['fields'] = 'ids';
585 +
586 + $query = new WP_Query( $query_args );
587 +
588 + return absint( $query->found_posts );
589 + }
590 +
591 + /**
592 + * Get the shared query arguments for unoptimized media.
593 + *
594 + * @return array
595 + */
596 + private function get_unoptimized_media_query_args() {
597 +
533 598 $settings = $this->get_settings();
534 599 $mode_enabled = $settings['quickwebp_settings_conversion'];
535 600 $ignore_same_format = is_array( $settings['quickwebp_settings_conversion_ignore_webp'] ) ? $settings['quickwebp_settings_conversion_ignore_webp'] : array();
536 601
@@ -576,14 +641,12 @@
576 641 'value' => '2',
577 642 );
578 643 }
579 644
580 - $media_ids = get_posts( array(
645 + return array(
581 646 'post_type' => 'attachment',
582 647 'post_mime_type' => $allowed_mime_types,
583 648 'post_status' => array_keys( $statuses ),
584 - 'posts_per_page' => -1,
585 - 'fields' => 'ids',
586 649 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
587 650 'meta_query' => array(
588 651 'relation' => 'AND',
589 652 array(
@@ -591,11 +654,9 @@
591 654 'compare' => 'NOT EXISTS'
592 655 ),
593 656 $meta_query_already_optimized,
594 657 ),
595 - ) );
596 -
597 - return $media_ids;
658 + );
598 659 }
599 660
600 661 /**
601 662 * Get the list of media files
@@ -645,14 +706,18 @@
645 706 */
646 707 public function optimize_local_file( $size ) {
647 708 global $quickwebp_surecart_client;
648 709
710 + quickwebp_debug_log( 'optimize_local_file:start', array( 'path' => $size['path'] ?? '' ) );
711 +
649 712 if ( !is_file($size['path']) ) {
713 + quickwebp_debug_log( 'optimize_local_file:missing_file', array( 'path' => $size['path'] ?? '' ) );
650 714 return false;
651 715 }
652 716
653 717 $real_type = mime_content_type( $size['path']);
654 718 if ( !in_array( $real_type, $this->allowed_mime_types ) ) {
719 + quickwebp_debug_log( 'optimize_local_file:invalid_mime_type', array( 'mime_type' => $real_type ) );
655 720 return false;
656 721 }
657 722
658 723 $settings = $this->get_settings();
@@ -666,13 +731,15 @@
666 731 $new_path = $size['path'] . '.' . ( '2' === $mode_enabled && $is_pro ? 'avif' : 'webp' );
667 732 $size_after = 0;
668 733
669 734 if ( '2' === $mode_enabled && $is_pro ) {
735 + quickwebp_debug_log( 'optimize_local_file:try_avif', array( 'path' => $size['path'], 'quality' => $quality ) );
670 736 $avif_image = $this->create_avif_image( $size['path'], $new_path, $quality );
671 737 if ( $avif_image ) {
672 738 $size_after = filesize( $new_path );
673 739 }
674 740 } elseif ( '1' === $mode_enabled ) {
741 + quickwebp_debug_log( 'optimize_local_file:try_webp', array( 'path' => $size['path'], 'quality' => $quality ) );
675 742 $webp_image = $this->create_webp_image( $size['path'], $new_path, $quality );
676 743 if ( $webp_image ) {
677 744 $size_after = filesize( $new_path );
678 745 }
@@ -678,8 +745,9 @@
678 745 }
679 746 }
680 747
681 748 if ( ! $size_after ) {
749 + quickwebp_debug_log( 'optimize_local_file:conversion_failed', array( 'path' => $size['path'], 'target_path' => $new_path ) );
682 750 return false;
683 751 }
684 752
685 753 $deference = $size_before - $size_after;
@@ -695,8 +763,180 @@
695 763 );
696 764 }
697 765
698 766 /**
767 + * Record aggregated optimization statistics for one image attachment.
768 + *
769 + * @param array $optimized_sizes Optimization results for the attachment sizes.
770 + */
771 + public function record_attachment_optimization_stats( $optimized_sizes ) {
772 + $stats = $this->get_attachment_optimization_stats( $optimized_sizes );
773 + $this->record_optimization_stats( $stats['bytes_before'], $stats['bytes_after'], $stats['images_optimized'] );
774 + }
775 +
776 + /**
777 + * Calculate optimization statistics for attachment sizes without persisting them.
778 + *
779 + * @param array $optimized_sizes Optimization results for the attachment sizes.
780 + * @return array
781 + */
782 + public function get_attachment_optimization_stats( $optimized_sizes ) {
783 + $stats = array(
784 + 'images_optimized' => 0,
785 + 'bytes_before' => 0,
786 + 'bytes_after' => 0,
787 + );
788 +
789 + foreach ( $optimized_sizes as $optimized_size ) {
790 + $stats['bytes_before'] += absint( $optimized_size['original_size'] ?? 0 );
791 + $stats['bytes_after'] += absint( $optimized_size['optimized_size'] ?? 0 );
792 + }
793 +
794 + if ( $stats['bytes_before'] && $stats['bytes_after'] ) {
795 + $stats['images_optimized'] = 1;
796 + }
797 +
798 + return $stats;
799 + }
800 +
801 + /**
802 + * Record local cumulative optimization statistics.
803 + *
804 + * @param int $size_before File size before optimization in bytes.
805 + * @param int $size_after File size after optimization in bytes.
806 + * @param int $images_optimized Number of optimized attachments.
807 + */
808 + public function record_optimization_stats( $size_before, $size_after, $images_optimized = 1 ) {
809 + $size_before = absint( $size_before );
810 + $size_after = absint( $size_after );
811 + $images_optimized = absint( $images_optimized );
812 +
813 + if ( ! $size_before || ! $size_after || ! $images_optimized ) {
814 + return;
815 + }
816 +
817 + $lock_token = $this->acquire_optimization_stats_lock();
818 + if ( ! $lock_token ) {
819 + $this->queue_optimization_stats_delta( $size_before, $size_after, $images_optimized );
820 + return;
821 + }
822 +
823 + $stats = get_option( 'quickwebp_optimization_stats', array() );
824 + $stats = wp_parse_args(
825 + is_array( $stats ) ? $stats : array(),
826 + array(
827 + 'images_optimized' => 0,
828 + 'bytes_before' => 0,
829 + 'bytes_after' => 0,
830 + 'bytes_saved' => 0,
831 + )
832 + );
833 + $pending_stats = $this->consume_optimization_stats_deltas();
834 +
835 + $stats['images_optimized'] = absint( $stats['images_optimized'] ) + $images_optimized + $pending_stats['images_optimized'];
836 + $stats['bytes_before'] = absint( $stats['bytes_before'] ) + $size_before + $pending_stats['bytes_before'];
837 + $stats['bytes_after'] = absint( $stats['bytes_after'] ) + $size_after + $pending_stats['bytes_after'];
838 + $stats['bytes_saved'] = max( 0, $stats['bytes_before'] - $stats['bytes_after'] );
839 +
840 + update_option( 'quickwebp_optimization_stats', $stats, false );
841 + $this->release_optimization_stats_lock( $lock_token );
842 + }
843 +
844 + /**
845 + * Queue a statistics delta when another request owns the update lock.
846 + *
847 + * @param int $size_before File size before optimization in bytes.
848 + * @param int $size_after File size after optimization in bytes.
849 + * @param int $images_optimized Number of optimized attachments.
850 + */
851 + private function queue_optimization_stats_delta( $size_before, $size_after, $images_optimized ) {
852 + $option_name = 'quickwebp_optimization_stats_delta_' . str_replace( '-', '', wp_generate_uuid4() );
853 + add_option(
854 + $option_name,
855 + array(
856 + 'images_optimized' => $images_optimized,
857 + 'bytes_before' => $size_before,
858 + 'bytes_after' => $size_after,
859 + ),
860 + '',
861 + false
862 + );
863 + }
864 +
865 + /**
866 + * Consume queued statistics deltas while holding the update lock.
867 + *
868 + * @return array
869 + */
870 + private function consume_optimization_stats_deltas() {
871 + global $wpdb;
872 +
873 + $pending_stats = array(
874 + 'images_optimized' => 0,
875 + 'bytes_before' => 0,
876 + 'bytes_after' => 0,
877 + );
878 + $option_prefix = 'quickwebp_optimization_stats_delta_';
879 + $option_names = $wpdb->get_col(
880 + $wpdb->prepare(
881 + "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s",
882 + $wpdb->esc_like( $option_prefix ) . '%'
883 + )
884 + );
885 +
886 + foreach ( $option_names as $option_name ) {
887 + $delta = get_option( $option_name, array() );
888 + if ( is_array( $delta ) ) {
889 + $pending_stats['images_optimized'] += absint( $delta['images_optimized'] ?? 0 );
890 + $pending_stats['bytes_before'] += absint( $delta['bytes_before'] ?? 0 );
891 + $pending_stats['bytes_after'] += absint( $delta['bytes_after'] ?? 0 );
892 + }
893 + delete_option( $option_name );
894 + }
895 +
896 + return $pending_stats;
897 + }
898 +
899 + /**
900 + * Acquire an expiring lock for cumulative statistics updates.
901 + *
902 + * @return string|false
903 + */
904 + private function acquire_optimization_stats_lock() {
905 + $lock_token = wp_generate_uuid4();
906 + $lock = array(
907 + 'token' => $lock_token,
908 + 'expires' => time() + MINUTE_IN_SECONDS,
909 + );
910 +
911 + if ( add_option( 'quickwebp_optimization_stats_lock', $lock, '', false ) ) {
912 + return $lock_token;
913 + }
914 +
915 + $existing_lock = get_option( 'quickwebp_optimization_stats_lock', array() );
916 + if ( is_array( $existing_lock ) && absint( $existing_lock['expires'] ?? 0 ) < time() ) {
917 + delete_option( 'quickwebp_optimization_stats_lock' );
918 + if ( add_option( 'quickwebp_optimization_stats_lock', $lock, '', false ) ) {
919 + return $lock_token;
920 + }
921 + }
922 +
923 + return false;
924 + }
925 +
926 + /**
927 + * Release the statistics lock owned by this process.
928 + *
929 + * @param string $lock_token Lock ownership token.
930 + */
931 + private function release_optimization_stats_lock( $lock_token ) {
932 + $lock = get_option( 'quickwebp_optimization_stats_lock', array() );
933 + if ( is_array( $lock ) && hash_equals( (string) ( $lock['token'] ?? '' ), $lock_token ) ) {
934 + delete_option( 'quickwebp_optimization_stats_lock' );
935 + }
936 + }
937 +
938 + /**
699 939 * Remove the related files of an optimized attachment
700 940 */
701 941 public function remove_related_files( $data ) {
702 942 foreach ( $data as $value ) {
@@ -760,8 +1000,9 @@
760 1000 $file_error = isset($file['error']) ? $file['error'] : '';
761 1001 $file_size = isset($file['size']) ? $file['size'] : '';
762 1002
763 1003 if ( empty($file_tmp_name) ) {
1004 + quickwebp_debug_log( 'file_is_image:missing_tmp_name', array( 'file_name' => $file_name ) );
764 1005 return false;
765 1006 }
766 1007
767 1008 $allowed_mime_types = apply_filters( 'quickwebp_mime_types_allowed', $this->allowed_mime_types );
@@ -767,8 +1008,9 @@
767 1008 $allowed_mime_types = apply_filters( 'quickwebp_mime_types_allowed', $this->allowed_mime_types );
768 1009 $is_image = wp_getimagesize($file_tmp_name);
769 1010 $mime_type = wp_get_image_mime($file_tmp_name);
770 1011 if ( ! $is_image || ! in_array( $mime_type, $allowed_mime_types ) ) {
1012 + quickwebp_debug_log( 'file_is_image:rejected', array( 'file_name' => $file_name, 'mime_type' => $mime_type ) );
771 1013 return false;
772 1014 }
773 1015
774 1016 $name = $file_name;
@@ -896,16 +1138,25 @@
896 1138 *
897 1139 * @return bool
898 1140 */
899 1141 private function create_image_with_wp_editor( $file_path, $output_path, $quality, $target_mime_type ) {
1142 + quickwebp_debug_log( 'create_image_with_wp_editor:start', array(
1143 + 'file_path' => $file_path,
1144 + 'output_path' => $output_path,
1145 + 'quality' => $quality,
1146 + 'target_mime_type' => $target_mime_type,
1147 + ) );
1148 +
900 1149 $editor = wp_get_image_editor( $file_path );
901 1150
902 1151 if ( is_wp_error( $editor ) ) {
1152 + quickwebp_debug_log( 'create_image_with_wp_editor:editor_error', array( 'message' => $editor->get_error_message() ) );
903 1153 return false;
904 1154 }
905 1155
906 1156 $extension = $this->get_extension_from_mime_type( $target_mime_type );
907 1157 if ( empty( $extension ) ) {
1158 + quickwebp_debug_log( 'create_image_with_wp_editor:missing_extension', array( 'target_mime_type' => $target_mime_type ) );
908 1159 return false;
909 1160 }
910 1161
911 1162 $save_path = $output_path;
@@ -916,15 +1167,33 @@
916 1167 $editor->set_quality( $quality );
917 1168 $result = $editor->save( $save_path, $target_mime_type );
918 1169
919 1170 if ( is_wp_error( $result ) || empty( $result['path'] ) || ! file_exists( $result['path'] ) ) {
1171 + quickwebp_debug_log( 'create_image_with_wp_editor:save_failed', array(
1172 + 'output_path' => $save_path,
1173 + 'error' => is_wp_error( $result ) ? $result->get_error_message() : 'missing_generated_file',
1174 + ) );
920 1175 return false;
921 1176 }
922 1177
1178 + $source_size = file_exists( $file_path ) ? filesize( $file_path ) : 0;
1179 + $generated_size = filesize( $result['path'] );
1180 +
1181 + if ( $source_size > 0 && $generated_size > $source_size ) {
1182 + quickwebp_debug_log( 'create_image_with_wp_editor:larger_than_source', array(
1183 + 'source_size' => $source_size,
1184 + 'generated_size' => $generated_size,
1185 + 'output_path' => $result['path'],
1186 + ) );
1187 + wp_delete_file( $result['path'] );
1188 + return false;
1189 + }
1190 +
923 1191 if ( $result['path'] !== $output_path ) {
924 1192 $file_contents = file_get_contents( $result['path'] );
925 1193
926 1194 if ( false === $file_contents || false === file_put_contents( $output_path, $file_contents ) ) {
1195 + quickwebp_debug_log( 'create_image_with_wp_editor:copy_failed', array( 'output_path' => $output_path ) );
927 1196 return false;
928 1197 }
929 1198
930 1199 wp_delete_file( $result['path'] );
@@ -929,8 +1198,14 @@
929 1198
930 1199 wp_delete_file( $result['path'] );
931 1200 }
932 1201
1202 + quickwebp_debug_log( 'create_image_with_wp_editor:success', array(
1203 + 'output_path' => $output_path,
1204 + 'generated_size' => file_exists( $output_path ) ? filesize( $output_path ) : $generated_size,
1205 + 'target_mime_type'=> $target_mime_type,
1206 + ) );
1207 +
933 1208 return true;
934 1209 }
935 1210
936 1211 /**
@@ -962,20 +1237,52 @@
962 1237 return $loader( $file_path );
963 1238 }
964 1239
965 1240 /**
1241 + * Check whether the source mime type can be loaded through GD.
1242 + *
1243 + * @param string $mime_type Source mime type.
1244 + *
1245 + * @return bool
1246 + */
1247 + private function can_load_image_with_gd( $mime_type ) {
1248 + $loaders = array(
1249 + 'image/avif' => 'imagecreatefromavif',
1250 + 'image/webp' => 'imagecreatefromwebp',
1251 + 'image/jpeg' => 'imagecreatefromjpeg',
1252 + 'image/png' => 'imagecreatefrompng',
1253 + );
1254 +
1255 + if ( empty( $loaders[ $mime_type ] ) ) {
1256 + return false;
1257 + }
1258 +
1259 + return function_exists( $loaders[ $mime_type ] );
1260 + }
1261 +
1262 + /**
966 1263 * Create the avif image
967 1264 */
968 1265 private function create_avif_image( $file_path, $output_path, $quality ) {
969 - if ( $this->create_image_with_wp_editor( $file_path, $output_path, $quality, 'image/avif' ) ) {
1266 + $image = false;
1267 + $mime_type = wp_get_image_mime( $file_path );
1268 +
1269 + quickwebp_debug_log( 'create_avif_image:start', array(
1270 + 'file_path' => $file_path,
1271 + 'output_path' => $output_path,
1272 + 'mime_type' => $mime_type,
1273 + 'quality' => $quality,
1274 + ) );
1275 +
1276 + if ( ! $this->can_load_image_with_gd( $mime_type ) && $this->create_image_with_wp_editor( $file_path, $output_path, $quality, 'image/avif' ) ) {
1277 + quickwebp_debug_log( 'create_avif_image:used_wp_editor', array( 'mime_type' => $mime_type ) );
970 1278 return true;
971 1279 }
972 1280
973 - $image = false;
974 - $mime_type = wp_get_image_mime( $file_path );
975 1281 $image = $this->create_gd_image_resource( $file_path, $mime_type );
976 1282
977 1283 if ( ! $image ) {
1284 + quickwebp_debug_log( 'create_avif_image:gd_load_failed', array( 'mime_type' => $mime_type ) );
978 1285 return false;
979 1286 }
980 1287
981 1288 // Handle EXIF orientation for proper image rotation
@@ -1036,11 +1343,17 @@
1036 1343
1037 1344 $avif_image = imageavif( $image, $output_path, $quality );
1038 1345 imagedestroy( $image );
1039 1346 if ( ! $avif_image ) {
1347 + quickwebp_debug_log( 'create_avif_image:gd_save_failed', array( 'output_path' => $output_path ) );
1040 1348 return false;
1041 1349 }
1042 1350
1351 + quickwebp_debug_log( 'create_avif_image:gd_success', array(
1352 + 'output_path' => $output_path,
1353 + 'file_size' => file_exists( $output_path ) ? filesize( $output_path ) : 0,
1354 + ) );
1355 +
1043 1356 return $avif_image;
1044 1357 }
1045 1358
1046 1359 /**
@@ -1046,17 +1359,27 @@
1046 1359 /**
1047 1360 * Create the webp image
1048 1361 */
1049 1362 private function create_webp_image( $file_path, $output_path, $quality ) {
1050 - if ( $this->create_image_with_wp_editor( $file_path, $output_path, $quality, 'image/webp' ) ) {
1363 + $image = false;
1364 + $mime_type = wp_get_image_mime( $file_path );
1365 +
1366 + quickwebp_debug_log( 'create_webp_image:start', array(
1367 + 'file_path' => $file_path,
1368 + 'output_path' => $output_path,
1369 + 'mime_type' => $mime_type,
1370 + 'quality' => $quality,
1371 + ) );
1372 +
1373 + if ( ! $this->can_load_image_with_gd( $mime_type ) && $this->create_image_with_wp_editor( $file_path, $output_path, $quality, 'image/webp' ) ) {
1374 + quickwebp_debug_log( 'create_webp_image:used_wp_editor', array( 'mime_type' => $mime_type ) );
1051 1375 return true;
1052 1376 }
1053 1377
1054 - $image = false;
1055 - $mime_type = wp_get_image_mime( $file_path );
1056 1378 $image = $this->create_gd_image_resource( $file_path, $mime_type );
1057 1379
1058 1380 if ( ! $image ) {
1381 + quickwebp_debug_log( 'create_webp_image:gd_load_failed', array( 'mime_type' => $mime_type ) );
1059 1382 return false;
1060 1383 }
1061 1384
1062 1385 // Handle EXIF orientation for proper image rotation
@@ -1117,10 +1440,16 @@
1117 1440
1118 1441 $webp_image = imagewebp( $image, $output_path, $quality );
1119 1442 imagedestroy( $image );
1120 1443 if ( ! $webp_image ) {
1444 + quickwebp_debug_log( 'create_webp_image:gd_save_failed', array( 'output_path' => $output_path ) );
1121 1445 return false;
1122 1446 }
1447 +
1448 + quickwebp_debug_log( 'create_webp_image:gd_success', array(
1449 + 'output_path' => $output_path,
1450 + 'file_size' => file_exists( $output_path ) ? filesize( $output_path ) : 0,
1451 + ) );
1123 1452
1124 1453 return $webp_image;
1125 1454 }
1126 1455 }