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