PluginProbe
Plus WebP or AVIF / 5.21
Plus WebP or AVIF v5.21
5.21 5.20 5.12 5.11 trunk 1.00 1.01 1.02 1.03 1.04 1.05 1.06 1.07 1.08 1.09 1.10 1.11 1.12 1.13 2.00 2.01 2.02 2.03 2.04 2.05 All 47 releases
plus-webp / lib / class-pluswebp.php

class-pluswebp.php in Plus WebP or AVIF 5.21, at lib/class-pluswebp.php

607 lines 19.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plus WebP or AVIF
4 *
5 * @package Plus WebP or AVIF
6 * @subpackage PlusWebp Main function
7 /* Copyright (c) 2019- Katsushi Kawamori (email : dodesyoswift312@gmail.com)
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; version 2 of the License.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 if ( ! defined( 'ABSPATH' ) ) {
23 exit;
24 }
25
26 $pluswebp = new PlusWebp();
27
28 /** ==================================================
29 * Class Main function
30 *
31 * @since 1.00
32 */
33 class PlusWebp {
34
35 /** ==================================================
36 * Dir
37 *
38 * @var $upload_dir DIR.
39 */
40 private $upload_dir;
41
42 /** ==================================================
43 * URL
44 *
45 * @var $upload_url URL.
46 */
47 private $upload_url;
48
49 /** ==================================================
50 * Construct
51 *
52 * @since 1.00
53 */
54 public function __construct() {
55
56 $wp_uploads = wp_upload_dir();
57
58 $upload_dir = wp_normalize_path( $wp_uploads['basedir'] );
59 $upload_url = $wp_uploads['baseurl'];
60 if ( is_ssl() ) {
61 $upload_url = str_replace( 'http:', 'https:', $upload_url );
62 }
63 $this->upload_dir = untrailingslashit( $upload_dir );
64 $this->upload_url = untrailingslashit( $upload_url );
65
66 /* Generate metadata */
67 add_filter( 'wp_generate_attachment_metadata', array( $this, 'generate_webp' ), 10, 2 );
68 /* Original hook */
69 add_filter( 'pluswebp_get_allimages', array( $this, 'get_allimages' ), 10, 2 );
70 add_filter( 'pluswebp_mail_messages', array( $this, 'mail_messages' ), 10, 2 );
71 add_action( 'pluswebp_mail_generate_message', array( $this, 'mail_generate_message' ), 10, 2 );
72
73 /* Raise memory */
74 add_filter( 'pluswebp_memory_limit', array( $this, 'raise_memory_limit' ) );
75 /* webp generation control */
76 add_filter( 'wp_upload_image_mime_transforms', array( $this, 'control_mime_type' ), 10, 2 );
77 }
78
79 /** ==================================================
80 * Webp of AVIF generate
81 *
82 * @param array $metadata metadata.
83 * @param int $attachment_id ID.
84 * @return array $metadata metadata.
85 * @since 1.00
86 */
87 public function generate_webp( $metadata, $attachment_id ) {
88
89 if ( ! is_array( $metadata ) ) {
90 return $metadata;
91 }
92
93 $pluswebp_settings = get_option( 'pluswebp' );
94
95 switch ( $pluswebp_settings['output_mime'] ) {
96 case 'image/webp':
97 $ext = 'webp';
98 break;
99 case 'image/avif':
100 $ext = 'avif';
101 break;
102 default:
103 $ext = 'webp';
104 }
105
106 $mime_type = get_post_mime_type( $attachment_id );
107 if ( in_array( $mime_type, $pluswebp_settings['types'] ) ) {
108 $metadata_webp = $metadata;
109 $file_webp = $this->change_ext( $metadata['file'], $ext, $pluswebp_settings['addext'] );
110 $metadata_webp['file'] = $file_webp;
111 if ( '.' === dirname( $file_webp ) ) {
112 $dir_name_url = '/';
113 $dir_name_path = wp_normalize_path( '/' );
114 } else {
115 $dir_name_url = '/' . dirname( $file_webp ) . '/';
116 $dir_name_path = wp_normalize_path( $dir_name_url );
117 }
118 $url = $this->upload_url . $dir_name_url;
119 $path = $this->upload_dir . $dir_name_path;
120
121 foreach ( (array) $metadata['sizes'] as $key => $value ) {
122 $file_thumb = $value['file'];
123 $file_thumb_webp = $this->change_ext( $file_thumb, $ext, $pluswebp_settings['addext'] );
124 $ret = $this->create_webp( $path . $file_thumb, $mime_type, $path . $file_thumb_webp, $pluswebp_settings['quality'], $pluswebp_settings['output_mime'] );
125 /* $ret -> ignore : Some metadata also has duplicate filenames */
126 $metadata_webp['sizes'][ $key ]['file'] = $file_thumb_webp;
127 $metadata_webp['sizes'][ $key ]['mime-type'] = $pluswebp_settings['output_mime'];
128 $webp_size = filesize( $path . wp_basename( $file_thumb_webp ) );
129 $metadata_webp['sizes'][ $key ]['filesize'] = $webp_size;
130 if ( $pluswebp_settings['replace'] ) {
131 wp_delete_file( $path . $file_thumb );
132 $this->change_db( $url . $file_thumb, $url . $file_thumb_webp );
133 }
134 }
135
136 $org_img_file = null;
137 if ( array_key_exists( 'original_image', $metadata ) && ! empty( $metadata['original_image'] ) ) {
138 $org_img_file = wp_normalize_path( wp_get_original_image_path( $attachment_id, false ) );
139 $org_webp_file = $this->change_ext( $org_img_file, $ext, $pluswebp_settings['addext'] );
140 $ret = $this->create_webp( $org_img_file, $mime_type, $org_webp_file, $pluswebp_settings['quality'], $pluswebp_settings['output_mime'] );
141 if ( $ret ) {
142 $metadata_webp['original_image'] = wp_basename( $org_webp_file );
143 }
144 }
145
146 $ret = $this->create_webp( $this->upload_dir . '/' . $metadata['file'], $mime_type, $path . wp_basename( $file_webp ), $pluswebp_settings['quality'], $pluswebp_settings['output_mime'] );
147 $webp_size = filesize( $path . wp_basename( $file_webp ) );
148 $metadata_webp['filesize'] = $webp_size;
149 if ( $ret ) {
150 if ( $pluswebp_settings['replace'] ) {
151 $up_post = array(
152 'ID' => $attachment_id,
153 'guid' => $this->upload_url . '/' . $file_webp,
154 'post_mime_type' => $pluswebp_settings['output_mime'],
155 );
156 wp_update_post( $up_post );
157 update_post_meta( $attachment_id, '_wp_attached_file', $file_webp );
158 /* for bulk generate */
159 update_post_meta( $attachment_id, '_wp_attachment_metadata', $metadata_webp );
160 /* delete org file */
161 wp_delete_file( $this->upload_dir . '/' . $metadata['file'] );
162 if ( $org_img_file ) {
163 wp_delete_file( $org_img_file );
164 }
165 /* Replace */
166 $this->change_db( $this->upload_url . '/' . $metadata['file'], $this->upload_url . '/' . $file_webp );
167 /* for hook */
168 $metadata = $metadata_webp;
169 /* for mail */
170 $attach_id = $attachment_id;
171 } else {
172 $post = get_post( $attachment_id );
173 $title = $post->post_title;
174 $attachment = array(
175 'guid' => $this->upload_url . '/' . $file_webp,
176 'post_mime_type' => $pluswebp_settings['output_mime'],
177 'post_title' => $title,
178 'post_content' => '',
179 'post_status' => 'inherit',
180 );
181 $file = $this->upload_dir . '/' . $file_webp;
182 $attach_id = wp_insert_attachment( $attachment, $file );
183
184 /* for XAMPP [ get_attached_file( $attach_id ): Unable to get correct value ] */
185 $metapath_name = str_replace( $this->upload_dir . '/', '', $file );
186 update_post_meta( $attach_id, '_wp_attached_file', $metapath_name );
187
188 wp_update_attachment_metadata( $attach_id, $metadata_webp );
189
190 $author = get_userdata( $post->post_author );
191 $userid = $author->ID;
192 $postdate = get_the_date( 'Y-m-d H:i:s', $attachment_id );
193 $postdategmt = get_gmt_from_date( $postdate );
194 $up_post = array(
195 'ID' => $attach_id,
196 'post_author' => $userid,
197 'post_date' => $postdate,
198 'post_date_gmt' => $postdategmt,
199 'post_modified' => $postdate,
200 'post_modified_gmt' => $postdategmt,
201 );
202 wp_update_post( $up_post );
203 }
204
205 update_option( 'pluswebp_generate', $attach_id );
206
207 /* for Media Library folders term by Organize Media Folder */
208 do_action( 'omf_folders_term_update', $metadata_webp, $attach_id );
209
210 /* for Term filter update by Organize Media Folder */
211 do_action( 'omf_term_filter_update' );
212
213 }
214 }
215
216 return $metadata;
217 }
218
219 /** ==================================================
220 * Thumbnail urls
221 *
222 * @param int $attach_id attach_id.
223 * @param array $metadata metadata.
224 * @param string $upload_url upload_url.
225 * @return array $image_thumbnail(string), $imagethumburls(array)
226 * @since 2.00
227 */
228 private function thumbnail_urls( $attach_id, $metadata, $upload_url ) {
229
230 $image_attr_thumbnail = wp_get_attachment_image_src( $attach_id, 'thumbnail', true );
231 $image_thumbnail = $image_attr_thumbnail[0];
232
233 $imagethumburls = array();
234 if ( ! empty( $metadata ) && array_key_exists( 'sizes', $metadata ) ) {
235 $thumbnails = $metadata['sizes'];
236 $path_file = get_post_meta( $attach_id, '_wp_attached_file', true );
237 $filename = wp_basename( $path_file );
238 $media_path = str_replace( $filename, '', $path_file );
239 $media_url = $upload_url . '/' . $media_path;
240 foreach ( $thumbnails as $key => $key2 ) {
241 $imagethumburls[ $key ] = $media_url . $key2['file'];
242 }
243 }
244
245 return array( $image_thumbnail, $imagethumburls );
246 }
247
248 /** ==================================================
249 * Output datas
250 *
251 * @param int $attach_id attach_id.
252 * @param array $metadata metadata.
253 * @return array (string) $attachment_link, $attachment_url, $filename, $original_image_url, $original_filename, $mime_type(string), $stamptime, $file_size
254 * @since 2.00
255 */
256 private function output_datas( $attach_id, $metadata ) {
257
258 $attachment_link = get_attachment_link( $attach_id );
259 $attachment_url = wp_get_attachment_url( $attach_id );
260 $filename = wp_basename( $attachment_url );
261 if ( ! empty( $metadata ) && array_key_exists( 'original_image', $metadata ) && ! empty( $metadata['original_image'] ) ) {
262 $original_image_url = wp_get_original_image_url( $attach_id );
263 $original_filename = wp_basename( $original_image_url );
264 } else {
265 $original_image_url = null;
266 $original_filename = null;
267 }
268 $mime_type = get_post_mime_type( $attach_id );
269
270 $stamptime = get_the_time( 'Y-n-j ', $attach_id ) . get_the_time( 'G:i:s', $attach_id );
271 if ( ! empty( $metadata ) && array_key_exists( 'filesize', $metadata ) && ! empty( $metadata['filesize'] ) ) {
272 $file_size = $metadata['filesize'];
273 } else {
274 $file_size = @filesize( get_attached_file( $attach_id ) );
275 }
276 if ( ! $file_size ) {
277 $file_size = __( 'Could not retrieve.', 'plus-webp' );
278 } else {
279 $file_size = size_format( $file_size );
280 }
281
282 return array( $attachment_link, $attachment_url, $filename, $original_image_url, $original_filename, $mime_type, $stamptime, $file_size );
283 }
284
285 /** ==================================================
286 * Get All Images
287 *
288 * @param array $types mime types.
289 * @param string $output_mime output mime type.
290 * @since 2.00
291 */
292 public function get_allimages( $types, $output_mime ) {
293
294 if ( empty( $types ) ) {
295 return array( array(), array() );
296 }
297
298 global $wpdb;
299
300 /* Retrieve a list of converted titles that match $output_mime */
301 $webp_titles = array();
302 if ( ! empty( $output_mime ) ) {
303 $webp_titles_raw = $wpdb->get_col(
304 $wpdb->prepare(
305 "SELECT DISTINCT post_title
306 FROM {$wpdb->posts}
307 WHERE post_type = 'attachment'
308 AND post_status = 'inherit'
309 AND post_mime_type = %s",
310 $output_mime
311 )
312 );
313
314 if ( ! empty( $webp_titles_raw ) ) {
315 /* Reverse the key and value to speed up searches (O(1) access) */
316 $webp_titles = array_flip( $webp_titles_raw );
317 }
318 }
319
320 /* Creating SQL placeholders for $types */
321 $types_placeholders = implode( ',', array_fill( 0, count( $types ), '%s' ) );
322
323 $post_ids = array();
324 $no_file_ids = array();
325
326 $chunk_size = 2000; /* Retrieve in batches of 2,000 */
327 $offset = 0;
328
329 do {
330 /* SQL Parameter Construction (array elements of $types + $chunk_size + $offset) */
331 $params = array_merge( $types, array( $chunk_size, $offset ) );
332
333 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared
334 $chunk_posts = $wpdb->get_results(
335 $wpdb->prepare(
336 "SELECT ID, post_title
337 FROM {$wpdb->posts}
338 WHERE post_type = 'attachment'
339 AND post_status = 'inherit'
340 AND post_mime_type IN ($types_placeholders)
341 ORDER BY post_date DESC
342 LIMIT %d OFFSET %d",
343 $params
344 )
345 );
346 // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared
347
348 if ( is_array( $chunk_posts ) ) {
349 $fetched_count = count( $chunk_posts );
350 } else {
351 break;
352 }
353 if ( 0 === $fetched_count ) {
354 break;
355 }
356
357 foreach ( $chunk_posts as $post ) {
358 if ( ! isset( $webp_titles[ $post->post_title ] ) ) {
359 $file_path = get_attached_file( $post->ID );
360
361 if ( $file_path && file_exists( $file_path ) ) {
362 $post_ids[] = (int) $post->ID;
363 } else {
364 $no_file_ids[] = (int) $post->ID;
365 }
366
367 /* Delete the meta-cache generated when retrieving the attachment path on a per-file basis */
368 clean_post_cache( $post->ID );
369 }
370 }
371
372 $offset += $chunk_size;
373
374 /* Reset the object cache to keep memory usage consistent */
375 wp_cache_flush();
376
377 } while ( $fetched_count === $chunk_size );
378
379 unset( $webp_titles, $chunk_posts );
380
381 return array( $post_ids, $no_file_ids );
382 }
383
384 /** ==================================================
385 * Mail messages hook
386 *
387 * @param array $messages messages.
388 * @param int $webp_id webp ID or avif ID.
389 *
390 * @since 4.00
391 */
392 public function mail_messages( $messages, $webp_id ) {
393
394 $metadata = wp_get_attachment_metadata( $webp_id );
395
396 $message = null;
397 if ( $metadata ) {
398 /* Thumbnail urls */
399 list( $image_thumbnail, $imagethumburls ) = $this->thumbnail_urls( $webp_id, $metadata, $this->upload_url );
400 /* Output datas*/
401 list( $attachment_link, $attachment_url, $filename, $original_image_url, $original_filename, $mime_type, $stamptime, $file_size ) = $this->output_datas( $webp_id, $metadata );
402
403 $message = 'ID: ' . $webp_id . "\n";
404 $message .= __( 'Title', 'plus-webp' ) . ': ' . get_the_title( $webp_id ) . "\n";
405 $message .= __( 'Permalink:', 'plus-webp' ) . ' ' . $attachment_link . "\n";
406 $message .= 'URL: ' . $attachment_url . "\n";
407 $message .= __( 'File name:', 'plus-webp' ) . ' ' . $filename . "\n";
408 if ( ! empty( $original_image_url ) ) {
409 $message .= __( 'Original URL:', 'plus-webp' ) . ' ' . $original_image_url . "\n";
410 $message .= __( 'Original File name:', 'plus-webp' ) . ' ' . $original_filename . "\n";
411 }
412 $message .= __( 'Date/Time', 'plus-webp' ) . ': ' . $stamptime . "\n";
413 $message .= __( 'File size:', 'plus-webp' ) . ' ' . $file_size . "\n";
414 if ( ! empty( $imagethumburls ) ) {
415 foreach ( $imagethumburls as $thumbsize => $imagethumburl ) {
416 $message .= $thumbsize . ': ' . $imagethumburl . "\n";
417 }
418 }
419 $message .= "\n";
420 $messages[] = $message;
421 }
422
423 return array( $message, $messages );
424 }
425
426 /** ==================================================
427 * Mail sent for Generate Messages
428 *
429 * @param array $messages mail messages.
430 * @param int $count convert count.
431 * @since 4.02
432 */
433 public function mail_generate_message( $messages, $count ) {
434
435 if ( function_exists( 'wp_date' ) ) {
436 $now_date_time = wp_date( 'Y-m-d H:i:s' );
437 } else {
438 $now_date_time = date_i18n( 'Y-m-d H:i:s' );
439 }
440 /* translators: Date and Time */
441 $message_head = sprintf( __( 'Plus WebP or AVIF : %s', 'plus-webp' ), $now_date_time ) . "\r\n\r\n";
442 /* translators: Generated count */
443 $message_head .= sprintf( __( 'Webp or AVIF generated %d.', 'plus-webp' ), $count ) . "\r\n\r\n";
444
445 $to = get_option( 'admin_email' );
446 /* translators: blogname for subject */
447 $subject = sprintf( __( '[%s] WebP or AVIF generate', 'plus-webp' ), get_option( 'blogname' ) );
448 wp_mail( $to, $subject, $message_head . implode( $messages ) );
449 }
450
451 /** ==================================================
452 * WebP or AVIF create
453 *
454 * @param string $filename input filename for pictures.
455 * @param string $mime_type mimetype.
456 * @param string $filename_webp output filename for webp or avif.
457 * @param int $quality quality of webp.
458 * @param string $output_mime output mime type.
459 * @return bool $ret create bool.
460 * @since 1.00
461 */
462 private function create_webp( $filename, $mime_type, $filename_webp, $quality, $output_mime ) {
463
464 if ( ! file_exists( $filename ) ) {
465 return false;
466 }
467 if ( file_exists( $filename_webp ) ) {
468 return false;
469 }
470
471 @set_time_limit( 60 );
472 wp_raise_memory_limit( 'pluswebp' );
473
474 $ret = false;
475 switch ( $mime_type ) {
476 case 'image/jpeg':
477 $src = imagecreatefromjpeg( $filename );
478 $img = imagecreatetruecolor( imagesx( $src ), imagesy( $src ) );
479 $bgcolor = imagecolorallocate( $img, 255, 255, 255 );
480 imagefill( $img, 0, 0, $bgcolor );
481 imagealphablending( $img, true );
482 break;
483 case 'image/png':
484 $src = imagecreatefrompng( $filename );
485 $img = imagecreatetruecolor( imagesx( $src ), imagesy( $src ) );
486 imagealphablending( $img, false );
487 imagesavealpha( $img, true );
488 $bgcolor = imagecolorallocatealpha( $img, 0, 0, 0, 127 );
489 imagefill( $img, 0, 0, $bgcolor );
490 imagecolortransparent( $img, $bgcolor );
491 break;
492 case 'image/bmp':
493 $src = imagecreatefrombmp( $filename );
494 $img = imagecreatetruecolor( imagesx( $src ), imagesy( $src ) );
495 break;
496 case 'image/gif':
497 $src = imagecreatefromgif( $filename );
498 $img = imagecreatetruecolor( imagesx( $src ), imagesy( $src ) );
499 $bgcolor = imagecolorallocatealpha( $img, 0, 0, 0, 127 );
500 imagefill( $img, 0, 0, $bgcolor );
501 imagecolortransparent( $img, $bgcolor );
502 break;
503 }
504
505 imagecopy( $img, $src, 0, 0, 0, 0, imagesx( $src ), imagesy( $src ) );
506 unset( $src );
507
508 switch ( $output_mime ) {
509 case 'image/webp':
510 $ret = imagewebp( $img, $filename_webp, $quality );
511 break;
512 case 'image/avif':
513 $ret = imageavif( $img, $filename_webp, $quality );
514 break;
515 default:
516 $ret = imagewebp( $img, $filename_webp, $quality );
517 }
518
519 unset( $img );
520
521 return $ret;
522 }
523
524 /** ==================================================
525 * Change ext
526 *
527 * @param string $before_file_name before_file_name.
528 * @param string $ext ext.
529 * @param bool $addext addext.
530 * @return array $after_file_name after_file_name.
531 * @since 1.00
532 */
533 private function change_ext( $before_file_name, $ext, $addext ) {
534
535 if ( $addext ) {
536 $after_file_name = $before_file_name . '.' . $ext;
537 } else {
538 $exts = explode( '.', $before_file_name );
539 $before_ext = '.' . end( $exts );
540 $after_ext = '.' . $ext;
541 $after_file_name = str_replace( $before_ext, $after_ext, $before_file_name );
542 }
543
544 return $after_file_name;
545 }
546
547 /** ==================================================
548 * Change DB
549 *
550 * @param string $before_url before_url.
551 * @param string $after_url after_url.
552 * @since 1.00
553 */
554 private function change_db( $before_url, $after_url ) {
555
556 global $wpdb;
557
558 /* Replace */
559 $wpdb->query(
560 $wpdb->prepare(
561 "
562 UPDATE {$wpdb->prefix}posts
563 SET post_content = replace( post_content, %s, %s )
564 ",
565 $before_url,
566 $after_url
567 )
568 );
569
570 /* Advanced change database */
571 $result = apply_filters( 'plus_webp_advanced_change_db', $before_url, $after_url );
572 if ( is_array( $result ) && count( $result ) >= 2 ) {
573 list( $before_url, $after_url ) = $result;
574 } else if ( is_string( $result ) ) {
575 $before_url = $result;
576 }
577 }
578
579 /** ==================================================
580 * Filter Raise Memory limit
581 *
582 * @since 3.00
583 *
584 * @param string $filtered_limit Memory limit.
585 */
586 public function raise_memory_limit( $filtered_limit ) {
587
588 return '256M';
589 }
590
591 /** ==================================================
592 * Filter the output mime types for a given input mime type and image size.
593 *
594 * @since 3.00
595 *
596 * @param array $image_mime_transforms A map with the valid mime transforms where the key is the source file mime type
597 * and the value is one or more mime file types to generate.
598 * @param int $attachment_id The ID of the attachment where the hook was dispatched.
599 */
600 public function control_mime_type( $image_mime_transforms, $attachment_id ) {
601
602 $image_mime_transforms = array();
603
604 return $image_mime_transforms;
605 }
606 }
607