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 +212 -7 4.1.04.1.2 View file →
@@ -106,8 +106,9 @@
106 106 if ( $avif_image ) {
107 107 $image_file['size'] = filesize( $image_file['tmp_name'] );
108 108 $image_file['type'] = 'image/avif';
109 109 $image_file['quickwebp'] = 'optimized';
110 + $this->record_optimization_stats( $image_file['new_size'], $image_file['size'] );
110 111 quickwebp_debug_log( 'image_optimization:avif_success', array( 'new_size' => $image_file['size'] ) );
111 112 }
112 113 }
113 114
@@ -117,8 +118,9 @@
117 118 if ( $webp_image ) {
118 119 $image_file['size'] = filesize( $image_file['tmp_name'] );
119 120 $image_file['type'] = 'image/webp';
120 121 $image_file['quickwebp'] = 'optimized';
122 + $this->record_optimization_stats( $image_file['new_size'], $image_file['size'] );
121 123 quickwebp_debug_log( 'image_optimization:webp_success', array( 'new_size' => $image_file['size'] ) );
122 124 }
123 125 }
124 126
@@ -189,8 +191,10 @@
189 191 }
190 192 }
191 193
192 194 if ( ! empty( $new_sizes ) ) {
195 + $this->record_attachment_optimization_stats( $new_sizes );
196 +
193 197 if ( '1' == $mode_enabled ) {
194 198 update_post_meta( $attachment_id, 'quickwebp_already_optimized', '1' );
195 199 } elseif ( '2' == $mode_enabled ) {
196 200 update_post_meta( $attachment_id, 'quickwebp_already_optimized', '2' );
@@ -411,8 +415,9 @@
411 415 }
412 416 }
413 417
414 418 if ( ! empty( $new_sizes ) ) {
419 + $this->record_attachment_optimization_stats( $new_sizes );
415 420
416 421 $data = get_post_meta( $attachment_id, 'quickwebp_data', true );
417 422 if ( ! empty( $data ) ) {
418 423 $this->remove_related_files( $data );
@@ -554,11 +559,43 @@
554 559 }
555 560
556 561 /**
557 562 * Get the unoptimized media ids
563 + *
564 + * @param int $limit Maximum number of media IDs to retrieve.
565 + * @return int[]
558 566 */
559 - 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;
560 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 +
561 598 $settings = $this->get_settings();
562 599 $mode_enabled = $settings['quickwebp_settings_conversion'];
563 600 $ignore_same_format = is_array( $settings['quickwebp_settings_conversion_ignore_webp'] ) ? $settings['quickwebp_settings_conversion_ignore_webp'] : array();
564 601
@@ -604,14 +641,12 @@
604 641 'value' => '2',
605 642 );
606 643 }
607 644
608 - $media_ids = get_posts( array(
645 + return array(
609 646 'post_type' => 'attachment',
610 647 'post_mime_type' => $allowed_mime_types,
611 648 'post_status' => array_keys( $statuses ),
612 - 'posts_per_page' => -1,
613 - 'fields' => 'ids',
614 649 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
615 650 'meta_query' => array(
616 651 'relation' => 'AND',
617 652 array(
@@ -619,11 +654,9 @@
619 654 'compare' => 'NOT EXISTS'
620 655 ),
621 656 $meta_query_already_optimized,
622 657 ),
623 - ) );
624 -
625 - return $media_ids;
658 + );
626 659 }
627 660
628 661 /**
629 662 * Get the list of media files
@@ -727,8 +760,180 @@
727 760 'percent' => round( $percent, 2 ),
728 761 'path' => $new_path,
729 762 'format' => $mode_enabled,
730 763 );
764 + }
765 +
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 + }
731 936 }
732 937
733 938 /**
734 939 * Remove the related files of an optimized attachment