PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.2.5
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.2.5
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.5, at webp/class-wpo-webp-alter-html.php

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