PluginProbe
ezCache / 2.5.3
ezCache v2.5.3
2.6.5 2.6.4 2.6.2 2.6.3 2.6.1 2.6.0 2.5.6 2.5.5 2.5.4 2.5.3 2.5.2 2.5.1 2.5 2.2.1 2.2.2 trunk 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.3 1.3.1 1.3.10 1.3.11 All 49 releases
ezcache / includes / FileOptimizer / WebpConverter.php

WebpConverter.php in ezCache 2.5.3, at includes/FileOptimizer/WebpConverter.php

182 lines 6.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Upress\EzCache\FileOptimizer;
4
5 use Upress\EzCache\BackgroundProcesses\ConvertWebpProcess;
6 use Upress\EzCache\Utilities\Logger;
7 use wpdb;
8
9 class WebpConverter extends BaseFileOptimizer {
10 /** @var string $cache_dir */
11 protected $cache_dir;
12 /** @var string $cache_file */
13 protected $cache_file;
14 /** @var ConvertWebpProcess $webp_processor */
15 protected $webp_processor;
16 /** @var wpdb $wpdb */
17 protected $wpdb;
18
19 function __construct( $cache_dir, $cache_file, $webp_processor, $wpdb ) {
20 $this->cache_dir = $cache_dir;
21 $this->cache_file = $cache_file;
22 $this->webp_processor = $webp_processor;
23 $this->wpdb = $wpdb;
24 }
25
26 /**
27 * Minifies CSS files
28 *
29 * @param string $html HTML content.
30 *
31 * @return string
32 */
33 public function optimize( $html ) {
34 /*
35 * we need to make sure to handle multiple formats
36 * <img src="elva-fairy-800w.jpg" alt="Elva dressed as a fairy">
37 * <img src="elva-fairy-320w.jpg" data-src="elva-fairy-800w.jpg" alt="Elva dressed as a fairy">
38 * <img srcset="elva-fairy-320w.jpg 320w, elva-fairy-480w.jpg 480w, elva-fairy-800w.jpg 800w" sizes="(max-width: 320px) 280px, (max-width: 480px) 440px, 800px" src="elva-fairy-800w.jpg" alt="Elva dressed as a fairy">
39 * <picture><source media="(max-width: 799px)" srcset="elva-480w-close-portrait.jpg"><img src="elva-480w-close-portrait.jpg" alt="Elva dressed as a fairy"></picture>
40 * <div style="background: url(elva-480w-close-portrait.jpg);">
41 * <div style="background-image: url(elva-480w-close-portrait.jpg);">
42 * <style>.foo { background-image: url(elva-480w-close-portrait.jpg); }</style>
43 */
44 $img_tags = $this->find( '<(?:img|source)\s+([^>]+[\s"\'])?(?:data-src|src|srcset)\s*=\s*[\'"]\s*?([^\'"]+(?:\?[^\'"]*)?)\s*?[\'"]([^>]+)?\/?>', $html );
45 $bg_images = $this->find( 'background(?:-image)\s*:.*url\s*\(\s*([\'"]?)([^\'"]+)\1\s*\)', $html );
46
47 if ( ! $img_tags ) {
48 $img_tags = [];
49 }
50 if ( ! $bg_images ) {
51 $bg_images = [];
52 }
53
54 $images = [];
55
56 foreach ( $img_tags as $img ) {
57 // handle srcset, normally $images[2] will catch only one of the images in the set
58 if ( isset( $img[0] ) && stripos( $img[0], 'srcset=' ) !== false ) {
59 $srcset = preg_replace( '/^.+srcset=[\'"](.+?)[\'"].+$/iu', '$1', $img[0] );
60 $srcset = explode( ',', $srcset );
61
62 foreach ( $srcset as $item ) {
63 $images[] = trim( preg_replace( '/^(.+?)\s+.+$/ui', '$1', $item ), " \t\n\r\0\x0B\"'" );
64 }
65 }
66
67 // same with data-src
68 if ( isset( $img[0] ) && stripos( $img[0], 'data-src=' ) !== false ) {
69 $src = preg_replace( '/^.+data-src=[\'"](.+?)[\'"].+$/iu', '$1', $img[0] );
70 $images[] = trim( preg_replace( '/^(.+?)\s+.+$/ui', '$1', $src ), " \t\n\r\0\x0B\"'" );
71 }
72
73 // for other images just grab the url part
74 $images[] = $img[2];
75 }
76
77 // for bg images grab the url part
78 foreach ( $bg_images as $img ) {
79 $images[] = trim( $img[2], " \t\n\r\0\x0B\"'" );
80 }
81
82 $images = array_unique( $images );
83
84 $need_to_process = false;
85 foreach ( $images as $image_url ) {
86 // we don't process external files or files that don't exist
87
88 if ( $this->is_external_file( $image_url ) ) {
89 Logger::log("{$image_url} is external");
90 continue;
91 }
92
93 $image_path = $this->get_file_path( $image_url );
94 if ( ! $image_path || ! file_exists( $image_path ) ) {
95 Logger::log("{$image_url} doesn't exist");
96 continue;
97 }
98
99 $ext = pathinfo( $image_path, PATHINFO_EXTENSION );
100 $mime = mime_content_type( $image_path );
101
102
103 $ext_quoted = preg_quote( $ext, '/' );
104 $image_webp_url = preg_replace( '/^(.+\.)' . $ext_quoted . '(\?.+)?$/u', '$1' . substr( md5( $ext ), - 6 ) . '.webp$2', $image_url );
105 $image_webp_path = preg_replace( '/^(.+\.)' . $ext_quoted . '$/u', '$1' . substr( md5( $ext ), - 6 ) . '.webp', $image_path );
106
107 // and skip non-images or images already in webp format
108 if ( 'webp' == $ext || ! preg_match( '/image\/.+/i', $mime ) || preg_match( '/image\/(svg.*|ico|gif|webp)/i', $mime ) ) {
109 Logger::log("{$image_url} is already webp");
110 continue;
111 }
112
113 // check if the image has a webp version and if it has then just replace it in the html
114 $image = $this->wpdb->get_row(
115 $this->wpdb->prepare(
116 "SELECT * FROM `{$this->wpdb->prefix}ezcache_webp_images` WHERE `uid` = %s LIMIT 1",
117 [ sha1( $image_path ) ]
118 )
119 );
120
121 if ( $image && 'completed' == $image->status ) {
122 $html = preg_replace( '/\b' . preg_quote( $image_url, '/' ) . '\b/u', $image->webp_url, $html );
123 Logger::log("{$image_url} already processed");
124 continue;
125 }
126
127 if ( $image && 'pending' == $image->status ) {
128 // image is being processed in background
129 Logger::log("{$image_url} still waiting to be processed");
130
131 // need to make sure that the scheuled task for process is running
132 $need_to_process = true;
133
134 // 'add_to_queue' will not add it again if already queued
135 $this->webp_processor->add_to_queue( $image->id, $this->cache_file );
136
137 continue;
138 }
139
140 if ( $image && 'failed' == $image->status ) {
141 // if the image has failed we need to requeue it
142 $this->wpdb->query(
143 $this->wpdb->prepare(
144 "DELETE FROM `{$this->wpdb->prefix}ezcache_webp_images` WHERE `id` = %d",
145 [ $image->id ]
146 )
147 );
148 }
149
150 // otherwise send the image to process
151 // this should parse the images and set a callback to update the cache file
152 $this->wpdb->query(
153 $this->wpdb->prepare(
154 "INSERT IGNORE INTO `{$this->wpdb->prefix}ezcache_webp_images` (`uid`, `url`, `webp_url`, `path`, `webp_path`, `status`, `created_at`, `updated_at`) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)",
155 [
156 sha1( $image_path ),
157 $image_url,
158 $image_webp_url,
159 $image_path,
160 $image_webp_path,
161 'pending',
162 date( 'Y-m-d H:i:s' ),
163 date( 'Y-m-d H:i:s' )
164 ]
165 )
166 );
167 if ( empty( $this->wpdb->last_error ) && $this->wpdb->insert_id ) {
168 $need_to_process = true;
169 $this->webp_processor->add_to_queue( $this->wpdb->insert_id, $this->cache_file );
170 } else {
171 Logger::log( 'ezCache WebP Converter Error: ' . $this->wpdb->last_error );
172 }
173 }
174
175 if ( $need_to_process ) {
176 $this->webp_processor->schedule();
177 }
178
179 return $html;
180 }
181 }
182