PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / trunk
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF vtrunk
2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.9 2.2.8 trunk 1.10 1.3.3 1.3.4 1.3.5 1.3.5.1 1.3.5.2 1.3.6 1.3.6.1 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.5 All 103 releases
← All changes | inc/functions/admin-stats.php +773 -135 1.3.5.1 → trunk View file →
@@ -1,214 +1,852 @@
1 1 <?php
2 -defined( 'ABSPATH' ) or die( 'Cheatin\' uh?' );
2 +defined( 'ABSPATH' ) || exit;
3 3
4 -/*
4 +/**
5 5 * Count number of attachments.
6 6 *
7 - * @since 1.0
7 + * @since 1.0
8 + * @author Jonathan Buttigieg
8 9 *
9 10 * @return int The number of attachments.
10 11 */
11 12 function imagify_count_attachments() {
12 - $count = wp_count_attachments( get_imagify_mime_type() );
13 - $count = get_object_vars( $count );
14 - $count = array_sum( $count );
15 - return (int) $count;
16 -}
13 + global $wpdb;
14 + static $count;
17 15
18 -/*
19 - * Count number of execeed attachments (size > 5MB).
20 - *
21 - * @since 1.0
22 - *
23 - * @return int The number of exceeded attachments.
24 - */
25 -function imagify_count_exceeding_attachments() {
26 - $count = 0;
27 - $query = new WP_Query(
28 - array(
29 - 'post_type' => 'attachment',
30 - 'post_status' => 'inherit',
31 - 'post_mime_type' => get_imagify_mime_type(),
32 - 'posts_per_page' => -1,
33 - 'update_post_term_cache' => false,
34 - 'no_found_rows' => true,
35 - 'fields' => 'ids'
36 - )
16 + /**
17 + * Filter the number of attachments.
18 + * 3rd party will be able to override the result.
19 + *
20 + * @since 1.5
21 + *
22 + * @param int|bool $pre_count Default is false. Provide an integer.
23 + */
24 + $pre_count = apply_filters( 'imagify_count_attachments', false );
25 +
26 + if ( false !== $pre_count ) {
27 + return (int) $pre_count;
28 + }
29 +
30 + if ( isset( $count ) ) {
31 + return $count;
32 + }
33 +
34 + $mime_types = Imagify_DB::get_mime_types();
35 + $statuses = Imagify_DB::get_post_statuses();
36 + $nodata_join = '';
37 + $nodata_where = '';
38 +
39 + if ( ! imagify_has_attachments_without_required_metadata() ) {
40 + $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause(
41 + 'p.ID',
42 + true,
43 + true,
44 + "AND p.post_mime_type IN ( $mime_types )
45 + AND p.post_type = 'attachment'
46 + AND p.post_status IN ( $statuses )"
47 + );
48 + $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
49 + }
50 +
51 + $count = (int) $wpdb->get_var( // WPCS: unprepared SQL ok.
52 + "
53 + SELECT COUNT( p.ID )
54 + FROM $wpdb->posts AS p
55 + $nodata_join
56 + WHERE p.post_mime_type IN ( $mime_types )
57 + AND p.post_type = 'attachment'
58 + AND p.post_status IN ( $statuses )
59 + $nodata_where"
37 60 );
38 - $attachments = (array) $query->posts;
39 61
40 - foreach( $attachments as $attachment_id ) {
41 - $attachment = new Imagify_Attachment( $attachment_id );
62 + if ( $count > imagify_get_unoptimized_attachment_limit() ) {
63 + set_transient( 'imagify_large_library', 1 );
64 + } elseif ( get_transient( 'imagify_large_library' ) ) {
65 + // In case the number is decreasing under our limit.
66 + delete_transient( 'imagify_large_library' );
67 + }
42 68
43 - // Check if the attachment extension is allowed
44 - if ( ! wp_attachment_is_image( $attachment_id ) ) {
45 - continue;
46 - }
47 -
48 - if ( $attachment->is_exceeded() ) {
49 - $count++;
50 - }
51 - }
52 -
53 - return (int) $count;
69 + return $count;
54 70 }
55 71
56 -/*
72 +/**
57 73 * Count number of optimized attachments with an error.
58 74 *
59 - * @since 1.0
75 + * @since 1.0
76 + * @author Jonathan Buttigieg
60 77 *
61 78 * @return int The number of attachments.
62 79 */
63 80 function imagify_count_error_attachments() {
64 - $query = new WP_Query(
65 - array(
66 - 'post_type' => 'attachment',
67 - 'post_status' => 'inherit',
68 - 'post_mime_type' => get_imagify_mime_type(),
69 - 'meta_key' => '_imagify_status',
70 - 'meta_value' => 'error',
71 - 'posts_per_page' => -1,
72 - 'update_post_term_cache' => false,
73 - 'no_found_rows' => true,
74 - 'fields' => 'ids'
75 - )
81 + global $wpdb;
82 + static $count;
83 +
84 + /**
85 + * Filter the number of optimized attachments with an error.
86 + * 3rd party will be able to override the result.
87 + *
88 + * @since 1.5
89 + *
90 + * @param int|bool $pre_count Default is false. Provide an integer.
91 + */
92 + $pre_count = apply_filters( 'imagify_count_error_attachments', false );
93 +
94 + if ( false !== $pre_count ) {
95 + return (int) $pre_count;
96 + }
97 +
98 + if ( isset( $count ) ) {
99 + return $count;
100 + }
101 +
102 + $mime_types = Imagify_DB::get_mime_types();
103 + $statuses = Imagify_DB::get_post_statuses();
104 + $nodata_join = '';
105 + $nodata_where = '';
106 +
107 + if ( ! imagify_has_attachments_without_required_metadata() ) {
108 + $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause();
109 + $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
110 + }
111 +
112 + $count = (int) $wpdb->get_var( // WPCS: unprepared SQL ok.
113 + "
114 + SELECT COUNT(*)
115 + FROM (
116 + SELECT p.ID
117 + FROM $wpdb->posts AS p
118 + $nodata_join
119 + INNER JOIN $wpdb->postmeta AS mt1
120 + ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' )
121 + WHERE p.post_mime_type IN ( $mime_types )
122 + AND p.post_type = 'attachment'
123 + AND p.post_status IN ( $statuses )
124 + AND mt1.meta_value = 'error'
125 + $nodata_where GROUP BY p.ID
126 + ) AS imagify_count_error"
76 127 );
77 128
78 - return (int) $query->post_count;
129 + return $count;
79 130 }
80 131
81 -/*
132 +/**
82 133 * Count number of optimized attachments (by Imagify or an other tool before).
83 134 *
84 - * @since 1.0
135 + * @since 1.0
136 + * @author Jonathan Buttigieg
85 137 *
86 138 * @return int The number of attachments.
87 139 */
88 140 function imagify_count_optimized_attachments() {
89 - $query = new WP_Query(
90 - array(
91 - 'post_type' => 'attachment',
92 - 'post_status' => 'inherit',
93 - 'post_mime_type' => get_imagify_mime_type(),
94 - 'meta_query' => array(
95 - 'relation' => 'or',
96 - array(
97 - 'key' => '_imagify_status',
98 - 'value' => 'success',
99 - 'compare' => '='
100 - ),
101 - array(
102 - 'key' => '_imagify_status',
103 - 'value' => 'already_optimized',
104 - 'compare' => '='
105 - )
106 - ),
107 - 'posts_per_page' => -1,
108 - 'update_post_term_cache' => false,
109 - 'no_found_rows' => true,
110 - 'fields' => 'ids'
111 - )
141 + global $wpdb;
142 + static $count;
143 +
144 + /**
145 + * Filter the number of optimized attachments.
146 + * 3rd party will be able to override the result.
147 + *
148 + * @since 1.5
149 + *
150 + * @param int|bool $pre_count Default is false. Provide an integer.
151 + */
152 + $pre_count = apply_filters( 'imagify_count_optimized_attachments', false );
153 +
154 + if ( false !== $pre_count ) {
155 + return (int) $pre_count;
156 + }
157 +
158 + if ( isset( $count ) ) {
159 + return $count;
160 + }
161 +
162 + $mime_types = Imagify_DB::get_mime_types();
163 + $statuses = Imagify_DB::get_post_statuses();
164 + $nodata_join = '';
165 + $nodata_where = '';
166 +
167 + if ( ! imagify_has_attachments_without_required_metadata() ) {
168 + $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause();
169 + $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
170 + }
171 +
172 + $count = (int) $wpdb->get_var( // WPCS: unprepared SQL ok.
173 + "
174 + SELECT COUNT( DISTINCT p.ID )
175 + FROM $wpdb->posts AS p
176 + $nodata_join
177 + INNER JOIN $wpdb->postmeta AS mt1
178 + ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' )
179 + WHERE p.post_mime_type IN ( $mime_types )
180 + AND p.post_type = 'attachment'
181 + AND p.post_status IN ( $statuses )
182 + AND mt1.meta_value IN ( 'success', 'already_optimized' )
183 + $nodata_where"
112 184 );
113 185
114 - return (int) $query->post_count;
186 + return $count;
115 187 }
116 188
117 -/*
189 +/**
118 190 * Count number of unoptimized attachments.
119 191 *
120 - * @since 1.0
192 + * @since 1.0
193 + * @author Jonathan Buttigieg
121 194 *
122 195 * @return int The number of attachments.
123 196 */
124 197 function imagify_count_unoptimized_attachments() {
125 - $count = imagify_count_attachments() - imagify_count_optimized_attachments() - imagify_count_error_attachments();
126 - return (int) $count;
198 + /**
199 + * Filter the number of unoptimized attachments.
200 + * 3rd party will be able to override the result.
201 + *
202 + * @since 1.5
203 + *
204 + * @param int|bool $pre_count Default is false. Provide an integer.
205 + */
206 + $pre_count = apply_filters( 'imagify_count_unoptimized_attachments', false );
207 +
208 + if ( false !== $pre_count ) {
209 + return (int) $pre_count;
210 + }
211 +
212 + return imagify_count_attachments() - imagify_count_optimized_attachments() - imagify_count_error_attachments();
127 213 }
128 214
129 -/*
215 +/**
130 216 * Count percent of optimized attachments.
131 217 *
132 - * @since 1.0
218 + * @since 1.0
219 + * @author Jonathan Buttigieg
133 220 *
134 221 * @return int The percent of optimized attachments.
135 222 */
136 223 function imagify_percent_optimized_attachments() {
137 - $total_attachments = imagify_count_attachments();
138 - $total_optimized_attachments = imagify_count_optimized_attachments();
224 + /**
225 + * Filter the percent of optimized attachments.
226 + * 3rd party will be able to override the result.
227 + *
228 + * @since 1.5
229 + *
230 + * @param int|bool $percent Default is false. Provide an integer.
231 + */
232 + $percent = apply_filters( 'imagify_percent_optimized_attachments', false );
139 233
140 - $percent = ( 0 !== $total_attachments ) ? round( ( 100 - ( ( $total_attachments - ( $total_optimized_attachments ) ) / $total_attachments ) * 100 ) ) : 0;
234 + if ( false !== $percent ) {
235 + return (int) $percent;
236 + }
141 237
142 - return $percent;
238 + $total_attachments = imagify_count_attachments();
239 + $total_optimized_attachments = imagify_count_optimized_attachments();
240 +
241 + if ( ! $total_attachments || ! $total_optimized_attachments ) {
242 + return 0;
243 + }
244 +
245 + return min( round( 100 * $total_optimized_attachments / $total_attachments ), 100 );
143 246 }
144 247
145 -/*
248 +/**
146 249 * Count percent, original & optimized size of all images optimized by Imagify.
147 250 *
148 - * @since 1.0
251 + * @since 1.0
252 + * @since 1.6.7 Revamped to handle huge libraries.
253 + * @author Jonathan Buttigieg
149 254 *
150 - * @return array An array containing the optimization data.
255 + * @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.
256 + * @return array|int An array containing the optimization data. A single data if $key is provided.
151 257 */
152 258 function imagify_count_saving_data( $key = '' ) {
153 259 global $wpdb;
154 260
261 + /**
262 + * Filter the query to get all optimized attachments.
263 + * 3rd party will be able to override the result.
264 + *
265 + * @since 1.5
266 + * @since 1.6.7 This filter should return an array containing the following keys: 'count', 'original_size', and 'optimized_size'.
267 + *
268 + * @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.
269 + */
270 + $attachments = apply_filters( 'imagify_count_saving_data', false );
271 +
155 272 $original_size = 0;
156 273 $optimized_size = 0;
157 - $query = new WP_Query(
158 - array(
159 - 'post_type' => 'attachment',
160 - 'post_status' => 'inherit',
161 - 'post_mime_type' => get_imagify_mime_type(),
162 - 'meta_key' => '_imagify_status',
163 - 'meta_value' => 'success',
164 - 'posts_per_page' => -1,
165 - 'update_post_term_cache' => false,
166 - 'no_found_rows' => true,
167 - 'fields' => 'ids'
168 - )
274 + $count = 0;
275 +
276 + if ( is_array( $attachments ) ) {
277 + /**
278 + * Bypass.
279 + */
280 + if ( isset( $attachments['count'], $attachments['original_size'], $attachments['optimized_size'] ) ) {
281 + /**
282 + * We have the results we need.
283 + */
284 + $attachments['percent'] = $attachments['optimized_size'] && $attachments['original_size'] ? ceil( ( ( $attachments['original_size'] - $attachments['optimized_size'] ) / $attachments['original_size'] ) * 100 ) : 0;
285 +
286 + return $attachments;
287 + }
288 +
289 + /**
290 + * Back compat'.
291 + * The following shouldn't be used. Sites with a huge library won't like it.
292 + */
293 + $attachments = array_map( 'maybe_unserialize', (array) $attachments );
294 +
295 + if ( $attachments ) {
296 + foreach ( $attachments as $attachment_data ) {
297 + if ( ! $attachment_data ) {
298 + continue;
299 + }
300 +
301 + ++$count;
302 + $original_data = $attachment_data['sizes']['full'];
303 +
304 + // Increment the original sizes.
305 + $original_size += $original_data['original_size'] ? $original_data['original_size'] : 0;
306 + $optimized_size += $original_data['optimized_size'] ? $original_data['optimized_size'] : 0;
307 +
308 + unset( $attachment_data['sizes']['full'] );
309 +
310 + // Increment the thumbnails sizes.
311 + if ( $attachment_data['sizes'] ) {
312 + foreach ( $attachment_data['sizes'] as $size_data ) {
313 + if ( ! empty( $size_data['success'] ) ) {
314 + $original_size += $size_data['original_size'] ? $size_data['original_size'] : 0;
315 + $optimized_size += $size_data['optimized_size'] ? $size_data['optimized_size'] : 0;
316 + }
317 + }
318 + }
319 + }
320 + }
321 + } else {
322 + /**
323 + * Filter the chunk size of the requests fetching the data.
324 + * 2,000 seems to be a good balance between memory used, speed, and number of DB hits,
325 + * while keeping the resulting `IN ()` clause well under hosts' SQL query size limits
326 + * (e.g. WP Engine's query governor).
327 + *
328 + * @param int $limit The maximum number of elements per chunk.
329 + */
330 + $limit = apply_filters( 'imagify_count_saving_data_limit', 2000 );
331 + $limit = absint( $limit );
332 +
333 + $mime_types = Imagify_DB::get_mime_types();
334 + $statuses = Imagify_DB::get_post_statuses();
335 + $nodata_join = '';
336 + $nodata_where = '';
337 +
338 + if ( ! imagify_has_attachments_without_required_metadata() ) {
339 + $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause();
340 + $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
341 + }
342 +
343 + $attachment_ids = $wpdb->get_col( // WPCS: unprepared SQL ok.
344 + "
345 + SELECT p.ID
346 + FROM $wpdb->posts AS p
347 + $nodata_join
348 + INNER JOIN $wpdb->postmeta AS mt1
349 + ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' )
350 + WHERE p.post_mime_type IN ( $mime_types )
351 + AND p.post_type = 'attachment'
352 + AND p.post_status IN ( $statuses )
353 + AND mt1.meta_value = 'success'
354 + $nodata_where
355 + ORDER BY CAST( p.ID AS UNSIGNED )"
356 + );
357 + $wpdb->flush();
358 +
359 + $attachment_ids = array_map( 'absint', array_unique( $attachment_ids ) );
360 + $attachment_ids = array_chunk( $attachment_ids, $limit );
361 +
362 + while ( $attachment_ids ) {
363 + $limit_ids = array_shift( $attachment_ids );
364 + // Safety net: further split the chunk if its rendered `IN ()` list would still be too long.
365 + $id_chunks = Imagify_DB::chunk_in_values( $limit_ids );
366 + $attachments = [];
367 +
368 + foreach ( $id_chunks as $id_chunk ) {
369 + $id_chunk_list = implode( ',', $id_chunk );
370 +
371 + $chunk_attachments = $wpdb->get_col( // WPCS: unprepared SQL ok.
372 + "
373 + SELECT meta_value
374 + FROM $wpdb->postmeta
375 + WHERE post_id IN ( $id_chunk_list )
376 + AND meta_key = '_imagify_data'"
377 + );
378 +
379 + if ( $chunk_attachments ) {
380 + $attachments = array_merge( $attachments, $chunk_attachments );
381 + }
382 + }
383 + $wpdb->flush();
384 +
385 + unset( $limit_ids );
386 +
387 + if ( ! $attachments ) {
388 + continue;
389 + }
390 +
391 + $attachments = array_map( 'maybe_unserialize', $attachments );
392 +
393 + foreach ( $attachments as $attachment_data ) {
394 + if ( ! $attachment_data ) {
395 + continue;
396 + }
397 +
398 + if ( empty( $attachment_data['sizes']['full']['success'] ) ) {
399 + /**
400 + * - 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.
401 + * - Case where this meta has no "full" entry.
402 + * Don't ask how it's possible, I don't know ¯\(°_o)/¯
403 + */
404 + continue;
405 + }
406 +
407 + $original_data = $attachment_data['sizes']['full'];
408 +
409 + ++$count;
410 +
411 + // Increment the original sizes.
412 + $original_size += ! empty( $original_data['original_size'] ) ? $original_data['original_size'] : 0;
413 + $optimized_size += ! empty( $original_data['optimized_size'] ) ? $original_data['optimized_size'] : 0;
414 +
415 + unset( $attachment_data['sizes']['full'], $original_data );
416 +
417 + // Increment the thumbnails sizes.
418 + if ( $attachment_data['sizes'] ) {
419 + foreach ( $attachment_data['sizes'] as $size_data ) {
420 + if ( ! empty( $size_data['success'] ) ) {
421 + $original_size += ! empty( $size_data['original_size'] ) ? $size_data['original_size'] : 0;
422 + $optimized_size += ! empty( $size_data['optimized_size'] ) ? $size_data['optimized_size'] : 0;
423 + }
424 + }
425 + }
426 +
427 + unset( $size_data );
428 + }
429 +
430 + unset( $attachments, $attachment_data );
431 + }
432 + }
433 +
434 + $data = [
435 + 'count' => $count,
436 + 'original_size' => $original_size,
437 + 'optimized_size' => $optimized_size,
438 + 'percent' => $original_size && $optimized_size ? ceil( ( ( $original_size - $optimized_size ) / $original_size ) * 100 ) : 0,
439 + ];
440 +
441 + if ( ! empty( $key ) ) {
442 + return isset( $data[ $key ] ) ? $data[ $key ] : 0;
443 + }
444 +
445 + return $data;
446 +}
447 +
448 +/**
449 + * Returns the estimated total size of the images not optimized.
450 + *
451 + * We estimate the total size of the images in the library by getting the latest 250 images and their thumbnails
452 + * add up their filesizes, and doing some maths to get the total size.
453 + *
454 + * @since 1.6
455 + * @author Remy Perona
456 + *
457 + * @return int The current estimated total size of images not optimized.
458 + */
459 +function imagify_calculate_total_size_images_library() {
460 + global $wpdb;
461 +
462 + $mime_types = Imagify_DB::get_mime_types();
463 + $statuses = Imagify_DB::get_post_statuses();
464 + $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause();
465 + $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
466 + $image_ids = $wpdb->get_col( // WPCS: unprepared SQL ok.
467 + "
468 + SELECT p.ID
469 + FROM $wpdb->posts AS p
470 + $nodata_join
471 + WHERE p.post_mime_type IN ( $mime_types )
472 + AND p.post_type = 'attachment'
473 + AND p.post_status IN ( $statuses )
474 + $nodata_where
475 + LIMIT 250
476 + "
169 477 );
170 - $attachments = (array) $query->posts;
171 478
172 - foreach( $attachments as $attachment_id ) {
173 - $attachment = new Imagify_Attachment( $attachment_id );
479 + if ( ! $image_ids ) {
480 + return 0;
481 + }
174 482
175 - // Check if the attachment extension is allowed
176 - if ( ! wp_attachment_is_image( $attachment_id ) ) {
483 + $count_latest_images = count( $image_ids );
484 + $count_total_images = imagify_count_attachments();
485 +
486 + return imagify_calculate_total_image_size( $image_ids, $count_latest_images, $count_total_images );
487 +}
488 +
489 +/**
490 + * Returns the estimated average size of the images uploaded per month.
491 + *
492 + * We estimate the average size of the images uploaded in the library per month by getting the latest 250 images and their thumbnails
493 + * for the 3 latest months, add up their filesizes, and doing some maths to get the total average size.
494 + *
495 + * @since 1.6
496 + * @since 1.7 Use wpdb instead of WP_Query.
497 + * @author Remy Perona
498 + *
499 + * @return int The current estimated average size of images uploaded per month.
500 + */
501 +function imagify_calculate_average_size_images_per_month() {
502 + global $wpdb;
503 +
504 + $mime_types = Imagify_DB::get_mime_types();
505 + $statuses = Imagify_DB::get_post_statuses();
506 + $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause( "$wpdb->posts.ID" );
507 + $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
508 + $limit = ' LIMIT 0, 250';
509 + $query = "
510 + SELECT $wpdb->posts.ID
511 + FROM $wpdb->posts
512 + $nodata_join
513 + WHERE $wpdb->posts.post_mime_type IN ( $mime_types )
514 + AND $wpdb->posts.post_type = 'attachment'
515 + AND $wpdb->posts.post_status IN ( $statuses )
516 + $nodata_where
517 + %date_query%";
518 +
519 + // Queries per month.
520 + $date_query = new WP_Date_Query(
521 + [
522 + [
523 + 'before' => 'now',
524 + 'after' => '1 month ago',
525 + ],
526 + ]
527 + );
528 +
529 + $partial_images_uploaded_last_month = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query . $limit ) ); // WPCS: unprepared SQL ok.
530 +
531 + $date_query = new WP_Date_Query(
532 + [
533 + [
534 + 'before' => '1 month ago',
535 + 'after' => '2 months ago',
536 + ],
537 + ]
538 + );
539 +
540 + $partial_images_uploaded_two_months_ago = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query . $limit ) ); // WPCS: unprepared SQL ok.
541 +
542 + $date_query = new WP_Date_Query(
543 + [
544 + [
545 + 'before' => '2 month ago',
546 + 'after' => '3 months ago',
547 + ],
548 + ]
549 + );
550 +
551 + $partial_images_uploaded_three_months_ago = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query . $limit ) ); // WPCS: unprepared SQL ok.
552 +
553 + // Total for the 3 months.
554 + $partial_images_uploaded_id = array_merge( $partial_images_uploaded_last_month, $partial_images_uploaded_two_months_ago, $partial_images_uploaded_three_months_ago );
555 +
556 + if ( ! $partial_images_uploaded_id ) {
557 + return 0;
558 + }
559 +
560 + // Total for the 3 months, without the "250" limit.
561 + $date_query = new WP_Date_Query(
562 + [
563 + [
564 + 'before' => 'now',
565 + 'after' => '3 month ago',
566 + ],
567 + ]
568 + );
569 +
570 + $images_uploaded_id = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query ) ); // WPCS: unprepared SQL ok.
571 +
572 + if ( ! $images_uploaded_id ) {
573 + return 0;
574 + }
575 +
576 + // Number of image attachments uploaded for the 3 latest months, limited to 250 per month.
577 + $partial_total_images_uploaded = count( $partial_images_uploaded_id );
578 + // Total number of image attachments uploaded for the 3 latest months.
579 + $total_images_uploaded = count( $images_uploaded_id );
580 +
581 + return imagify_calculate_total_image_size( $partial_images_uploaded_id, $partial_total_images_uploaded, $total_images_uploaded ) / 3;
582 +}
583 +
584 +/**
585 + * Returns the estimated total size of images.
586 + *
587 + * @since 1.6
588 + * @author Remy Perona
589 + *
590 + * @param array $image_ids Array of image IDs.
591 + * @param int $partial_total_images The number of image attachments we're doing the calculation with.
592 + * @param int $total_images The total number of image attachments.
593 + * @return int The estimated total size of images.
594 + */
595 +function imagify_calculate_total_image_size( $image_ids, $partial_total_images, $total_images ) {
596 + global $wpdb;
597 +
598 + $image_ids = array_filter( array_map( 'absint', $image_ids ) );
599 +
600 + if ( ! $image_ids ) {
601 + return 0;
602 + }
603 +
604 + $results = Imagify_DB::get_metas(
605 + [
606 + // Get attachments filename.
607 + 'filenames' => '_wp_attached_file',
608 + // Get attachments data.
609 + 'data' => '_wp_attachment_metadata',
610 + // Get Imagify data.
611 + 'imagify_data' => '_imagify_data',
612 + // Get attachments status.
613 + 'statuses' => '_imagify_status',
614 + ],
615 + $image_ids
616 + );
617 +
618 + // Number of image attachments we're doing the calculation with. In case array_filter() removed results.
619 + $partial_total_images = count( $image_ids );
620 + // Total size of unoptimized size.
621 + $partial_size_images = 0;
622 + // Total number of thumbnails.
623 + $partial_total_intermediate_images = 0;
624 +
625 + $filesystem = imagify_get_filesystem();
626 + $is_active_for_network = imagify_is_active_for_network();
627 + $disallowed_sizes = get_imagify_option( 'disallowed-sizes' );
628 +
629 + foreach ( $image_ids as $i => $image_id ) {
630 + $attachment_status = isset( $results['statuses'][ $image_id ] ) ? $results['statuses'][ $image_id ] : false;
631 +
632 + if ( 'success' === $attachment_status ) {
633 + /**
634 + * The image files have been optimized.
635 + */
636 + // Original size.
637 + $partial_size_images += isset( $results['imagify_data'][ $image_id ]['stats']['original_size'] ) ? $results['imagify_data'][ $image_id ]['stats']['original_size'] : 0;
638 + // Number of thumbnails.
639 + $partial_total_intermediate_images += count( $results['imagify_data'][ $image_id ]['sizes'] );
640 + unset(
641 + $image_ids[ $i ],
642 + $results['filenames'][ $image_id ],
643 + $results['data'][ $image_id ],
644 + $results['imagify_data'][ $image_id ],
645 + $results['statuses'][ $image_id ]
646 + );
177 647 continue;
178 648 }
179 649
180 - $stats_data = $attachment->get_stats_data();
181 - $original_data = $attachment->get_size_data( 'full' );
650 + /**
651 + * The image files are not optimized.
652 + */
653 + // Create an array containing all this attachment files.
654 + $files = [
655 + 'full' => get_imagify_attached_file( $results['filenames'][ $image_id ] ),
656 + ];
182 657
183 - // Incremente the original sizes
184 - if ( $attachment->is_optimized() ) {
185 - $original_size += $original_data['original_size'];
186 - $optimized_size += $original_data['optimized_size'];
658 + $sizes = isset( $results['data'][ $image_id ]['sizes'] ) ? $results['data'][ $image_id ]['sizes'] : [];
659 +
660 + if ( $sizes && is_array( $sizes ) ) {
661 + if ( ! $is_active_for_network ) {
662 + $sizes = array_diff_key( $sizes, $disallowed_sizes );
663 + }
664 +
665 + if ( $sizes ) {
666 + $full_dirname = $filesystem->dir_path( $files['full'] );
667 +
668 + foreach ( $sizes as $size_key => $size_data ) {
669 + $files[ $size_key ] = $full_dirname . '/' . $size_data['file'];
670 + }
671 + }
187 672 }
188 -
189 - $metadata = wp_get_attachment_metadata( $attachment_id );
190 - $sizes = ( isset( $metadata['sizes'] ) ) ? (array) $metadata['sizes'] : array();
191 673
192 - // Incremente the thumbnails sizes
193 - foreach ( $sizes as $size_key => $size_data ) {
194 - $size_data = $attachment->get_size_data( $size_key );
195 - if ( ! empty( $size_data['success'] ) ) {
196 - $original_size += $size_data['original_size'];
197 - $optimized_size += $size_data['optimized_size'];
674 + /**
675 + * Allow to provide all files size and the number of thumbnails.
676 + *
677 + * @since 1.6.7
678 + * @author Grégory Viguier
679 + *
680 + * @param bool $size_and_count False by default.
681 + * @param int $image_id The attachment ID.
682 + * @param array $files An array of file paths with thumbnail sizes as keys.
683 + * @param array $image_ids An array of all attachment IDs.
684 + * @return bool|array False by default. Provide an array with the keys 'filesize' (containing the total filesize) and 'thumbnails' (containing the number of thumbnails).
685 + */
686 + $size_and_count = apply_filters( 'imagify_total_attachment_filesize', false, $image_id, $files, $image_ids );
687 +
688 + if ( is_array( $size_and_count ) ) {
689 + $partial_size_images += $size_and_count['filesize'];
690 + $partial_total_intermediate_images += $size_and_count['thumbnails'];
691 + } else {
692 + foreach ( $files as $file ) {
693 + if ( $filesystem->exists( $file ) ) {
694 + $partial_size_images += $filesystem->size( $file );
695 + }
198 696 }
697 +
698 + unset( $files['full'] );
699 + $partial_total_intermediate_images += count( $files );
199 700 }
701 +
702 + unset(
703 + $image_ids[ $i ],
704 + $results['filenames'][ $image_id ],
705 + $results['data'][ $image_id ],
706 + $results['imagify_data'][ $image_id ],
707 + $results['statuses'][ $image_id ]
708 + );
200 709 }
201 710
202 - $data = array(
203 - 'count' => $query->post_count,
204 - 'original_size' => (int) $original_size,
205 - 'optimized_size' => (int) $optimized_size,
206 - 'percent' => ( 0 !== $optimized_size ) ? ceil( ( ( $original_size - $optimized_size ) / $original_size ) * 100 ) : 0
711 + // Number of thumbnails per attachment = Number of thumbnails / Number of attachments.
712 + $intermediate_images_per_image = $partial_total_intermediate_images / $partial_total_images;
713 + /**
714 + * Note: Number of attachments ($partial_total_images) === Number of full sizes.
715 + * Average image size = Size of the images / ( Number of full sizes + Number of thumbnails ).
716 + * Average image size = Size of the images / Number of images.
717 + */
718 + $average_size_images = $partial_size_images / ( $partial_total_images + $partial_total_intermediate_images );
719 + /**
720 + * Note: Total number of attachments ($total_images) === Total number of full sizes.
721 + * Total images size = Average image size * ( Total number of full sizes + ( Number of thumbnails per attachment * Total number of attachments ) ).
722 + * Total images size = Average image size * ( Total number of full sizes + Total number of thumbnails ).
723 + */
724 + $total_size_images = $average_size_images * ( $total_images + ( $intermediate_images_per_image * $total_images ) );
725 +
726 + return $total_size_images;
727 +}
728 +
729 +/**
730 + * Get all generic stats to be used in the bulk optimization page.
731 + *
732 + * @since 1.7.1
733 + * @author Grégory Viguier
734 + *
735 + * @param array $types The folder types. If a folder type is "library", the context should be suffixed after a pipe character. They are passed as array keys.
736 + * @param array $args {
737 + * Optional. An array of arguments.
738 + *
739 + * @type bool $fullset True to return the full set of data. False to return only the main data.
740 + * @type bool $formatting Some of the data is returned formatted.
741 + * }
742 + * @return array
743 + */
744 +function imagify_get_bulk_stats( $types, $args = [] ) {
745 + $types = $types && is_array( $types ) ? $types : [];
746 + $args = array_merge(
747 + [
748 + 'fullset' => false,
749 + 'formatting' => true,
750 + ],
751 + (array) $args
207 752 );
208 753
209 - if ( ! empty( $key ) ) {
210 - return $data[ $key ];
754 + $data = [
755 + // Global chart.
756 + 'total_attachments' => 0,
757 + 'unoptimized_attachments' => 0,
758 + 'optimized_attachments' => 0,
759 + 'errors_attachments' => 0,
760 + // Stats block.
761 + 'already_optimized_attachments' => 0,
762 + 'original_human' => 0,
763 + 'optimized_human' => 0,
764 + ];
765 +
766 + if ( isset( $types['library|wp'] ) ) {
767 + /**
768 + * Library.
769 + */
770 + $saving_data = imagify_count_saving_data();
771 +
772 + // Global chart.
773 + $data['total_attachments'] += imagify_count_attachments();
774 + $data['unoptimized_attachments'] += imagify_count_unoptimized_attachments();
775 + $data['optimized_attachments'] += imagify_count_optimized_attachments();
776 + $data['errors_attachments'] += imagify_count_error_attachments();
777 + // Stats block.
778 + $data['already_optimized_attachments'] += $saving_data['count'];
779 + $data['original_human'] += $saving_data['original_size'];
780 + $data['optimized_human'] += $saving_data['optimized_size'];
211 781 }
212 782
783 + if ( isset( $types['custom-folders|custom-folders'] ) ) {
784 + /**
785 + * Custom folders.
786 + */
787 + // Global chart.
788 + $data['total_attachments'] += Imagify_Files_Stats::count_all_files();
789 + $data['unoptimized_attachments'] += Imagify_Files_Stats::count_no_status_files();
790 + $data['optimized_attachments'] += Imagify_Files_Stats::count_optimized_files();
791 + $data['errors_attachments'] += Imagify_Files_Stats::count_error_files();
792 + // Stats block.
793 + $data['already_optimized_attachments'] += Imagify_Files_Stats::count_success_files();
794 + $data['original_human'] += Imagify_Files_Stats::get_original_size();
795 + $data['optimized_human'] += Imagify_Files_Stats::get_optimized_size();
796 + }
797 +
798 + /**
799 + * Full set of data.
800 + */
801 + if ( $args['fullset'] ) {
802 + // User account.
803 + $views = Imagify_Views::get_instance();
804 +
805 + $data['unconsumed_quota'] = $views->get_quota_percent();
806 + $data['quota_class'] = $views->get_quota_class();
807 + $data['quota_icon'] = $views->get_quota_icon();
808 + }
809 +
810 + /**
811 + * Filter the generic stats used in the bulk optimization page.
812 + *
813 + * @since 1.7.1
814 + * @author Grégory Viguier
815 + *
816 + * @param array $data The data.
817 + * @param array $types The folder types. They are passed as array keys.
818 + * @param array $args {
819 + * Optional. An array of arguments.
820 + *
821 + * @type bool $fullset True to return the full set of data. False to return only the main data.
822 + * @type bool $formatting Some of the data is returned formatted.
823 + * }
824 + */
825 + $data = apply_filters( 'imagify_bulk_stats', $data, $types, $args );
826 +
827 + /**
828 + * Percentages.
829 + */
830 + if ( $data['total_attachments'] && $data['optimized_attachments'] ) {
831 + $data['optimized_attachments_percent'] = round( 100 * $data['optimized_attachments'] / $data['total_attachments'] );
832 + } else {
833 + $data['optimized_attachments_percent'] = 0;
834 + }
835 +
836 + if ( $data['original_human'] && $data['optimized_human'] ) {
837 + $data['optimized_percent'] = ceil( 100 - ( 100 * $data['optimized_human'] / $data['original_human'] ) );
838 + } else {
839 + $data['optimized_percent'] = 0;
840 + }
841 +
842 + /**
843 + * Formating.
844 + */
845 + if ( $args['formatting'] ) {
846 + $data['already_optimized_attachments'] = number_format_i18n( $data['already_optimized_attachments'] );
847 + $data['original_human'] = imagify_size_format( $data['original_human'], 1 );
848 + $data['optimized_human'] = imagify_size_format( $data['optimized_human'], 1 );
849 + }
850 +
213 851 return $data;
214 -}
852 +}