PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / trunk
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF vtrunk
2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.9 2.2.8 trunk 1.10 1.3.3 1.3.4 1.3.5 1.3.5.1 1.3.5.2 1.3.6 1.3.6.1 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.5 All 103 releases
imagify / inc / 3rd-party / regenerate-thumbnails / classes / Main.php

Main.php in Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF trunk, at inc/3rd-party/regenerate-thumbnails/classes/Main.php

402 lines 11.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Imagify\ThirdParty\RegenerateThumbnails;
3
4 use Imagify_Requirements;
5
6 defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
7
8 /**
9 * Class that handles compatibility with Regenerate Thumbnails plugin.
10 *
11 * @since 1.7.1
12 * @author Grégory Viguier
13 */
14 class Main extends \Imagify_Regenerate_Thumbnails_Deprecated {
15 use \Imagify\Traits\InstanceGetterTrait;
16
17 /**
18 * Class version.
19 *
20 * @var string
21 * @since 1.7.1
22 * @author Grégory Viguier
23 */
24 const VERSION = '1.2';
25
26 /**
27 * Action used for the ajax callback.
28 *
29 * @var string
30 * @since 1.9
31 * @author Grégory Viguier
32 */
33 const HOOK_SUFFIX = 'regenerate_thumbnails';
34
35 /**
36 * List of optimization processes.
37 *
38 * @var array An array of ProcessInterface objects. The array keys are the media IDs.
39 * @since 1.7.1
40 * @access protected
41 * @author Grégory Viguier
42 */
43 protected $processes = [];
44
45 /**
46 * Tell if we’re playing in WP 5.3’s garden.
47 *
48 * @var bool
49 * @since 1.9.8
50 * @access protected
51 * @author Grégory Viguier
52 */
53 protected $is_wp53;
54
55
56 /** ----------------------------------------------------------------------------------------- */
57 /** INIT ==================================================================================== */
58 /** ----------------------------------------------------------------------------------------- */
59
60 /**
61 * Launch the hooks.
62 *
63 * @since 1.7.1
64 * @access public
65 * @author Grégory Viguier
66 */
67 public function init() {
68 if ( ! class_exists( '\RegenerateThumbnails_Regenerator' ) ) {
69 return;
70 }
71
72 add_filter( 'rest_dispatch_request', [ $this, 'maybe_init_attachment' ], 4, 4 );
73 add_action( 'imagify_after_' . static::HOOK_SUFFIX, [ $this, 'after_regenerate_thumbnails' ], 8, 2 );
74 }
75
76
77 /** ----------------------------------------------------------------------------------------- */
78 /** HOOKS =================================================================================== */
79 /** ----------------------------------------------------------------------------------------- */
80
81 /**
82 * Filter the REST dispatch request result, to hook before Regenerate Thumbnails starts its magic.
83 *
84 * @since 1.7.1
85 * @access public
86 * @author Grégory Viguier
87 *
88 * @param bool $dispatch_result Dispatch result, will be used if not empty.
89 * @param WP_REST_Request $request Request used to generate the response.
90 * @param string $route Route matched for the request.
91 * @param array $handler Route handler used for the request.
92 * @return bool
93 */
94 public function maybe_init_attachment( $dispatch_result, $request, $route = null, $handler = null ) {
95 if ( strpos( $route, static::get_route_prefix() ) === false ) {
96 return $dispatch_result;
97 }
98
99 $media_id = $request->get_param( 'id' );
100
101 if ( ! $this->set_process( $media_id ) ) {
102 return $dispatch_result;
103 }
104
105 $media_id = $this->get_process( $media_id )->get_media()->get_id();
106
107 // The attachment can be regenerated: keep the optimized full-sized file safe, and replace it by the backup file.
108 $this->backup_optimized_file( $media_id );
109 // Prevent automatic optimization.
110 \Imagify_Auto_Optimization::prevent_optimization( $media_id );
111 // Launch the needed hook.
112 add_filter( 'wp_generate_attachment_metadata', [ $this, 'launch_async_optimization' ], IMAGIFY_INT_MAX - 30, 2 );
113
114 return $dispatch_result;
115 }
116
117 /**
118 * Auto-optimize after an attachment is regenerated.
119 *
120 * @since 1.7.1
121 * @access public
122 * @author Grégory Viguier
123 *
124 * @param array $metadata An array of attachment meta data, containing the sizes that have been regenerated.
125 * @param int $media_id Current media ID.
126 * @return array
127 */
128 public function launch_async_optimization( $metadata, $media_id ) {
129 $process = $this->get_process( $media_id );
130
131 if ( ! $process ) {
132 return $metadata;
133 }
134
135 $sizes = isset( $metadata['sizes'] ) && is_array( $metadata['sizes'] ) ? $metadata['sizes'] : [];
136 $media = $process->get_media();
137 $fullsize_path = $media->get_raw_fullsize_path();
138
139 if ( $fullsize_path ) {
140 $original_path = $media->get_original_path();
141
142 if ( $original_path && $this->is_wp_53() && $original_path !== $fullsize_path ) {
143 /**
144 * The original file and the full-sized file are not the same:
145 * That means wp_generate_attachment_metadata() recreated the full-sized file, based on the original one.
146 * So it must be optimized again now.
147 */
148 $sizes['full'] = [];
149 }
150 }
151
152 if ( ! $sizes ) {
153 // Put the optimized full-sized file back.
154 $this->put_optimized_file_back( $media_id );
155 return $metadata;
156 }
157
158 /**
159 * Optimize the sizes that have been regenerated.
160 */
161 // If the media has next-gen versions, recreate them for the sizes that have been regenerated.
162 $data = $process->get_data();
163 $optimization_data = $data->get_optimization_data();
164
165 if ( ! empty( $optimization_data['sizes'] ) ) {
166 foreach ( $optimization_data['sizes'] as $size_name => $size_data ) {
167 $non_nextgen_size_name = $process->is_size_next_gen( $size_name );
168
169 if ( ! $non_nextgen_size_name || ! isset( $sizes[ $non_nextgen_size_name ] ) ) {
170 continue;
171 }
172
173 // Add the next-gen size.
174 $sizes[ $size_name ] = [];
175 }
176 }
177
178 $sizes = array_keys( $sizes );
179 $optimization_level = $data->get_optimization_level();
180 $optimization_args = [ 'hook_suffix' => static::HOOK_SUFFIX ];
181
182 // Delete related optimization data or nothing will be optimized.
183 $data->delete_sizes_optimization_data( $sizes );
184 $process->optimize_sizes( $sizes, $optimization_level, $optimization_args );
185
186 $this->unset_process( $media_id );
187
188 return $metadata;
189 }
190
191 /**
192 * Fires after regenerating the thumbnails.
193 * This puts the full-sized optimized file back.
194 *
195 * @since 1.9
196 * @access public
197 * @author Grégory Viguier
198 *
199 * @param ProcessInterface $process The optimization process.
200 * @param array $item The item being processed. See $this->task().
201 */
202 public function after_regenerate_thumbnails( $process, $item ) {
203 $media_id = $process->get_media()->get_id();
204
205 $this->processes[ $media_id ] = $process;
206
207 $this->put_optimized_file_back( $media_id );
208 }
209
210
211 /** ----------------------------------------------------------------------------------------- */
212 /** INTERNAL TOOLS ========================================================================== */
213 /** ----------------------------------------------------------------------------------------- */
214
215 /**
216 * Set an optimization process.
217 *
218 * @since 1.9
219 * @author Grégory Viguier
220 * @access protected
221 *
222 * @param int $media_id The media ID.
223 * @return bool
224 */
225 protected function set_process( $media_id ) {
226 if ( ! $media_id || ! Imagify_Requirements::is_api_key_valid() ) {
227 return false;
228 }
229
230 $process = imagify_get_optimization_process( $media_id, 'wp' );
231
232 if ( ! $process->is_valid() || ! $process->get_media()->is_image() || ! $process->get_data()->is_optimized() ) {
233 // Invalid, not animage, or no optimization have been attempted yet.
234 return false;
235 }
236
237 $this->processes[ $media_id ] = $process;
238
239 return true;
240 }
241
242 /**
243 * Unset an optimization process.
244 *
245 * @since 1.9
246 * @access protected
247 * @author Grégory Viguier
248 *
249 * @param int $media_id The media ID.
250 */
251 protected function unset_process( $media_id ) {
252 unset( $this->processes[ $media_id ] );
253 }
254
255 /**
256 * Unset an optimization process.
257 *
258 * @since 1.9
259 * @access protected
260 * @author Grégory Viguier
261 *
262 * @param int $media_id The media ID.
263 * @return ProcessInterface|bool An optimization process object. False on failure.
264 */
265 protected function get_process( $media_id ) {
266 return ! empty( $this->processes[ $media_id ] ) ? $this->processes[ $media_id ] : false;
267 }
268
269 /**
270 * Backup the optimized full-sized file and replace it by the original backup file.
271 *
272 * @since 1.7.1
273 * @access protected
274 * @author Grégory Viguier
275 *
276 * @param int $media_id Media ID.
277 */
278 protected function backup_optimized_file( $media_id ) {
279 $media = $this->get_process( $media_id )->get_media();
280 $fullsize_path = $media->get_raw_fullsize_path();
281
282 if ( ! $fullsize_path ) {
283 // Uh?
284 return;
285 }
286
287 $original_path = $media->get_original_path();
288
289 if ( $original_path && $this->is_wp_53() && $original_path !== $fullsize_path ) {
290 /**
291 * The original file and the full-sized file are not the same:
292 * That means wp_generate_attachment_metadata() will recreate the full-sized file, based on the original one.
293 * Then, the thumbnails will be created from a newly created (unoptimized) file.
294 */
295 return;
296 }
297
298 $backup_path = $media->get_backup_path();
299
300 if ( ! $backup_path ) {
301 // No backup file, too bad.
302 return;
303 }
304
305 /**
306 * Replace the optimized full-sized file by the backup, so any optimization will not use an optimized file, but the original one.
307 * The optimized full-sized file is kept and renamed, and will be put back in place at the end of the optimization process.
308 */
309 $filesystem = \Imagify_Filesystem::get_instance();
310
311 if ( $filesystem->exists( $fullsize_path ) ) {
312 $tmp_file_path = static::get_temporary_file_path( $fullsize_path );
313 $moved = $filesystem->move( $fullsize_path, $tmp_file_path, true );
314 }
315
316 $filesystem->copy( $backup_path, $fullsize_path );
317 }
318
319 /**
320 * Put the optimized full-sized file back.
321 *
322 * @since 1.7.1
323 * @since 1.9 Replaced $attachment parameter by $media_id.
324 * @access protected
325 * @author Grégory Viguier
326 *
327 * @param int $media_id Media ID.
328 */
329 protected function put_optimized_file_back( $media_id ) {
330 $file_path = $this->get_process( $media_id )->get_media()->get_raw_fullsize_path();
331 $tmp_file_path = static::get_temporary_file_path( $file_path );
332 $filesystem = \Imagify_Filesystem::get_instance();
333
334 if ( $filesystem->exists( $tmp_file_path ) ) {
335 $moved = $filesystem->move( $tmp_file_path, $file_path, true );
336 }
337 }
338
339 /**
340 * Tell if we’re playing in WP 5.3’s garden.
341 *
342 * @since 1.9.8
343 * @access protected
344 * @author Grégory Viguier
345 *
346 * @return bool
347 */
348 protected function is_wp_53() {
349 if ( isset( $this->is_wp53 ) ) {
350 return $this->is_wp53;
351 }
352
353 $this->is_wp53 = function_exists( 'wp_get_original_image_path' );
354
355 return $this->is_wp53;
356 }
357
358
359 /** ----------------------------------------------------------------------------------------- */
360 /** PUBLIC TOOLS ============================================================================ */
361 /** ----------------------------------------------------------------------------------------- */
362
363 /**
364 * Get the beginning of the route used to regenerate thumbnails.
365 *
366 * @since 1.7.1
367 * @access public
368 * @author Grégory Viguier
369 *
370 * @return string
371 */
372 public static function get_route_prefix() {
373 static $route;
374
375 if ( ! isset( $route ) ) {
376 $regen = \RegenerateThumbnails();
377
378 if ( ( empty( $regen->rest_api ) || ! is_object( $regen->rest_api ) ) && method_exists( $regen, 'rest_api_init' ) ) {
379 $regen->rest_api_init();
380 }
381
382 $route = '/' . trim( $regen->rest_api->namespace, '/' ) . '/regenerate/';
383 }
384
385 return $route;
386 }
387
388 /**
389 * Get the path to the temporary file.
390 *
391 * @since 1.7.1
392 * @access public
393 * @author Grégory Viguier
394 *
395 * @param string $file_path The optimized full-sized file path.
396 * @return string
397 */
398 public static function get_temporary_file_path( $file_path ) {
399 return $file_path . '_backup';
400 }
401 }
402