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 +258 -67 1.0.21.7.7 View file →
@@ -1,115 +1,306 @@
1 1 <?php
2 2
3 -namespace ImageOptimizer\Modules\Optimization\Components;
3 +namespace ImageOptimization\Modules\Optimization\Components;
4 4
5 -use ImageOptimizer\Classes\Async_Operation\Async_Operation_Hook;
6 -use ImageOptimizer\Classes\Image\{
7 - Image,
5 +use ImageOptimization\Classes\Async_Operation\{
6 + Async_Operation,
7 + Async_Operation_Hook,
8 + Async_Operation_Queue,
9 +};
10 +
11 +use ImageOptimization\Classes\Image\{
8 12 Image_Meta,
9 - Image_Optimization_Error_Type,
10 - Image_Restore,
11 13 Image_Status
12 14 };
13 -use ImageOptimizer\Classes\Logger;
14 -use ImageOptimizer\Modules\Oauth\Classes\Exceptions\Quota_Exceeded_Error;
15 -use ImageOptimizer\Modules\Optimization\Classes\Optimize_Image;
16 -use ImageOptimizer\Modules\Optimization\Classes\Bulk_Optimization as Bulk_Optimization_Controller;
15 +
16 +use ImageOptimization\Modules\Optimization\{
17 + Classes\Exceptions\Bulk_Token_Expired_Error,
18 + Classes\Optimize_Image,
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 +};
24 +
25 +use ImageOptimization\Classes\Logger;
26 +use ImageOptimization\Classes\Utils;
27 +use ImageOptimization\Classes\Exceptions\Quota_Exceeded_Error;
17 28 use Throwable;
18 29
30 +// @codeCoverageIgnoreStart
19 31 if ( ! defined( 'ABSPATH' ) ) {
20 32 exit; // Exit if accessed directly.
21 33 }
34 +// @codeCoverageIgnoreEnd
22 35
23 36 class Bulk_Optimization {
24 - const BULK_OPTIMIZATION_BASE_SLUG = 'image-optimizer-bulk-optimization';
25 - const BULK_OPTIMIZATION_CAPABILITY = 'manage_options';
37 + /** @async */
38 + public function optimize_bulk() {
39 + $queue = new Bulk_Optimization_Queue( Bulk_Optimization_Queue_Type::OPTIMIZATION );
26 40
27 - public function render_app() {
28 - ?>
29 - <!-- The hack required to wrap WP notifications -->
30 - <div class="wrap">
31 - <h1 style="display: none;" role="presentation"></h1>
32 - </div>
41 + if ( ! $queue->exists() ) {
42 + Logger::debug( 'Bulk optimization queue did not found' );
33 43
34 - <div id="image-optimizer-app"></div>
35 - <?php
36 - }
44 + return;
45 + }
37 46
38 - public function register_page() {
39 - add_media_page(
40 - '',
41 - __( 'Bulk Optimization', 'image-optimizer' ),
42 - self::BULK_OPTIMIZATION_CAPABILITY,
43 - self::BULK_OPTIMIZATION_BASE_SLUG,
44 - [ $this, 'render_app' ],
45 - 7
46 - );
47 - }
47 + if ( ! $queue->has_more_images() ) {
48 + Logger::debug( 'No more pending images, deleting queue' );
49 + $queue->delete();
48 50
49 - /** @async */
50 - 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 +
51 100 try {
52 - $bulk_token = Bulk_Optimization_Controller::get_bulk_operation_token( $operation_id );
53 -
54 101 $oi = new Optimize_Image(
55 102 $image_id,
56 103 'bulk',
57 - $bulk_token
104 + $queue->get_bulk_token()
58 105 );
59 106
60 107 $oi->optimize();
61 - } catch ( Quota_Exceeded_Error $qe ) {
62 - ( new Image_Meta( $image_id ) )
63 - ->set_status( Image_Status::OPTIMIZATION_FAILED )
64 - ->set_error_type( Image_Optimization_Error_Type::QUOTA_EXCEEDED )
108 +
109 + $queue
110 + ->mark_image_completed( $image_id )
111 + ->increment_optimized_counter()
65 112 ->save();
113 +
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 + }
66 146 } catch ( Throwable $t ) {
67 - Logger::log( Logger::LEVEL_ERROR, 'Optimization error. Reason: ' . $t->getMessage() );
68 -
69 - ( new Image_Meta( $image_id ) )
70 - ->set_status( Image_Status::OPTIMIZATION_FAILED )
71 - ->set_error_type( Image_Optimization_Error_Type::GENERIC )
72 - ->save();
147 + Logger::error( 'Failed to create next async operation or delete queue: ' . $t->getMessage() );
73 148 }
74 149 }
75 150
76 151 /** @async */
77 - public function reoptimize_bulk( int $image_id, string $operation_id ) {
78 - try {
79 - $image = new Image( $image_id );
152 + public function reoptimize_bulk() {
153 + $queue = new Bulk_Optimization_Queue( Bulk_Optimization_Queue_Type::REOPTIMIZATION );
80 154
81 - if ( $image->can_be_restored() ) {
82 - 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;
83 191 }
192 + }
84 193
85 - $bulk_token = Bulk_Optimization_Controller::get_bulk_operation_token( $operation_id );
194 + $image_id = $queue->get_next_image();
86 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 {
87 209 $oi = new Optimize_Image(
88 210 $image_id,
89 - 'bulk',
90 - $bulk_token
211 + 'bulk-reoptimize',
212 + $queue->get_bulk_token(),
213 + true
91 214 );
92 215
93 216 $oi->optimize();
94 - } catch ( Quota_Exceeded_Error $qe ) {
95 - ( new Image_Meta( $image_id ) )
96 - ->set_status( Image_Status::REOPTIMIZING_FAILED )
97 - ->set_error_type( Image_Optimization_Error_Type::QUOTA_EXCEEDED )
217 +
218 + $queue
219 + ->mark_image_completed( $image_id )
220 + ->increment_optimized_counter()
98 221 ->save();
222 +
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 + }
99 261 } catch ( Throwable $t ) {
100 - Logger::log( Logger::LEVEL_ERROR, 'Reoptimization error. Reason: ' . $t->getMessage() );
262 + Logger::error( 'Failed to create next async operation or delete queue: ' . $t->getMessage() );
263 + }
264 + }
101 265
102 - ( new Image_Meta( $image_id ) )
103 - ->set_status( Image_Status::REOPTIMIZING_FAILED )
104 - ->set_error_type( Image_Optimization_Error_Type::GENERIC )
105 - ->save();
106 - }
266 + /**
267 + * Renders the bulk optimization notice
268 + *
269 + * @return void
270 + */
271 + public function render_bulk_optimization_notice() {
272 + $queue = new Bulk_Optimization_Queue( Bulk_Optimization_Queue_Type::OPTIMIZATION );
273 + $is_in_progress = $queue->exists();
274 + ?>
275 + <div class="notice notice-info notice image-optimizer__notice image-optimizer__notice--info image-optimizer__notice--bulk-tip"
276 + style="display: <?php echo $is_in_progress ? 'block' : 'none'; ?>">
277 + <p>
278 + <b>
279 + <?php esc_html_e(
280 + 'Heads up!',
281 + 'image-optimization'
282 + ); ?>
283 + </b>
284 +
285 + <span>
286 + <?php esc_html_e(
287 + 'Bulk optimizing may take a lot of processing and server time, depending on the number of images. Your site will still work smoothly until the processing is all done, without any downtime.',
288 + 'image-optimization'
289 + ); ?>
290 + </span>
291 + </p>
292 + </div>
293 + <?php
107 294 }
108 295
109 296 public function __construct() {
110 - 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' ] );
111 299
112 - add_action( Async_Operation_Hook::OPTIMIZE_BULK, [ $this, 'optimize_bulk' ], 10, 2 );
113 - add_action( Async_Operation_Hook::REOPTIMIZE_BULK, [ $this, 'reoptimize_bulk' ], 10, 2 );
300 + add_action( 'current_screen', function () {
301 + if ( Utils::is_bulk_optimization_page() ) {
302 + add_filter( 'admin_footer_text', [ $this, 'render_bulk_optimization_notice' ] );
303 + }
304 + } );
114 305 }
115 306 }