PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / 2.2.3.1
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF v2.2.3.1
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.3.1, at classes/Optimization/File.php

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