PluginProbe
QuickWebP – WebP & AVIF Image Optimizer, Compression & SEO for WordPress / 4.0.0
QuickWebP – WebP & AVIF Image Optimizer, Compression & SEO for WordPress v4.0.0
4.1.2 4.1.1 4.1.0 4.0.1 4.0.0 3.3.1 3.3.0 trunk 1.0.0 2.0.0 2.1.0 3.0.0 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8
quickwebp / admin / class-image-optimizer.php

class-image-optimizer.php in QuickWebP – WebP & AVIF Image Optimizer, Compression & SEO for WordPress 4.0.0, at admin/class-image-optimizer.php

1,032 lines 33.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * The core functionality for image optimizing.
5 *
6 * @link http://webdeclic.com
7 * @since 1.0.0
8 *
9 * @package Quickwebp
10 * @subpackage Quickwebp/admin
11 */
12 class Quickwebp_Image_Optimizer {
13
14 /**
15 * The ID of this plugin.
16 *
17 * @since 1.0.0
18 * @access private
19 * @var string $plugin_name The ID of this plugin.
20 */
21 private $plugin_name;
22
23 /**
24 * The version of this plugin.
25 *
26 * @since 1.0.0
27 * @access private
28 * @var string $version The current version of this plugin.
29 */
30 private $version;
31
32 /**
33 * Allowed mime types for the optimazation
34 */
35 public $allowed_mime_types;
36
37 /**
38 * Initialize the class and set its properties.
39 *
40 * @since 1.0.0
41 * @param string $plugin_name The name of this plugin.
42 * @param string $version The version of this plugin.
43 */
44 public function __construct( $plugin_name, $version ) {
45
46 $this->plugin_name = $plugin_name;
47 $this->version = $version;
48 $this->allowed_mime_types = array( 'image/jpeg', 'image/png', 'image/webp', 'image/avif' );
49 }
50
51 /**
52 * Optimize an image added in wp_media
53 */
54 public function image_optimization( $file ) {
55 global $quickwebp_surecart_client;
56
57 $settings = $this->get_settings();
58 $image_file = $this->file_is_image( $file, $settings );
59
60 if ( ! $image_file ) {
61 return $file;
62 }
63
64 $mode_enabled = $settings['quickwebp_settings_conversion'];
65 if ( $mode_enabled === '0' ) {
66 return $image_file;
67 }
68
69 $save_original = is_array( $settings['quickwebp_settings_conversion_save_original'] ) ? $settings['quickwebp_settings_conversion_save_original'] : array();
70 if ( in_array( 'checked', $save_original ) ) {
71 return $image_file;
72 }
73
74 $ignore_same_format = is_array( $settings['quickwebp_settings_conversion_ignore_webp'] ) ? $settings['quickwebp_settings_conversion_ignore_webp'] : array();
75 $mime_type = wp_get_image_mime( $image_file['tmp_name'] );
76 if ( in_array( 'checked', $ignore_same_format ) ) {
77 if ( '1' == $mode_enabled && 'image/webp' == $mime_type ) {
78 return $image_file;
79 } elseif( '2' == $mode_enabled && 'image/avif' == $mime_type ) {
80 return $image_file;
81 }
82 }
83
84 $quality = $this->get_the_quality( $settings );
85 $is_pro = false;
86 if ( $quickwebp_surecart_client ) {
87 $is_pro = $quickwebp_surecart_client->license()->is_valid();
88 }
89
90 if ( '2' === $mode_enabled && $is_pro ) {
91 $avif_image = $this->create_avif_image( $image_file['tmp_name'], $image_file['tmp_name'], $quality );
92 if ( $avif_image ) {
93 $image_file['size'] = filesize( $image_file['tmp_name'] );
94 $image_file['type'] = 'image/avif';
95 $image_file['quickwebp'] = 'optimized';
96 }
97 }
98
99 if ( '1' === $mode_enabled ) {
100 $webp_image = $this->create_webp_image( $image_file['tmp_name'], $image_file['tmp_name'], $quality );
101 if ( $webp_image ) {
102 $image_file['size'] = filesize( $image_file['tmp_name'] );
103 $image_file['type'] = 'image/webp';
104 $image_file['quickwebp'] = 'optimized';
105 }
106 }
107
108 return $image_file;
109 }
110
111 /**
112 * Add post meta to the attachment that already optimized
113 */
114 public function add_already_optimized_meta( $metadata, $attachment_id, $context ) {
115 //phpcs:ignore WordPress.Security.NonceVerification.Missing
116 $quickwebp = sanitize_text_field( wp_unslash( $_FILES['async-upload']['quickwebp'] ?? '' ) );
117
118 if ( 'optimized' == $quickwebp ) {
119 //phpcs:ignore WordPress.Security.NonceVerification.Missing
120 $type = sanitize_text_field( wp_unslash( $_FILES['async-upload']['type'] ?? '' ) );
121
122 if ( 'image/webp' == $type ) {
123 update_post_meta( $attachment_id, 'quickwebp_already_optimized', '1' );
124 } elseif ( 'image/avif' == $type ) {
125 update_post_meta( $attachment_id, 'quickwebp_already_optimized', '2' );
126 }
127 }
128
129 return $metadata;
130 }
131
132 /**
133 * Save the original image in the attachment meta
134 */
135 public function save_original_image( $metadata, $attachment_id, $context ) {
136
137 if ( $context != 'create' ) {
138 return $metadata;
139 }
140
141 $settings = $this->get_settings();
142
143 $mode_enabled = $settings['quickwebp_settings_conversion'];
144 if ( $mode_enabled === '0' ) {
145 return $metadata;
146 }
147
148 $save_original = is_array( $settings['quickwebp_settings_conversion_save_original'] ) ? $settings['quickwebp_settings_conversion_save_original'] : array();
149 if ( ! in_array( 'checked', $save_original ) ) {
150 return $metadata;
151 }
152
153 $ignore_same_format = is_array( $settings['quickwebp_settings_conversion_ignore_webp'] ) ? $settings['quickwebp_settings_conversion_ignore_webp'] : array();
154 //phpcs:ignore WordPress.Security.NonceVerification.Missing
155 $mime_type = sanitize_text_field( wp_unslash( $_FILES['async-upload']['type'] ?? '' ) );
156 if ( in_array( 'checked', $ignore_same_format ) ) {
157 if ( '1' == $mode_enabled && 'image/webp' == $mime_type ) {
158 return $metadata;
159 } elseif( '2' == $mode_enabled && 'image/avif' == $mime_type ) {
160 return $metadata;
161 }
162 }
163
164 $sizes = $this->get_media_files( $attachment_id );
165 $new_sizes = array();
166
167 foreach ( $sizes as $key => $size ) {
168 $result = $this->optimize_local_file( $size );
169
170 if ( $result ) {
171 $new_sizes[$key] = $result;
172 }
173 }
174
175 if ( ! empty( $new_sizes ) ) {
176 if ( '1' == $mode_enabled ) {
177 update_post_meta( $attachment_id, 'quickwebp_already_optimized', '1' );
178 } elseif ( '2' == $mode_enabled ) {
179 update_post_meta( $attachment_id, 'quickwebp_already_optimized', '2' );
180 }
181
182 update_post_meta( $attachment_id, 'quickwebp_data', $new_sizes );
183 delete_post_meta( $attachment_id, 'quickwebp_has_error' );
184 } else {
185 update_post_meta( $attachment_id, 'quickwebp_has_error', '1' );
186 }
187
188 return $metadata;
189 }
190
191 /**
192 * Add data to the attachment
193 */
194 public function add_data_to_attachment( $metadata, $attachment_id, $context ) {
195
196 if ( $context != 'create' ) {
197 return $metadata;
198 }
199
200 $settings = $this->get_settings();
201 if ( $settings['quickwebp_settings_completion'] != '1' ) {
202 return $metadata;
203 }
204
205 //phpcs:ignore WordPress.Security.NonceVerification.Missing
206 $original_name = isset( $_FILES['async-upload']['original_name'] ) ? sanitize_text_field( wp_unslash( $_FILES['async-upload']['original_name'] ) ) : sanitize_text_field( $_FILES['async-upload']['name'] ?? '' );
207 if ( ! empty( $original_name ) ) {
208 $original_name = pathinfo( $original_name, PATHINFO_FILENAME );
209 $post_arr = array();
210
211 $completion_options = is_array( $settings['quickwebp_settings_completion_options'] ) ? $settings['quickwebp_settings_completion_options'] : array();
212
213 if ( in_array( 'title', $completion_options ) ) {
214 $post_arr['post_title'] = $original_name;
215 }
216
217 if ( in_array( 'caption', $completion_options ) ) {
218 $post_arr['post_excerpt'] = $original_name;
219 }
220
221 if ( in_array( 'alt', $completion_options ) ) {
222 $post_arr['meta_input']['_wp_attachment_image_alt'] = $original_name;
223 }
224
225 if ( in_array( 'description', $completion_options ) ) {
226 $post_arr['post_content'] = $original_name;
227 }
228
229 if ( ! empty( $post_arr ) ) {
230 $post_arr['ID'] = $attachment_id;
231 wp_update_post( $post_arr );
232 }
233 }
234
235 return $metadata;
236 }
237
238 /**
239 * Change the default size of wp editor
240 */
241 public function change_wp_max_size( $max_size, $imagesize ) {
242
243 $settings = $this->get_settings();
244 $resize_active = $settings['quickwebp_settings_resize'];
245
246 if ( $resize_active == '1' ) {
247
248 $imagesize_width = isset($imagesize[0]) ? $imagesize[0] : 0;
249 $resize_value = $settings['quickwebp_settings_resize_value'];
250
251 if ( $imagesize_width > $resize_value ) {
252 $max_size = $resize_value;
253 }
254 }
255
256 return $max_size;
257 }
258
259 /**
260 * Change the default quality of wp editor
261 */
262 public function change_wp_quality( $default_quality ) {
263
264 $settings = $this->get_settings();
265 $mode_enabled = $settings['quickwebp_settings_conversion'];
266 if ( $mode_enabled === '0' ) {
267 return $default_quality;
268 }
269
270 return 100;
271 }
272
273 /**
274 * Optimize image through ajax
275 */
276 public function image_optimization_ajax() {
277 global $quickwebp_surecart_client;
278
279 if ( ! current_user_can( 'upload_files' ) ) {
280 wp_send_json_error( __( 'You are not allowed to upload files.', 'quickwebp' ), 403 );
281 }
282
283 // verify the nonce.
284 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
285 if( !wp_verify_nonce( $nonce, 'image_optimize_nonce' ) ) {
286 wp_send_json_error( __( 'Refresh the page and try again.', 'quickwebp' ) );
287 }
288
289 // Get the file
290 $file = count($_FILES) > 0 ? array_shift($_FILES) : array();
291 if ( empty($file) ) {
292 wp_send_json_error( __( 'No image uploaded, try again.', 'quickwebp' ) );
293 }
294
295 $settings = $this->get_ajax_settings();
296 $image_file = $this->file_is_image( $file, $settings );
297
298 if ( ! $image_file ) {
299 wp_send_json_error( __( 'No image uploaded, try again.', 'quickwebp' ) );
300 }
301
302 $mode_enabled = $settings['quickwebp_settings_conversion'];
303 if ( $mode_enabled === '0' ) {
304 $image_file['new_size'] = $image_file['size'];
305 $image_file['new_type'] = $image_file['type'];
306 $this->return_ajax_data( $image_file );
307 }
308
309 $quality = $this->get_the_quality( $settings );
310 $is_pro = false;
311 if ( $quickwebp_surecart_client ) {
312 $is_pro = $quickwebp_surecart_client->license()->is_valid();
313 }
314
315 if ( '2' === $mode_enabled && $is_pro ) {
316 $avif_image = $this->create_avif_image( $image_file['tmp_name'], $image_file['tmp_name'], $quality );
317 if ( $avif_image ) {
318 $image_file['new_size'] = filesize( $image_file['tmp_name'] );
319 $image_file['new_type'] = 'image/avif';
320 }
321 }
322
323 if ( '1' === $mode_enabled ) {
324 $webp_image = $this->create_webp_image( $image_file['tmp_name'], $image_file['tmp_name'], $quality );
325 if ( $webp_image ) {
326 $image_file['new_size'] = filesize( $image_file['tmp_name'] );
327 $image_file['new_type'] = 'image/webp';
328 }
329 }
330
331 $this->return_ajax_data( $image_file );
332 }
333
334 /**
335 * Optimize a single media
336 */
337 public function single_optimization_ajax() {
338
339 // verify the nonce.
340 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
341 if( !wp_verify_nonce( $nonce, 'quickwebp_admin_attachment' ) ) {
342 wp_send_json_error( __( 'Refresh the page and try again.', 'quickwebp' ) );
343 }
344
345 // Sanitize data
346 $attachment_id = isset( $_POST['attachment_id'] ) ? absint( wp_unslash( $_POST['attachment_id'] ) ) : 0;
347
348 if ( ! $attachment_id ) {
349 wp_send_json_error( __( 'No attachment id.', 'quickwebp' ) );
350 }
351
352 $settings = $this->get_settings();
353 $mode_enabled = $settings['quickwebp_settings_conversion'];
354 if ( '0' === $mode_enabled ) {
355 wp_send_json_error( __( 'Choose an image format and save the settings.', 'quickwebp' ) );
356 }
357
358 $already_optimized = get_post_meta( $attachment_id, 'quickwebp_already_optimized', true );
359
360 if ( '1' == $already_optimized && '1' == $mode_enabled ) {
361 wp_send_json_error( __( 'Already optimized.', 'quickwebp' ) );
362 }
363
364 if ( '2' == $already_optimized && '2' == $mode_enabled ) {
365 wp_send_json_error( __( 'Already optimized.', 'quickwebp' ) );
366 }
367
368 $post_mime_type = get_post_mime_type( $attachment_id );
369 if ( ! in_array( $post_mime_type, $this->allowed_mime_types ) ) {
370 wp_send_json_error( __( 'Not a valid image.', 'quickwebp' ) );
371 }
372
373 $sizes = $this->get_media_files( $attachment_id );
374 $new_sizes = array();
375
376 foreach ( $sizes as $key => $size ) {
377 $result = $this->optimize_local_file( $size );
378
379 if ( $result ) {
380 $new_sizes[$key] = $result;
381 }
382 }
383
384 if ( ! empty( $new_sizes ) ) {
385
386 $data = get_post_meta( $attachment_id, 'quickwebp_data', true );
387 if ( ! empty( $data ) ) {
388 $this->remove_related_files( $data );
389 }
390
391 if ( '1' == $mode_enabled ) {
392 update_post_meta( $attachment_id, 'quickwebp_already_optimized', '1' );
393 } elseif ( '2' == $mode_enabled ) {
394 update_post_meta( $attachment_id, 'quickwebp_already_optimized', '2' );
395 }
396
397 update_post_meta( $attachment_id, 'quickwebp_data', $new_sizes );
398 delete_post_meta( $attachment_id, 'quickwebp_has_error' );
399 } else {
400 update_post_meta( $attachment_id, 'quickwebp_has_error', '1' );
401 delete_post_meta( $attachment_id, 'quickwebp_already_optimized' );
402 delete_post_meta( $attachment_id, 'quickwebp_data' );
403 }
404
405 $wp_media_extend = new Quickwebp_Wp_Media_Extends( $this->plugin_name, $this->version );
406 $html = $wp_media_extend->attachment_data( $new_sizes, $attachment_id );
407
408 wp_send_json_success( $html );
409 }
410
411 /**
412 * Undo a single media optimization
413 */
414 public function undo_single_optimization_ajax() {
415
416 // verify the nonce.
417 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
418 if( !wp_verify_nonce( $nonce, 'quickwebp_admin_attachment' ) ) {
419 wp_send_json_error( __( 'Refresh the page and try again.', 'quickwebp' ) );
420 }
421
422 // Sanitize data
423 $attachment_id = isset( $_POST['attachment_id'] ) ? absint( wp_unslash( $_POST['attachment_id'] ) ) : 0;
424
425 if ( ! $attachment_id ) {
426 wp_send_json_error( __( 'No attachment id.', 'quickwebp' ) );
427 }
428
429 $data = get_post_meta( $attachment_id, 'quickwebp_data', true );
430 if ( ! empty( $data ) ) {
431 $this->remove_related_files( $data );
432 delete_post_meta( $attachment_id, 'quickwebp_already_optimized' );
433 delete_post_meta( $attachment_id, 'quickwebp_data' );
434
435 $wp_media_extend = new Quickwebp_Wp_Media_Extends( $this->plugin_name, $this->version );
436 $html = $wp_media_extend->optimize_btn( $attachment_id );
437 wp_send_json_success( $html );
438 } else {
439 wp_send_json_error( __( 'Not optimized.', 'quickwebp' ) );
440 }
441 }
442
443 /**
444 * Trigger before delete attachment
445 */
446 public function before_delete_attachment( $post_id ) {
447 $data = get_post_meta( $post_id, 'quickwebp_data', true );
448 if ( ! empty( $data ) ) {
449 $this->remove_related_files( $data );
450 }
451 }
452
453 /**
454 * Get the image data for the preview in the settings page
455 */
456 public function get_image_data_for_preview_ajax() {
457
458 // verify the nonce.
459 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
460 if( !wp_verify_nonce( $nonce, 'image_optimize_nonce' ) ) {
461 wp_send_json_error( __( 'Refresh the page and try again.', 'quickwebp' ) );
462 }
463
464 // Get the file
465 $file = count($_FILES) > 0 ? array_shift($_FILES) : array();
466 if ( empty($file) ) {
467 wp_send_json_error( __( 'No image uploaded, try again.', 'quickwebp' ) );
468 }
469
470 $image_file = $this->file_is_image( $file );
471 if ( ! $image_file ) {
472 wp_send_json_error( __( 'No image uploaded, try again.', 'quickwebp' ) );
473 }
474
475 $preview_image_data = array(
476 'name' => $image_file['name'],
477 'size' => size_format( $image_file['size'], 2 ),
478 'dimensions' => $image_file['width'] . ' x ' . $image_file['height'],
479 );
480
481 $conversions = array( '1', '2' );
482 $qualities = array( 'low', 'medium', 'high', 'extra_high' );
483
484 foreach ( $conversions as $conversion ) {
485 foreach ( $qualities as $quality ) {
486
487 $size_after = 0;
488
489 if ( '2' == $conversion ) {
490 $new_path = $image_file['tmp_name'] . '-' . $quality . '.avif';
491 $quality_value = $this->get_the_quality_values( $quality, $conversion );
492 $avif_image = $this->create_avif_image( $image_file['tmp_name'], $new_path, $quality_value );
493
494 if ( $avif_image ) {
495 $size_after = filesize( $new_path );
496 }
497 } elseif ( '1' == $conversion ) {
498 $new_path = $image_file['tmp_name'] . '-' . $quality . '.webp';
499 $quality_value = $this->get_the_quality_values( $quality, $conversion );
500 $webp_image = $this->create_webp_image( $image_file['tmp_name'], $new_path, $quality_value );
501
502 if ( $webp_image ) {
503 $size_after = filesize( $new_path );
504 }
505 }
506
507 $deference = $image_file['size'] - $size_after;
508 $percent = $deference / $image_file['size'] * 100;
509
510 $preview_image_data[$conversion . '_' . $quality] = array(
511 'size' => size_format( $size_after, 2 ),
512 'save' => size_format( $deference, 2 ),
513 'save_percent' => round( $percent ) . '%',
514 'percent' => round( 100 - $percent ) . '%',
515 );
516 }
517 }
518
519 wp_send_json_success( $preview_image_data );
520 }
521
522 /**
523 * Get the unoptimized media ids
524 */
525 public function get_unoptimized_media_ids() {
526
527 $settings = $this->get_settings();
528 $mode_enabled = $settings['quickwebp_settings_conversion'];
529 $ignore_same_format = is_array( $settings['quickwebp_settings_conversion_ignore_webp'] ) ? $settings['quickwebp_settings_conversion_ignore_webp'] : array();
530
531 $statuses = array(
532 'inherit' => 'inherit',
533 'private' => 'private',
534 );
535 $custom_statuses = get_post_stati( array( 'public' => true ) );
536 unset( $custom_statuses['publish'] );
537 if ( $custom_statuses ) {
538 $statuses = array_merge( $statuses, $custom_statuses );
539 }
540
541 $allowed_mime_types = $this->allowed_mime_types;
542 if ( in_array( 'checked', $ignore_same_format ) ) {
543 if ( '1' == $mode_enabled ) {
544 $webp_index = array_search( 'image/webp', $allowed_mime_types );
545 unset( $allowed_mime_types[$webp_index] );
546 } elseif ( '2' == $mode_enabled ) {
547 $avif_index = array_search( 'image/avif', $allowed_mime_types );
548 unset( $allowed_mime_types[$avif_index] );
549 }
550 }
551
552 $meta_query_already_optimized = array(
553 'relation' => 'OR',
554 array(
555 'key' => 'quickwebp_already_optimized',
556 'compare' => 'NOT EXISTS'
557 ),
558 );
559
560 if ( 'webp' == $mode_enabled ) {
561 $meta_query_already_optimized[] = array(
562 'key' => 'quickwebp_already_optimized',
563 'compare' => '!=',
564 'value' => '1',
565 );
566 } elseif ( 'avif' == $mode_enabled ) {
567 $meta_query_already_optimized[] = array(
568 'key' => 'quickwebp_already_optimized',
569 'compare' => '!=',
570 'value' => '2',
571 );
572 }
573
574 $media_ids = get_posts( array(
575 'post_type' => 'attachment',
576 'post_mime_type' => $allowed_mime_types,
577 'post_status' => array_keys( $statuses ),
578 'posts_per_page' => -1,
579 'fields' => 'ids',
580 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
581 'meta_query' => array(
582 'relation' => 'AND',
583 array(
584 'key' => 'quickwebp_has_error',
585 'compare' => 'NOT EXISTS'
586 ),
587 $meta_query_already_optimized,
588 ),
589 ) );
590
591 return $media_ids;
592 }
593
594 /**
595 * Get the list of media files
596 */
597 public function get_media_files( $media_id ) {
598 $fullsize_path = get_attached_file( $media_id );
599
600 if ( ! $fullsize_path ) {
601 return array();
602 }
603
604 $media_data = wp_get_attachment_image_src( $media_id, 'full' );
605 $file_type = wp_check_filetype( $fullsize_path );
606
607 $all_sizes = [
608 'full' => [
609 'size' => 'full',
610 'path' => $fullsize_path,
611 'width' => $media_data[1],
612 'height' => $media_data[2],
613 'mime-type' => $file_type['type'],
614 'disabled' => false,
615 ],
616 ];
617
618 $sizes = wp_get_attachment_metadata( $media_id, true );
619 $sizes = ! empty( $sizes['sizes'] ) && is_array( $sizes['sizes'] ) ? $sizes['sizes'] : [];
620
621 $dir_path = trailingslashit( dirname( $fullsize_path ) );
622
623 foreach ( $sizes as $size => $size_data ) {
624 $all_sizes[ $size ] = [
625 'size' => $size,
626 'path' => $dir_path . $size_data['file'],
627 'width' => $size_data['width'],
628 'height' => $size_data['height'],
629 'mime-type' => $size_data['mime-type'],
630 'disabled' => false
631 ];
632 }
633
634 return $all_sizes;
635 }
636
637 /**
638 * Optimize a local file
639 */
640 public function optimize_local_file( $size ) {
641 global $quickwebp_surecart_client;
642
643 if ( !is_file($size['path']) ) {
644 return false;
645 }
646
647 $real_type = mime_content_type( $size['path']);
648 if ( !in_array( $real_type, $this->allowed_mime_types ) ) {
649 return false;
650 }
651
652 $settings = $this->get_settings();
653 $quality = $this->get_the_quality( $settings );
654 $is_pro = false;
655 if ( $quickwebp_surecart_client ) {
656 $is_pro = $quickwebp_surecart_client->license()->is_valid();
657 }
658 $mode_enabled = $settings['quickwebp_settings_conversion'];
659 $size_before = filesize( $size['path'] );
660 $new_path = $size['path'] . '.' . ( '2' === $mode_enabled && $is_pro ? 'avif' : 'webp' );
661
662 if ( '2' === $mode_enabled && $is_pro ) {
663 $avif_image = $this->create_avif_image( $size['path'], $new_path, $quality );
664 if ( $avif_image ) {
665 $size_after = filesize( $new_path );
666 }
667 } elseif ( '1' === $mode_enabled ) {
668 $webp_image = $this->create_webp_image( $size['path'], $new_path, $quality );
669 if ( $webp_image ) {
670 $size_after = filesize( $new_path );
671 }
672 }
673
674 $deference = $size_before - $size_after;
675 $percent = $deference / $size_before * 100;
676
677 return array(
678 'success' => 1,
679 'original_size' => $size_before,
680 'optimized_size' => $size_after,
681 'percent' => round( $percent, 2 ),
682 'path' => $new_path,
683 'format' => $mode_enabled,
684 );
685 }
686
687 /**
688 * Remove the related files of an optimized attachment
689 */
690 public function remove_related_files( $data ) {
691 foreach ( $data as $value ) {
692 $path = $value['path'] ?? '';
693
694 if ( ! empty( $path ) && file_exists( $path ) ) {
695 wp_delete_file( $path );
696 }
697 }
698 }
699
700 /**
701 * Get the optimization settings
702 */
703 public function get_settings() {
704 return array(
705 'quickwebp_settings_conversion' => get_option('quickwebp_settings_conversion', quickwebp_settings_default('quickwebp_settings_conversion') ),
706 'quickwebp_settings_conversion_quality' => get_option('quickwebp_settings_conversion_quality', quickwebp_settings_default('quickwebp_settings_conversion_quality') ),
707 'quickwebp_settings_conversion_ignore_webp' => get_option('quickwebp_settings_conversion_ignore_webp', quickwebp_settings_default('quickwebp_settings_conversion_ignore_webp') ),
708 'quickwebp_settings_conversion_save_original' => get_option('quickwebp_settings_conversion_save_original', quickwebp_settings_default('quickwebp_settings_conversion_save_original') ),
709 'quickwebp_settings_resize' => get_option('quickwebp_settings_resize', quickwebp_settings_default('quickwebp_settings_resize') ),
710 'quickwebp_settings_resize_value' => get_option('quickwebp_settings_resize_value', quickwebp_settings_default('quickwebp_settings_resize_value') ),
711 'quickwebp_settings_completion' => get_option('quickwebp_settings_completion', quickwebp_settings_default('quickwebp_settings_completion') ),
712 'quickwebp_settings_completion_options' => get_option('quickwebp_settings_completion_options', quickwebp_settings_default('quickwebp_settings_completion_options') ),
713 'quickwebp_settings_cleanup' => get_option('quickwebp_settings_cleanup', quickwebp_settings_default('quickwebp_settings_cleanup') ),
714 );
715 }
716
717 /**
718 * Get the optimization settings
719 */
720 private function get_ajax_settings() {
721 //phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
722 $raw_settings = isset( $_POST['settings'] ) ? wp_unslash( $_POST['settings'] ) : '';
723 $settings = is_string( $raw_settings ) ? json_decode( $raw_settings, true ) : array();
724
725 if ( ! is_array( $settings ) ) {
726 $settings = array();
727 }
728
729 return array(
730 'quickwebp_settings_conversion' => isset( $settings['quickwebp_settings_conversion'] ) ? sanitize_text_field( (string) $settings['quickwebp_settings_conversion'] ) : '0',
731 'quickwebp_settings_conversion_quality' => isset( $settings['quickwebp_settings_conversion_quality'] ) ? sanitize_text_field( $settings['quickwebp_settings_conversion_quality'] ) : 'high',
732 'quickwebp_settings_conversion_ignore_webp' => isset( $settings['quickwebp_settings_conversion_ignore_webp'] ) && is_array( $settings['quickwebp_settings_conversion_ignore_webp'] ) ? array_map( 'sanitize_text_field', $settings['quickwebp_settings_conversion_ignore_webp'] ) : array(),
733 'quickwebp_settings_resize' => isset( $settings['quickwebp_settings_resize'] ) ? sanitize_text_field( (string) $settings['quickwebp_settings_resize'] ) : '',
734 'quickwebp_settings_resize_value' => isset( $settings['quickwebp_settings_resize_value'] ) ? absint( $settings['quickwebp_settings_resize_value'] ) : 0,
735 'quickwebp_settings_completion' => isset( $settings['quickwebp_settings_completion'] ) ? sanitize_text_field( (string) $settings['quickwebp_settings_completion'] ) : '',
736 'quickwebp_settings_completion_options' => isset( $settings['quickwebp_settings_completion_options'] ) && is_array( $settings['quickwebp_settings_completion_options'] ) ? array_map( 'sanitize_text_field', $settings['quickwebp_settings_completion_options'] ) : array(),
737 'quickwebp_settings_cleanup' => isset( $settings['quickwebp_settings_cleanup'] ) ? sanitize_text_field( (string) $settings['quickwebp_settings_cleanup'] ) : '',
738 );
739 }
740
741 /**
742 * Check if the file is an image
743 */
744 private function file_is_image( $file, $settings = array() ) {
745
746 $file_name = isset($file['name']) ? $file['name'] : '';
747 $file_type = isset($file['type']) ? $file['type'] : '';
748 $file_tmp_name = isset($file['tmp_name']) ? $file['tmp_name'] : '';
749 $file_error = isset($file['error']) ? $file['error'] : '';
750 $file_size = isset($file['size']) ? $file['size'] : '';
751
752 if ( empty($file_tmp_name) ) {
753 return false;
754 }
755
756 $allowed_mime_types = apply_filters( 'quickwebp_mime_types_allowed', $this->allowed_mime_types );
757 $is_image = wp_getimagesize($file_tmp_name);
758 $mime_type = wp_get_image_mime($file_tmp_name);
759 if ( ! $is_image || ! in_array( $mime_type, $allowed_mime_types ) ) {
760 return false;
761 }
762
763 $name = $file_name;
764
765 if ( isset($settings['quickwebp_settings_cleanup']) && $settings['quickwebp_settings_cleanup'] == '1' ) {
766 $name = quickwebp_sanitize_name($file_name);
767 }
768
769 return array(
770 'original_name' => $file_name,
771 'name' => $name,
772 'type' => $file_type,
773 'tmp_name' => $file_tmp_name,
774 'error' => $file_error,
775 'size' => $file_size,
776 'width' => $is_image[0],
777 'height' => $is_image[1],
778 );
779 }
780
781 /**
782 * Return ajax data
783 */
784 private function return_ajax_data( $data ) {
785 $return = array(
786 'original_name' => $data['original_name'],
787 'size' => $data['size'],
788 'type' => $this->type_from_mime_type( $data['type'] ),
789 'mime_type' => $data['type'],
790 'image' => 'data:'.$data['new_type'].';base64,' . base64_encode( file_get_contents( $data['tmp_name'] ) ),
791 'name' => $data['name'],
792 'new_size' => $data['new_size'],
793 'new_type' => $this->type_from_mime_type( $data['new_type'] ),
794 'new_mime_type' => $data['new_type']
795 );
796
797 wp_send_json_success( $return );
798 }
799
800 /**
801 * Get the type from the mime type
802 */
803 private function type_from_mime_type( $mime_type ) {
804 $array = array(
805 'image/jpeg' => 'JPEG',
806 'image/png' => 'PNG',
807 'image/webp' => 'WebP',
808 'image/avif' => 'AVIF',
809 );
810
811 return $array[$mime_type] ?? '';
812 }
813
814 /**
815 * Get the quality
816 */
817 private function get_the_quality( $settings ) {
818 $mode_enabled = $settings['quickwebp_settings_conversion'];
819 $quality = $settings['quickwebp_settings_conversion_quality'];
820
821 return $this->get_the_quality_values( $quality, $mode_enabled );
822 }
823
824 /**
825 * Get the quality values for avif and webp
826 */
827 private function get_the_quality_values( $quality, $mode_enabled ) {
828 $result = 50;
829
830 switch ( $quality ) {
831 case 'low':
832 $result = 50;
833 if ( '2' === $mode_enabled ) {
834 $result = 30;
835 }
836 break;
837 case 'medium':
838 $result = 60;
839 if ( '2' === $mode_enabled ) {
840 $result = 40;
841 }
842 break;
843 case 'high':
844 $result = 75;
845 if ( '2' === $mode_enabled ) {
846 $result = 50;
847 }
848 break;
849 case 'extra_high':
850 $result = 90;
851 if ( '2' === $mode_enabled ) {
852 $result = 70;
853 }
854 break;
855 }
856
857 return $result;
858 }
859
860 /**
861 * Create the avif image
862 */
863 private function create_avif_image( $file_path, $output_path, $quality ) {
864 $image = false;
865 $mime_type = wp_get_image_mime( $file_path );
866
867 if ( 'image/avif' == $mime_type ) {
868 $image = imagecreatefromavif( $file_path );
869 } elseif ( 'image/webp' == $mime_type ) {
870 $image = imagecreatefromwebp( $file_path );
871 } elseif ( 'image/jpeg' == $mime_type ) {
872 $image = imagecreatefromjpeg( $file_path );
873 } elseif ( 'image/png' == $mime_type ) {
874 $image = imagecreatefrompng( $file_path );
875 }
876
877 if ( ! $image ) {
878 return false;
879 }
880
881 // Handle EXIF orientation for proper image rotation
882 $exif_data = @exif_read_data( $file_path );
883 if ( ! empty( $exif_data['Orientation'] ) ) {
884 $orientation = (int) $exif_data['Orientation'];
885 //phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
886 $orientation = apply_filters( 'wp_image_maybe_exif_rotate', $orientation, $file_path );
887 if ( $orientation && 1 !== $orientation ) {
888 switch ( $orientation ) {
889 case 2:
890 // Flip horizontally.
891 imageflip( $image, IMG_FLIP_HORIZONTAL );
892 break;
893 case 3:
894 // Rotate 180 degrees.
895 $image = imagerotate( $image, 180, 0 );
896 break;
897 case 4:
898 // Flip vertically.
899 imageflip( $image, IMG_FLIP_VERTICAL );
900 break;
901 case 5:
902 // Rotate 90 degrees counter-clockwise and flip vertically.
903 $image = imagerotate( $image, -90, 0 );
904 imageflip( $image, IMG_FLIP_VERTICAL );
905 break;
906 case 6:
907 // Rotate 90 degrees clockwise (270 counter-clockwise).
908 $image = imagerotate( $image, -90, 0 );
909 break;
910 case 7:
911 // Rotate 90 degrees counter-clockwise and flip horizontally.
912 $image = imagerotate( $image, 90, 0 );
913 imageflip( $image, IMG_FLIP_HORIZONTAL );
914 break;
915 case 8:
916 // Rotate 90 degrees counter-clockwise.
917 $image = imagerotate( $image, 90, 0 );
918 break;
919 }
920 }
921 }
922
923 if ( ! imageistruecolor( $image ) ) {
924 $truecolor = imagecreatetruecolor( imagesx( $image ), imagesy( $image ) );
925
926 if ( $mime_type === 'image/png' ) {
927 imagealphablending( $truecolor, false );
928 imagesavealpha( $truecolor, true );
929 $transparent = imagecolorallocatealpha( $truecolor, 0, 0, 0, 127 );
930 imagefilledrectangle( $truecolor, 0, 0, imagesx( $image ), imagesy( $image ), $transparent );
931 }
932
933 imagecopy( $truecolor, $image, 0, 0, 0, 0, imagesx( $image ), imagesy( $image ) );
934 $image = $truecolor;
935 }
936
937 $avif_image = imageavif( $image, $output_path, $quality );
938 imagedestroy( $image );
939 if ( ! $avif_image ) {
940 return false;
941 }
942
943 return $avif_image;
944 }
945
946 /**
947 * Create the webp image
948 */
949 private function create_webp_image( $file_path, $output_path, $quality ) {
950 $image = false;
951 $mime_type = wp_get_image_mime( $file_path );
952
953 if ( 'image/avif' == $mime_type ) {
954 $image = imagecreatefromavif( $file_path );
955 } elseif ( 'image/webp' == $mime_type ) {
956 $image = imagecreatefromwebp( $file_path );
957 } elseif ( 'image/jpeg' == $mime_type ) {
958 $image = imagecreatefromjpeg( $file_path );
959 } elseif ( 'image/png' == $mime_type ) {
960 $image = imagecreatefrompng( $file_path );
961 }
962
963 if ( ! $image ) {
964 return false;
965 }
966
967 // Handle EXIF orientation for proper image rotation
968 $exif_data = @exif_read_data( $file_path );
969 if ( ! empty( $exif_data['Orientation'] ) ) {
970 $orientation = (int) $exif_data['Orientation'];
971 //phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
972 $orientation = apply_filters( 'wp_image_maybe_exif_rotate', $orientation, $file_path );
973 if ( $orientation && 1 !== $orientation ) {
974 switch ( $orientation ) {
975 case 2:
976 // Flip horizontally.
977 imageflip( $image, IMG_FLIP_HORIZONTAL );
978 break;
979 case 3:
980 // Rotate 180 degrees.
981 $image = imagerotate( $image, 180, 0 );
982 break;
983 case 4:
984 // Flip vertically.
985 imageflip( $image, IMG_FLIP_VERTICAL );
986 break;
987 case 5:
988 // Rotate 90 degrees counter-clockwise and flip vertically.
989 $image = imagerotate( $image, -90, 0 );
990 imageflip( $image, IMG_FLIP_VERTICAL );
991 break;
992 case 6:
993 // Rotate 90 degrees clockwise (270 counter-clockwise).
994 $image = imagerotate( $image, -90, 0 );
995 break;
996 case 7:
997 // Rotate 90 degrees counter-clockwise and flip horizontally.
998 $image = imagerotate( $image, 90, 0 );
999 imageflip( $image, IMG_FLIP_HORIZONTAL );
1000 break;
1001 case 8:
1002 // Rotate 90 degrees counter-clockwise.
1003 $image = imagerotate( $image, 90, 0 );
1004 break;
1005 }
1006 }
1007 }
1008
1009 if ( ! imageistruecolor( $image ) ) {
1010 $truecolor = imagecreatetruecolor( imagesx( $image ), imagesy( $image ) );
1011
1012 if ( $mime_type === 'image/png' ) {
1013 imagealphablending( $truecolor, false );
1014 imagesavealpha( $truecolor, true );
1015 $transparent = imagecolorallocatealpha( $truecolor, 0, 0, 0, 127 );
1016 imagefilledrectangle( $truecolor, 0, 0, imagesx( $image ), imagesy( $image ), $transparent );
1017 }
1018
1019 imagecopy( $truecolor, $image, 0, 0, 0, 0, imagesx( $image ), imagesy( $image ) );
1020 $image = $truecolor;
1021 }
1022
1023 $webp_image = imagewebp( $image, $output_path, $quality );
1024 imagedestroy( $image );
1025 if ( ! $webp_image ) {
1026 return false;
1027 }
1028
1029 return $webp_image;
1030 }
1031 }
1032