PluginProbe
Image Optimizer – Compress Images and Convert to WebP or AVIF / 1.7.7
Image Optimizer – Compress Images and Convert to WebP or AVIF v1.7.7
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/components/bulk-optimization.php +218 -101 1.5.01.7.7 View file →
@@ -1,144 +1,266 @@
1 1 <?php
2 2
3 3 namespace ImageOptimization\Modules\Optimization\Components;
4 4
5 -use ImageOptimization\Classes\Async_Operation\Async_Operation_Hook;
5 +use ImageOptimization\Classes\Async_Operation\{
6 + Async_Operation,
7 + Async_Operation_Hook,
8 + Async_Operation_Queue,
9 +};
10 +
6 11 use ImageOptimization\Classes\Image\{
7 - Image,
8 12 Image_Meta,
9 - Image_Optimization_Error_Type,
10 - Image_Restore,
11 13 Image_Status
12 14 };
13 -use ImageOptimization\Classes\Async_Operation\Exceptions\Async_Operation_Exception;
14 -use ImageOptimization\Classes\Logger;
15 -use ImageOptimization\Classes\Utils;
16 -use ImageOptimization\Classes\Exceptions\Quota_Exceeded_Error;
15 +
17 16 use ImageOptimization\Modules\Optimization\{
18 17 Classes\Exceptions\Bulk_Token_Expired_Error,
19 - Classes\Exceptions\Image_File_Already_Exists_Error,
20 18 Classes\Optimize_Image,
21 - Classes\Bulk_Optimization_Controller,
22 - Components\Exceptions\Bulk_Optimization_Token_Not_Found_Error,
19 + Classes\Bulk_Optimization\Bulk_Optimization_Queue,
20 + Classes\Bulk_Optimization\Bulk_Optimization_Queue_Status,
21 + Classes\Bulk_Optimization\Bulk_Optimization_Queue_Type,
22 + Classes\Bulk_Optimization\Bulk_Optimization_Token_Manager,
23 23 };
24 24
25 -use ImageOptimization\Modules\Stats\Classes\Optimization_Stats;
25 +use ImageOptimization\Classes\Logger;
26 +use ImageOptimization\Classes\Utils;
27 +use ImageOptimization\Classes\Exceptions\Quota_Exceeded_Error;
26 28 use Throwable;
27 29
30 +// @codeCoverageIgnoreStart
28 31 if ( ! defined( 'ABSPATH' ) ) {
29 32 exit; // Exit if accessed directly.
30 33 }
34 +// @codeCoverageIgnoreEnd
31 35
32 36 class Bulk_Optimization {
33 - const BULK_OPTIMIZATION_BASE_SLUG = 'image-optimization-bulk-optimization';
34 - const BULK_OPTIMIZATION_CAPABILITY = 'manage_options';
37 + /** @async */
38 + public function optimize_bulk() {
39 + $queue = new Bulk_Optimization_Queue( Bulk_Optimization_Queue_Type::OPTIMIZATION );
35 40
36 - public function render_app() {
37 - ?>
38 - <!-- The hack required to wrap WP notifications -->
39 - <div class="wrap">
40 - <h1 style="display: none;" role="presentation"></h1>
41 - </div>
41 + if ( ! $queue->exists() ) {
42 + Logger::debug( 'Bulk optimization queue did not found' );
42 43
43 - <div id="image-optimization-app"></div>
44 - <?php
45 - }
44 + return;
45 + }
46 46
47 - public function register_page() {
48 - add_media_page(
49 - __( 'Bulk Optimization', 'image-optimization' ),
50 - __( 'Bulk Optimization', 'image-optimization' ),
51 - self::BULK_OPTIMIZATION_CAPABILITY,
52 - self::BULK_OPTIMIZATION_BASE_SLUG,
53 - [ $this, 'render_app' ],
54 - 7
55 - );
56 - }
47 + if ( ! $queue->has_more_images() ) {
48 + Logger::debug( 'No more pending images, deleting queue' );
49 + $queue->delete();
57 50
58 - /** @async */
59 - public function optimize_bulk( int $image_id, string $operation_id ) {
51 + return;
52 + }
53 +
54 + if ( $queue->should_refresh_token() ) {
55 + Logger::debug( 'Refreshing bulk token' );
56 +
57 + try {
58 + $token_data = Bulk_Optimization_Token_Manager::obtain_token(
59 + $queue->get_stats()[ Bulk_Optimization_Queue_Status::PENDING ],
60 + $queue->get_max_batch_size()
61 + );
62 +
63 + $queue
64 + ->set_bulk_token(
65 + $token_data['token'],
66 + time() + HOUR_IN_SECONDS,
67 + $token_data['batch_size']
68 + )
69 + ->save();
70 + } catch ( Throwable $t ) {
71 + Logger::info( 'Failed to obtain a bulk token: ' . $t->getMessage() );
72 +
73 + foreach ( $queue->get_images_by_status( Bulk_Optimization_Queue_Status::PENDING ) as $image ) {
74 + ( new Image_Meta( $image['id'] ) )
75 + ->set_status( Image_Status::NOT_OPTIMIZED )
76 + ->save();
77 + }
78 +
79 + $queue->delete();
80 +
81 + return;
82 + }
83 + }
84 +
85 + $image_id = $queue->get_next_image();
86 +
87 + Logger::debug( 'Processing image: ' . $image_id );
88 +
89 + if ( ! $image_id ) {
90 + Logger::debug( 'No image to process, deleting queue' );
91 + $queue->delete();
92 +
93 + return;
94 + }
95 +
96 + $queue
97 + ->set_current_image_id( $image_id )
98 + ->save();
99 +
60 100 try {
61 - $bulk_token = Bulk_Optimization_Controller::get_bulk_operation_token( $operation_id );
62 -
63 101 $oi = new Optimize_Image(
64 102 $image_id,
65 103 'bulk',
66 - $bulk_token
104 + $queue->get_bulk_token()
67 105 );
68 106
69 107 $oi->optimize();
70 - } catch ( Quota_Exceeded_Error $qe ) {
71 - ( new Image_Meta( $image_id ) )
72 - ->set_status( Image_Status::OPTIMIZATION_FAILED )
73 - ->set_error_type( Image_Optimization_Error_Type::QUOTA_EXCEEDED )
108 +
109 + $queue
110 + ->mark_image_completed( $image_id )
111 + ->increment_optimized_counter()
74 112 ->save();
75 - } catch ( Image_File_Already_Exists_Error $fe ) {
76 - ( new Image_Meta( $image_id ) )
77 - ->set_status( Image_Status::OPTIMIZATION_FAILED )
78 - ->set_error_type( Image_Optimization_Error_Type::FILE_ALREADY_EXISTS )
79 - ->save();
80 - } catch ( Bulk_Token_Expired_Error | Bulk_Optimization_Token_Not_Found_Error $bte ) {
81 - ( new Image_Meta( $image_id ) )
82 - ->set_status( Image_Status::NOT_OPTIMIZED )
83 - ->save();
84 113
85 - Bulk_Optimization_Controller::reschedule_bulk_optimization();
114 + } catch ( Bulk_Token_Expired_Error $btee ) {
115 + $queue->mark_image_failed( $image_id )->save();
116 +
117 + Logger::info( "Token expired while optimizing image {$image_id}" );
118 + } catch ( Quota_Exceeded_Error $qee ) {
119 + $queue->mark_image_failed( $image_id )->save();
120 +
121 + Logger::info( "Quota exceeded while optimizing image {$image_id}" );
122 + } catch ( Throwable $e ) {
123 + $queue->mark_image_failed( $image_id )->save();
124 +
125 + Logger::info( "Failed to optimize image {$image_id}: " . $e->getMessage() );
126 + }
127 +
128 + $queue
129 + ->set_current_image_id( null )
130 + ->save();
131 +
132 + try {
133 + if ( $queue->has_more_images() ) {
134 + Async_Operation::create(
135 + Async_Operation_Hook::OPTIMIZE_BULK,
136 + [ 'operation_id' => $queue->get_operation_id() ],
137 + Async_Operation_Queue::OPTIMIZE
138 + );
139 +
140 + Logger::debug( 'Async operation created' );
141 + } else {
142 + Logger::debug( 'All images were optimized, deleting queue' );
143 +
144 + $queue->delete();
145 + }
86 146 } catch ( Throwable $t ) {
87 - Logger::log( Logger::LEVEL_ERROR, 'Optimization error. Reason: ' . $t->getMessage() );
88 -
89 - ( new Image_Meta( $image_id ) )
90 - ->set_status( Image_Status::OPTIMIZATION_FAILED )
91 - ->set_error_type( Image_Optimization_Error_Type::GENERIC )
92 - ->save();
93 - } finally {
94 - Optimization_Stats::get_image_stats( null, true );
147 + Logger::error( 'Failed to create next async operation or delete queue: ' . $t->getMessage() );
95 148 }
96 149 }
97 150
98 151 /** @async */
99 - public function reoptimize_bulk( int $image_id, string $operation_id ) {
100 - try {
101 - $image = new Image( $image_id );
152 + public function reoptimize_bulk() {
153 + $queue = new Bulk_Optimization_Queue( Bulk_Optimization_Queue_Type::REOPTIMIZATION );
102 154
103 - if ( $image->can_be_restored() ) {
104 - Image_Restore::restore( $image_id, true );
155 + if ( ! $queue->exists() ) {
156 + return;
157 + }
158 +
159 + if ( ! $queue->has_more_images() ) {
160 + $queue->delete();
161 +
162 + return;
163 + }
164 +
165 + if ( $queue->should_refresh_token() ) {
166 + try {
167 + $token_data = Bulk_Optimization_Token_Manager::obtain_token(
168 + $queue->get_stats()[ Bulk_Optimization_Queue_Status::PENDING ],
169 + $queue->get_max_batch_size()
170 + );
171 +
172 + $queue
173 + ->set_bulk_token(
174 + $token_data['token'],
175 + time() + HOUR_IN_SECONDS,
176 + $token_data['batch_size']
177 + )
178 + ->save();
179 + } catch ( Throwable $t ) {
180 + Logger::info( 'Failed to obtain bulk token: ' . $t->getMessage() );
181 +
182 + foreach ( $queue->get_images_by_status( Bulk_Optimization_Queue_Status::PENDING ) as $image ) {
183 + ( new Image_Meta( $image['id'] ) )
184 + ->set_status( Image_Status::OPTIMIZATION_FAILED )
185 + ->save();
186 + }
187 +
188 + $queue->delete();
189 +
190 + return;
105 191 }
192 + }
106 193
107 - $bulk_token = Bulk_Optimization_Controller::get_bulk_operation_token( $operation_id );
194 + $image_id = $queue->get_next_image();
108 195
196 + if ( ! $image_id ) {
197 + $queue
198 + ->set_status( Bulk_Optimization_Queue_Status::COMPLETED )
199 + ->save();
200 +
201 + return;
202 + }
203 +
204 + $queue
205 + ->set_current_image_id( $image_id )
206 + ->save();
207 +
208 + try {
109 209 $oi = new Optimize_Image(
110 210 $image_id,
111 - 'bulk',
112 - $bulk_token
211 + 'bulk-reoptimize',
212 + $queue->get_bulk_token(),
213 + true
113 214 );
114 215
115 216 $oi->optimize();
116 - } catch ( Quota_Exceeded_Error $qe ) {
117 - ( new Image_Meta( $image_id ) )
118 - ->set_status( Image_Status::REOPTIMIZING_FAILED )
119 - ->set_error_type( Image_Optimization_Error_Type::QUOTA_EXCEEDED )
217 +
218 + $queue
219 + ->mark_image_completed( $image_id )
220 + ->increment_optimized_counter()
120 221 ->save();
121 - } catch ( Image_File_Already_Exists_Error $fe ) {
122 - ( new Image_Meta( $image_id ) )
123 - ->set_status( Image_Status::REOPTIMIZING_FAILED )
124 - ->set_error_type( Image_Optimization_Error_Type::FILE_ALREADY_EXISTS )
125 - ->save();
126 - } catch ( Bulk_Token_Expired_Error | Bulk_Optimization_Token_Not_Found_Error $bte ) {
127 - ( new Image_Meta( $image_id ) )
128 - ->set_status( Image_Status::NOT_OPTIMIZED )
129 - ->save();
130 222
131 - Bulk_Optimization_Controller::reschedule_bulk_reoptimization();
223 + } catch ( Bulk_Token_Expired_Error $btee ) {
224 + $queue->mark_image_failed( $image_id )->save();
225 +
226 + Logger::info( "Token expired while reoptimizing image {$image_id}" );
227 + } catch ( Quota_Exceeded_Error $qee ) {
228 + $queue->mark_image_failed( $image_id )->save();
229 +
230 + foreach ( $queue->get_images_by_status( Bulk_Optimization_Queue_Status::PENDING ) as $image ) {
231 + ( new Image_Meta( $image['id'] ) )
232 + ->set_status( Image_Status::OPTIMIZATION_FAILED )
233 + ->save();
234 + }
235 +
236 + $queue->delete();
237 +
238 + Logger::info( "Quota exceeded while reoptimizing image {$image_id}" );
239 +
240 + return;
241 + } catch ( Throwable $e ) {
242 + $queue->mark_image_failed( $image_id )->save();
243 +
244 + Logger::info( "Failed to reoptimize image {$image_id}: " . $e->getMessage() );
245 + }
246 +
247 + $queue
248 + ->set_current_image_id( null )
249 + ->save();
250 +
251 + try {
252 + if ( $queue->has_more_images() ) {
253 + Async_Operation::create(
254 + Async_Operation_Hook::REOPTIMIZE_BULK,
255 + [ 'operation_id' => $queue->get_operation_id() ],
256 + Async_Operation_Queue::OPTIMIZE
257 + );
258 + } else {
259 + $queue->delete();
260 + }
132 261 } catch ( Throwable $t ) {
133 - Logger::log( Logger::LEVEL_ERROR, 'Reoptimization error. Reason: ' . $t->getMessage() );
134 -
135 - ( new Image_Meta( $image_id ) )
136 - ->set_status( Image_Status::REOPTIMIZING_FAILED )
137 - ->set_error_type( Image_Optimization_Error_Type::GENERIC )
138 - ->save();
139 - } finally {
140 - Optimization_Stats::get_image_stats( null, true );
262 + Logger::error( 'Failed to create next async operation or delete queue: ' . $t->getMessage() );
141 263 }
142 264 }
143 265
144 266 /**
@@ -146,13 +268,10 @@
146 268 *
147 269 * @return void
148 270 */
149 271 public function render_bulk_optimization_notice() {
150 - try {
151 - $is_in_progress = Bulk_Optimization_Controller::is_optimization_in_progress();
152 - } catch ( Async_Operation_Exception $aoe ) {
153 - $is_in_progress = false;
154 - }
272 + $queue = new Bulk_Optimization_Queue( Bulk_Optimization_Queue_Type::OPTIMIZATION );
273 + $is_in_progress = $queue->exists();
155 274 ?>
156 275 <div class="notice notice-info notice image-optimizer__notice image-optimizer__notice--info image-optimizer__notice--bulk-tip"
157 276 style="display: <?php echo $is_in_progress ? 'block' : 'none'; ?>">
158 277 <p>
@@ -174,16 +293,14 @@
174 293 <?php
175 294 }
176 295
177 296 public function __construct() {
178 - add_action( 'admin_menu', [ $this, 'register_page' ] );
297 + add_action( Async_Operation_Hook::OPTIMIZE_BULK, [ $this, 'optimize_bulk' ] );
298 + add_action( Async_Operation_Hook::REOPTIMIZE_BULK, [ $this, 'reoptimize_bulk' ] );
179 299
180 - add_action( Async_Operation_Hook::OPTIMIZE_BULK, [ $this, 'optimize_bulk' ], 10, 2 );
181 - add_action( Async_Operation_Hook::REOPTIMIZE_BULK, [ $this, 'reoptimize_bulk' ], 10, 2 );
182 -
183 - add_action('current_screen', function () {
300 + add_action( 'current_screen', function () {
184 301 if ( Utils::is_bulk_optimization_page() ) {
185 302 add_filter( 'admin_footer_text', [ $this, 'render_bulk_optimization_notice' ] );
186 303 }
187 - });
304 + } );
188 305 }
189 306 }