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 / classes / Media / WP.php

WP.php in Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF trunk, at classes/Media/WP.php

471 lines 12.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Imagify\Media;
3
4 use WP_Error;
5
6 /**
7 * Media class for the medias in the WP library.
8 *
9 * @since 1.9
10 * @author Grégory Viguier
11 */
12 class WP extends AbstractMedia {
13 use \Imagify\Deprecated\Traits\Media\WPDeprecatedTrait;
14
15 /**
16 * Tell if we’re playing in WP 5.3’s garden.
17 *
18 * @var bool
19 * @since 1.9.8
20 * @access protected
21 * @author Grégory Viguier
22 */
23 protected $is_wp53 = false;
24
25 /**
26 * The constructor.
27 *
28 * @since 1.9
29 * @access public
30 * @author Grégory Viguier
31 *
32 * @param int|\WP_Post $id The attachment ID, or \WP_Post object.
33 */
34 public function __construct( $id ) {
35 if ( ! static::constructor_accepts( $id ) ) {
36 parent::__construct( 0 );
37 return;
38 }
39
40 if ( is_numeric( $id ) ) {
41 $id = get_post( (int) $id );
42 }
43
44 if ( ! $id || 'attachment' !== $id->post_type ) {
45 parent::__construct( 0 );
46 return;
47 }
48
49 parent::__construct( $id->ID );
50 }
51
52 /**
53 * Tell if the given entry can be accepted in the constructor.
54 *
55 * @since 1.9
56 * @access public
57 * @author Grégory Viguier
58 *
59 * @param mixed $id Whatever.
60 * @return bool
61 */
62 public static function constructor_accepts( $id ) {
63 return $id && ( is_numeric( $id ) || $id instanceof \WP_Post );
64 }
65
66
67 /** ----------------------------------------------------------------------------------------- */
68 /** ORIGINAL FILE =========================================================================== */
69 /** ----------------------------------------------------------------------------------------- */
70
71 /**
72 * Get the original file path, even if the file doesn't exist.
73 *
74 * @since 1.9
75 * @access public
76 * @author Grégory Viguier
77 *
78 * @return string|bool The file path. False on failure.
79 */
80 public function get_raw_original_path() {
81 if ( ! $this->is_valid() ) {
82 return false;
83 }
84
85 if ( $this->get_cdn() ) {
86 return $this->get_cdn()->get_file_path( 'original' );
87 }
88
89 if ( $this->is_wp_53() ) {
90 // `wp_get_original_image_path()` may return false.
91 $path = wp_get_original_image_path( $this->id );
92 } else {
93 $path = false;
94 }
95
96 if ( ! $path ) {
97 $path = get_attached_file( $this->id );
98 }
99
100 return $path ? $path : false;
101 }
102
103
104 /** ----------------------------------------------------------------------------------------- */
105 /** FULL SIZE FILE ========================================================================== */
106 /** ----------------------------------------------------------------------------------------- */
107
108 /**
109 * Get the URL of the media’s full size file.
110 *
111 * @since 1.9.8
112 * @access public
113 * @author Grégory Viguier
114 *
115 * @return string|bool The file URL. False on failure.
116 */
117 public function get_fullsize_url() {
118 if ( ! $this->is_valid() ) {
119 return false;
120 }
121
122 if ( $this->get_cdn() ) {
123 return $this->get_cdn()->get_file_url();
124 }
125
126 $url = wp_get_attachment_url( $this->id );
127
128 return $url ? $url : false;
129 }
130
131 /**
132 * Get the path to the media’s full size file, even if the file doesn't exist.
133 *
134 * @since 1.9.8
135 * @access public
136 * @author Grégory Viguier
137 *
138 * @return string|bool The file path. False on failure.
139 */
140 public function get_raw_fullsize_path() {
141 if ( ! $this->is_valid() ) {
142 return false;
143 }
144
145 if ( $this->get_cdn() ) {
146 return $this->get_cdn()->get_file_path();
147 }
148
149 $path = get_attached_file( $this->id );
150
151 return $path ? $path : false;
152 }
153
154
155 /** ----------------------------------------------------------------------------------------- */
156 /** BACKUP FILE ============================================================================= */
157 /** ----------------------------------------------------------------------------------------- */
158
159 /**
160 * Get the backup URL, even if the file doesn't exist.
161 *
162 * @since 1.9
163 * @access public
164 * @author Grégory Viguier
165 *
166 * @return string|bool The file URL. False on failure.
167 */
168 public function get_backup_url() {
169 if ( ! $this->is_valid() ) {
170 return false;
171 }
172
173 return get_imagify_attachment_url( $this->get_raw_backup_path() );
174 }
175
176 /**
177 * Get the backup file path, even if the file doesn't exist.
178 *
179 * @since 1.9
180 * @access public
181 * @author Grégory Viguier
182 *
183 * @return string|bool The file path. False on failure.
184 */
185 public function get_raw_backup_path() {
186 if ( ! $this->is_valid() ) {
187 return false;
188 }
189
190 return get_imagify_attachment_backup_path( $this->get_raw_original_path() );
191 }
192
193
194 /** ----------------------------------------------------------------------------------------- */
195 /** THUMBNAILS ============================================================================== */
196 /** ----------------------------------------------------------------------------------------- */
197
198 /**
199 * Create the media thumbnails.
200 * With WP 5.3+, this will also generate a new full size file if the original file is wider or taller than a defined threshold.
201 *
202 * @since 1.9
203 * @access public
204 * @author Grégory Viguier
205 *
206 * @return bool|WP_Error True on success. A \WP_Error instance on failure.
207 */
208 public function generate_thumbnails() {
209 if ( ! $this->is_valid() ) {
210 return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
211 }
212
213 if ( ! function_exists( 'wp_generate_attachment_metadata' ) ) {
214 require_once ABSPATH . 'wp-admin/includes/image.php';
215 }
216
217 // Store the path to the current full size file before generating the thumbnails.
218 $old_full_size_path = $this->get_raw_fullsize_path();
219 $previous_metadata = wp_get_attachment_metadata( $this->get_id() );
220 $metadata = wp_generate_attachment_metadata( $this->get_id(), $this->get_raw_original_path() );
221
222 if ( ! is_array( $metadata ) ) {
223 $metadata = [];
224 }
225
226 $metadata = $this->keep_companion_files( $metadata, $previous_metadata );
227
228 if ( empty( $metadata['file'] ) ) {
229 update_post_meta( $this->get_id(), '_wp_attachment_metadata', $metadata );
230
231 return true;
232 }
233
234 /**
235 * Don't change the full size file name.
236 * WP 5.3+ will rename the full size file if the resizing threshold has changed (not the same as the one used to generate it previously).
237 * This will force WP to keep the previous file name.
238 */
239 $old_full_size_file_name = $this->filesystem->file_name( $old_full_size_path );
240 $new_full_size_file_name = $this->filesystem->file_name( $metadata['file'] );
241
242 if ( $new_full_size_file_name !== $old_full_size_file_name ) {
243 $new_full_size_path = $this->filesystem->dir_path( $old_full_size_path ) . $new_full_size_file_name;
244
245 $moved = $this->filesystem->move( $new_full_size_path, $old_full_size_path, true );
246
247 if ( $moved ) {
248 $metadata['file'] = $this->filesystem->dir_path( $metadata['file'] ) . $old_full_size_file_name;
249 update_post_meta( $this->get_id(), '_wp_attached_file', $metadata['file'] );
250 }
251 }
252
253 update_post_meta( $this->get_id(), '_wp_attachment_metadata', $metadata );
254
255 return true;
256 }
257
258 /**
259 * Carry the companion files WordPress stores next to an attachment over to fresh metadata.
260 *
261 * WordPress 7.1 can let the browser convert an upload and keep the file it started from:
262 * the HEIC a photo was uploaded as, next to the JPEG the site serves, or the original GIF
263 * next to the video it was turned into. Those file names live in the attachment metadata
264 * and nowhere else, and `wp_generate_attachment_metadata()` does not produce them, so
265 * replacing the metadata wholesale would lose them.
266 *
267 * Losing them means `wp_delete_attachment_files()` can no longer find those files, and they
268 * stay on disk for good, even once the attachment is deleted.
269 *
270 * @since 2.3.3
271 *
272 * @param array $metadata Freshly generated attachment metadata.
273 * @param mixed $previous_metadata Metadata as it was stored before being regenerated.
274 * @return array
275 */
276 protected function keep_companion_files( $metadata, $previous_metadata ) {
277 if ( ! is_array( $previous_metadata ) ) {
278 return $metadata;
279 }
280
281 $companion_keys = [
282 'source_image',
283 'animated_video',
284 'animated_video_poster',
285 ];
286
287 foreach ( $companion_keys as $key ) {
288 if ( ! empty( $previous_metadata[ $key ] ) && empty( $metadata[ $key ] ) ) {
289 $metadata[ $key ] = $previous_metadata[ $key ];
290 }
291 }
292
293 return $metadata;
294 }
295
296
297 /** ----------------------------------------------------------------------------------------- */
298 /** MEDIA DATA ============================================================================== */
299 /** ----------------------------------------------------------------------------------------- */
300
301 /**
302 * Tell if the current media has the required data (the data containing the file paths and thumbnails).
303 *
304 * @since 1.9
305 * @access public
306 * @author Grégory Viguier
307 *
308 * @return bool
309 */
310 public function has_required_media_data() {
311 if ( ! $this->is_valid() ) {
312 return false;
313 }
314
315 $file = get_post_meta( $this->id, '_wp_attached_file', true );
316
317 if ( ! $file || preg_match( '@://@', $file ) || preg_match( '@^.:\\\@', $file ) ) {
318 return false;
319 }
320
321 return (bool) wp_get_attachment_metadata( $this->id, true );
322 }
323
324 /**
325 * Get the list of the files of this media, including the full size file.
326 *
327 * @since 1.9
328 * @access public
329 * @author Grégory Viguier
330 *
331 * @return array {
332 * An array with the size names as keys ('full' is used for the full size file), and arrays of data as values:
333 *
334 * @type string $size The size name.
335 * @type string $path Absolute path to the file.
336 * @type int $width The file width.
337 * @type int $height The file height.
338 * @type string $mime-type The file mime type.
339 * @type bool $disabled True if the size is disabled in the plugin’s settings.
340 * }
341 */
342 public function get_media_files() {
343 if ( ! $this->is_valid() ) {
344 return [];
345 }
346
347 $fullsize_path = $this->get_raw_fullsize_path();
348
349 if ( ! $fullsize_path ) {
350 return [];
351 }
352
353 $dimensions = $this->get_dimensions();
354 $all_sizes = [
355 'full' => [
356 'size' => 'full',
357 'path' => $fullsize_path,
358 'width' => $dimensions['width'],
359 'height' => $dimensions['height'],
360 'mime-type' => $this->get_mime_type(),
361 'disabled' => false,
362 ],
363 ];
364
365 if ( $this->is_image() ) {
366 $sizes = wp_get_attachment_metadata( $this->id, true );
367 $sizes = ! empty( $sizes['sizes'] ) && is_array( $sizes['sizes'] ) ? $sizes['sizes'] : [];
368 $sizes = array_intersect_key( $sizes, $this->get_context_instance()->get_thumbnail_sizes() );
369 } else {
370 $sizes = [];
371 }
372
373 if ( ! $sizes ) {
374 return $all_sizes;
375 }
376
377 $dir_path = $this->filesystem->dir_path( $fullsize_path );
378 $disallowed_sizes = get_imagify_option( 'disallowed-sizes' );
379 $is_active_for_network = imagify_is_active_for_network();
380
381 foreach ( $sizes as $size => $size_data ) {
382 $all_sizes[ $size ] = [
383 'size' => $size,
384 'path' => $dir_path . $size_data['file'],
385 'width' => $size_data['width'],
386 'height' => $size_data['height'],
387 'mime-type' => $size_data['mime-type'],
388 'disabled' => ! $is_active_for_network && isset( $disallowed_sizes[ $size ] ),
389 ];
390 }
391
392 return $this->filter_media_files( $all_sizes );
393 }
394
395 /**
396 * If the media is an image, get its width and height.
397 *
398 * @since 1.9
399 * @access public
400 * @author Grégory Viguier
401 *
402 * @return array
403 */
404 public function get_dimensions() {
405 if ( ! $this->is_image() ) {
406 return [
407 'width' => 0,
408 'height' => 0,
409 ];
410 }
411
412 $values = wp_get_attachment_image_src( $this->id, 'full' );
413
414 return [
415 'width' => $values[1],
416 'height' => $values[2],
417 ];
418 }
419
420 /**
421 * Update the media data dimensions.
422 *
423 * @since 1.9
424 * @access protected
425 * @author Grégory Viguier
426 *
427 * @param array $dimensions {
428 * An array containing width and height.
429 *
430 * @type int $width The image width.
431 * @type int $height The image height.
432 * }
433 */
434 protected function update_media_data_dimensions( $dimensions ) {
435 $metadata = wp_get_attachment_metadata( $this->id );
436
437 if ( ! is_array( $metadata ) ) {
438 $row = [];
439 }
440
441 if ( isset( $metadata['width'], $metadata['height'] ) && $metadata['width'] === $dimensions['width'] && $metadata['height'] === $dimensions['height'] ) {
442 return;
443 }
444
445 $metadata['width'] = $dimensions['width'];
446 $metadata['height'] = $dimensions['height'];
447
448 update_post_meta( $this->get_id(), '_wp_attachment_metadata', $metadata );
449 }
450
451
452 /** ----------------------------------------------------------------------------------------- */
453 /** INTERNAL TOOLS ========================================================================== */
454 /** ----------------------------------------------------------------------------------------- */
455
456 /**
457 * Tell if we’re playing in WP 5.3’s garden.
458 *
459 * @since 1.9.8
460 * @access protected
461 * @author Grégory Viguier
462 *
463 * @return bool
464 */
465 protected function is_wp_53() {
466 $this->is_wp53 = function_exists( 'wp_get_original_image_path' );
467
468 return $this->is_wp53;
469 }
470 }
471