| @@ -11,9 +11,11 @@ | ||
| 11 | 11 | Exceptions\Image_Backup_Creation_Error, |
| 12 | 12 | Exceptions\Invalid_Image_Exception, |
| 13 | 13 | Image, |
| 14 | 14 | Image_Backup, |
| 15 | + Image_Conversion, | |
| 15 | 16 | Image_DB_Update, |
| 17 | + Image_Dimensions, | |
| 16 | 18 | Image_Meta, |
| 17 | 19 | Image_Status, |
| 18 | 20 | WP_Image_Meta |
| 19 | 21 | }; |
| @@ -19,9 +21,8 @@ | ||
| 19 | 21 | }; |
| 20 | 22 | use ImageOptimization\Classes\Logger; |
| 21 | 23 | use ImageOptimization\Classes\Utils; |
| 22 | 24 | use ImageOptimization\Classes\Exceptions\Quota_Exceeded_Error; |
| 23 | -use ImageOptimization\Modules\Oauth\Components\Connect; | |
| 24 | 25 | use ImageOptimization\Modules\Optimization\Classes\{ |
| 25 | 26 | Exceptions\Image_File_Already_Exists_Error, |
| 26 | 27 | Exceptions\Image_Optimization_Error, |
| 27 | 28 | Exceptions\Bulk_Token_Expired_Error, |
| @@ -26,8 +27,9 @@ | ||
| 26 | 27 | Exceptions\Image_Optimization_Error, |
| 27 | 28 | Exceptions\Bulk_Token_Expired_Error, |
| 28 | 29 | Exceptions\Image_Already_Optimized_Error, |
| 29 | 30 | }; |
| 31 | +use ImageOptimization\Modules\Connect\Classes\Exceptions\Connection_Error; | |
| 30 | 32 | use ImageOptimization\Modules\Settings\Classes\Settings; |
| 31 | 33 | use Throwable; |
| 32 | 34 | |
| 33 | 35 | use ImageOptimization\Plugin; |
| @@ -42,9 +44,9 @@ | ||
| 42 | 44 | * |
| 43 | 45 | * This class is used by manual, bulk and on-upload optimization flows. |
| 44 | 46 | */ |
| 45 | 47 | class Optimize_Image { |
| 46 | - private const IMAGE_OPTIMIZE_ENDPOINT = 'image/optimize'; | |
| 48 | + public const IMAGE_OPTIMIZE_ENDPOINT = 'image/optimize'; | |
| 47 | 49 | |
| 48 | 50 | protected ?Image $image; |
| 49 | 51 | protected WP_Image_Meta $wp_meta; |
| 50 | 52 | protected string $initiator; |
| @@ -50,23 +52,21 @@ | ||
| 50 | 52 | protected string $initiator; |
| 51 | 53 | protected ?string $bulk_token; |
| 52 | 54 | private string $current_image_path; |
| 53 | 55 | private string $current_image_size; |
| 54 | - private bool $convert_to_webp; | |
| 55 | 56 | private bool $keep_backups; |
| 56 | 57 | private array $current_size_duplicates; |
| 58 | + protected Image_Conversion $image_conversion; | |
| 59 | + private bool $is_reoptimize; | |
| 57 | 60 | |
| 58 | 61 | /** |
| 59 | - * @throws Quota_Exceeded_Error|Bulk_Token_Expired_Error|Image_File_Already_Exists_Error|Image_Optimization_Error | |
| 62 | + * @throws Quota_Exceeded_Error|Connection_Error|Bulk_Token_Expired_Error|Image_File_Already_Exists_Error|Image_Optimization_Error | |
| 60 | 63 | */ |
| 61 | 64 | public function optimize(): void { |
| 62 | 65 | $sizes_enabled = Settings::get( Settings::CUSTOM_SIZES_OPTION_NAME ); |
| 63 | 66 | $sizes_exist = $this->wp_meta->get_size_keys(); |
| 64 | 67 | |
| 65 | - Logger::log( | |
| 66 | - Logger::LEVEL_INFO, | |
| 67 | - "Start optimization of {$this->image->get_id()}" | |
| 68 | - ); | |
| 68 | + Logger::debug( "Start optimization of {$this->image->get_id()}" ); | |
| 69 | 69 | |
| 70 | 70 | foreach ( $sizes_exist as $size_exist ) { |
| 71 | 71 | // If some image sizes optimization is disabled in settings, we check if the current one is still enabled |
| 72 | 72 | if ( |
| @@ -84,24 +84,18 @@ | ||
| 84 | 84 | |
| 85 | 85 | $image_meta = new Image_Meta( $this->image->get_id() ); |
| 86 | 86 | |
| 87 | 87 | // If the current size was already optimized -- ignore it. |
| 88 | - if ( in_array( $size_exist, $image_meta->get_optimized_sizes(), true ) ) { | |
| 89 | - Logger::log( | |
| 90 | - Logger::LEVEL_INFO, | |
| 91 | - "Size `$size_exist` is already optimized" | |
| 92 | - ); | |
| 88 | + if ( ! $this->is_reoptimize && in_array( $size_exist, $image_meta->get_optimized_sizes(), true ) ) { | |
| 89 | + Logger::debug( "Size `$size_exist` is already optimized" ); | |
| 93 | 90 | |
| 94 | 91 | continue; |
| 95 | 92 | } |
| 96 | 93 | |
| 97 | 94 | if ( ! file_exists( $this->image->get_file_path( $size_exist ) ) ) { |
| 98 | - Logger::log( | |
| 99 | - Logger::LEVEL_ERROR, | |
| 100 | - "Can't access file for size `$size_exist`" | |
| 101 | - ); | |
| 95 | + Logger::debug( "Can't access file for size `$size_exist`" ); | |
| 102 | 96 | |
| 103 | - throw new Image_Optimization_Error( esc_html__( 'File is missing. Verify the upload', 'image-optimization' ) ); | |
| 97 | + continue; | |
| 104 | 98 | } |
| 105 | 99 | |
| 106 | 100 | $this->current_image_size = $size_exist; |
| 107 | 101 | $this->current_size_duplicates = $this->wp_meta->get_size_duplicates( $size_exist ); |
| @@ -119,12 +113,9 @@ | ||
| 119 | 113 | if ( ! $this->keep_backups ) { |
| 120 | 114 | Image_Backup::remove( $this->image->get_id() ); |
| 121 | 115 | } |
| 122 | 116 | |
| 123 | - Logger::log( | |
| 124 | - Logger::LEVEL_INFO, | |
| 125 | - "End optimization of {$this->image->get_id()}" | |
| 126 | - ); | |
| 117 | + Logger::debug( "End optimization of {$this->image->get_id()}" ); | |
| 127 | 118 | } |
| 128 | 119 | |
| 129 | 120 | private function optimize_current_size(): void { |
| 130 | 121 | try { |
| @@ -143,19 +134,19 @@ | ||
| 143 | 134 | |
| 144 | 135 | // This should only be updated after meta |
| 145 | 136 | $this->update_attachment_post(); |
| 146 | 137 | } catch ( Image_Already_Optimized_Error $iao ) { |
| 147 | - // If we can't optimize it further, just file update the meta | |
| 138 | + // If we can't optimize it further, just update the meta | |
| 148 | 139 | $original_size = $this->wp_meta->get_file_size( $this->current_image_size ) |
| 149 | 140 | ?? File_System::size( $this->image->get_file_path( $this->current_image_size ) ); |
| 150 | 141 | |
| 151 | - $this->update_attachment_meta( $original_size ); | |
| 152 | - $this->update_attachment_post(); | |
| 153 | - } catch ( Bulk_Token_Expired_Error | Quota_Exceeded_Error | Image_File_Already_Exists_Error $e ) { | |
| 142 | + $this->update_attachment_meta( $original_size, true ); | |
| 143 | + $this->update_attachment_post( true ); | |
| 144 | + } catch ( Bulk_Token_Expired_Error | Quota_Exceeded_Error | Connection_Error | Image_File_Already_Exists_Error $e ) { | |
| 154 | 145 | throw $e; |
| 155 | 146 | } catch ( Throwable $t ) { |
| 156 | 147 | // In case of anything else |
| 157 | - throw new Image_Optimization_Error( $t->getMessage() ); | |
| 148 | + throw new Image_Optimization_Error( esc_html( $t->getMessage() ) ); | |
| 158 | 149 | } |
| 159 | 150 | } |
| 160 | 151 | |
| 161 | 152 | private function send_file() { |
| @@ -169,9 +160,9 @@ | ||
| 169 | 160 | } |
| 170 | 161 | |
| 171 | 162 | $optimization_options = [ |
| 172 | 163 | 'compression_level' => Settings::get( Settings::COMPRESSION_LEVEL_OPTION_NAME ), |
| 173 | - 'convert_to_webp' => $this->convert_to_webp, | |
| 164 | + 'convert_to_format' => $this->image_conversion->get_current_conversion_option(), | |
| 174 | 165 | 'strip_exif' => Settings::get( Settings::STRIP_EXIF_METADATA_OPTION_NAME ), |
| 175 | 166 | ]; |
| 176 | 167 | |
| 177 | 168 | if ( Settings::get( Settings::RESIZE_LARGER_IMAGES_OPTION_NAME ) ) { |
| @@ -201,12 +192,9 @@ | ||
| 201 | 192 | Plugin::instance()->modules_manager->get_modules( 'connect-manager' )->connect_instance->update_usage_data( $response->stats ); |
| 202 | 193 | } |
| 203 | 194 | |
| 204 | 195 | if ( ! isset( $response->imageKey ) || $image_key !== $response->imageKey ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase |
| 205 | - Logger::log( | |
| 206 | - Logger::LEVEL_ERROR, | |
| 207 | - "Image key must be $image_key, instead got $response->imageKey" | |
| 208 | - ); | |
| 196 | + Logger::error( "Image key must be $image_key, instead got $response->imageKey" ); | |
| 209 | 197 | |
| 210 | 198 | throw new Image_Optimization_Error( esc_html__( 'Service response is incorrect', 'image-optimization' ) ); |
| 211 | 199 | } |
| 212 | 200 | |
| @@ -212,12 +200,9 @@ | ||
| 212 | 200 | |
| 213 | 201 | $received_file_hash = md5( base64_decode( $response->image, true ) ); |
| 214 | 202 | |
| 215 | 203 | if ( ! isset( $response->checksum ) || $received_file_hash !== $response->checksum ) { |
| 216 | - Logger::log( | |
| 217 | - Logger::LEVEL_ERROR, | |
| 218 | - "Image key must be $response->checksum, instead calculated $received_file_hash" | |
| 219 | - ); | |
| 204 | + Logger::error( "Image key must be $response->checksum, instead calculated $received_file_hash" ); | |
| 220 | 205 | |
| 221 | 206 | throw new Image_Optimization_Error( esc_html__( 'Service response is incorrect', 'image-optimization' ) ); |
| 222 | 207 | } |
| 223 | 208 | |
| @@ -235,18 +220,18 @@ | ||
| 235 | 220 | $tmp_path = File_Utils::replace_extension( $path, 'tmp' ); |
| 236 | 221 | |
| 237 | 222 | File_System::put_contents( $tmp_path, $file_data ); |
| 238 | 223 | |
| 239 | - if ( $this->convert_to_webp ) { | |
| 240 | - $webp_path = File_Utils::replace_extension( $tmp_path, 'webp' ); | |
| 241 | - $original_file_is_webp = strtolower( $path ) === strtolower( $webp_path ); | |
| 224 | + if ( $this->image_conversion->is_enabled() ) { | |
| 225 | + $converted_path = File_Utils::replace_extension( $tmp_path, $this->image_conversion->get_current_file_extension() ); | |
| 226 | + $original_is_already_converted = strtolower( $path ) === strtolower( $converted_path ); | |
| 242 | 227 | |
| 243 | - if ( ! $original_file_is_webp && File_System::exists( $webp_path ) ) { | |
| 228 | + if ( ! $original_is_already_converted && File_System::exists( $converted_path ) ) { | |
| 244 | 229 | throw new Image_File_Already_Exists_Error(); |
| 245 | 230 | } |
| 246 | 231 | |
| 247 | 232 | // We want to keep both original as a fallback and optimized webp to prevent 404s |
| 248 | - if ( ! $original_file_is_webp ) { | |
| 233 | + if ( ! $original_is_already_converted ) { | |
| 249 | 234 | $this->keep_backups = true; |
| 250 | 235 | |
| 251 | 236 | ( new Image_Meta( $this->image->get_id() ) ) |
| 252 | 237 | ->set_image_backup_path( $this->current_image_size, $this->current_image_path ) |
| @@ -252,15 +237,15 @@ | ||
| 252 | 237 | ->set_image_backup_path( $this->current_image_size, $this->current_image_path ) |
| 253 | 238 | ->save(); |
| 254 | 239 | } |
| 255 | 240 | |
| 256 | - if ( $original_file_is_webp ) { | |
| 241 | + if ( $original_is_already_converted ) { | |
| 257 | 242 | Image_Backup::create( $this->image->get_id(), $this->current_image_size, $this->current_image_path ); |
| 258 | 243 | } |
| 259 | 244 | |
| 260 | - File_System::move( $tmp_path, $webp_path, true ); | |
| 245 | + File_System::move( $tmp_path, $converted_path, true ); | |
| 261 | 246 | |
| 262 | - $path = $webp_path; | |
| 247 | + $path = $converted_path; | |
| 263 | 248 | } else { |
| 264 | 249 | Image_Backup::create( $this->image->get_id(), $this->current_image_size, $this->current_image_path ); |
| 265 | 250 | |
| 266 | 251 | File_System::move( $tmp_path, $path, true ); |
| @@ -279,16 +264,20 @@ | ||
| 279 | 264 | * Updates attachment records in the `wp_posts` table. |
| 280 | 265 | * |
| 281 | 266 | * @return void |
| 282 | 267 | */ |
| 283 | - private function update_attachment_post() { | |
| 268 | + private function update_attachment_post( ?bool $is_fully_optimized = false ) { | |
| 284 | 269 | $update_query = []; |
| 285 | 270 | |
| 286 | - if ( $this->convert_to_webp ) { | |
| 271 | + if ( $this->image_conversion->is_enabled() && ! $is_fully_optimized ) { | |
| 287 | 272 | $attachment_object = $this->image->get_attachment_object(); |
| 288 | 273 | |
| 289 | - $update_query['guid'] = File_Utils::replace_extension( $attachment_object->guid, 'webp' ); | |
| 290 | - $update_query['post_mime_type'] = 'image/webp'; | |
| 274 | + $update_query['guid'] = File_Utils::replace_extension( | |
| 275 | + $attachment_object->guid, | |
| 276 | + $this->image_conversion->get_current_file_extension() | |
| 277 | + ); | |
| 278 | + | |
| 279 | + $update_query['post_mime_type'] = $this->image_conversion->get_current_mime_type(); | |
| 291 | 280 | } |
| 292 | 281 | |
| 293 | 282 | $update_query['post_modified'] = current_time( 'mysql' ); |
| 294 | 283 | $update_query['post_modified_gmt'] = current_time( 'mysql', true ); |
| @@ -299,16 +288,16 @@ | ||
| 299 | 288 | /** |
| 300 | 289 | * Updates attachment records in the `wp_postmeta` table. |
| 301 | 290 | * |
| 302 | 291 | * @param int $optimized_size |
| 292 | + * @param bool|null $is_fully_optimized | |
| 303 | 293 | * |
| 304 | 294 | * @return void |
| 305 | 295 | */ |
| 306 | - private function update_attachment_meta( int $optimized_size ) { | |
| 296 | + private function update_attachment_meta( int $optimized_size, ?bool $is_fully_optimized = false ) { | |
| 307 | 297 | $meta = new Image_Meta( $this->image->get_id() ); |
| 298 | + $dimensions = Image_Dimensions::get_by_path( $this->current_image_path ); | |
| 308 | 299 | |
| 309 | - list($width, $height) = getimagesize( $this->current_image_path ); | |
| 310 | - | |
| 311 | 300 | $sizes_to_update = [ $this->current_image_size, ...$this->current_size_duplicates ]; |
| 312 | 301 | |
| 313 | 302 | foreach ( $sizes_to_update as $size ) { |
| 314 | 303 | $meta |
| @@ -316,16 +305,16 @@ | ||
| 316 | 305 | ->add_optimized_size( $size ) |
| 317 | 306 | ->add_original_data( $size, $this->wp_meta->get_size_data( $size ) ); |
| 318 | 307 | |
| 319 | 308 | $this->wp_meta |
| 320 | - ->set_width( $size, $width ) | |
| 321 | - ->set_height( $size, $height ) | |
| 309 | + ->set_width( $size, $dimensions->width ) | |
| 310 | + ->set_height( $size, $dimensions->height ) | |
| 322 | 311 | ->set_file_size( $size, $optimized_size ); |
| 323 | 312 | |
| 324 | - if ( $this->convert_to_webp ) { | |
| 313 | + if ( $this->image_conversion->is_enabled() && ! $is_fully_optimized ) { | |
| 325 | 314 | $this->wp_meta |
| 326 | 315 | ->set_file_path( $size, $this->current_image_path ) |
| 327 | - ->set_mime_type( $size, 'image/webp' ); | |
| 316 | + ->set_mime_type( $size, $this->image_conversion->get_current_mime_type() ); | |
| 328 | 317 | } |
| 329 | 318 | } |
| 330 | 319 | |
| 331 | 320 | $meta->save(); |
| @@ -362,13 +351,14 @@ | ||
| 362 | 351 | |
| 363 | 352 | /** |
| 364 | 353 | * @throws Invalid_Image_Exception |
| 365 | 354 | */ |
| 366 | - public function __construct( int $image_id, string $initiator, ?string $bulk_token = null ) { | |
| 355 | + public function __construct( int $image_id, string $initiator, ?string $bulk_token = null, ?bool $is_reoptimize = false ) { | |
| 367 | 356 | $this->image = new Image( $image_id ); |
| 368 | 357 | $this->wp_meta = new WP_Image_Meta( $image_id, $this->image ); |
| 369 | 358 | $this->initiator = $initiator; |
| 370 | 359 | $this->bulk_token = $bulk_token; |
| 371 | - $this->convert_to_webp = Settings::get( Settings::CONVERT_TO_WEBP_OPTION_NAME ); | |
| 360 | + $this->is_reoptimize = $is_reoptimize; | |
| 361 | + $this->image_conversion = new Image_Conversion(); | |
| 372 | 362 | $this->keep_backups = Settings::get( Settings::BACKUP_ORIGINAL_IMAGES_OPTION_NAME ); |
| 373 | 363 | } |
| 374 | 364 | } |