PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.2.22
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.2.22
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
wp-optimize / webp / class-wpo-webp-alter-html.php

class-wpo-webp-alter-html.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 3.2.22, at webp/class-wpo-webp-alter-html.php

261 lines 6.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('WPO_VERSION')) die('No direct access allowed');
4
5 if (!class_exists('WPO_WebP_Alter_HTML')) :
6
7 class WPO_WebP_Alter_HTML {
8
9 private $tags = array('img', 'source', 'input', 'iframe', 'div', 'li', 'link', 'a', 'section', 'video');
10
11 /**
12 * Constructor
13 */
14 private function __construct() {
15 add_action('template_redirect', array($this, 'start'), 9999);
16 }
17
18 /**
19 * Returns singleton instance
20 *
21 * @return WPO_WebP_Alter_HTML
22 */
23 public static function get_instance() {
24 static $_instance = null;
25 if (null === $_instance) {
26 $_instance = new self();
27 }
28 return $_instance;
29 }
30
31 /**
32 * Start to alter html in output buffer
33 */
34 public function start() {
35 if (apply_filters('wpo_disable_webp_alter_html', false)) {
36 return;
37 }
38
39 if (!is_admin() || (defined('DOING_AJAX') && DOING_AJAX)) {
40 ob_start(array(__CLASS__, 'alter_html'));
41 }
42 }
43
44 /**
45 * Alter html to change image related tags to specify webp images
46 *
47 * @param string $html - HTML document as string
48 * @return string
49 */
50 public function alter_html($html) {
51
52 if (!$this->is_valid_html($html)) return $html;
53
54 $this->maybe_include_simple_html_dom();
55
56 $dom = str_get_html($html, false, false, 'UTF-8', false, DEFAULT_BR_TEXT, DEFAULT_SPAN_TEXT, false);
57
58 // MAX_FILE_SIZE is defined in simple_html_dom.
59 // For safety sake, we make sure it is defined before using
60 defined('MAX_FILE_SIZE') || define('MAX_FILE_SIZE', 600000);
61
62 if (false === $dom) {
63 if (strlen($html) > MAX_FILE_SIZE) {
64 return '<!-- Alter HTML was skipped because the HTML is too big to process! ' .
65 '(limit is set to ' . MAX_FILE_SIZE . ' bytes) -->' . "\n" . $html;
66 }
67 return '<!-- Alter HTML was skipped because the helper library refused to process the html -->' .
68 "\n" . $html;
69 }
70
71 // Replace attributes (src, srcset, data-src, etc)
72 foreach ($this->tags as $tag) {
73 $elems = $dom->find($tag);
74 foreach ($elems as $elem) {
75 $attributes = $elem->getAllAttributes();
76 foreach ($attributes as $attr_name => $attr_value) {
77 if ($this->is_image_attribute($attr_name)) {
78 $elem->setAttribute($attr_name, $this->handle_attribute($attr_value));
79 }
80 }
81 }
82 }
83
84 return $dom->save();
85 }
86
87 /**
88 * Checks whether supplied string is a valid html document or not
89 *
90 * @param string $html - HTML document as string
91 * @return bool
92 */
93 private function is_valid_html($html) {
94 if (is_feed()) return false;
95
96 // To prevent issue with `simple_html_dom` class
97 // Exit if it doesn't look like HTML
98 // https://github.com/rosell-dk/webp-express/issues/228
99 if (!preg_match("#^\\s*<#", $html)) return false;
100
101 if ('' == $html) return false;
102 return true;
103 }
104
105 /**
106 * Include simple html dom script if not available
107 */
108 private function maybe_include_simple_html_dom() {
109 if (!function_exists('str_get_html')) {
110 require_once WPO_PLUGIN_MAIN_PATH . 'vendor/simplehtmldom/simplehtmldom/simple_html_dom.php';
111 }
112 }
113
114 /**
115 * Append image urls with `.webp` extension
116 *
117 * @param strinng $url - Image URL
118 * @return string
119 */
120 private function replace_url($url) {
121 return $url . '.webp';
122 }
123
124 /**
125 * If webp version for supplied image url is available then replace extension
126 *
127 * @param string $url - URL of image
128 * @return string
129 */
130 private function maybe_replace_url($url) {
131 if ($this->is_webp_version_available($url)) {
132 $url = $this->replace_url($url);
133 }
134 return $url;
135 }
136
137 /**
138 * Modifies src attribute value, if needed
139 *
140 * @param string $attr_value
141 * @return string
142 */
143 private function handle_src($attr_value) {
144 return $this->maybe_replace_url($attr_value);
145 }
146
147 /**
148 * Modified `srcset` attribute value, if needed
149 *
150 * @param string $attr_value
151 * @return string
152 */
153 private function handle_srcset($attr_value) {
154 // $attr_value is ie: <img data-x="1.jpg 1000w, 2.jpg">
155 $srcset_arr = explode(',', $attr_value);
156 foreach ($srcset_arr as $i => $srcset_entry) {
157 // $srcset_entry is ie "image.jpg 520w", but can also lack width, ie just "image.jpg"
158 // it can also be ie "image.jpg 2x"
159 $srcset_entry = trim($srcset_entry);
160 $entry_parts = preg_split('/\s+/', $srcset_entry, 2);
161 if (count($entry_parts) == 2) {
162 list($src, $descriptors) = $entry_parts;
163 } else {
164 $src = $srcset_entry;
165 $descriptors = null;
166 }
167
168 $url = $this->maybe_replace_url($src);
169 $srcset_arr[$i] = $url . (isset($descriptors) ? ' ' . $descriptors : '');
170 }
171 return implode(', ', $srcset_arr);
172 }
173
174 /**
175 * Decides whether given value is a `srcset` or not
176 *
177 * @return bool
178 */
179 private function looks_like_srcset($value) {
180 if (preg_match('#\s\d*(w|x)#', $value)) {
181 return true;
182 }
183 return false;
184 }
185
186 /**
187 * Handle attribute value based on attribute name, src or srcset
188 *
189 * @return string
190 */
191 private function handle_attribute($value) {
192 if ($this->looks_like_srcset($value)) {
193 return $this->handle_srcset($value);
194 }
195 return $this->handle_src($value);
196 }
197
198 /**
199 * Decide whether given attribute name is an image attribute or not
200 *
201 * @return bool
202 */
203 private function is_image_attribute($attr_name) {
204 return preg_match('#^(src|srcset|poster|(data-[^=]*(lazy|small|slide|img|large|src|thumb|source|set|bg-url)[^=]*))$#i', $attr_name);
205 }
206
207 /**
208 * Does webp image file exists
209 *
210 * @param string $url
211 * @return boolean
212 */
213 private function is_webp_version_available($url) {
214 $filename = $this->get_file_path($url);
215 if (empty($filename)) return false;
216 return file_exists($filename . '.webp');
217 }
218
219 /**
220 * Get the file path
221 *
222 * @param string $url
223 * @return string
224 */
225 private function get_file_path($url) {
226 if (is_multisite()) {
227 if (function_exists('get_main_site_id')) {
228 $site_id = get_main_site_id();
229 } else {
230 $network = get_network();
231 $site_id = $network->site_id;
232 }
233 switch_to_blog($site_id);
234 }
235 $upload_dir = wp_upload_dir();
236 $uploads_url = trailingslashit($upload_dir['baseurl']);
237 $uploads_dir = trailingslashit($upload_dir['basedir']);
238 if (is_multisite()) {
239 restore_current_blog();
240 }
241 $possible_urls = array(
242 WP_CONTENT_URL => WP_CONTENT_DIR,
243 WP_PLUGIN_URL => WP_PLUGIN_DIR,
244 $uploads_url => $uploads_dir,
245 get_template_directory_uri() => get_template_directory(),
246 includes_url() => preg_replace('/wp-content$/', trailingslashit('wp-includes'), WP_CONTENT_DIR),
247 );
248 $file = '';
249 foreach ($possible_urls as $possible_url => $path) {
250 $pos = strpos($url, $possible_url);
251 if (false !== $pos) {
252 $file = substr_replace($url, $path, $pos, strlen($possible_url));
253 break;
254 }
255 }
256 return $file;
257 }
258 }
259
260 endif;
261