PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / 2.2.7
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF v2.2.7
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 / Optimization / File.php

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

931 lines 23.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Imagify\Optimization;
3
4 use Imagify_Requirements;
5 use WP_Error;
6
7 /**
8 * A generic optimization class focused on the file itself.
9 *
10 * @since 1.9
11 * @author Grégory Viguier
12 */
13 class File {
14
15 /**
16 * Absolute path to the file.
17 *
18 * @var string
19 * @since 1.9
20 * @author Grégory Viguier
21 */
22 protected $path;
23
24 /**
25 * Tell if the file is an image.
26 *
27 * @var bool
28 * @since 1.9
29 * @see $this->is_image()
30 * @author Grégory Viguier
31 */
32 protected $is_image;
33
34 /**
35 * Store the file mime type + file extension (if the file is supported).
36 *
37 * @var array
38 * @since 1.9
39 * @see $this->get_file_type()
40 * @author Grégory Viguier
41 */
42 protected $file_type;
43
44 /**
45 * Filesystem object.
46 *
47 * @var \Imagify_Filesystem
48 * @since 1.9
49 * @author Grégory Viguier
50 */
51 protected $filesystem;
52
53 /**
54 * The editor instance used to resize the file.
55 *
56 * @var \WP_Image_Editor_Imagick|\WP_Image_Editor_GD|WP_Error.
57 * @since 1.9
58 * @author Grégory Viguier
59 */
60 protected $editor;
61
62 /**
63 * Used to cache the plugin’s options.
64 *
65 * @var array
66 * @since 1.9
67 * @author Grégory Viguier
68 */
69 protected $options = [];
70
71 /**
72 * The constructor.
73 *
74 * @since 1.9
75 * @author Grégory Viguier
76 *
77 * @param string $file_path Absolute path to the file.
78 */
79 public function __construct( $file_path ) {
80 $this->path = $file_path;
81 $this->filesystem = \Imagify_Filesystem::get_instance();
82 }
83
84 /**
85 * Tell if the file is valid.
86 *
87 * @since 1.9
88 * @author Grégory Viguier
89 *
90 * @return bool
91 */
92 public function is_valid() {
93 return (bool) $this->path;
94 }
95
96 /**
97 * Tell if the file can be processed.
98 *
99 * @since 1.9
100 * @author Grégory Viguier
101 *
102 * @return bool|WP_Error
103 */
104 public function can_be_processed() {
105 if ( ! $this->path ) {
106 return new \WP_Error( 'empty_path', __( 'File path is empty.', 'imagify' ) );
107 }
108
109 if ( ! empty( $this->filesystem->errors->errors ) ) {
110 return new \WP_Error( 'filesystem_error', __( 'Filesystem error.', 'imagify' ), $this->filesystem->errors );
111 }
112
113 if ( ! $this->filesystem->exists( $this->path ) ) {
114 return new \WP_Error(
115 'not_exists',
116 sprintf(
117 /* translators: %s is a file path. */
118 __( 'The file %s does not seem to exist.', 'imagify' ),
119 '<code>' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '</code>'
120 )
121 );
122 }
123
124 if ( ! $this->filesystem->is_file( $this->path ) ) {
125 return new \WP_Error(
126 'not_a_file',
127 sprintf(
128 /* translators: %s is a file path. */
129 __( 'This does not seem to be a file: %s.', 'imagify' ),
130 '<code>' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '</code>'
131 )
132 );
133 }
134
135 if ( ! $this->filesystem->is_writable( $this->path ) ) {
136 return new \WP_Error(
137 'not_writable',
138 sprintf(
139 /* translators: %s is a file path. */
140 __( 'The file %s does not seem to be writable.', 'imagify' ),
141 '<code>' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '</code>'
142 )
143 );
144 }
145
146 $parent_folder = $this->filesystem->dir_path( $this->path );
147
148 if ( ! $this->filesystem->is_writable( $parent_folder ) ) {
149 return new \WP_Error(
150 'folder_not_writable',
151 sprintf(
152 /* translators: %s is a file path. */
153 __( 'The folder %s does not seem to be writable.', 'imagify' ),
154 '<code>' . esc_html( $this->filesystem->make_path_relative( $parent_folder ) ) . '</code>'
155 )
156 );
157 }
158
159 return true;
160 }
161
162
163 /** ----------------------------------------------------------------------------------------- */
164 /** EDITION ================================================================================= */
165 /** ----------------------------------------------------------------------------------------- */
166
167 /**
168 * Resize (and rotate) an image if it is bigger than the maximum width provided.
169 *
170 * @since 1.9
171 * @author Grégory Viguier
172 * @author Remy Perona
173 *
174 * @param array $dimensions {
175 * Array of image dimensions.
176 *
177 * @type int $width The image width.
178 * @type int $height The image height.
179 * }
180 * @param int $max_width Maximum width to resize to.
181 * @return string|WP_Error Path the the resized image. A WP_Error object on failure.
182 */
183 public function resize( $dimensions = [], $max_width = 0 ) {
184 $can_be_processed = $this->can_be_processed();
185
186 if ( is_wp_error( $can_be_processed ) ) {
187 return $can_be_processed;
188 }
189
190 if ( ! $max_width ) {
191 return new \WP_Error(
192 'no_resizing_threshold',
193 __( 'No threshold provided for resizing.', 'imagify' )
194 );
195 }
196
197 if ( ! $this->is_image() ) {
198 return new \WP_Error(
199 'not_an_image',
200 sprintf(
201 /* translators: %s is a file path. */
202 __( 'The file %s does not seem to be an image, and cannot be resized.', 'imagify' ),
203 '<code>' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '</code>'
204 )
205 );
206 }
207
208 $editor = $this->get_editor();
209
210 if ( is_wp_error( $editor ) ) {
211 return $editor;
212 }
213
214 // Try to correct the auto-rotation if the info is available.
215 if ( $this->filesystem->can_get_exif() && 'image/jpeg' === $this->get_mime_type() ) {
216 $exif = $this->filesystem->get_image_exif( $this->path );
217 $orientation = isset( $exif['Orientation'] ) ? (int) $exif['Orientation'] : 1;
218
219 switch ( $orientation ) {
220 case 2:
221 // Flip horizontally.
222 $editor->flip( true, false );
223 break;
224 case 3:
225 // Rotate 180 degrees or flip horizontally and vertically.
226 // Flipping seems faster/uses less resources.
227 $editor->flip( true, true );
228 break;
229 case 4:
230 // Flip vertically.
231 $editor->flip( false, true );
232 break;
233 case 5:
234 // Rotate 90 degrees counter-clockwise and flip vertically.
235 $result = $editor->rotate( 90 );
236
237 if ( ! is_wp_error( $result ) ) {
238 $editor->flip( false, true );
239 }
240 break;
241 case 6:
242 // Rotate 90 degrees clockwise (270 counter-clockwise).
243 $editor->rotate( 270 );
244 break;
245 case 7:
246 // Rotate 90 degrees counter-clockwise and flip horizontally.
247 $result = $editor->rotate( 90 );
248
249 if ( ! is_wp_error( $result ) ) {
250 $editor->flip( true, false );
251 }
252 break;
253 case 8:
254 // Rotate 90 degrees counter-clockwise.
255 $editor->rotate( 90 );
256 break;
257 }
258 }
259
260 if ( ! $dimensions ) {
261 $dimensions = $this->get_dimensions();
262 }
263
264 // Prevent removal of the exif data when resizing (only works with Imagick).
265 add_filter( 'image_strip_meta', '__return_false', 789 );
266
267 // Resize.
268 $new_sizes = wp_constrain_dimensions( $dimensions['width'], $dimensions['height'], $max_width );
269 $resized = $editor->resize( $new_sizes[0], $new_sizes[1], false );
270
271 // Remove the filter when we're done to prevent any conflict.
272 remove_filter( 'image_strip_meta', '__return_false', 789 );
273
274 if ( is_wp_error( $resized ) ) {
275 return $resized;
276 }
277
278 $resized_image_path = $editor->generate_filename( 'imagifyresized' );
279 $resized_image_saved = $editor->save( $resized_image_path );
280
281 if ( is_wp_error( $resized_image_saved ) ) {
282 return $resized_image_saved;
283 }
284
285 return $resized_image_path;
286 }
287
288 /**
289 * Create a thumbnail.
290 * Warning: If the destination file already exists, it will be overwritten.
291 *
292 * @since 1.9
293 * @author Grégory Viguier
294 *
295 * @param array $destination {
296 * The thumbnail data.
297 *
298 * @type string $path Path to the destination file.
299 * @type int $width The image width.
300 * @type int $height The image height.
301 * @type bool $crop True to crop, false to resize.
302 * @type bool $adjust_filename True to adjust the file name like what `$editor->multi_resize()` returns, like WP default behavior (default). False to prevent it, and use the file name from $path instead.
303 * }
304 * @return bool|array|WP_Error {
305 * A WP_Error object on error. True if the file exists.
306 * An array of thumbnail data if the file has just been created:
307 *
308 * @type string $file File name.
309 * @type int $width The image width.
310 * @type int $height The image height.
311 * @type string $mime-type The mime type.
312 * }
313 */
314 public function create_thumbnail( $destination ) {
315 $can_be_processed = $this->can_be_processed();
316
317 if ( is_wp_error( $can_be_processed ) ) {
318 return $can_be_processed;
319 }
320
321 if ( ! $this->is_image() ) {
322 return new WP_Error(
323 'not_an_image',
324 sprintf(
325 /* translators: %s is a file path. */
326 __( 'The file %s does not seem to be an image, and cannot be resized.', 'imagify' ),
327 '<code>' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '</code>'
328 )
329 );
330 }
331
332 $editor = $this->get_editor();
333
334 if ( is_wp_error( $editor ) ) {
335 return $editor;
336 }
337
338 // Create the file.
339 $result = $editor->multi_resize( [ $destination ] );
340
341 if ( ! $result ) {
342 return new WP_Error( 'image_resize_error', __( 'The thumbnail could not be created.', 'imagify' ) );
343 }
344
345 $result = reset( $result );
346
347 $filename = $result['file'];
348 $source_thumb_path = $this->filesystem->dir_path( $this->path ) . $filename;
349
350 if ( ! isset( $destination['adjust_filename'] ) || $destination['adjust_filename'] ) {
351 // The file name can change from what we expected (1px wider, etc), let's use the resulting data to move the file to the right place.
352 $destination_thumb_path = $this->filesystem->dir_path( $destination['path'] ) . $filename;
353 } else {
354 // Respect what is set in $path.
355 $destination_thumb_path = $destination['path'];
356 $result['file'] = $this->filesystem->file_name( $destination['path'] );
357 }
358
359 if ( $source_thumb_path === $destination_thumb_path ) {
360 return $result;
361 }
362
363 $moved = $this->filesystem->move( $source_thumb_path, $destination_thumb_path, true );
364
365 if ( ! $moved ) {
366 return new WP_Error( 'move_error', __( 'The file could not be moved to its final destination.', 'imagify' ) );
367 }
368
369 return $result;
370 }
371
372 /**
373 * Backup a file.
374 *
375 * @since 1.9
376 * @since 1.9.8 Added $backup_source argument.
377 * @author Grégory Viguier
378 *
379 * @param string $backup_path The backup path.
380 * @param string $backup_source Path to the file to backup. This is useful in WP 5.3+ when we want to optimize the full size: in that case we need to backup the original file.
381 * @return bool|WP_Error True on success. False if the backup option is disabled. A WP_Error object on failure.
382 */
383 public function backup( $backup_path = null, $backup_source = null ) {
384 $can_be_processed = $this->can_be_processed();
385
386 if ( is_wp_error( $can_be_processed ) ) {
387 return $can_be_processed;
388 }
389
390 // Make sure the backups directory has no errors.
391 if ( ! $backup_path ) {
392 return new \WP_Error( 'wp_upload_error', __( 'Error while retrieving the backups directory path.', 'imagify' ) );
393 }
394
395 // Create sub-directories.
396 $created = $this->filesystem->make_dir( $this->filesystem->dir_path( $backup_path ) );
397
398 if ( ! $created ) {
399 return new \WP_Error( 'backup_dir_not_writable', __( 'The backup directory is not writable.', 'imagify' ) );
400 }
401
402 $path = $backup_source && $this->filesystem->exists( $backup_source ) ? $backup_source : $this->path;
403
404 /**
405 * Allow to overwrite the backup file if it already exists.
406 *
407 * @since 1.6.9
408 * @author Grégory Viguier
409 *
410 * @param bool $overwrite Whether to overwrite the backup file.
411 * @param string $path The file path.
412 * @param string $backup_path The backup path.
413 */
414 $overwrite = apply_filters( 'imagify_backup_overwrite_backup', false, $path, $backup_path );
415
416 // Copy the file.
417 $this->filesystem->copy( $path, $backup_path, $overwrite, FS_CHMOD_FILE );
418
419 // Make sure the backup copy exists.
420 if ( ! $this->filesystem->exists( $backup_path ) ) {
421 return new \WP_Error(
422 'backup_doesnt_exist',
423 __( 'The file could not be saved.', 'imagify' ),
424 [
425 'file_path' => $this->filesystem->make_path_relative( $path ),
426 'backup_path' => $this->filesystem->make_path_relative( $backup_path ),
427 ]
428 );
429 }
430
431 // Check if a '-scaled' version of the image exists.
432 $scaled_path = preg_replace( '/(\.)([^\.]+)$/', '-scaled.$2', $backup_source );
433 if ( $this->filesystem->exists( $scaled_path ) ) {
434 // Create a backup path for the scaled image.
435 $scaled_backup_path = preg_replace( '/(\.)([^\.]+)$/', '-scaled.$2', $backup_path );
436 // Copy the '-scaled' version to the backup.
437 $this->filesystem->copy( $scaled_path, $scaled_backup_path, $overwrite, FS_CHMOD_FILE );
438
439 if ( ! $this->filesystem->exists( $scaled_backup_path ) ) {
440 return new \WP_Error(
441 'backup_doesnt_exist',
442 __( 'The file could not be saved.', 'imagify' ),
443 [
444 'file_path' => $this->filesystem->make_path_relative( $scaled_path ),
445 'backup_path' => $this->filesystem->make_path_relative( $scaled_backup_path ),
446 ]
447 );
448 }
449 }
450
451 return true;
452 }
453
454 /**
455 * Optimize a file with Imagify.
456 *
457 * @since 1.9
458 * @author Grégory Viguier
459 *
460 * @param array $args {
461 * Optional. An array of arguments.
462 *
463 * @type bool $backup False to prevent backup. True to follow the user's setting. A backup can't be forced.
464 * @type string $backup_path If a backup must be done, this is the path to use. Default is the backup path used for the WP Media Library.
465 * @type int $optimization_level The optimization level (2=ultra, 1=aggressive, 0=normal).
466 * @type string $convert Set to 'webp' to convert the image to WebP, 'avif' to convert image to AVIF.
467 * @type string $context The context.
468 * @type int $original_size The file size, sent to the API.
469 * }
470 * @return \sdtClass|\WP_Error Optimized image data. A \WP_Error object on error.
471 */
472 public function optimize( $args = [] ) {
473 $args = array_merge(
474 [
475 'backup' => true,
476 'backup_path' => null,
477 'backup_source' => null,
478 'optimization_level' => 0,
479 'convert' => '',
480 'context' => 'wp',
481 'original_size' => 0,
482 ],
483 $args
484 );
485
486 $can_be_processed = $this->can_be_processed();
487
488 if ( is_wp_error( $can_be_processed ) ) {
489 return $can_be_processed;
490 }
491
492 // Check if external HTTP requests are blocked.
493 if ( Imagify_Requirements::is_imagify_blocked() ) {
494 return new \WP_Error( 'http_block_external', __( 'External HTTP requests are blocked.', 'imagify' ) );
495 }
496
497 /**
498 * Fires before a media file optimization.
499 *
500 * @since 1.9
501 * @author Grégory Viguier
502 *
503 * @param string $path Absolute path to the media file.
504 * @param array $args Arguments passed to the method.
505 */
506 do_action( 'imagify_before_optimize_file', $this->path, $args );
507
508 /**
509 * Fires before to optimize the Image with Imagify.
510 *
511 * @since 1.0
512 * @deprecated
513 *
514 * @param string $path Absolute path to the image file.
515 * @param bool $backup True if a backup will be make.
516 */
517 do_action_deprecated( 'before_do_imagify', [ $this->path, $args['backup'] ], '1.9', 'imagify_before_optimize_file' );
518
519 if ( $args['backup'] ) {
520 $backup_result = $this->backup( $args['backup_path'], $args['backup_source'] );
521
522 if ( is_wp_error( $backup_result ) ) {
523 // Stop the process if we can't backup the file.
524 return $backup_result;
525 }
526 }
527
528 // Send file for optimization and fetch the response.
529 $data = [
530 'normal' => 0 === $args['optimization_level'],
531 'aggressive' => 1 === $args['optimization_level'],
532 'ultra' => 2 === $args['optimization_level'],
533 'keep_exif' => true,
534 'original_size' => $args['original_size'],
535 'context' => $args['context'],
536 ];
537
538 if ( $args['convert'] ) {
539 $data['convert'] = $args['convert'];
540 $format = $args['convert'];
541 }
542
543 $response = upload_imagify_image(
544 [
545 'image' => $this->path,
546 'data' => wp_json_encode( $data ),
547 ]
548 );
549
550 if ( is_wp_error( $response ) ) {
551 return new \WP_Error( 'api_error', $response->get_error_message() );
552 }
553
554 if ( ! function_exists( 'download_url' ) ) {
555 require_once ABSPATH . 'wp-admin/includes/file.php';
556 }
557
558 $temp_file = download_url( $response->image );
559
560 if ( is_wp_error( $temp_file ) ) {
561 return new \WP_Error( 'temp_file_not_found', $temp_file->get_error_message() );
562 }
563
564 if ( property_exists( $response, 'message' ) ) {
565 $args['convert'] = '';
566 }
567
568 $formats = [
569 'webp',
570 'avif',
571 ];
572 if ( in_array( $args['convert'], $formats, true ) ) {
573 $destination_path = $this->get_path_to_nextgen( $args['convert'] );
574 $this->path = $destination_path;
575 $this->file_type = null;
576 $this->editor = null;
577 } else {
578 $destination_path = $this->path;
579 }
580
581 $moved = $this->filesystem->move( $temp_file, $destination_path, true );
582
583 if ( ! $moved ) {
584 return new \WP_Error( 'move_error', __( 'The file could not be moved to its final destination.', 'imagify' ) );
585 }
586
587 /**
588 * Fires after to optimize the Image with Imagify.
589 *
590 * @since 1.0
591 * @deprecated
592 *
593 * @param string $path Absolute path to the image file.
594 * @param bool $backup True if a backup has been made.
595 */
596 do_action_deprecated( 'after_do_imagify', [ $this->path, $args['backup'] ], '1.9', 'imagify_before_optimize_file' );
597
598 /**
599 * Fires after a media file optimization.
600 *
601 * @since 1.9
602 * @author Grégory Viguier
603 *
604 * @param string $path Absolute path to the media file.
605 * @param array $args Arguments passed to the method.
606 */
607 do_action( 'imagify_after_optimize_file', $this->path, $args );
608
609 return $response;
610 }
611
612
613 /** ----------------------------------------------------------------------------------------- */
614 /** IMAGE EDITOR (GD/IMAGEMAGICK) =========================================================== */
615 /** ----------------------------------------------------------------------------------------- */
616
617 /**
618 * Get an image editor instance (WP_Image_Editor_Imagick, WP_Image_Editor_GD).
619 *
620 * @since 1.9
621 * @author Grégory Viguier
622 *
623 * @return WP_Image_Editor_Imagick|WP_Image_Editor_GD|WP_Error
624 */
625 protected function get_editor() {
626 if ( isset( $this->editor ) ) {
627 return $this->editor;
628 }
629
630 $this->editor = wp_get_image_editor(
631 $this->path,
632 [
633 'methods' => $this->get_editor_methods(),
634 ]
635 );
636
637 if ( ! is_wp_error( $this->editor ) ) {
638 return $this->editor;
639 }
640
641 $this->editor = new \WP_Error(
642 'image_editor',
643 sprintf(
644 /* translators: %1$s is an error message, %2$s is a "More info?" link. */
645 __( 'No php extensions are available to edit images on the server. ImageMagick or GD is required. The internal error is: %1$s. %2$s', 'imagify' ),
646 $this->editor->get_error_message(),
647 '<a href="' . esc_url( imagify_get_external_url( 'documentation-imagick-gd' ) ) . '" target="_blank">' . __( 'More info?', 'imagify' ) . '</a>'
648 )
649 );
650
651 return $this->editor;
652 }
653
654 /**
655 * Get the image editor methods we will use.
656 *
657 * @since 1.9
658 * @author Grégory Viguier
659 *
660 * @return array
661 */
662 protected function get_editor_methods() {
663 static $methods;
664
665 if ( isset( $methods ) ) {
666 return $methods;
667 }
668
669 $methods = [
670 'resize',
671 'multi_resize',
672 'generate_filename',
673 'save',
674 ];
675
676 if ( $this->filesystem->can_get_exif() ) {
677 $methods[] = 'rotate';
678 }
679
680 return $methods;
681 }
682
683
684 /** ----------------------------------------------------------------------------------------- */
685 /** VARIOUS TOOLS =========================================================================== */
686 /** ----------------------------------------------------------------------------------------- */
687
688 /**
689 * Check if a file exceeds the weight limit (> 5mo).
690 *
691 * @since 1.9
692 * @author Grégory Viguier
693 *
694 * @return bool
695 */
696 public function is_exceeded() {
697 if ( ! $this->is_valid() ) {
698 return false;
699 }
700
701 $size = $this->filesystem->size( $this->path );
702
703 return $size > IMAGIFY_MAX_BYTES;
704 }
705
706 /**
707 * Tell if the current file is supported for a given context.
708 *
709 * @since 1.9
710 * @see imagify_get_mime_types()
711 * @author Grégory Viguier
712 *
713 * @param array $allowed_mime_types A list of allowed mime types.
714 * @return bool
715 */
716 public function is_supported( $allowed_mime_types ) {
717 return in_array( $this->get_mime_type(), $allowed_mime_types, true );
718 }
719
720 /**
721 * Tell if the file is an image.
722 *
723 * @since 1.9
724 * @author Grégory Viguier
725 *
726 * @return bool
727 */
728 public function is_image() {
729 if ( isset( $this->is_image ) ) {
730 return $this->is_image;
731 }
732
733 $this->is_image = strpos( $this->get_mime_type(), 'image/' ) === 0;
734
735 return $this->is_image;
736 }
737
738 /**
739 * Tell if the file is a pdf.
740 *
741 * @since 1.9
742 * @author Grégory Viguier
743 *
744 * @return bool
745 */
746 public function is_pdf() {
747 return 'application/pdf' === $this->get_mime_type();
748 }
749
750 /**
751 * Get the file mime type.
752 *
753 * @since 1.9
754 * @author Grégory Viguier
755 *
756 * @return string
757 */
758 public function get_mime_type() {
759 return $this->get_file_type()->type;
760 }
761
762 /**
763 * Get the file extension.
764 *
765 * @since 1.9
766 *
767 * @return string|false
768 */
769 public function get_extension() {
770 return $this->get_file_type()->ext;
771 }
772
773 /**
774 * Get the file path.
775 *
776 * @since 1.9
777 * @author Grégory Viguier
778 *
779 * @return string
780 */
781 public function get_path() {
782 return $this->path;
783 }
784
785 /**
786 * Replace the file extension by WebP.
787 *
788 * @since 1.9
789 * @author Grégory Viguier
790 *
791 * @return string|bool The file path on success. False if not an image or on failure.
792 */
793 public function get_path_to_webp() {
794 if ( ! $this->is_image() ) {
795 return false;
796 }
797
798 if ( $this->is_webp() ) {
799 return false;
800 }
801
802 return imagify_path_to_webp( $this->path );
803 }
804
805 /**
806 * Replace the file extension by its next-gen format extension.
807 *
808 * @since 2.2
809 *
810 * @param string $format the format we are targeting.
811 * @return string|bool The file path on success. False if not an image or on failure.
812 */
813 public function get_path_to_nextgen( string $format ) {
814 if ( ! $this->is_image() ) {
815 return false;
816 }
817
818 if ( $this->is_webp() || $this->is_avif() ) {
819 return false;
820 }
821
822 return imagify_path_to_nextgen( $this->path, $format );
823 }
824
825 /**
826 * Tell if the file is a WebP image.
827 * Rejects "path/to/.webp" files.
828 *
829 * @since 1.9
830 * @author Grégory Viguier
831 *
832 * @return bool
833 */
834 public function is_webp() {
835 return preg_match( '@(?!^|/|\\\)\.webp$@i', $this->path );
836 }
837
838 /**
839 * Tell if the file is an AVIF image.
840 * Rejects "path/to/.avif" files.
841 *
842 * @since 2.2
843 *
844 * @return bool
845 */
846 public function is_avif() {
847 return preg_match( '@(?!^|/|\\\)\.avif$@i', $this->path );
848 }
849
850 /**
851 * Get the file mime type + file extension.
852 *
853 * @since 1.9
854 * @see wp_check_filetype()
855 * @author Grégory Viguier
856 *
857 * @return object {
858 * @type string $ext The file extension.
859 * @type string $type The mime type.
860 * }
861 */
862 protected function get_file_type() {
863 if ( isset( $this->file_type ) ) {
864 return $this->file_type;
865 }
866
867 $this->file_type = (object) [
868 'ext' => '',
869 'type' => '',
870 ];
871
872 if ( ! $this->is_valid() ) {
873 return $this->file_type;
874 }
875
876 $this->file_type = (object) wp_check_filetype( $this->path );
877
878 return $this->file_type;
879 }
880
881 /**
882 * If the media is an image, get its width and height.
883 *
884 * @since 1.9
885 * @author Grégory Viguier
886 *
887 * @return array
888 */
889 public function get_dimensions() {
890 if ( ! $this->is_image() ) {
891 return [
892 'width' => 0,
893 'height' => 0,
894 ];
895 }
896
897 $values = $this->filesystem->get_image_size( $this->path );
898
899 if ( empty( $values ) ) {
900 return [
901 'width' => 0,
902 'height' => 0,
903 ];
904 }
905
906 return [
907 'width' => $values['width'],
908 'height' => $values['height'],
909 ];
910 }
911
912 /**
913 * Get a plugin’s option.
914 *
915 * @since 1.9
916 * @author Grégory Viguier
917 *
918 * @param string $option_name The option nme.
919 * @return mixed
920 */
921 protected function get_option( $option_name ) {
922 if ( isset( $this->options[ $option_name ] ) ) {
923 return $this->options[ $option_name ];
924 }
925
926 $this->options[ $option_name ] = get_imagify_option( $option_name );
927
928 return $this->options[ $option_name ];
929 }
930 }
931