PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / 1.7
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF v1.7
2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.9 2.2.8 trunk 1.10 1.3.3 1.3.4 1.3.5 1.3.5.1 1.3.5.2 1.3.6 1.3.6.1 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.5 All 103 releases
imagify / inc / functions / admin-stats.php

admin-stats.php in Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF 1.7, at inc/functions/admin-stats.php

668 lines 20.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 defined( 'ABSPATH' ) || die( 'Cheatin\' uh?' );
3
4 /**
5 * Count number of attachments.
6 *
7 * @since 1.0
8 * @author Jonathan Buttigieg
9 *
10 * @return int The number of attachments.
11 */
12 function imagify_count_attachments() {
13 global $wpdb;
14 static $count;
15
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 = Imagify_DB::get_required_wp_metadata_join_clause();
37 $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
38 $count = (int) $wpdb->get_var( // WPCS: unprepared SQL ok.
39 "
40 SELECT COUNT( p.ID )
41 FROM $wpdb->posts AS p
42 $nodata_join
43 WHERE p.post_mime_type IN ( $mime_types )
44 AND p.post_type = 'attachment'
45 AND p.post_status IN ( $statuses )
46 $nodata_where"
47 );
48
49 if ( $count > imagify_get_unoptimized_attachment_limit() ) {
50 set_transient( 'imagify_large_library', 1 );
51 } elseif ( get_transient( 'imagify_large_library' ) ) {
52 // In case the number is decreasing under our limit.
53 delete_transient( 'imagify_large_library' );
54 }
55
56 return $count;
57 }
58
59 /**
60 * Count number of optimized attachments with an error.
61 *
62 * @since 1.0
63 * @author Jonathan Buttigieg
64 *
65 * @return int The number of attachments.
66 */
67 function imagify_count_error_attachments() {
68 global $wpdb;
69 static $count;
70
71 /**
72 * Filter the number of optimized attachments with an error.
73 * 3rd party will be able to override the result.
74 *
75 * @since 1.5
76 *
77 * @param int|bool $pre_count Default is false. Provide an integer.
78 */
79 $pre_count = apply_filters( 'imagify_count_error_attachments', false );
80
81 if ( false !== $pre_count ) {
82 return (int) $pre_count;
83 }
84
85 if ( isset( $count ) ) {
86 return $count;
87 }
88
89 $mime_types = Imagify_DB::get_mime_types();
90 $statuses = Imagify_DB::get_post_statuses();
91 $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause();
92 $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
93 $count = (int) $wpdb->get_var( // WPCS: unprepared SQL ok.
94 "
95 SELECT COUNT( DISTINCT p.ID )
96 FROM $wpdb->posts AS p
97 $nodata_join
98 INNER JOIN $wpdb->postmeta AS mt1
99 ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' )
100 WHERE p.post_mime_type IN ( $mime_types )
101 AND p.post_type = 'attachment'
102 AND p.post_status IN ( $statuses )
103 AND mt1.meta_value = 'error'
104 $nodata_where"
105 );
106
107 return $count;
108 }
109
110 /**
111 * Count number of optimized attachments (by Imagify or an other tool before).
112 *
113 * @since 1.0
114 * @author Jonathan Buttigieg
115 *
116 * @return int The number of attachments.
117 */
118 function imagify_count_optimized_attachments() {
119 global $wpdb;
120 static $count;
121
122 /**
123 * Filter the number of optimized attachments.
124 * 3rd party will be able to override the result.
125 *
126 * @since 1.5
127 *
128 * @param int|bool $pre_count Default is false. Provide an integer.
129 */
130 $pre_count = apply_filters( 'imagify_count_optimized_attachments', false );
131
132 if ( false !== $pre_count ) {
133 return (int) $pre_count;
134 }
135
136 if ( isset( $count ) ) {
137 return $count;
138 }
139
140 $mime_types = Imagify_DB::get_mime_types();
141 $statuses = Imagify_DB::get_post_statuses();
142 $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause();
143 $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
144 $count = (int) $wpdb->get_var( // WPCS: unprepared SQL ok.
145 "
146 SELECT COUNT( DISTINCT p.ID )
147 FROM $wpdb->posts AS p
148 $nodata_join
149 INNER JOIN $wpdb->postmeta AS mt1
150 ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' )
151 WHERE p.post_mime_type IN ( $mime_types )
152 AND p.post_type = 'attachment'
153 AND p.post_status IN ( $statuses )
154 AND mt1.meta_value IN ( 'success', 'already_optimized' )
155 $nodata_where"
156 );
157
158 return $count;
159 }
160
161 /**
162 * Count number of unoptimized attachments.
163 *
164 * @since 1.0
165 * @author Jonathan Buttigieg
166 *
167 * @return int The number of attachments.
168 */
169 function imagify_count_unoptimized_attachments() {
170 /**
171 * Filter the number of unoptimized attachments.
172 * 3rd party will be able to override the result.
173 *
174 * @since 1.5
175 *
176 * @param int|bool $pre_count Default is false. Provide an integer.
177 */
178 $pre_count = apply_filters( 'imagify_count_unoptimized_attachments', false );
179
180 if ( false !== $pre_count ) {
181 return (int) $pre_count;
182 }
183
184 return imagify_count_attachments() - imagify_count_optimized_attachments() - imagify_count_error_attachments();
185 }
186
187 /**
188 * Count percent of optimized attachments.
189 *
190 * @since 1.0
191 * @author Jonathan Buttigieg
192 *
193 * @return int The percent of optimized attachments.
194 */
195 function imagify_percent_optimized_attachments() {
196 /**
197 * Filter the percent of optimized attachments.
198 * 3rd party will be able to override the result.
199 *
200 * @since 1.5
201 *
202 * @param int|bool $percent Default is false. Provide an integer.
203 */
204 $percent = apply_filters( 'imagify_percent_optimized_attachments', false );
205
206 if ( false !== $percent ) {
207 return (int) $percent;
208 }
209
210 $total_attachments = imagify_count_attachments();
211 $total_optimized_attachments = imagify_count_optimized_attachments();
212
213 if ( ! $total_attachments || ! $total_optimized_attachments ) {
214 return 0;
215 }
216
217 return min( round( 100 * $total_optimized_attachments / $total_attachments ), 100 );
218 }
219
220 /**
221 * Count percent, original & optimized size of all images optimized by Imagify.
222 *
223 * @since 1.0
224 * @since 1.6.7 Revamped to handle huge libraries.
225 * @author Jonathan Buttigieg
226 *
227 * @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.
228 * @return array|int An array containing the optimization data. A single data if $key is provided.
229 */
230 function imagify_count_saving_data( $key = '' ) {
231 global $wpdb;
232
233 /**
234 * Filter the query to get all optimized attachments.
235 * 3rd party will be able to override the result.
236 *
237 * @since 1.5
238 * @since 1.6.7 This filter should return an array containing the following keys: 'count', 'original_size', and 'optimized_size'.
239 *
240 * @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.
241 */
242 $attachments = apply_filters( 'imagify_count_saving_data', false );
243
244 $original_size = 0;
245 $optimized_size = 0;
246 $count = 0;
247
248 if ( is_array( $attachments ) ) {
249 /**
250 * Bypass.
251 */
252 if ( isset( $attachments['count'], $attachments['original_size'], $attachments['optimized_size'] ) ) {
253 /**
254 * We have the results we need.
255 */
256 $attachments['percent'] = $attachments['optimized_size'] && $attachments['original_size'] ? ceil( ( ( $attachments['original_size'] - $attachments['optimized_size'] ) / $attachments['original_size'] ) * 100 ) : 0;
257
258 return $attachments;
259 }
260
261 /**
262 * Back compat'.
263 * The following shouldn't be used. Sites with a huge library won't like it.
264 */
265 $attachments = array_map( 'maybe_unserialize', (array) $attachments );
266
267 if ( $attachments ) {
268 foreach ( $attachments as $attachment_data ) {
269 if ( ! $attachment_data ) {
270 continue;
271 }
272
273 ++$count;
274 $original_data = $attachment_data['sizes']['full'];
275
276 // Increment the original sizes.
277 $original_size += $original_data['original_size'] ? $original_data['original_size'] : 0;
278 $optimized_size += $original_data['optimized_size'] ? $original_data['optimized_size'] : 0;
279
280 unset( $attachment_data['sizes']['full'] );
281
282 // Increment the thumbnails sizes.
283 foreach ( $attachment_data['sizes'] as $size_data ) {
284 if ( ! empty( $size_data['success'] ) ) {
285 $original_size += $size_data['original_size'] ? $size_data['original_size'] : 0;
286 $optimized_size += $size_data['optimized_size'] ? $size_data['optimized_size'] : 0;
287 }
288 }
289 }
290 }
291 } else {
292 /**
293 * Filter the chunk size of the requests fetching the data.
294 * 15,000 seems to be a good balance between memory used, speed, and number of DB hits.
295 *
296 * @param int $limit The maximum number of elements per chunk.
297 */
298 $limit = apply_filters( 'imagify_count_saving_data_limit', 15000 );
299 $limit = absint( $limit );
300
301 $mime_types = Imagify_DB::get_mime_types();
302 $statuses = Imagify_DB::get_post_statuses();
303 $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause();
304 $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
305 $attachment_ids = $wpdb->get_col( // WPCS: unprepared SQL ok.
306 "
307 SELECT p.ID
308 FROM $wpdb->posts AS p
309 $nodata_join
310 INNER JOIN $wpdb->postmeta AS mt1
311 ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' )
312 WHERE p.post_mime_type IN ( $mime_types )
313 AND p.post_type = 'attachment'
314 AND p.post_status IN ( $statuses )
315 AND mt1.meta_value = 'success'
316 $nodata_where
317 ORDER BY CAST( p.ID AS UNSIGNED )"
318 );
319 $wpdb->flush();
320
321 $attachment_ids = array_map( 'absint', array_unique( $attachment_ids ) );
322 $attachment_ids = array_chunk( $attachment_ids, $limit );
323
324 while ( $attachment_ids ) {
325 $limit_ids = array_shift( $attachment_ids );
326 $limit_ids = implode( ',', $limit_ids );
327
328 $attachments = $wpdb->get_col( // WPCS: unprepared SQL ok.
329 "
330 SELECT meta_value
331 FROM $wpdb->postmeta
332 WHERE post_id IN ( $limit_ids )
333 AND meta_key = '_imagify_data'"
334 );
335 $wpdb->flush();
336
337 unset( $limit_ids );
338
339 if ( ! $attachments ) {
340 // Uh?!
341 continue;
342 }
343
344 $attachments = array_map( 'maybe_unserialize', $attachments );
345
346 foreach ( $attachments as $attachment_data ) {
347 if ( ! $attachment_data ) {
348 continue;
349 }
350
351 $original_data = $attachment_data['sizes']['full'];
352
353 if ( empty( $original_data['success'] ) ) {
354 /**
355 * 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.
356 * Don't ask how it's possible, I don't know.
357 */
358 continue;
359 }
360
361 ++$count;
362
363 // Increment the original sizes.
364 $original_size += ! empty( $original_data['original_size'] ) ? $original_data['original_size'] : 0;
365 $optimized_size += ! empty( $original_data['optimized_size'] ) ? $original_data['optimized_size'] : 0;
366
367 unset( $attachment_data['sizes']['full'], $original_data );
368
369 // Increment the thumbnails sizes.
370 foreach ( $attachment_data['sizes'] as $size_data ) {
371 if ( ! empty( $size_data['success'] ) ) {
372 $original_size += ! empty( $size_data['original_size'] ) ? $size_data['original_size'] : 0;
373 $optimized_size += ! empty( $size_data['optimized_size'] ) ? $size_data['optimized_size'] : 0;
374 }
375 }
376
377 unset( $size_data );
378 }
379
380 unset( $attachments, $attachment_data );
381 } // End while().
382 } // End if().
383
384 $data = array(
385 'count' => $count,
386 'original_size' => $original_size,
387 'optimized_size' => $optimized_size,
388 'percent' => $original_size && $optimized_size ? ceil( ( ( $original_size - $optimized_size ) / $original_size ) * 100 ) : 0,
389 );
390
391 if ( ! empty( $key ) ) {
392 return isset( $data[ $key ] ) ? $data[ $key ] : 0;
393 }
394
395 return $data;
396 }
397
398 /**
399 * Returns the estimated total size of the images not optimized.
400 *
401 * We estimate the total size of the images in the library by getting the latest 250 images and their thumbnails
402 * add up their filesizes, and doing some maths to get the total size.
403 *
404 * @since 1.6
405 * @author Remy Perona
406 *
407 * @return int The current estimated total size of images not optimized.
408 */
409 function imagify_calculate_total_size_images_library() {
410 global $wpdb;
411
412 $mime_types = Imagify_DB::get_mime_types();
413 $statuses = Imagify_DB::get_post_statuses();
414 $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause();
415 $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
416 $image_ids = $wpdb->get_col( // WPCS: unprepared SQL ok.
417 "
418 SELECT p.ID
419 FROM $wpdb->posts AS p
420 $nodata_join
421 WHERE p.post_mime_type IN ( $mime_types )
422 AND p.post_type = 'attachment'
423 AND p.post_status IN ( $statuses )
424 $nodata_where
425 LIMIT 250
426 " );
427
428 if ( ! $image_ids ) {
429 return 0;
430 }
431
432 $count_latest_images = count( $image_ids );
433 $count_total_images = imagify_count_attachments();
434
435 return imagify_calculate_total_image_size( $image_ids, $count_latest_images, $count_total_images );
436 }
437
438 /**
439 * Returns the estimated average size of the images uploaded per month.
440 *
441 * We estimate the average size of the images uploaded in the library per month by getting the latest 250 images and their thumbnails
442 * for the 3 latest months, add up their filesizes, and doing some maths to get the total average size.
443 *
444 * @since 1.6
445 * @since 1.7 Use wpdb instead of WP_Query.
446 * @author Remy Perona
447 *
448 * @return int The current estimated average size of images uploaded per month.
449 */
450 function imagify_calculate_average_size_images_per_month() {
451 global $wpdb;
452
453 $mime_types = Imagify_DB::get_mime_types();
454 $statuses = Imagify_DB::get_post_statuses();
455 $nodata_join = Imagify_DB::get_required_wp_metadata_join_clause( "$wpdb->posts.ID" );
456 $nodata_where = Imagify_DB::get_required_wp_metadata_where_clause();
457 $limit = ' LIMIT 0, 250';
458 $query = "
459 SELECT $wpdb->posts.ID
460 FROM $wpdb->posts
461 $nodata_join
462 WHERE $wpdb->posts.post_mime_type IN ( $mime_types )
463 AND $wpdb->posts.post_type = 'attachment'
464 AND $wpdb->posts.post_status IN ( $statuses )
465 $nodata_where
466 %date_query%";
467
468 // Queries per month.
469 $date_query = new WP_Date_Query( array(
470 array(
471 'before' => 'now',
472 'after' => '1 month ago',
473 ),
474 ) );
475
476 $partial_images_uploaded_last_month = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query . $limit ) ); // WPCS: unprepared SQL ok.
477
478 $date_query = new WP_Date_Query( array(
479 array(
480 'before' => '1 month ago',
481 'after' => '2 months ago',
482 ),
483 ) );
484
485 $partial_images_uploaded_two_months_ago = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query . $limit ) ); // WPCS: unprepared SQL ok.
486
487 $date_query = new WP_Date_Query( array(
488 array(
489 'before' => '2 month ago',
490 'after' => '3 months ago',
491 ),
492 ) );
493
494 $partial_images_uploaded_three_months_ago = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query . $limit ) ); // WPCS: unprepared SQL ok.
495
496 // Total for the 3 months.
497 $partial_images_uploaded_id = array_merge( $partial_images_uploaded_last_month, $partial_images_uploaded_two_months_ago, $partial_images_uploaded_three_months_ago );
498
499 if ( ! $partial_images_uploaded_id ) {
500 return 0;
501 }
502
503 // Total for the 3 months, without the "250" limit.
504 $date_query = new WP_Date_Query( array(
505 array(
506 'before' => 'now',
507 'after' => '3 month ago',
508 ),
509 ) );
510
511 $images_uploaded_id = $wpdb->get_col( str_replace( '%date_query%', $date_query->get_sql(), $query ) ); // WPCS: unprepared SQL ok.
512
513 if ( ! $images_uploaded_id ) {
514 return 0;
515 }
516
517 // Number of image attachments uploaded for the 3 latest months, limited to 250 per month.
518 $partial_total_images_uploaded = count( $partial_images_uploaded_id );
519 // Total number of image attachments uploaded for the 3 latest months.
520 $total_images_uploaded = count( $images_uploaded_id );
521
522 return imagify_calculate_total_image_size( $partial_images_uploaded_id, $partial_total_images_uploaded, $total_images_uploaded ) / 3;
523 }
524
525 /**
526 * Returns the estimated total size of images.
527 *
528 * @since 1.6
529 * @author Remy Perona
530 *
531 * @param array $image_ids Array of image IDs.
532 * @param int $partial_total_images The number of image attachments we're doing the calculation with.
533 * @param int $total_images The total number of image attachments.
534 * @return int The estimated total size of images.
535 */
536 function imagify_calculate_total_image_size( $image_ids, $partial_total_images, $total_images ) {
537 global $wpdb;
538
539 $image_ids = array_filter( array_map( 'absint', $image_ids ) );
540
541 if ( ! $image_ids ) {
542 return 0;
543 }
544
545 $results = Imagify_DB::get_metas( array(
546 // Get attachments filename.
547 'filenames' => '_wp_attached_file',
548 // Get attachments data.
549 'data' => '_wp_attachment_metadata',
550 // Get Imagify data.
551 'imagify_data' => '_imagify_data',
552 // Get attachments status.
553 'statuses' => '_imagify_status',
554 ), $image_ids );
555
556 // Number of image attachments we're doing the calculation with. In case array_filter() removed results.
557 $partial_total_images = count( $image_ids );
558 // Total size of unoptimized size.
559 $partial_size_images = 0;
560 // Total number of thumbnails.
561 $partial_total_intermediate_images = 0;
562
563 $is_active_for_network = imagify_is_active_for_network();
564 $disallowed_sizes = get_imagify_option( 'disallowed-sizes' );
565
566 foreach ( $image_ids as $i => $image_id ) {
567 $attachment_status = isset( $results['statuses'][ $image_id ] ) ? $results['statuses'][ $image_id ] : false;
568
569 if ( 'success' === $attachment_status ) {
570 /**
571 * The image files have been optimized.
572 */
573 // Original size.
574 $partial_size_images += isset( $results['imagify_data'][ $image_id ]['stats']['original_size'] ) ? $results['imagify_data'][ $image_id ]['stats']['original_size'] : 0;
575 // Number of thumbnails.
576 $partial_total_intermediate_images += count( $results['imagify_data'][ $image_id ]['sizes'] );
577 unset(
578 $image_ids[ $i ],
579 $results['filenames'][ $image_id ],
580 $results['data'][ $image_id ],
581 $results['imagify_data'][ $image_id ],
582 $results['statuses'][ $image_id ]
583 );
584 continue;
585 }
586
587 /**
588 * The image files are not optimized.
589 */
590 // Create an array containing all this attachment files.
591 $files = array(
592 'full' => get_imagify_attached_file( $results['filenames'][ $image_id ] ),
593 );
594
595 /** This filter is documented in inc/functions/process.php. */
596 $files['full'] = apply_filters( 'imagify_file_path', $files['full'] );
597
598 $sizes = isset( $results['data'][ $image_id ]['sizes'] ) ? $results['data'][ $image_id ]['sizes'] : array();
599
600 if ( $sizes && is_array( $sizes ) ) {
601 if ( ! $is_active_for_network ) {
602 $sizes = array_diff_key( $sizes, $disallowed_sizes );
603 }
604
605 if ( $sizes ) {
606 $full_dirname = dirname( $files['full'] );
607
608 foreach ( $sizes as $size_key => $size_data ) {
609 $files[ $size_key ] = $full_dirname . '/' . $size_data['file'];
610 }
611 }
612 }
613
614 /**
615 * Allow to provide all files size and the number of thumbnails.
616 *
617 * @since 1.6.7
618 * @author Grégory Viguier
619 *
620 * @param bool $size_and_count False by default.
621 * @param int $image_id The attachment ID.
622 * @param array $files An array of file paths with thumbnail sizes as keys.
623 * @param array $image_ids An array of all attachment IDs.
624 * @return bool|array False by default. Provide an array with the keys 'filesize' (containing the total filesize) and 'thumbnails' (containing the number of thumbnails).
625 */
626 $size_and_count = apply_filters( 'imagify_total_attachment_filesize', false, $image_id, $files, $image_ids );
627
628 if ( is_array( $size_and_count ) ) {
629 $partial_size_images += $size_and_count['filesize'];
630 $partial_total_intermediate_images += $size_and_count['thumbnails'];
631 } else {
632 foreach ( $files as $file ) {
633 if ( file_exists( $file ) ) {
634 $partial_size_images += filesize( $file );
635 }
636 }
637
638 unset( $files['full'] );
639 $partial_total_intermediate_images += count( $files );
640 }
641
642 unset(
643 $image_ids[ $i ],
644 $results['filenames'][ $image_id ],
645 $results['data'][ $image_id ],
646 $results['imagify_data'][ $image_id ],
647 $results['statuses'][ $image_id ]
648 );
649 } // End foreach().
650
651 // Number of thumbnails per attachment = Number of thumbnails / Number of attachments.
652 $intermediate_images_per_image = $partial_total_intermediate_images / $partial_total_images;
653 /**
654 * Note: Number of attachments ($partial_total_images) === Number of full sizes.
655 * Average image size = Size of the images / ( Number of full sizes + Number of thumbnails ).
656 * Average image size = Size of the images / Number of images.
657 */
658 $average_size_images = $partial_size_images / ( $partial_total_images + $partial_total_intermediate_images );
659 /**
660 * Note: Total number of attachments ($total_images) === Total number of full sizes.
661 * Total images size = Average image size * ( Total number of full sizes + ( Number of thumbnails per attachment * Total number of attachments ) ).
662 * Total images size = Average image size * ( Total number of full sizes + Total number of thumbnails ).
663 */
664 $total_size_images = $average_size_images * ( $total_images + ( $intermediate_images_per_image * $total_images ) );
665
666 return $total_size_images;
667 }
668