Edit File by line
/home/zeestwma/ajeebong.../wp-conte.../plugins/image-op.../modules/stats/classes
File: optimization-stats.php
<?php
[0] Fix | Delete
[1] Fix | Delete
namespace ImageOptimization\Modules\Stats\Classes;
[2] Fix | Delete
[3] Fix | Delete
use ImageOptimization\Classes\Image\{
[4] Fix | Delete
Image,
[5] Fix | Delete
Image_Meta,
[6] Fix | Delete
Image_Query_Builder,
[7] Fix | Delete
Image_Status,
[8] Fix | Delete
WP_Image_Meta,
[9] Fix | Delete
Exceptions\Invalid_Image_Exception
[10] Fix | Delete
};
[11] Fix | Delete
use ImageOptimization\Classes\Async_Operation\{
[12] Fix | Delete
Async_Operation,
[13] Fix | Delete
Async_Operation_Hook,
[14] Fix | Delete
Async_Operation_Queue,
[15] Fix | Delete
Exceptions\Async_Operation_Exception,
[16] Fix | Delete
Queries\Operation_Query,
[17] Fix | Delete
};
[18] Fix | Delete
use ImageOptimization\Classes\File_System\{
[19] Fix | Delete
Exceptions\File_System_Operation_Error,
[20] Fix | Delete
File_System,
[21] Fix | Delete
};
[22] Fix | Delete
use ImageOptimization\Classes\Logger;
[23] Fix | Delete
use ImageOptimization\Modules\Optimization\Classes\Exceptions\Image_Validation_Error;
[24] Fix | Delete
use ImageOptimization\Modules\Optimization\Classes\Validate_Image;
[25] Fix | Delete
use ImageOptimization\Modules\Settings\Classes\Settings;
[26] Fix | Delete
[27] Fix | Delete
if ( ! defined( 'ABSPATH' ) ) {
[28] Fix | Delete
exit; // Exit if accessed directly.
[29] Fix | Delete
}
[30] Fix | Delete
[31] Fix | Delete
class Optimization_Stats {
[32] Fix | Delete
const PAGING_SIZE = 2000;
[33] Fix | Delete
const STATS_CALCULATION_DELAY = 60 * 15;
[34] Fix | Delete
[35] Fix | Delete
/**
[36] Fix | Delete
* Returns image stats.
[37] Fix | Delete
* If the library is too big, it queries images in chunks.
[38] Fix | Delete
*
[39] Fix | Delete
* @return array{total_image_count: int, optimized_image_count: int, current_image_size: int, initial_image_size:
[40] Fix | Delete
* int}
[41] Fix | Delete
*/
[42] Fix | Delete
public static function get_image_stats( ?int $image_id = null, ?bool $no_cache = false ): array {
[43] Fix | Delete
// No caching needed if we check a specific image.
[44] Fix | Delete
if ( $image_id ) {
[45] Fix | Delete
$output = self::get_image_stats_chunk( 1, $image_id );
[46] Fix | Delete
[47] Fix | Delete
unset( $output['pages'] );
[48] Fix | Delete
[49] Fix | Delete
return $output;
[50] Fix | Delete
}
[51] Fix | Delete
[52] Fix | Delete
// Look at the stored data first. If it's still valid -- use it.
[53] Fix | Delete
$stats = self::get_stored_stats();
[54] Fix | Delete
[55] Fix | Delete
if ( ! $no_cache && ( time() - $stats['updated_at'] ) <= self::STATS_CALCULATION_DELAY ) {
[56] Fix | Delete
unset( $stats['updated_at'] );
[57] Fix | Delete
[58] Fix | Delete
return $stats;
[59] Fix | Delete
}
[60] Fix | Delete
[61] Fix | Delete
// Otherwise, recalculate the stats.
[62] Fix | Delete
$output = self::get_image_stats_chunk( 1, $image_id );
[63] Fix | Delete
$pages_count = $output['pages'];
[64] Fix | Delete
[65] Fix | Delete
if ( $pages_count <= 1 ) {
[66] Fix | Delete
unset( $output['pages'] );
[67] Fix | Delete
[68] Fix | Delete
return $output;
[69] Fix | Delete
}
[70] Fix | Delete
[71] Fix | Delete
if ( $pages_count > 1 && Async_Operation::OPERATION_STATUS_NOT_STARTED === self::get_stats_calculation_status() ) {
[72] Fix | Delete
try {
[73] Fix | Delete
Async_Operation::create(
[74] Fix | Delete
Async_Operation_Hook::CALCULATE_OPTIMIZATION_STATS,
[75] Fix | Delete
[
[76] Fix | Delete
'page' => 2,
[77] Fix | Delete
'pages_count' => $pages_count,
[78] Fix | Delete
'output' => $output,
[79] Fix | Delete
],
[80] Fix | Delete
Async_Operation_Queue::STATS
[81] Fix | Delete
);
[82] Fix | Delete
} catch ( Async_Operation_Exception $aoe ) {
[83] Fix | Delete
Logger::log( Logger::LEVEL_ERROR, 'Error while creating a stats calculation task: ' . $aoe->getMessage() );
[84] Fix | Delete
}
[85] Fix | Delete
}
[86] Fix | Delete
[87] Fix | Delete
unset( $stats['updated_at'] );
[88] Fix | Delete
[89] Fix | Delete
return $stats;
[90] Fix | Delete
}
[91] Fix | Delete
[92] Fix | Delete
/**
[93] Fix | Delete
* @return array{pages: int, total_image_count: int, optimized_image_count: int, current_image_size: int,
[94] Fix | Delete
* initial_image_size: int}
[95] Fix | Delete
*/
[96] Fix | Delete
public static function get_image_stats_chunk( int $paged = 1, ?int $image_id = null ): array {
[97] Fix | Delete
$output = [
[98] Fix | Delete
'pages' => 1,
[99] Fix | Delete
'total_image_count' => 0,
[100] Fix | Delete
'optimized_image_count' => 0,
[101] Fix | Delete
'current_image_size' => 0,
[102] Fix | Delete
'initial_image_size' => 0,
[103] Fix | Delete
];
[104] Fix | Delete
[105] Fix | Delete
$query = ( new Image_Query_Builder() )
[106] Fix | Delete
->set_paging_size( self::PAGING_SIZE )
[107] Fix | Delete
->set_current_page( $paged );
[108] Fix | Delete
[109] Fix | Delete
if ( $image_id ) {
[110] Fix | Delete
$query->set_image_ids( [ $image_id ] );
[111] Fix | Delete
}
[112] Fix | Delete
[113] Fix | Delete
$query = $query->execute();
[114] Fix | Delete
[115] Fix | Delete
$output['pages'] = (int) $query->max_num_pages;
[116] Fix | Delete
[117] Fix | Delete
foreach ( $query->posts as $attachment_id ) {
[118] Fix | Delete
try {
[119] Fix | Delete
Validate_Image::is_valid( $attachment_id );
[120] Fix | Delete
[121] Fix | Delete
$image = new Image( $attachment_id );
[122] Fix | Delete
$wp_meta = new WP_Image_Meta( $attachment_id );
[123] Fix | Delete
} catch ( Invalid_Image_Exception | Image_Validation_Error $ie ) {
[124] Fix | Delete
continue;
[125] Fix | Delete
}
[126] Fix | Delete
[127] Fix | Delete
$meta = new Image_Meta( $attachment_id );
[128] Fix | Delete
$image_sizes = self::filter_only_enabled_sizes( $wp_meta->get_size_keys() );
[129] Fix | Delete
[130] Fix | Delete
foreach ( $image_sizes as $image_size ) {
[131] Fix | Delete
if ( ! $image->file_exists( $image_size ) ) {
[132] Fix | Delete
continue;
[133] Fix | Delete
}
[134] Fix | Delete
[135] Fix | Delete
$output['total_image_count']++;
[136] Fix | Delete
[137] Fix | Delete
$output['current_image_size'] += self::calculate_current_image_file_size( $attachment_id, $wp_meta, $image_size );
[138] Fix | Delete
$output['initial_image_size'] += self::calculate_initial_image_file_size( $attachment_id, $meta, $wp_meta, $image_size );
[139] Fix | Delete
}
[140] Fix | Delete
[141] Fix | Delete
$optimized_sizes = self::filter_only_enabled_sizes( $meta->get_optimized_sizes() );
[142] Fix | Delete
$output['optimized_image_count'] += count( $optimized_sizes );
[143] Fix | Delete
}
[144] Fix | Delete
[145] Fix | Delete
return $output;
[146] Fix | Delete
}
[147] Fix | Delete
[148] Fix | Delete
public static function get_optimization_details( int $attachment_id ): array {
[149] Fix | Delete
try {
[150] Fix | Delete
$wp_meta = new WP_Image_Meta( $attachment_id );
[151] Fix | Delete
} catch ( Invalid_Image_Exception $iie ) {
[152] Fix | Delete
Logger::log( Logger::LEVEL_ERROR, "No metadata found for image ID: {$attachment_id}" );
[153] Fix | Delete
[154] Fix | Delete
throw $iie;
[155] Fix | Delete
}
[156] Fix | Delete
[157] Fix | Delete
$output = [
[158] Fix | Delete
'total' => 0,
[159] Fix | Delete
'sizes' => [],
[160] Fix | Delete
];
[161] Fix | Delete
[162] Fix | Delete
$meta = new Image_Meta( $attachment_id );
[163] Fix | Delete
$image_sizes = self::filter_only_enabled_sizes( $wp_meta->get_size_keys() );
[164] Fix | Delete
[165] Fix | Delete
foreach ( $image_sizes as $image_size ) {
[166] Fix | Delete
$current_image_size = self::calculate_current_image_file_size( $attachment_id, $wp_meta, $image_size );
[167] Fix | Delete
$output['total'] += $current_image_size;
[168] Fix | Delete
[169] Fix | Delete
$status = self::get_image_size_optimization_status( $attachment_id, $image_size );
[170] Fix | Delete
[171] Fix | Delete
if ( Image_Status::OPTIMIZED === $status ) {
[172] Fix | Delete
$dimensions_updated = $wp_meta->get_width( $image_size ) !== (int) $meta->get_original_width( $image_size ) ||
[173] Fix | Delete
$wp_meta->get_height( $image_size ) !== (int) $meta->get_original_height( $image_size );
[174] Fix | Delete
[175] Fix | Delete
$new_dimensions = $dimensions_updated ? [
[176] Fix | Delete
'width' => $wp_meta->get_width( $image_size ),
[177] Fix | Delete
'height' => $wp_meta->get_height( $image_size ),
[178] Fix | Delete
] : null;
[179] Fix | Delete
[180] Fix | Delete
$initial_image_size = self::calculate_initial_image_file_size( $attachment_id, $meta, $wp_meta, $image_size );
[181] Fix | Delete
[182] Fix | Delete
$saved = [
[183] Fix | Delete
'relative' => max( 100 - round( $current_image_size / $initial_image_size * 100 ), 0 ),
[184] Fix | Delete
'absolute' => $initial_image_size - $current_image_size,
[185] Fix | Delete
];
[186] Fix | Delete
}
[187] Fix | Delete
[188] Fix | Delete
$output['sizes'][] = [
[189] Fix | Delete
'size_name' => $image_size,
[190] Fix | Delete
'file_size' => $current_image_size,
[191] Fix | Delete
'status' => self::get_image_size_optimization_status( $attachment_id, $image_size ),
[192] Fix | Delete
'saved' => $saved ?? null,
[193] Fix | Delete
'new_dimensions' => $new_dimensions ?? null,
[194] Fix | Delete
];
[195] Fix | Delete
}
[196] Fix | Delete
[197] Fix | Delete
return $output;
[198] Fix | Delete
}
[199] Fix | Delete
[200] Fix | Delete
private static function get_image_size_optimization_status( int $attachment_id, string $size_name ): string {
[201] Fix | Delete
$image = new Image( $attachment_id );
[202] Fix | Delete
$meta = new Image_Meta( $attachment_id );
[203] Fix | Delete
[204] Fix | Delete
if ( in_array( $size_name, $meta->get_optimized_sizes(), true ) ) {
[205] Fix | Delete
return Image_Status::OPTIMIZED;
[206] Fix | Delete
}
[207] Fix | Delete
[208] Fix | Delete
if ( ! $image->file_exists( $size_name ) ) {
[209] Fix | Delete
return 'file-not-found';
[210] Fix | Delete
}
[211] Fix | Delete
[212] Fix | Delete
try {
[213] Fix | Delete
Validate_Image::validate_file_size( $attachment_id, $size_name );
[214] Fix | Delete
} catch ( Image_Validation_Error $ive ) {
[215] Fix | Delete
return 'file-too-large';
[216] Fix | Delete
}
[217] Fix | Delete
[218] Fix | Delete
return Image_Status::NOT_OPTIMIZED;
[219] Fix | Delete
}
[220] Fix | Delete
[221] Fix | Delete
private static function calculate_current_image_file_size( int $image_id, WP_Image_Meta $wp_meta, string $image_size ): int {
[222] Fix | Delete
$size_from_meta = $wp_meta->get_file_size( $image_size );
[223] Fix | Delete
[224] Fix | Delete
if ( $size_from_meta ) {
[225] Fix | Delete
return $size_from_meta;
[226] Fix | Delete
}
[227] Fix | Delete
[228] Fix | Delete
try {
[229] Fix | Delete
return File_System::size( ( new Image( $image_id ) )->get_file_path( $image_size ) );
[230] Fix | Delete
} catch ( File_System_Operation_Error $e ) {
[231] Fix | Delete
return 0;
[232] Fix | Delete
}
[233] Fix | Delete
}
[234] Fix | Delete
[235] Fix | Delete
private static function calculate_initial_image_file_size( int $image_id, Image_Meta $meta, WP_Image_Meta $wp_meta, string $image_size ): int {
[236] Fix | Delete
$size_from_meta = $meta->get_original_file_size( $image_size ) ?? $wp_meta->get_file_size( $image_size );
[237] Fix | Delete
[238] Fix | Delete
if ( $size_from_meta ) {
[239] Fix | Delete
return $size_from_meta;
[240] Fix | Delete
}
[241] Fix | Delete
[242] Fix | Delete
try {
[243] Fix | Delete
return File_System::size( ( new Image( $image_id ) )->get_file_path( $image_size ) );
[244] Fix | Delete
} catch ( File_System_Operation_Error $e ) {
[245] Fix | Delete
return 0;
[246] Fix | Delete
}
[247] Fix | Delete
}
[248] Fix | Delete
[249] Fix | Delete
private static function filter_only_enabled_sizes( array $size_keys ): array {
[250] Fix | Delete
$enabled_sizes = Settings::get( Settings::CUSTOM_SIZES_OPTION_NAME );
[251] Fix | Delete
[252] Fix | Delete
if ( 'all' === $enabled_sizes ) {
[253] Fix | Delete
return array_filter( $size_keys, fn( string $size_key ) => ! str_starts_with( $size_key, 'elementor_' ) );
[254] Fix | Delete
}
[255] Fix | Delete
[256] Fix | Delete
return array_filter($size_keys, function( string $size ) use ( $enabled_sizes ) {
[257] Fix | Delete
if ( Image::SIZE_FULL === $size ) {
[258] Fix | Delete
return true;
[259] Fix | Delete
}
[260] Fix | Delete
[261] Fix | Delete
if ( in_array( $size, $enabled_sizes, true ) ) {
[262] Fix | Delete
return true;
[263] Fix | Delete
}
[264] Fix | Delete
[265] Fix | Delete
return false;
[266] Fix | Delete
});
[267] Fix | Delete
}
[268] Fix | Delete
[269] Fix | Delete
/**
[270] Fix | Delete
* Retrieves stats data.
[271] Fix | Delete
*
[272] Fix | Delete
* @return array{total_image_count: ?int, optimized_image_count: ?int, current_image_size: ?int, initial_image_size:
[273] Fix | Delete
* ?int, updated_at: ?int}
[274] Fix | Delete
*/
[275] Fix | Delete
public static function get_stored_stats(): array {
[276] Fix | Delete
$default = [
[277] Fix | Delete
'total_image_count' => null,
[278] Fix | Delete
'optimized_image_count' => null,
[279] Fix | Delete
'current_image_size' => null,
[280] Fix | Delete
'initial_image_size' => null,
[281] Fix | Delete
'updated_at' => null,
[282] Fix | Delete
];
[283] Fix | Delete
[284] Fix | Delete
$output = json_decode( get_option( 'image_optimizer_optimization_stats' ), ARRAY_A );
[285] Fix | Delete
[286] Fix | Delete
return is_array( $output ) ? $output : $default;
[287] Fix | Delete
}
[288] Fix | Delete
[289] Fix | Delete
/**
[290] Fix | Delete
* Updates the optimization stats with fresh values.
[291] Fix | Delete
*
[292] Fix | Delete
* @param array $stats
[293] Fix | Delete
*
[294] Fix | Delete
* @return bool
[295] Fix | Delete
*/
[296] Fix | Delete
public static function set_stored_stats( array $stats ): bool {
[297] Fix | Delete
$stats['updated_at'] = time();
[298] Fix | Delete
[299] Fix | Delete
return update_option( 'image_optimizer_optimization_stats', json_encode( $stats ) );
[300] Fix | Delete
}
[301] Fix | Delete
[302] Fix | Delete
private static function get_stats_calculation_status(): string {
[303] Fix | Delete
$active_query = ( new Operation_Query() )
[304] Fix | Delete
->set_hook( Async_Operation_Hook::CALCULATE_OPTIMIZATION_STATS )
[305] Fix | Delete
->set_status( [
[306] Fix | Delete
Async_Operation::OPERATION_STATUS_PENDING,
[307] Fix | Delete
Async_Operation::OPERATION_STATUS_RUNNING,
[308] Fix | Delete
] )
[309] Fix | Delete
->set_limit( 1 );
[310] Fix | Delete
[311] Fix | Delete
$active_operations = Async_Operation::get( $active_query );
[312] Fix | Delete
[313] Fix | Delete
return ! empty( $active_operations )
[314] Fix | Delete
? Async_Operation::OPERATION_STATUS_RUNNING
[315] Fix | Delete
: Async_Operation::OPERATION_STATUS_NOT_STARTED;
[316] Fix | Delete
}
[317] Fix | Delete
}
[318] Fix | Delete
[319] Fix | Delete
It is recommended that you Edit text format, this type of Fix handles quite a lot in one request
Function