diff --git a/classes/Admin/ServiceProvider.php b/classes/Admin/ServiceProvider.php index 08098d832..2b0889778 100644 --- a/classes/Admin/ServiceProvider.php +++ b/classes/Admin/ServiceProvider.php @@ -6,6 +6,7 @@ use Imagify\Dependencies\League\Container\ServiceProvider\AbstractServiceProvider; use Imagify\Dependencies\WPMedia\PluginFamily\Controller\PluginFamily; use Imagify\User\User; +use Imagify\Admin\Stats\{ Controller as AdminStatsController, Subscriber as AdminStatsSubscriber }; /** * Service provider for Admin. @@ -21,6 +22,7 @@ class ServiceProvider extends AbstractServiceProvider { AdminSubscriber::class, PluginFamily::class, PluginFamilySubscriber::class, + AdminStatsController::class, ]; /** @@ -32,6 +34,7 @@ class ServiceProvider extends AbstractServiceProvider { AdminBar::class, AdminSubscriber::class, PluginFamilySubscriber::class, + AdminStatsSubscriber::class, ]; /** @@ -59,6 +62,10 @@ public function register(): void { $this->getContainer()->add( PluginFamily::class ); $this->getContainer()->addShared( PluginFamilySubscriber::class ) ->addArgument( PluginFamily::class ); + + $this->getContainer()->add( AdminStatsController::class ); + $this->getContainer()->addShared( AdminStatsSubscriber::class ) + ->addArgument( AdminStatsController::class ); } /** diff --git a/classes/Admin/Stats/Controller.php b/classes/Admin/Stats/Controller.php new file mode 100644 index 000000000..7a3d5844a --- /dev/null +++ b/classes/Admin/Stats/Controller.php @@ -0,0 +1,745 @@ + 0, + 'attachments_optimized_count' => 0, + 'attachments_error_count' => 0, + 'saving_data_count' => [ + 'count' => 0, + 'original_size' => 0, + 'optimized_size' => 0, + 'percent' => 0, + ], + 'images_library_total_size' => 0, + 'images_average_size_per_month' => 0, + ]; + + /** + * AS Queue group. + * + * @var string + */ + private $group = 'imagify-stats'; + + /** + * Register actions to run in background. + * + * @return void + */ + public function register_actions(): void { + $counter = 1; + + foreach ( self::$actions as $action ) { + try { + if ( ! as_has_scheduled_action( $action, [], $this->group ) ) { + // Schedule the action to run every day with a 5 second offset for each action. + as_schedule_recurring_action( time() + ( $counter * 5 ), DAY_IN_SECONDS, $action, [], $this->group ); + } + } catch ( Exception $exception ) { + continue; + } + + $counter++; + } + } + + /** + * Cancel all occurrence of scheduled action. + * + * @return void + */ + public function unregister_actions(): void { + foreach ( self::$actions as $action ) { + try { + if ( as_has_scheduled_action( $action, [], $this->group ) ) { + // Remove action from schedule. + as_unschedule_action( $action, [], $this->group ); + } + } catch ( Exception $exception ) { + continue; + } + } + } + + /** + * Get AS actions. + * + * @return array Array of actions. + */ + public static function get_actions(): array { + return self::$actions; + } + + /** + * Register admin stat options. + * + * @return void + */ + public function register_stats_option() { + if ( false !== get_option( $this->stats_option ) ) { + return; + } + + add_option( $this->stats_option, $this->admin_stats ); + } + + /** + * Update stats option. + * + * @param string $key Option key. + * @param mixed $value Option value. + * @return void + */ + public function update_stats( $key, $value ): void { + $stats = get_option( $this->stats_option ); + + if ( ! $stats ) { + $this->admin_stats[ $key ] = $value; + add_option( $this->stats_option, $this->admin_stats ); + + return; + } + + $stats[ $key ] = $value; + update_option( $this->stats_option, $stats ); + } + + /** + * Count number of attachments. + * + * @since 1.0 + * @author Jonathan Buttigieg + * + * @return void + */ + public function imagify_count_attachments(): void { + global $wpdb; + + /** + * Filter the number of attachments. + * 3rd party will be able to override the result. + * + * @since 1.5 + * + * @param int|bool $pre_count Default is false. Provide an integer. + */ + $pre_count = apply_filters( 'imagify_count_attachments', false ); + + if ( false !== $pre_count ) { + $this->update_stats( 'attachments_count', (int) $pre_count ); + return; + } + + $mime_types = Imagify_DB::get_mime_types(); + $statuses = Imagify_DB::get_post_statuses(); + $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause('p.ID', true, true, + "AND p.post_mime_type IN ( $mime_types ) + AND p.post_type = 'attachment' + AND p.post_status IN ( $statuses )"); + $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause(); + $count = (int) $wpdb->get_var( // WPCS: unprepared SQL ok. + " + SELECT COUNT( p.ID ) + FROM $wpdb->posts AS p + $nodata_join + WHERE p.post_mime_type IN ( $mime_types ) + AND p.post_type = 'attachment' + AND p.post_status IN ( $statuses ) + $nodata_where" + ); + + if ( $count > imagify_get_unoptimized_attachment_limit() ) { + set_transient( 'imagify_large_library', 1 ); + } elseif ( get_transient( 'imagify_large_library' ) ) { + // In case the number is decreasing under our limit. + delete_transient( 'imagify_large_library' ); + } + + $this->update_stats( 'attachments_count', $count ); + } + + /** + * Count number of optimized attachments with an error. + * + * @since 1.0 + * @author Jonathan Buttigieg + * + * @return void + */ + public function imagify_count_error_attachments(): void { + global $wpdb; + + /** + * Filter the number of optimized attachments with an error. + * 3rd party will be able to override the result. + * + * @since 1.5 + * + * @param int|bool $pre_count Default is false. Provide an integer. + */ + $pre_count = apply_filters( 'imagify_count_error_attachments', false ); + + if ( false !== $pre_count ) { + $this->update_stats( 'attachments_error_count', (int) $pre_count ); + return; + } + + $mime_types = Imagify_DB::get_mime_types(); + $statuses = Imagify_DB::get_post_statuses(); + $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause(); + $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause(); + $count = (int) $wpdb->get_var( // WPCS: unprepared SQL ok. + " + SELECT COUNT( DISTINCT p.ID ) + FROM $wpdb->posts AS p + $nodata_join + INNER JOIN $wpdb->postmeta AS mt1 + ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' ) + WHERE p.post_mime_type IN ( $mime_types ) + AND p.post_type = 'attachment' + AND p.post_status IN ( $statuses ) + AND mt1.meta_value = 'error' + $nodata_where" + ); + + $this->update_stats( 'attachments_error_count', $count ); + } + + /** + * Count number of optimized attachments (by Imagify or an other tool before). + * + * @since 1.0 + * @author Jonathan Buttigieg + * + * @return void + */ + public function imagify_count_optimized_attachments(): void { + global $wpdb; + + /** + * Filter the number of optimized attachments. + * 3rd party will be able to override the result. + * + * @since 1.5 + * + * @param int|bool $pre_count Default is false. Provide an integer. + */ + $pre_count = apply_filters( 'imagify_count_optimized_attachments', false ); + + if ( false !== $pre_count ) { + $this->update_stats( 'attachments_optimized_count', (int) $pre_count ); + return; + } + + $mime_types = Imagify_DB::get_mime_types(); + $statuses = Imagify_DB::get_post_statuses(); + $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause(); + $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause(); + $count = (int) $wpdb->get_var( // WPCS: unprepared SQL ok. + " + SELECT COUNT( DISTINCT p.ID ) + FROM $wpdb->posts AS p + $nodata_join + INNER JOIN $wpdb->postmeta AS mt1 + ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' ) + WHERE p.post_mime_type IN ( $mime_types ) + AND p.post_type = 'attachment' + AND p.post_status IN ( $statuses ) + AND mt1.meta_value IN ( 'success', 'already_optimized' ) + $nodata_where" + ); + + $this->update_stats( 'attachments_optimized_count', $count ); + } + + /** + * Count percent, original & optimized size of all images optimized by Imagify. + * + * @since 1.0 + * @since 1.6.7 Revamped to handle huge libraries. + * @author Jonathan Buttigieg + * + * @return void + */ + public function imagify_count_saving_data(): void { + global $wpdb; + + /** + * Filter the query to get all optimized attachments. + * 3rd party will be able to override the result. + * + * @since 1.5 + * @since 1.6.7 This filter should return an array containing the following keys: 'count', 'original_size', and 'optimized_size'. + * + * @param bool|array $attachments An array containing the keys ('count', 'original_size', and 'optimized_size'), or an array of attachments (back compat', deprecated), or false. + */ + $attachments = apply_filters( 'imagify_count_saving_data', false ); + + $original_size = 0; + $optimized_size = 0; + $count = 0; + + if ( is_array( $attachments ) ) { + /** + * Bypass. + */ + if ( isset( $attachments['count'], $attachments['original_size'], $attachments['optimized_size'] ) ) { + /** + * We have the results we need. + */ + $attachments['percent'] = $attachments['optimized_size'] && $attachments['original_size'] ? ceil( ( ( $attachments['original_size'] - $attachments['optimized_size'] ) / $attachments['original_size'] ) * 100 ) : 0; + + $this->update_stats( 'saving_data_count', $attachments ); + return; + } + + /** + * Back compat'. + * The following shouldn't be used. Sites with a huge library won't like it. + */ + $attachments = array_map( 'maybe_unserialize', (array) $attachments ); + + if ( $attachments ) { + foreach ( $attachments as $attachment_data ) { + if ( ! $attachment_data ) { + continue; + } + + ++$count; + $original_data = $attachment_data['sizes']['full']; + + // Increment the original sizes. + $original_size += $original_data['original_size'] ? $original_data['original_size'] : 0; + $optimized_size += $original_data['optimized_size'] ? $original_data['optimized_size'] : 0; + + unset( $attachment_data['sizes']['full'] ); + + // Increment the thumbnails sizes. + if ( $attachment_data['sizes'] ) { + foreach ( $attachment_data['sizes'] as $size_data ) { + if ( ! empty( $size_data['success'] ) ) { + $original_size += $size_data['original_size'] ? $size_data['original_size'] : 0; + $optimized_size += $size_data['optimized_size'] ? $size_data['optimized_size'] : 0; + } + } + } + } + } + } else { + /** + * Filter the chunk size of the requests fetching the data. + * 15,000 seems to be a good balance between memory used, speed, and number of DB hits. + * + * @param int $limit The maximum number of elements per chunk. + */ + $limit = apply_filters( 'imagify_count_saving_data_limit', 15000 ); + $limit = absint( $limit ); + + $mime_types = Imagify_DB::get_mime_types(); + $statuses = Imagify_DB::get_post_statuses(); + $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause(); + $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause(); + $attachment_ids = $wpdb->get_col( // WPCS: unprepared SQL ok. + " + SELECT p.ID + FROM $wpdb->posts AS p + $nodata_join + INNER JOIN $wpdb->postmeta AS mt1 + ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' ) + WHERE p.post_mime_type IN ( $mime_types ) + AND p.post_type = 'attachment' + AND p.post_status IN ( $statuses ) + AND mt1.meta_value = 'success' + $nodata_where + ORDER BY CAST( p.ID AS UNSIGNED )" + ); + $wpdb->flush(); + + $attachment_ids = array_map( 'absint', array_unique( $attachment_ids ) ); + $attachment_ids = array_chunk( $attachment_ids, $limit ); + + while ( $attachment_ids ) { + $limit_ids = array_shift( $attachment_ids ); + $limit_ids = implode( ',', $limit_ids ); + + $attachments = $wpdb->get_col( // WPCS: unprepared SQL ok. + " + SELECT meta_value + FROM $wpdb->postmeta + WHERE post_id IN ( $limit_ids ) + AND meta_key = '_imagify_data'" + ); + $wpdb->flush(); + + unset( $limit_ids ); + + if ( ! $attachments ) { + // Uh?! + continue; + } + + $attachments = array_map( 'maybe_unserialize', $attachments ); + + foreach ( $attachments as $attachment_data ) { + if ( ! $attachment_data ) { + continue; + } + + if ( empty( $attachment_data['sizes']['full']['success'] ) ) { + /** + * - Case where this attachment has multiple '_imagify_status' metas, and is fetched (in the above query) as a "success" while the '_imagify_data' says otherwise. + * - Case where this meta has no "full" entry. + * Don't ask how it's possible, I don't know ¯\(°_o)/¯ + */ + continue; + } + + $original_data = $attachment_data['sizes']['full']; + + ++$count; + + // Increment the original sizes. + $original_size += ! empty( $original_data['original_size'] ) ? $original_data['original_size'] : 0; + $optimized_size += ! empty( $original_data['optimized_size'] ) ? $original_data['optimized_size'] : 0; + + unset( $attachment_data['sizes']['full'], $original_data ); + + // Increment the thumbnails sizes. + if ( $attachment_data['sizes'] ) { + foreach ( $attachment_data['sizes'] as $size_data ) { + if ( ! empty( $size_data['success'] ) ) { + $original_size += ! empty( $size_data['original_size'] ) ? $size_data['original_size'] : 0; + $optimized_size += ! empty( $size_data['optimized_size'] ) ? $size_data['optimized_size'] : 0; + } + } + } + + unset( $size_data ); + } + + unset( $attachments, $attachment_data ); + } // End while(). + } // End if(). + + $data = array( + 'count' => $count, + 'original_size' => $original_size, + 'optimized_size' => $optimized_size, + 'percent' => $original_size && $optimized_size ? ceil( ( ( $original_size - $optimized_size ) / $original_size ) * 100 ) : 0, + ); + + $this->update_stats( 'saving_data_count', $data ); + } + + /** + * Calculate the estimated total size of the images not optimized. + * + * We estimate the total size of the images in the library by getting the latest 250 images and their thumbnails + * add up their filesizes, and doing some maths to get the total size. + * + * @since 1.6 + * @author Remy Perona + * + * @return void + */ + public function imagify_calculate_total_size_images_library(): void { + global $wpdb; + + $mime_types = Imagify_DB::get_mime_types(); + $statuses = Imagify_DB::get_post_statuses(); + $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause(); + $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause(); + $image_ids = $wpdb->get_col( // WPCS: unprepared SQL ok. + " + SELECT p.ID + FROM $wpdb->posts AS p + $nodata_join + WHERE p.post_mime_type IN ( $mime_types ) + AND p.post_type = 'attachment' + AND p.post_status IN ( $statuses ) + $nodata_where + LIMIT 250 + " ); + + if ( ! $image_ids ) { + $this->update_stats( 'images_library_total_size', 0 ); + return; + } + + $count_latest_images = count( $image_ids ); + $count_total_images = get_imagify_option( 'attachments_count' ); + + $total = $this->imagify_calculate_total_image_size( $image_ids, $count_latest_images, $count_total_images ); + $this->update_stats( 'images_library_total_size', $total ); + } + + /** + * Calculate the estimated average size of the images uploaded per month. + * + * We estimate the average size of the images uploaded in the library per month by getting the latest 250 images and their thumbnails + * for the 3 latest months, add up their filesizes, and doing some maths to get the total average size. + * + * @since 1.6 + * @since 1.7 Use wpdb instead of WP_Query. + * @author Remy Perona + * + * @return void + */ + public function imagify_calculate_average_size_images_per_month(): void { + global $wpdb; + + $mime_types = Imagify_DB::get_mime_types(); + $statuses = Imagify_DB::get_post_statuses(); + $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause( "$wpdb->posts.ID" ); + $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause(); + $limit = ' LIMIT 0, 250'; + $query = " + SELECT $wpdb->posts.ID + FROM $wpdb->posts + $nodata_join + WHERE $wpdb->posts.post_mime_type IN ( $mime_types ) + AND $wpdb->posts.post_type = 'attachment' + AND $wpdb->posts.post_status IN ( $statuses ) + $nodata_where + %date_query%"; + + // Queries per month. + $date_query = new WP_Date_Query( array( + array( + 'before' => 'now', + 'after' => '1 month ago', + ), + ) ); + + $partial_images_uploaded_last_month = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query . $limit ) ); // WPCS: unprepared SQL ok. + + $date_query = new WP_Date_Query( array( + array( + 'before' => '1 month ago', + 'after' => '2 months ago', + ), + ) ); + + $partial_images_uploaded_two_months_ago = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query . $limit ) ); // WPCS: unprepared SQL ok. + + $date_query = new WP_Date_Query( array( + array( + 'before' => '2 month ago', + 'after' => '3 months ago', + ), + ) ); + + $partial_images_uploaded_three_months_ago = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query . $limit ) ); // WPCS: unprepared SQL ok. + + // Total for the 3 months. + $partial_images_uploaded_id = array_merge( $partial_images_uploaded_last_month, $partial_images_uploaded_two_months_ago, $partial_images_uploaded_three_months_ago ); + + if ( ! $partial_images_uploaded_id ) { + $this->update_stats( 'images_average_size_per_month', 0 ); + return; + } + + // Total for the 3 months, without the "250" limit. + $date_query = new WP_Date_Query( array( + array( + 'before' => 'now', + 'after' => '3 month ago', + ), + ) ); + + $images_uploaded_id = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query ) ); // WPCS: unprepared SQL ok. + + if ( ! $images_uploaded_id ) { + $this->update_stats( 'images_average_size_per_month', 0 ); + return; + } + + // Number of image attachments uploaded for the 3 latest months, limited to 250 per month. + $partial_total_images_uploaded = count( $partial_images_uploaded_id ); + // Total number of image attachments uploaded for the 3 latest months. + $total_images_uploaded = count( $images_uploaded_id ); + + $average = $this->imagify_calculate_total_image_size( $partial_images_uploaded_id, $partial_total_images_uploaded, $total_images_uploaded ) / 3; + $this->update_stats( 'images_average_size_per_month', $average ); + } + + /** + * Returns the estimated total size of images. + * + * @since 1.6 + * @author Remy Perona + * + * @param array $image_ids Array of image IDs. + * @param int $partial_total_images The number of image attachments we're doing the calculation with. + * @param int $total_images The total number of image attachments. + * @return int The estimated total size of images. + */ + private function imagify_calculate_total_image_size( $image_ids, $partial_total_images, $total_images ) { + global $wpdb; + + $image_ids = array_filter( array_map( 'absint', $image_ids ) ); + + if ( ! $image_ids ) { + return 0; + } + + $results = Imagify_DB::get_metas( array( + // Get attachments filename. + 'filenames' => '_wp_attached_file', + // Get attachments data. + 'data' => '_wp_attachment_metadata', + // Get Imagify data. + 'imagify_data' => '_imagify_data', + // Get attachments status. + 'statuses' => '_imagify_status', + ), $image_ids ); + + // Number of image attachments we're doing the calculation with. In case array_filter() removed results. + $partial_total_images = count( $image_ids ); + // Total size of unoptimized size. + $partial_size_images = 0; + // Total number of thumbnails. + $partial_total_intermediate_images = 0; + + $filesystem = imagify_get_filesystem(); + $is_active_for_network = imagify_is_active_for_network(); + $disallowed_sizes = get_imagify_option( 'disallowed-sizes' ); + + foreach ( $image_ids as $i => $image_id ) { + $attachment_status = isset( $results['statuses'][ $image_id ] ) ? $results['statuses'][ $image_id ] : false; + + if ( 'success' === $attachment_status ) { + /** + * The image files have been optimized. + */ + // Original size. + $partial_size_images += isset( $results['imagify_data'][ $image_id ]['stats']['original_size'] ) ? $results['imagify_data'][ $image_id ]['stats']['original_size'] : 0; + // Number of thumbnails. + $partial_total_intermediate_images += count( $results['imagify_data'][ $image_id ]['sizes'] ); + unset( + $image_ids[ $i ], + $results['filenames'][ $image_id ], + $results['data'][ $image_id ], + $results['imagify_data'][ $image_id ], + $results['statuses'][ $image_id ] + ); + continue; + } + + /** + * The image files are not optimized. + */ + // Create an array containing all this attachment files. + $files = array( + 'full' => get_imagify_attached_file( $results['filenames'][ $image_id ] ), + ); + + $sizes = isset( $results['data'][ $image_id ]['sizes'] ) ? $results['data'][ $image_id ]['sizes'] : array(); + + if ( $sizes && is_array( $sizes ) ) { + if ( ! $is_active_for_network ) { + $sizes = array_diff_key( $sizes, $disallowed_sizes ); + } + + if ( $sizes ) { + $full_dirname = $filesystem->dir_path( $files['full'] ); + + foreach ( $sizes as $size_key => $size_data ) { + $files[ $size_key ] = $full_dirname . '/' . $size_data['file']; + } + } + } + + /** + * Allow to provide all files size and the number of thumbnails. + * + * @since 1.6.7 + * @author Grégory Viguier + * + * @param bool $size_and_count False by default. + * @param int $image_id The attachment ID. + * @param array $files An array of file paths with thumbnail sizes as keys. + * @param array $image_ids An array of all attachment IDs. + * @return bool|array False by default. Provide an array with the keys 'filesize' (containing the total filesize) and 'thumbnails' (containing the number of thumbnails). + */ + $size_and_count = apply_filters( 'imagify_total_attachment_filesize', false, $image_id, $files, $image_ids ); + + if ( is_array( $size_and_count ) ) { + $partial_size_images += $size_and_count['filesize']; + $partial_total_intermediate_images += $size_and_count['thumbnails']; + } else { + foreach ( $files as $file ) { + if ( $filesystem->exists( $file ) ) { + $partial_size_images += $filesystem->size( $file ); + } + } + + unset( $files['full'] ); + $partial_total_intermediate_images += count( $files ); + } + + unset( + $image_ids[ $i ], + $results['filenames'][ $image_id ], + $results['data'][ $image_id ], + $results['imagify_data'][ $image_id ], + $results['statuses'][ $image_id ] + ); + } // End foreach(). + + // Number of thumbnails per attachment = Number of thumbnails / Number of attachments. + $intermediate_images_per_image = $partial_total_intermediate_images / $partial_total_images; + /** + * Note: Number of attachments ($partial_total_images) === Number of full sizes. + * Average image size = Size of the images / ( Number of full sizes + Number of thumbnails ). + * Average image size = Size of the images / Number of images. + */ + $average_size_images = $partial_size_images / ( $partial_total_images + $partial_total_intermediate_images ); + /** + * Note: Total number of attachments ($total_images) === Total number of full sizes. + * Total images size = Average image size * ( Total number of full sizes + ( Number of thumbnails per attachment * Total number of attachments ) ). + * Total images size = Average image size * ( Total number of full sizes + Total number of thumbnails ). + */ + $total_size_images = $average_size_images * ( $total_images + ( $intermediate_images_per_image * $total_images ) ); + + return $total_size_images; + } +} diff --git a/classes/Admin/Stats/Subscriber.php b/classes/Admin/Stats/Subscriber.php new file mode 100644 index 000000000..cb10387c9 --- /dev/null +++ b/classes/Admin/Stats/Subscriber.php @@ -0,0 +1,128 @@ +controller = $controller; + } + + /** + * Returns an array of events this subscriber listens to + * + * @return array + */ + public static function get_subscribed_events(): array { + $events = []; + + $events['init'] = 'register_actions'; + $events['admin_init'] = 'register_stats_option'; + $events['imagify_deactivation'] = 'unregister_actions'; + + foreach ( Controller::get_actions() as $action ) { + $events[ $action ] = str_replace( '_as', '', $action ); + } + + return $events; + } + + /** + * Register actions to run in background. + * + * @return void + */ + public function register_actions(): void { + $this->controller->register_actions(); + } + + /** + * Cancel all occurrence of scheduled action. + * + * @return void + */ + public function unregister_actions(): void { + $this->controller->unregister_actions(); + } + + /** + * Register admin stat options. + * + * @return void + */ + public function register_stats_option(): void { + $this->controller->register_stats_option(); + } + + /** + * Count number of attachments. + * + * @return void + */ + public function imagify_count_attachments(): void { + $this->controller->imagify_count_attachments(); + } + + /** + * Count number of optimized attachments with an error. + * + * @return void + */ + public function imagify_count_error_attachments(): void { + $this->controller->imagify_count_error_attachments(); + } + + /** + * Count number of optimized attachments (by Imagify or an other tool before). + * + * @return void + */ + public function imagify_count_optimized_attachments(): void { + $this->controller->imagify_count_optimized_attachments(); + } + + /** + * Count percent, original & optimized size of all images optimized by Imagify. + * + * @return void + */ + public function imagify_count_saving_data(): void { + $this->controller->imagify_count_saving_data(); + } + + /** + * Gets the estimated total size of the images not optimized. + * + * @return void + */ + public function imagify_calculate_total_size_images_library(): void { + $this->controller->imagify_calculate_total_size_images_library(); + } + + /** + * Gets the estimated average size of the images uploaded per month. + * + * @return void + */ + public function imagify_calculate_average_size_images_per_month(): void { + $this->controller->imagify_calculate_average_size_images_per_month(); + } +} diff --git a/classes/Bulk/WP.php b/classes/Bulk/WP.php index ddbddeefd..22096fea6 100644 --- a/classes/Bulk/WP.php +++ b/classes/Bulk/WP.php @@ -306,10 +306,10 @@ public function get_optimized_media_ids_without_format( $format ) { * } */ public function get_context_data() { - $total_saving_data = imagify_count_saving_data(); + $total_saving_data = imagify_get_admin_stats( 'saving_data_count' ); $data = [ - 'count-optimized' => imagify_count_optimized_attachments(), - 'count-errors' => imagify_count_error_attachments(), + 'count-optimized' => imagify_get_admin_stats( 'attachments_optimized_count' ), + 'count-errors' => imagify_get_admin_stats( 'attachments_error_count' ), 'optimized-size' => $total_saving_data['optimized_size'], 'original-size' => $total_saving_data['original_size'], 'errors_url' => get_imagify_admin_url( 'folder-errors', $this->context ), diff --git a/inc/3rd-party/nextgen-gallery/classes/Bulk/NGG.php b/inc/3rd-party/nextgen-gallery/classes/Bulk/NGG.php index f32660f10..89b92fa8c 100644 --- a/inc/3rd-party/nextgen-gallery/classes/Bulk/NGG.php +++ b/inc/3rd-party/nextgen-gallery/classes/Bulk/NGG.php @@ -226,7 +226,7 @@ public function has_optimized_media_without_nextgen() { * } */ public function get_context_data() { - $total_saving_data = imagify_count_saving_data(); + $total_saving_data = imagify_get_admin_stats( 'saving_data_count' ); $data = [ 'count-optimized' => imagify_ngg_count_optimized_attachments(), 'count-errors' => imagify_ngg_count_error_attachments(), diff --git a/inc/3rd-party/nextgen-gallery/inc/admin/bulk.php b/inc/3rd-party/nextgen-gallery/inc/admin/bulk.php index 0226b3b32..c08c1aa13 100644 --- a/inc/3rd-party/nextgen-gallery/inc/admin/bulk.php +++ b/inc/3rd-party/nextgen-gallery/inc/admin/bulk.php @@ -36,7 +36,7 @@ function imagify_ngg_bulk_stats( $data, $types ) { } add_filter( 'imagify_count_saving_data', 'imagify_ngg_count_saving_data', 8 ); - $total_saving_data = imagify_count_saving_data(); + $total_saving_data = imagify_get_admin_stats( 'saving_data_count' ); remove_filter( 'imagify_count_saving_data', 'imagify_ngg_count_saving_data', 8 ); // Global chart. diff --git a/inc/admin/upload.php b/inc/admin/upload.php index b24249a5b..8e893e630 100755 --- a/inc/admin/upload.php +++ b/inc/admin/upload.php @@ -51,9 +51,9 @@ function _imagify_attachments_filter_dropdown() { return; } - $optimized = imagify_count_optimized_attachments(); + $optimized = imagify_get_admin_stats( 'attachments_optimized_count' ); $unoptimized = imagify_count_unoptimized_attachments(); - $errors = imagify_count_error_attachments(); + $errors = imagify_get_admin_stats( 'attachments_error_count' ); $status = isset( $_GET['imagify-status'] ) ? wp_unslash( $_GET['imagify-status'] ) : 0; // WPCS: CSRF ok. $options = array( 'optimized' => _x( 'Optimized', 'Media Files', 'imagify' ), diff --git a/inc/classes/class-imagify-admin-ajax-post.php b/inc/classes/class-imagify-admin-ajax-post.php index 37c7bd7c6..673fb530c 100755 --- a/inc/classes/class-imagify-admin-ajax-post.php +++ b/inc/classes/class-imagify-admin-ajax-post.php @@ -913,8 +913,8 @@ public function imagify_get_images_counts_callback() { imagify_die(); } - $raw_total_size_in_library = imagify_calculate_total_size_images_library() + Imagify_Files_Stats::get_overall_original_size(); - $raw_average_per_month = imagify_calculate_average_size_images_per_month() + Imagify_Files_Stats::calculate_average_size_per_month(); + $raw_total_size_in_library = imagify_get_admin_stats( 'images_library_total_size' ) + Imagify_Files_Stats::get_overall_original_size(); + $raw_average_per_month = imagify_get_admin_stats( 'images_average_size_per_month' ) + Imagify_Files_Stats::calculate_average_size_per_month(); Imagify_Data::get_instance()->set( array( 'total_size_images_library' => $raw_total_size_in_library, @@ -945,8 +945,8 @@ public function imagify_update_estimate_sizes_callback() { imagify_die(); } - $raw_total_size_in_library = imagify_calculate_total_size_images_library() + Imagify_Files_Stats::get_overall_original_size(); - $raw_average_per_month = imagify_calculate_average_size_images_per_month() + Imagify_Files_Stats::calculate_average_size_per_month(); + $raw_total_size_in_library = imagify_get_admin_stats( 'images_library_total_size' ) + Imagify_Files_Stats::get_overall_original_size(); + $raw_average_per_month = imagify_get_admin_stats( 'images_average_size_per_month' ) + Imagify_Files_Stats::calculate_average_size_per_month(); Imagify_Data::get_instance()->set( array( 'total_size_images_library' => $raw_total_size_in_library, diff --git a/inc/classes/class-imagify-views.php b/inc/classes/class-imagify-views.php index 52eb092f7..aa52643e9 100644 --- a/inc/classes/class-imagify-views.php +++ b/inc/classes/class-imagify-views.php @@ -658,7 +658,7 @@ private function get_attachments_number_modal() { return $transient; } - $attachments_number = imagify_count_attachments() + Imagify_Files_Stats::count_all_files(); + $attachments_number = imagify_get_admin_stats( 'attachments_count' ) + Imagify_Files_Stats::count_all_files(); set_transient( 'imagify_attachments_number_modal', $attachments_number, 1 * DAY_IN_SECONDS ); diff --git a/inc/deprecated/3rd-party.php b/inc/deprecated/3rd-party.php index 79820f735..9822758a2 100644 --- a/inc/deprecated/3rd-party.php +++ b/inc/deprecated/3rd-party.php @@ -62,7 +62,7 @@ function _imagify_ngg_heartbeat_received( $response, $data ) { } add_filter( 'imagify_count_saving_data', 'imagify_ngg_count_saving_data', 8 ); - $saving_data = imagify_count_saving_data(); + $saving_data = imagify_get_admin_stats( 'saving_data_count' ); $user = new User(); $response['imagify_bulk_data'] = array( @@ -213,7 +213,7 @@ function imagify_ngg_get_folder_type_data( $data, $context ) { } // Already filtered in imagify_ngg_bulk_page_data(). - $total_saving_data = imagify_count_saving_data(); + $total_saving_data = imagify_get_admin_stats( 'saving_data_count' ); return [ 'images-optimized' => imagify_ngg_count_optimized_attachments(), diff --git a/inc/deprecated/deprecated.php b/inc/deprecated/deprecated.php index aef5cadac..607a6f44a 100644 --- a/inc/deprecated/deprecated.php +++ b/inc/deprecated/deprecated.php @@ -1066,8 +1066,8 @@ function imagify_get_folder_type_data( $context ) { case 'wp': $total_saving_data = imagify_count_saving_data(); $data = array( - 'images-optimized' => imagify_count_optimized_attachments(), - 'errors' => imagify_count_error_attachments(), + 'images-optimized' => imagify_get_admin_stats( 'attachments_optimized_count' ), + 'errors' => imagify_get_admin_stats( 'attachments_error_count' ), 'optimized' => $total_saving_data['optimized_size'], 'original' => $total_saving_data['original_size'], 'errors_url' => get_imagify_admin_url( 'folder-errors', $context ), diff --git a/inc/functions/admin-stats.php b/inc/functions/admin-stats.php index 84d534de9..133205ec5 100755 --- a/inc/functions/admin-stats.php +++ b/inc/functions/admin-stats.php @@ -1,166 +1,6 @@ get_var( // WPCS: unprepared SQL ok. - " - SELECT COUNT( p.ID ) - FROM $wpdb->posts AS p - $nodata_join - WHERE p.post_mime_type IN ( $mime_types ) - AND p.post_type = 'attachment' - AND p.post_status IN ( $statuses ) - $nodata_where" - ); - - if ( $count > imagify_get_unoptimized_attachment_limit() ) { - set_transient( 'imagify_large_library', 1 ); - } elseif ( get_transient( 'imagify_large_library' ) ) { - // In case the number is decreasing under our limit. - delete_transient( 'imagify_large_library' ); - } - - return $count; -} - -/** - * Count number of optimized attachments with an error. - * - * @since 1.0 - * @author Jonathan Buttigieg - * - * @return int The number of attachments. - */ -function imagify_count_error_attachments() { - global $wpdb; - static $count; - - /** - * Filter the number of optimized attachments with an error. - * 3rd party will be able to override the result. - * - * @since 1.5 - * - * @param int|bool $pre_count Default is false. Provide an integer. - */ - $pre_count = apply_filters( 'imagify_count_error_attachments', false ); - - if ( false !== $pre_count ) { - return (int) $pre_count; - } - - if ( isset( $count ) ) { - return $count; - } - - $mime_types = Imagify_DB::get_mime_types(); - $statuses = Imagify_DB::get_post_statuses(); - $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause(); - $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause(); - $count = (int) $wpdb->get_var( // WPCS: unprepared SQL ok. - " - SELECT COUNT( DISTINCT p.ID ) - FROM $wpdb->posts AS p - $nodata_join - INNER JOIN $wpdb->postmeta AS mt1 - ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' ) - WHERE p.post_mime_type IN ( $mime_types ) - AND p.post_type = 'attachment' - AND p.post_status IN ( $statuses ) - AND mt1.meta_value = 'error' - $nodata_where" - ); - - return $count; -} - -/** - * Count number of optimized attachments (by Imagify or an other tool before). - * - * @since 1.0 - * @author Jonathan Buttigieg - * - * @return int The number of attachments. - */ -function imagify_count_optimized_attachments() { - global $wpdb; - static $count; - - /** - * Filter the number of optimized attachments. - * 3rd party will be able to override the result. - * - * @since 1.5 - * - * @param int|bool $pre_count Default is false. Provide an integer. - */ - $pre_count = apply_filters( 'imagify_count_optimized_attachments', false ); - - if ( false !== $pre_count ) { - return (int) $pre_count; - } - - if ( isset( $count ) ) { - return $count; - } - - $mime_types = Imagify_DB::get_mime_types(); - $statuses = Imagify_DB::get_post_statuses(); - $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause(); - $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause(); - $count = (int) $wpdb->get_var( // WPCS: unprepared SQL ok. - " - SELECT COUNT( DISTINCT p.ID ) - FROM $wpdb->posts AS p - $nodata_join - INNER JOIN $wpdb->postmeta AS mt1 - ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' ) - WHERE p.post_mime_type IN ( $mime_types ) - AND p.post_type = 'attachment' - AND p.post_status IN ( $statuses ) - AND mt1.meta_value IN ( 'success', 'already_optimized' ) - $nodata_where" - ); - - return $count; -} - /** * Count number of unoptimized attachments. * @@ -184,7 +24,8 @@ function imagify_count_unoptimized_attachments() { return (int) $pre_count; } - return imagify_count_attachments() - imagify_count_optimized_attachments() - imagify_count_error_attachments(); + $count = get_imagify_option( 'attachments_count' ) - imagify_get_admin_stats( 'attachments_optimized_count' ) - imagify_get_admin_stats( 'attachments_error_count' ); + return $count; } /** @@ -210,8 +51,8 @@ function imagify_percent_optimized_attachments() { return (int) $percent; } - $total_attachments = imagify_count_attachments(); - $total_optimized_attachments = imagify_count_optimized_attachments(); + $total_attachments = imagify_get_admin_stats( 'attachments_count' ); + $total_optimized_attachments = imagify_get_admin_stats( 'attachments_optimized_count' ); if ( ! $total_attachments || ! $total_optimized_attachments ) { return 0; @@ -220,458 +61,6 @@ function imagify_percent_optimized_attachments() { return min( round( 100 * $total_optimized_attachments / $total_attachments ), 100 ); } -/** - * Count percent, original & optimized size of all images optimized by Imagify. - * - * @since 1.0 - * @since 1.6.7 Revamped to handle huge libraries. - * @author Jonathan Buttigieg - * - * @param string $key What data to return. Choices are between 'count', 'original_size', 'optimized_size', and 'percent'. If left empty, the whole array is returned. - * @return array|int An array containing the optimization data. A single data if $key is provided. - */ -function imagify_count_saving_data( $key = '' ) { - global $wpdb; - - /** - * Filter the query to get all optimized attachments. - * 3rd party will be able to override the result. - * - * @since 1.5 - * @since 1.6.7 This filter should return an array containing the following keys: 'count', 'original_size', and 'optimized_size'. - * - * @param bool|array $attachments An array containing the keys ('count', 'original_size', and 'optimized_size'), or an array of attachments (back compat', deprecated), or false. - */ - $attachments = apply_filters( 'imagify_count_saving_data', false ); - - $original_size = 0; - $optimized_size = 0; - $count = 0; - - if ( is_array( $attachments ) ) { - /** - * Bypass. - */ - if ( isset( $attachments['count'], $attachments['original_size'], $attachments['optimized_size'] ) ) { - /** - * We have the results we need. - */ - $attachments['percent'] = $attachments['optimized_size'] && $attachments['original_size'] ? ceil( ( ( $attachments['original_size'] - $attachments['optimized_size'] ) / $attachments['original_size'] ) * 100 ) : 0; - - return $attachments; - } - - /** - * Back compat'. - * The following shouldn't be used. Sites with a huge library won't like it. - */ - $attachments = array_map( 'maybe_unserialize', (array) $attachments ); - - if ( $attachments ) { - foreach ( $attachments as $attachment_data ) { - if ( ! $attachment_data ) { - continue; - } - - ++$count; - $original_data = $attachment_data['sizes']['full']; - - // Increment the original sizes. - $original_size += $original_data['original_size'] ? $original_data['original_size'] : 0; - $optimized_size += $original_data['optimized_size'] ? $original_data['optimized_size'] : 0; - - unset( $attachment_data['sizes']['full'] ); - - // Increment the thumbnails sizes. - if ( $attachment_data['sizes'] ) { - foreach ( $attachment_data['sizes'] as $size_data ) { - if ( ! empty( $size_data['success'] ) ) { - $original_size += $size_data['original_size'] ? $size_data['original_size'] : 0; - $optimized_size += $size_data['optimized_size'] ? $size_data['optimized_size'] : 0; - } - } - } - } - } - } else { - /** - * Filter the chunk size of the requests fetching the data. - * 15,000 seems to be a good balance between memory used, speed, and number of DB hits. - * - * @param int $limit The maximum number of elements per chunk. - */ - $limit = apply_filters( 'imagify_count_saving_data_limit', 15000 ); - $limit = absint( $limit ); - - $mime_types = Imagify_DB::get_mime_types(); - $statuses = Imagify_DB::get_post_statuses(); - $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause(); - $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause(); - $attachment_ids = $wpdb->get_col( // WPCS: unprepared SQL ok. - " - SELECT p.ID - FROM $wpdb->posts AS p - $nodata_join - INNER JOIN $wpdb->postmeta AS mt1 - ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' ) - WHERE p.post_mime_type IN ( $mime_types ) - AND p.post_type = 'attachment' - AND p.post_status IN ( $statuses ) - AND mt1.meta_value = 'success' - $nodata_where - ORDER BY CAST( p.ID AS UNSIGNED )" - ); - $wpdb->flush(); - - $attachment_ids = array_map( 'absint', array_unique( $attachment_ids ) ); - $attachment_ids = array_chunk( $attachment_ids, $limit ); - - while ( $attachment_ids ) { - $limit_ids = array_shift( $attachment_ids ); - $limit_ids = implode( ',', $limit_ids ); - - $attachments = $wpdb->get_col( // WPCS: unprepared SQL ok. - " - SELECT meta_value - FROM $wpdb->postmeta - WHERE post_id IN ( $limit_ids ) - AND meta_key = '_imagify_data'" - ); - $wpdb->flush(); - - unset( $limit_ids ); - - if ( ! $attachments ) { - // Uh?! - continue; - } - - $attachments = array_map( 'maybe_unserialize', $attachments ); - - foreach ( $attachments as $attachment_data ) { - if ( ! $attachment_data ) { - continue; - } - - if ( empty( $attachment_data['sizes']['full']['success'] ) ) { - /** - * - Case where this attachment has multiple '_imagify_status' metas, and is fetched (in the above query) as a "success" while the '_imagify_data' says otherwise. - * - Case where this meta has no "full" entry. - * Don't ask how it's possible, I don't know ¯\(°_o)/¯ - */ - continue; - } - - $original_data = $attachment_data['sizes']['full']; - - ++$count; - - // Increment the original sizes. - $original_size += ! empty( $original_data['original_size'] ) ? $original_data['original_size'] : 0; - $optimized_size += ! empty( $original_data['optimized_size'] ) ? $original_data['optimized_size'] : 0; - - unset( $attachment_data['sizes']['full'], $original_data ); - - // Increment the thumbnails sizes. - if ( $attachment_data['sizes'] ) { - foreach ( $attachment_data['sizes'] as $size_data ) { - if ( ! empty( $size_data['success'] ) ) { - $original_size += ! empty( $size_data['original_size'] ) ? $size_data['original_size'] : 0; - $optimized_size += ! empty( $size_data['optimized_size'] ) ? $size_data['optimized_size'] : 0; - } - } - } - - unset( $size_data ); - } - - unset( $attachments, $attachment_data ); - } // End while(). - } // End if(). - - $data = array( - 'count' => $count, - 'original_size' => $original_size, - 'optimized_size' => $optimized_size, - 'percent' => $original_size && $optimized_size ? ceil( ( ( $original_size - $optimized_size ) / $original_size ) * 100 ) : 0, - ); - - if ( ! empty( $key ) ) { - return isset( $data[ $key ] ) ? $data[ $key ] : 0; - } - - return $data; -} - -/** - * Returns the estimated total size of the images not optimized. - * - * We estimate the total size of the images in the library by getting the latest 250 images and their thumbnails - * add up their filesizes, and doing some maths to get the total size. - * - * @since 1.6 - * @author Remy Perona - * - * @return int The current estimated total size of images not optimized. - */ -function imagify_calculate_total_size_images_library() { - global $wpdb; - - $mime_types = Imagify_DB::get_mime_types(); - $statuses = Imagify_DB::get_post_statuses(); - $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause(); - $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause(); - $image_ids = $wpdb->get_col( // WPCS: unprepared SQL ok. - " - SELECT p.ID - FROM $wpdb->posts AS p - $nodata_join - WHERE p.post_mime_type IN ( $mime_types ) - AND p.post_type = 'attachment' - AND p.post_status IN ( $statuses ) - $nodata_where - LIMIT 250 - " ); - - if ( ! $image_ids ) { - return 0; - } - - $count_latest_images = count( $image_ids ); - $count_total_images = imagify_count_attachments(); - - return imagify_calculate_total_image_size( $image_ids, $count_latest_images, $count_total_images ); -} - -/** - * Returns the estimated average size of the images uploaded per month. - * - * We estimate the average size of the images uploaded in the library per month by getting the latest 250 images and their thumbnails - * for the 3 latest months, add up their filesizes, and doing some maths to get the total average size. - * - * @since 1.6 - * @since 1.7 Use wpdb instead of WP_Query. - * @author Remy Perona - * - * @return int The current estimated average size of images uploaded per month. - */ -function imagify_calculate_average_size_images_per_month() { - global $wpdb; - - $mime_types = Imagify_DB::get_mime_types(); - $statuses = Imagify_DB::get_post_statuses(); - $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause( "$wpdb->posts.ID" ); - $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause(); - $limit = ' LIMIT 0, 250'; - $query = " - SELECT $wpdb->posts.ID - FROM $wpdb->posts - $nodata_join - WHERE $wpdb->posts.post_mime_type IN ( $mime_types ) - AND $wpdb->posts.post_type = 'attachment' - AND $wpdb->posts.post_status IN ( $statuses ) - $nodata_where - %date_query%"; - - // Queries per month. - $date_query = new WP_Date_Query( array( - array( - 'before' => 'now', - 'after' => '1 month ago', - ), - ) ); - - $partial_images_uploaded_last_month = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query . $limit ) ); // WPCS: unprepared SQL ok. - - $date_query = new WP_Date_Query( array( - array( - 'before' => '1 month ago', - 'after' => '2 months ago', - ), - ) ); - - $partial_images_uploaded_two_months_ago = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query . $limit ) ); // WPCS: unprepared SQL ok. - - $date_query = new WP_Date_Query( array( - array( - 'before' => '2 month ago', - 'after' => '3 months ago', - ), - ) ); - - $partial_images_uploaded_three_months_ago = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query . $limit ) ); // WPCS: unprepared SQL ok. - - // Total for the 3 months. - $partial_images_uploaded_id = array_merge( $partial_images_uploaded_last_month, $partial_images_uploaded_two_months_ago, $partial_images_uploaded_three_months_ago ); - - if ( ! $partial_images_uploaded_id ) { - return 0; - } - - // Total for the 3 months, without the "250" limit. - $date_query = new WP_Date_Query( array( - array( - 'before' => 'now', - 'after' => '3 month ago', - ), - ) ); - - $images_uploaded_id = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query ) ); // WPCS: unprepared SQL ok. - - if ( ! $images_uploaded_id ) { - return 0; - } - - // Number of image attachments uploaded for the 3 latest months, limited to 250 per month. - $partial_total_images_uploaded = count( $partial_images_uploaded_id ); - // Total number of image attachments uploaded for the 3 latest months. - $total_images_uploaded = count( $images_uploaded_id ); - - return imagify_calculate_total_image_size( $partial_images_uploaded_id, $partial_total_images_uploaded, $total_images_uploaded ) / 3; -} - -/** - * Returns the estimated total size of images. - * - * @since 1.6 - * @author Remy Perona - * - * @param array $image_ids Array of image IDs. - * @param int $partial_total_images The number of image attachments we're doing the calculation with. - * @param int $total_images The total number of image attachments. - * @return int The estimated total size of images. - */ -function imagify_calculate_total_image_size( $image_ids, $partial_total_images, $total_images ) { - global $wpdb; - - $image_ids = array_filter( array_map( 'absint', $image_ids ) ); - - if ( ! $image_ids ) { - return 0; - } - - $results = Imagify_DB::get_metas( array( - // Get attachments filename. - 'filenames' => '_wp_attached_file', - // Get attachments data. - 'data' => '_wp_attachment_metadata', - // Get Imagify data. - 'imagify_data' => '_imagify_data', - // Get attachments status. - 'statuses' => '_imagify_status', - ), $image_ids ); - - // Number of image attachments we're doing the calculation with. In case array_filter() removed results. - $partial_total_images = count( $image_ids ); - // Total size of unoptimized size. - $partial_size_images = 0; - // Total number of thumbnails. - $partial_total_intermediate_images = 0; - - $filesystem = imagify_get_filesystem(); - $is_active_for_network = imagify_is_active_for_network(); - $disallowed_sizes = get_imagify_option( 'disallowed-sizes' ); - - foreach ( $image_ids as $i => $image_id ) { - $attachment_status = isset( $results['statuses'][ $image_id ] ) ? $results['statuses'][ $image_id ] : false; - - if ( 'success' === $attachment_status ) { - /** - * The image files have been optimized. - */ - // Original size. - $partial_size_images += isset( $results['imagify_data'][ $image_id ]['stats']['original_size'] ) ? $results['imagify_data'][ $image_id ]['stats']['original_size'] : 0; - // Number of thumbnails. - $partial_total_intermediate_images += count( $results['imagify_data'][ $image_id ]['sizes'] ); - unset( - $image_ids[ $i ], - $results['filenames'][ $image_id ], - $results['data'][ $image_id ], - $results['imagify_data'][ $image_id ], - $results['statuses'][ $image_id ] - ); - continue; - } - - /** - * The image files are not optimized. - */ - // Create an array containing all this attachment files. - $files = array( - 'full' => get_imagify_attached_file( $results['filenames'][ $image_id ] ), - ); - - $sizes = isset( $results['data'][ $image_id ]['sizes'] ) ? $results['data'][ $image_id ]['sizes'] : array(); - - if ( $sizes && is_array( $sizes ) ) { - if ( ! $is_active_for_network ) { - $sizes = array_diff_key( $sizes, $disallowed_sizes ); - } - - if ( $sizes ) { - $full_dirname = $filesystem->dir_path( $files['full'] ); - - foreach ( $sizes as $size_key => $size_data ) { - $files[ $size_key ] = $full_dirname . '/' . $size_data['file']; - } - } - } - - /** - * Allow to provide all files size and the number of thumbnails. - * - * @since 1.6.7 - * @author Grégory Viguier - * - * @param bool $size_and_count False by default. - * @param int $image_id The attachment ID. - * @param array $files An array of file paths with thumbnail sizes as keys. - * @param array $image_ids An array of all attachment IDs. - * @return bool|array False by default. Provide an array with the keys 'filesize' (containing the total filesize) and 'thumbnails' (containing the number of thumbnails). - */ - $size_and_count = apply_filters( 'imagify_total_attachment_filesize', false, $image_id, $files, $image_ids ); - - if ( is_array( $size_and_count ) ) { - $partial_size_images += $size_and_count['filesize']; - $partial_total_intermediate_images += $size_and_count['thumbnails']; - } else { - foreach ( $files as $file ) { - if ( $filesystem->exists( $file ) ) { - $partial_size_images += $filesystem->size( $file ); - } - } - - unset( $files['full'] ); - $partial_total_intermediate_images += count( $files ); - } - - unset( - $image_ids[ $i ], - $results['filenames'][ $image_id ], - $results['data'][ $image_id ], - $results['imagify_data'][ $image_id ], - $results['statuses'][ $image_id ] - ); - } // End foreach(). - - // Number of thumbnails per attachment = Number of thumbnails / Number of attachments. - $intermediate_images_per_image = $partial_total_intermediate_images / $partial_total_images; - /** - * Note: Number of attachments ($partial_total_images) === Number of full sizes. - * Average image size = Size of the images / ( Number of full sizes + Number of thumbnails ). - * Average image size = Size of the images / Number of images. - */ - $average_size_images = $partial_size_images / ( $partial_total_images + $partial_total_intermediate_images ); - /** - * Note: Total number of attachments ($total_images) === Total number of full sizes. - * Total images size = Average image size * ( Total number of full sizes + ( Number of thumbnails per attachment * Total number of attachments ) ). - * Total images size = Average image size * ( Total number of full sizes + Total number of thumbnails ). - */ - $total_size_images = $average_size_images * ( $total_images + ( $intermediate_images_per_image * $total_images ) ); - - return $total_size_images; -} - /** * Get all generic stats to be used in the bulk optimization page. * @@ -710,13 +99,13 @@ function imagify_get_bulk_stats( $types, $args = array() ) { /** * Library. */ - $saving_data = imagify_count_saving_data(); + $saving_data = imagify_get_admin_stats( 'saving_data_count' ); // Global chart. - $data['total_attachments'] += imagify_count_attachments(); + $data['total_attachments'] += imagify_get_admin_stats( 'attachments_count' ); $data['unoptimized_attachments'] += imagify_count_unoptimized_attachments(); - $data['optimized_attachments'] += imagify_count_optimized_attachments(); - $data['errors_attachments'] += imagify_count_error_attachments(); + $data['optimized_attachments'] += imagify_get_admin_stats( 'attachments_optimized_count' ); + $data['errors_attachments'] += imagify_get_admin_stats( 'attachments_error_count' ); // Stats block. $data['already_optimized_attachments'] += $saving_data['count']; $data['original_human'] += $saving_data['original_size']; diff --git a/inc/functions/options.php b/inc/functions/options.php index 44c2d6088..37ab307b9 100755 --- a/inc/functions/options.php +++ b/inc/functions/options.php @@ -97,3 +97,20 @@ function imagify_load_network_options( $option_names, $prefixes = '' ) { wp_cache_set( $notoptions_key, $notoptions, $cache_group ); } + +/** + * Get admin stats. + * + * @param string $key Option key. + * @param integer $default default value to return. + * @return mixed + */ +function imagify_get_admin_stats( string $key, $default = 0 ) { + $stats = get_option( 'imagify_admin_stats' ); + + if ( ! $stats ) { + return $default; + } + + return $stats[ $key ]; +} diff --git a/uninstall.php b/uninstall.php index 9cfcab637..73ffb3e47 100755 --- a/uninstall.php +++ b/uninstall.php @@ -11,6 +11,7 @@ delete_option( 'imagify_data' ); delete_option( 'ngg_imagify_data_db_version' ); delete_option( $wpdb->prefix . 'ngg_imagify_data_db_version' ); +delete_option( 'imagify_admin_stats' ); // Delete all transients. delete_site_transient( 'imagify_activation' );