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

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

270 lines 8.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Imagify\Optimization\Process;
3
4 use Imagify\Optimization\File;
5 use WP_Error;
6
7 /**
8 * Optimization class for the attachments in the WP library.
9 * This class constructor accepts:
10 * - A post ID (int).
11 * - A \WP_Post object.
12 * - A \Imagify\Media\MediaInterface object.
13 * - A \Imagify\Media\DataInterface object.
14 *
15 * @since 1.9
16 * @author Grégory Viguier
17 */
18 class WP extends AbstractProcess {
19
20 /** ----------------------------------------------------------------------------------------- */
21 /** MISSING THUMBNAILS ====================================================================== */
22 /** ----------------------------------------------------------------------------------------- */
23
24 /**
25 * Get the sizes for this media that have not get through optimization.
26 * No sizes are returned if the file is not optimized, has no backup, or is not an image.
27 * The 'full' size os never returned.
28 *
29 * @since 1.9
30 * @access public
31 * @author Grégory Viguier
32 *
33 * @return array|WP_Error {
34 * A WP_Error object on failure.
35 * An array of data for the thumbnail sizes on success.
36 * Size names are used as array keys.
37 *
38 * @type int $width The image width.
39 * @type int $height The image height.
40 * @type bool $crop True to crop, false to resize.
41 * @type string $name The size name.
42 * @type string $file The name the thumbnail "should" have.
43 * }
44 */
45 public function get_missing_sizes() {
46 // The media must have been optimized once and have a backup.
47 if ( ! $this->is_valid() ) {
48 return new WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
49 }
50
51 $media = $this->get_media();
52
53 if ( ! $media ) {
54 return new WP_Error( 'no_media', __( 'No media found.', 'imagify' ) );
55 }
56
57 if ( ! $media->is_supported() ) {
58 return new WP_Error( 'media_not_supported', __( 'This media is not supported.', 'imagify' ) );
59 }
60
61 $data = $this->get_data();
62
63 if ( ! $data->is_optimized() ) {
64 return new WP_Error( 'media_not_optimized', __( 'This media is not optimized yet.', 'imagify' ) );
65 }
66
67 if ( ! $media->has_backup() ) {
68 return new WP_Error( 'no_backup', __( 'This file has no backup file.', 'imagify' ) );
69 }
70
71 if ( ! $media->is_image() ) {
72 return new WP_Error( 'media_not_an_image', __( 'This media is not an image.', 'imagify' ) );
73 }
74
75 // Compare registered sizes and optimized sizes.
76 $context_sizes = $media->get_context_instance()->get_thumbnail_sizes();
77 $optimized_sizes = $data->get_optimization_data();
78 $missing_sizes = array_diff_key( $context_sizes, $optimized_sizes['sizes'] );
79
80 if ( ! $missing_sizes ) {
81 // We have everything we need.
82 return [];
83 }
84
85 $media_sizes = $media->get_media_files();
86 $full_size = $media_sizes['full'];
87
88 if ( ! $full_size['path'] || ! $full_size['width'] || ! $full_size['height'] ) {
89 return [];
90 }
91
92 $file_name = $this->filesystem->path_info( $full_size['path'] );
93 $file_name = $file_name['file_base'] . '-{%suffix%}.' . $file_name['extension'];
94
95 // Test if the missing sizes are needed.
96 foreach ( $missing_sizes as $size_name => $size_data ) {
97 if ( $full_size['width'] === $size_data['width'] && $full_size['height'] === $size_data['height'] ) {
98 // Same dimensions as the full size.
99 unset( $missing_sizes[ $size_name ] );
100 continue;
101 }
102
103 if ( ! empty( $media_sizes[ $size_name ]['disabled'] ) ) {
104 // This size must not be optimized.
105 unset( $missing_sizes[ $size_name ] );
106 continue;
107 }
108
109 $resize_result = image_resize_dimensions( $full_size['width'], $full_size['height'], $size_data['width'], $size_data['height'], $size_data['crop'] );
110
111 if ( ! $resize_result ) {
112 // This thumbnail is not needed, it is smaller than this size.
113 unset( $missing_sizes[ $size_name ] );
114 continue;
115 }
116
117 // Provide what should be the file name.
118 list( , , , , $new_width, $new_height ) = $resize_result;
119 $missing_sizes[ $size_name ]['file'] = str_replace( '{%suffix%}', "{$new_width}x{$new_height}", $file_name );
120 }
121
122 return $missing_sizes;
123 }
124
125 /**
126 * Optimize missing thumbnail sizes.
127 *
128 * @since 1.9
129 * @access public
130 * @author Grégory Viguier
131 *
132 * @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
133 */
134 public function optimize_missing_thumbnails() {
135 if ( ! $this->is_valid() ) {
136 return new WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
137 }
138
139 $media = $this->get_media();
140
141 if ( ! $media ) {
142 return new WP_Error( 'no_media', __( 'No media found.', 'imagify' ) );
143 }
144
145 if ( ! $media->is_supported() ) {
146 return new WP_Error( 'media_not_supported', __( 'This media is not supported.', 'imagify' ) );
147 }
148
149 $missing_sizes = $this->get_missing_sizes();
150
151 if ( ! $missing_sizes ) {
152 return new WP_Error( 'no_sizes', __( 'No thumbnails seem to be missing.', 'imagify' ) );
153 }
154
155 if ( is_wp_error( $missing_sizes ) ) {
156 return $missing_sizes;
157 }
158
159 if ( $this->is_locked() ) {
160 return new WP_Error( 'media_locked', __( 'This media is already being processed.', 'imagify' ) );
161 }
162
163 $this->lock();
164
165 // Create the missing thumbnails.
166 $sizes = $this->create_missing_thumbnails( $missing_sizes );
167
168 if ( ! $sizes ) {
169 $this->unlock();
170 return new WP_Error( 'thumbnail_creation_failed', __( 'The thumbnails failed to be created.', 'imagify' ) );
171 }
172
173 $optimization_level = $this->get_data()->get_optimization_level();
174
175 if ( false === $optimization_level ) {
176 $this->unlock();
177 return new \WP_Error( 'optimization_level_not_set', __( 'The optimization level of this media seems to have disappear from the database. You should restore this media and then launch a new optimization.', 'imagify' ) );
178 }
179
180 $args = [
181 'hook_suffix' => 'optimize_missing_thumbnails',
182 'locked' => true,
183 ];
184
185 // Optimize.
186 return $this->optimize_sizes( array_keys( $sizes ), $optimization_level, $args );
187 }
188
189 /**
190 * Create all missing thumbnails if they don't exist and update the attachment metadata.
191 *
192 * @since 1.9
193 * @access protected
194 * @see $this->get_missing_sizes()
195 * @author Grégory Viguier
196 *
197 * @param array $missing_sizes array {
198 * An array of data for the thumbnail sizes on success.
199 * Size names are used as array keys.
200 *
201 * @type int $width The image width.
202 * @type int $height The image height.
203 * @type bool $crop True to crop, false to resize.
204 * @type string $name The size name.
205 * @type string $file The name the thumbnail "should" have.
206 * }
207 * @return array {
208 * An array of thumbnail data (those without errors):
209 *
210 * @type string $file File name.
211 * @type int $width The image width.
212 * @type int $height The image height.
213 * @type string $mime-type The mime type.
214 * }
215 */
216 protected function create_missing_thumbnails( $missing_sizes ) {
217 if ( ! $missing_sizes ) {
218 return [];
219 }
220
221 $media = $this->get_media();
222
223 if ( ! $media ) {
224 return [];
225 }
226
227 $media_id = $media->get_id();
228 $metadata = wp_get_attachment_metadata( $media_id );
229 $metadata['sizes'] = ! empty( $metadata['sizes'] ) && is_array( $metadata['sizes'] ) ? $metadata['sizes'] : [];
230
231 $destination_dir = $this->filesystem->dir_path( $media->get_raw_fullsize_path() );
232 $backup_file = new File( $media->get_backup_path() );
233 $without_errors = [];
234 $has_new_data = false;
235
236 // Create the missing thumbnails.
237 foreach ( $missing_sizes as $size_name => $thumbnail_data ) {
238 // The path to the destination file.
239 $thumbnail_data['path'] = $destination_dir . $thumbnail_data['file'];
240
241 if ( ! $this->filesystem->exists( $thumbnail_data['path'] ) ) {
242 $result = $backup_file->create_thumbnail( $thumbnail_data );
243
244 if ( is_array( $result ) ) {
245 // New file.
246 $metadata['sizes'][ $size_name ] = $result;
247 $has_new_data = true;
248 }
249 } else {
250 $result = true;
251 }
252
253 if ( ! empty( $metadata['sizes'][ $size_name ] ) && ! is_wp_error( $result ) ) { // phpstan-ignore-line
254 // Not an error.
255 $without_errors[ $size_name ] = $metadata['sizes'][ $size_name ];
256 }
257 }
258
259 // Save the new data into the attachment metadata.
260 if ( $has_new_data ) {
261 /**
262 * Here we don't use wp_update_attachment_metadata() to prevent triggering unwanted hooks.
263 */
264 update_post_meta( $media_id, '_wp_attachment_metadata', $metadata );
265 }
266
267 return $without_errors;
268 }
269 }
270