PluginProbe ʕ •ᴥ•ʔ
Modern Image Formats / 1.0.2
Modern Image Formats v1.0.2
2.7.0 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.1.0 1.1.1 2.0.0 2.0.1 2.0.2 2.1.0 2.2.0 2.3.0 2.4.0 2.5.0 2.5.1 2.6.0 2.6.1
webp-uploads / helper.php
webp-uploads Last commit date
can-load.php 2 years ago fallback.js 3 years ago helper.php 2 years ago hooks.php 2 years ago image-edit.php 2 years ago load.php 2 years ago readme.txt 2 years ago rest-api.php 2 years ago settings.php 2 years ago
helper.php
340 lines
1 <?php
2 /**
3 * Helper functions used for WebP Uploads.
4 *
5 * @package webp-uploads
6 * @since 1.0.0
7 */
8
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit; // Exit if accessed directly.
11 }
12
13 /**
14 * Returns an array with the list of valid mime types that a specific mime type can be converted into it,
15 * for example an image/jpeg can be converted into an image/webp.
16 *
17 * @since 1.0.0
18 *
19 * @return array<string, array<string>> An array of valid mime types, where the key is the mime type and the value is the extension type.
20 */
21 function webp_uploads_get_upload_image_mime_transforms() {
22 $default_transforms = array(
23 'image/jpeg' => array( 'image/webp' ),
24 'image/webp' => array( 'image/webp' ),
25 );
26
27 // Check setting for whether to generate both JPEG and WebP.
28 if ( true === (bool) get_option( 'perflab_generate_webp_and_jpeg' ) ) {
29 $default_transforms = array(
30 'image/jpeg' => array( 'image/jpeg', 'image/webp' ),
31 'image/webp' => array( 'image/webp', 'image/jpeg' ),
32 );
33 }
34
35 /**
36 * Filter to allow the definition of a custom mime types, in which a defined mime type
37 * can be transformed and provide a wide range of mime types.
38 *
39 * The order of supported mime types matters. If the original mime type of the uploaded image
40 * is not needed, then the first mime type in the list supported by the image editor will be
41 * selected for the default subsizes.
42 *
43 * @since 1.0.0
44 *
45 * @param array $default_transforms A map with the valid mime transforms.
46 */
47 $transforms = apply_filters( 'webp_uploads_upload_image_mime_transforms', $default_transforms );
48
49 // Return the default mime transforms if a non-array result is returned from the filter.
50 if ( ! is_array( $transforms ) ) {
51 return $default_transforms;
52 }
53
54 // Ensure that all mime types have correct transforms. If a mime type has invalid transforms array,
55 // then fallback to the original mime type to make sure that the correct subsizes are created.
56 foreach ( $transforms as $mime_type => $transform_types ) {
57 if ( ! is_array( $transform_types ) || empty( $transform_types ) ) {
58 $transforms[ $mime_type ] = array( $mime_type );
59 }
60 }
61
62 return $transforms;
63 }
64
65 /**
66 * Creates a resized image with the provided dimensions out of an original attachment, the created image
67 * would be saved in the specified mime and stored in the destination file. If the image can't be saved correctly
68 * a WP_Error would be returned otherwise an array with the file and filesize properties.
69 *
70 * @since 1.0.0
71 * @access private
72 *
73 * @param int $attachment_id The ID of the attachment from where this image would be created.
74 * @param string $image_size The size name that would be used to create the image source, out of the registered subsizes.
75 * @param array $size_data An array with the dimensions of the image: height, width and crop.
76 * @param string $mime The target mime in which the image should be created.
77 * @param string|null $destination_file_name The path where the file would be stored, including the extension. If null, `generate_filename` is used to create the destination file name.
78 *
79 * @return array|WP_Error An array with the file and filesize if the image was created correctly, otherwise a WP_Error.
80 */
81 function webp_uploads_generate_additional_image_source( $attachment_id, $image_size, array $size_data, $mime, $destination_file_name = null ) {
82 /**
83 * Filter to allow the generation of additional image sources, in which a defined mime type
84 * can be transformed and create additional mime types for the file.
85 *
86 * Returning an image data array or WP_Error here effectively short-circuits the default logic to generate the image source.
87 *
88 * @since 1.1.0
89 *
90 * @param array|null|WP_Error $image Image data {'path'=>string, 'file'=>string, 'width'=>int, 'height'=>int, 'mime-type'=>string} or null or WP_Error.
91 * @param int $attachment_id The ID of the attachment from where this image would be created.
92 * @param string $image_size The size name that would be used to create this image, out of the registered subsizes.
93 * @param array $size_data An array with the dimensions of the image: height, width and crop {'height'=>int, 'width'=>int, 'crop'}.
94 * @param string $mime The target mime in which the image should be created.
95 */
96 $image = apply_filters( 'webp_uploads_pre_generate_additional_image_source', null, $attachment_id, $image_size, $size_data, $mime );
97 if ( is_wp_error( $image ) ) {
98 return $image;
99 }
100
101 if (
102 is_array( $image ) &&
103 ! empty( $image['file'] ) &&
104 (
105 ! empty( $image['path'] ) ||
106 array_key_exists( 'filesize', $image )
107 )
108 ) {
109 return array(
110 'file' => $image['file'],
111 'filesize' => array_key_exists( 'filesize', $image )
112 ? $image['filesize']
113 : wp_filesize( $image['path'] ),
114 );
115 }
116
117 $allowed_mimes = array_flip( wp_get_mime_types() );
118 if ( ! isset( $allowed_mimes[ $mime ] ) || ! is_string( $allowed_mimes[ $mime ] ) ) {
119 return new WP_Error( 'image_mime_type_invalid', __( 'The provided mime type is not allowed.', 'webp-uploads' ) );
120 }
121
122 if ( ! wp_image_editor_supports( array( 'mime_type' => $mime ) ) ) {
123 return new WP_Error( 'image_mime_type_not_supported', __( 'The provided mime type is not supported.', 'webp-uploads' ) );
124 }
125
126 $image_path = wp_get_original_image_path( $attachment_id );
127 if ( ! file_exists( $image_path ) ) {
128 return new WP_Error( 'original_image_file_not_found', __( 'The original image file does not exists, subsizes are created out of the original image.', 'webp-uploads' ) );
129 }
130
131 $editor = wp_get_image_editor( $image_path, array( 'mime_type' => $mime ) );
132 if ( is_wp_error( $editor ) ) {
133 return $editor;
134 }
135
136 $height = isset( $size_data['height'] ) ? (int) $size_data['height'] : 0;
137 $width = isset( $size_data['width'] ) ? (int) $size_data['width'] : 0;
138 $crop = isset( $size_data['crop'] ) && $size_data['crop'];
139 if ( $width <= 0 && $height <= 0 ) {
140 return new WP_Error( 'image_wrong_dimensions', __( 'At least one of the dimensions must be a positive number.', 'webp-uploads' ) );
141 }
142
143 $image_meta = wp_get_attachment_metadata( $attachment_id );
144 // If stored EXIF data exists, rotate the source image before creating sub-sizes.
145 if ( ! empty( $image_meta['image_meta'] ) ) {
146 $editor->maybe_exif_rotate();
147 }
148
149 $editor->resize( $width, $height, $crop );
150
151 if ( null === $destination_file_name ) {
152 $ext = pathinfo( $image_path, PATHINFO_EXTENSION );
153 $suffix = $editor->get_suffix();
154 $suffix .= "-{$ext}";
155 $extension = explode( '|', $allowed_mimes[ $mime ] );
156 $destination_file_name = $editor->generate_filename( $suffix, null, $extension[0] );
157 }
158
159 remove_filter( 'image_editor_output_format', 'webp_uploads_filter_image_editor_output_format', 10, 3 );
160 $image = $editor->save( $destination_file_name, $mime );
161 add_filter( 'image_editor_output_format', 'webp_uploads_filter_image_editor_output_format', 10, 3 );
162
163 if ( is_wp_error( $image ) ) {
164 return $image;
165 }
166
167 if ( empty( $image['file'] ) ) {
168 return new WP_Error( 'image_file_not_present', __( 'The file key is not present on the image data', 'webp-uploads' ) );
169 }
170
171 return array(
172 'file' => $image['file'],
173 'filesize' => isset( $image['path'] ) ? wp_filesize( $image['path'] ) : 0,
174 );
175 }
176
177 /**
178 * Creates a new image based of the specified attachment with a defined mime type
179 * this image would be stored in the same place as the provided size name inside the
180 * metadata of the attachment.
181 *
182 * @since 1.0.0
183 *
184 * @see wp_create_image_subsizes()
185 *
186 * @param int $attachment_id The ID of the attachment we are going to use as a reference to create the image.
187 * @param string $size The size name that would be used to create this image, out of the registered subsizes.
188 * @param string $mime A mime type we are looking to use to create this image.
189 *
190 * @return array|WP_Error
191 */
192 function webp_uploads_generate_image_size( $attachment_id, $size, $mime ) {
193 $sizes = wp_get_registered_image_subsizes();
194 $metadata = wp_get_attachment_metadata( $attachment_id );
195
196 if (
197 ! isset( $metadata['sizes'][ $size ], $sizes[ $size ] )
198 || ! is_array( $metadata['sizes'][ $size ] )
199 || ! is_array( $sizes[ $size ] )
200 ) {
201 return new WP_Error( 'image_mime_type_invalid_metadata', __( 'The image does not have a valid metadata.', 'webp-uploads' ) );
202 }
203
204 $size_data = array(
205 'width' => 0,
206 'height' => 0,
207 'crop' => false,
208 );
209
210 if ( isset( $sizes[ $size ]['width'] ) ) {
211 $size_data['width'] = $sizes[ $size ]['width'];
212 } elseif ( isset( $metadata['sizes'][ $size ]['width'] ) ) {
213 $size_data['width'] = $metadata['sizes'][ $size ]['width'];
214 }
215
216 if ( isset( $sizes[ $size ]['height'] ) ) {
217 $size_data['height'] = $sizes[ $size ]['height'];
218 } elseif ( isset( $metadata['sizes'][ $size ]['height'] ) ) {
219 $size_data['height'] = $metadata['sizes'][ $size ]['height'];
220 }
221
222 if ( isset( $sizes[ $size ]['crop'] ) ) {
223 $size_data['crop'] = (bool) $sizes[ $size ]['crop'];
224 }
225
226 return webp_uploads_generate_additional_image_source( $attachment_id, $size, $size_data, $mime );
227 }
228
229 /**
230 * Returns the attachment sources array ordered by filesize.
231 *
232 * @since 1.0.0
233 *
234 * @param int $attachment_id The attachment ID.
235 * @param string $size The attachment size.
236 * @return array The attachment sources array.
237 */
238 function webp_uploads_get_attachment_sources( $attachment_id, $size = 'thumbnail' ) {
239 // Check for the sources attribute in attachment metadata.
240 $metadata = wp_get_attachment_metadata( $attachment_id );
241
242 // Return full image size sources.
243 if ( 'full' === $size && ! empty( $metadata['sources'] ) ) {
244 return $metadata['sources'];
245 }
246
247 // Return the resized image sources.
248 if ( ! empty( $metadata['sizes'][ $size ]['sources'] ) ) {
249 return $metadata['sizes'][ $size ]['sources'];
250 }
251
252 // Return an empty array if no sources found.
253 return array();
254 }
255
256 /**
257 * Returns mime types that should be used for an image in the specific context.
258 *
259 * @since 1.4.0
260 *
261 * @param int $attachment_id The attachment ID.
262 * @param string $context The current context.
263 * @return array Mime types to use for the image.
264 */
265 function webp_uploads_get_content_image_mimes( $attachment_id, $context ) {
266 $target_mimes = array( 'image/webp', 'image/jpeg' );
267
268 /**
269 * Filters mime types that should be used to update all images in the content. The order of
270 * mime types matters. The first mime type in the list will be used if it is supported by an image.
271 *
272 * @since 1.0.0
273 *
274 * @param array $target_mimes The list of mime types that can be used to update images in the content.
275 * @param int $attachment_id The attachment ID.
276 * @param string $context The current context.
277 */
278 $target_mimes = apply_filters( 'webp_uploads_content_image_mimes', $target_mimes, $attachment_id, $context );
279 if ( ! is_array( $target_mimes ) ) {
280 $target_mimes = array();
281 }
282
283 return $target_mimes;
284 }
285
286 /**
287 * Verifies if the request is for a frontend context within the <body> tag.
288 *
289 * @since 1.3.0
290 *
291 * @return bool True if in the <body> within a frontend request, false otherwise.
292 */
293 function webp_uploads_in_frontend_body() {
294 global $wp_query;
295
296 // Check if this request is generally outside (or before) any frontend context.
297 if ( ! isset( $wp_query ) || defined( 'REST_REQUEST' ) || defined( 'XMLRPC_REQUEST' ) || is_feed() ) {
298 return false;
299 }
300
301 // Check if we're anywhere before 'template_redirect' or within the 'wp_head' action.
302 if ( ! did_action( 'template_redirect' ) || doing_action( 'wp_head' ) ) {
303 return false;
304 }
305
306 return true;
307 }
308
309 /**
310 * Check whether the additional image is larger than the original image.
311 *
312 * @since 1.3.0
313 *
314 * @param array $original An array with the metadata of the attachment.
315 * @param array $additional An array containing the filename and file size for additional mime.
316 * @return bool True if the additional image is larger than the original image, otherwise false.
317 */
318 function webp_uploads_should_discard_additional_image_file( array $original, array $additional ) {
319 $original_image_filesize = isset( $original['filesize'] ) ? (int) $original['filesize'] : 0;
320 $additional_image_filesize = isset( $additional['filesize'] ) ? (int) $additional['filesize'] : 0;
321 if ( $original_image_filesize > 0 && $additional_image_filesize > 0 ) {
322 /**
323 * Filter whether WebP images that are larger than the matching JPEG should be discarded.
324 *
325 * By default the performance lab plugin will use the mime type with the smaller filesize
326 * rather than defaulting to `webp`.
327 *
328 * @since 1.3.0
329 *
330 * @param bool $preferred_filesize Prioritize file size over mime type. Default true.
331 */
332 $webp_discard_larger_images = apply_filters( 'webp_uploads_discard_larger_generated_images', true );
333
334 if ( $webp_discard_larger_images && $additional_image_filesize >= $original_image_filesize ) {
335 return true;
336 }
337 }
338 return false;
339 }
340