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 +217 -101 1.6.01.7.7 View file →
@@ -1,145 +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(),
113 213 true
114 214 );
115 215
116 216 $oi->optimize();
117 - } catch ( Quota_Exceeded_Error $qe ) {
118 - ( new Image_Meta( $image_id ) )
119 - ->set_status( Image_Status::REOPTIMIZING_FAILED )
120 - ->set_error_type( Image_Optimization_Error_Type::QUOTA_EXCEEDED )
217 +
218 + $queue
219 + ->mark_image_completed( $image_id )
220 + ->increment_optimized_counter()
121 221 ->save();
122 - } catch ( Image_File_Already_Exists_Error $fe ) {
123 - ( new Image_Meta( $image_id ) )
124 - ->set_status( Image_Status::REOPTIMIZING_FAILED )
125 - ->set_error_type( Image_Optimization_Error_Type::FILE_ALREADY_EXISTS )
126 - ->save();
127 - } catch ( Bulk_Token_Expired_Error | Bulk_Optimization_Token_Not_Found_Error $bte ) {
128 - ( new Image_Meta( $image_id ) )
129 - ->set_status( Image_Status::NOT_OPTIMIZED )
130 - ->save();
131 222
132 - 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 + }
133 261 } catch ( Throwable $t ) {
134 - Logger::log( Logger::LEVEL_ERROR, 'Reoptimization error. Reason: ' . $t->getMessage() );
135 -
136 - ( new Image_Meta( $image_id ) )
137 - ->set_status( Image_Status::REOPTIMIZING_FAILED )
138 - ->set_error_type( Image_Optimization_Error_Type::GENERIC )
139 - ->save();
140 - } finally {
141 - Optimization_Stats::get_image_stats( null, true );
262 + Logger::error( 'Failed to create next async operation or delete queue: ' . $t->getMessage() );
142 263 }
143 264 }
144 265
145 266 /**
@@ -147,13 +268,10 @@
147 268 *
148 269 * @return void
149 270 */
150 271 public function render_bulk_optimization_notice() {
151 - try {
152 - $is_in_progress = Bulk_Optimization_Controller::is_optimization_in_progress();
153 - } catch ( Async_Operation_Exception $aoe ) {
154 - $is_in_progress = false;
155 - }
272 + $queue = new Bulk_Optimization_Queue( Bulk_Optimization_Queue_Type::OPTIMIZATION );
273 + $is_in_progress = $queue->exists();
156 274 ?>
157 275 <div class="notice notice-info notice image-optimizer__notice image-optimizer__notice--info image-optimizer__notice--bulk-tip"
158 276 style="display: <?php echo $is_in_progress ? 'block' : 'none'; ?>">
159 277 <p>
@@ -175,16 +293,14 @@
175 293 <?php
176 294 }
177 295
178 296 public function __construct() {
179 - 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' ] );
180 299
181 - add_action( Async_Operation_Hook::OPTIMIZE_BULK, [ $this, 'optimize_bulk' ], 10, 2 );
182 - add_action( Async_Operation_Hook::REOPTIMIZE_BULK, [ $this, 'reoptimize_bulk' ], 10, 2 );
183 -
184 - add_action('current_screen', function () {
300 + add_action( 'current_screen', function () {
185 301 if ( Utils::is_bulk_optimization_page() ) {
186 302 add_filter( 'admin_footer_text', [ $this, 'render_bulk_optimization_notice' ] );
187 303 }
188 - });
304 + } );
189 305 }
190 306 }