PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.7.0
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.7.0
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
← All changes | webp/class-wpo-webp-alter-html.php +85 -144 3.2.5 → 4.7.0 View file →
@@ -1,27 +1,35 @@
1 1 <?php
2 2
3 -if (!defined('WPO_VERSION')) die('No direct access allowed');
3 +if (!defined('ABSPATH')) die('No direct access allowed');
4 4
5 5 if (!class_exists('WPO_WebP_Alter_HTML')) :
6 6
7 7 class WPO_WebP_Alter_HTML {
8 8
9 - private $tags = array('img', 'source', 'input', 'iframe', 'div', 'li', 'link', 'a', 'section', 'video');
9 + /**
10 + * HTML tags that may contain image-related attributes eligible for WebP replacement.
11 + */
12 + const SUPPORTED_TAGS = array('img', 'source', 'input', 'iframe', 'div', 'li', 'link', 'a', 'section', 'video');
10 13
11 14 /**
12 - * Constructor
15 + * Regex pattern to identify image-related attribute names such as src, srcset,
16 + * poster, and common data-* attributes used for lazy loading or responsive images.
13 17 */
18 + const IMAGE_ATTRIBUTE_PATTERN = '#^(src|srcset|poster|(data-[^=]*(lazy|small|slide|img|large|src|thumb|source|set|bg-url)[^=]*))$#i';
19 +
20 + /**
21 + * Constructor — private to enforce singleton pattern.
22 + */
14 23 private function __construct() {
15 - add_action('template_redirect', array($this, 'start'), 9999);
16 24 }
17 25
18 26 /**
19 - * Returns singleton instance
27 + * Returns singleton instance.
20 28 *
21 - * @return WPO_WebP_Alter_HTML
29 + * @return self
22 30 */
23 - public static function get_instance() {
31 + public static function get_instance(): self {
24 32 static $_instance = null;
25 33 if (null === $_instance) {
26 34 $_instance = new self();
27 35 }
@@ -26,49 +34,45 @@
26 34 $_instance = new self();
27 35 }
28 36 return $_instance;
29 37 }
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 38
40 39 /**
41 - * Alter html to change image related tags to specify webp images
40 + * Alter HTML to replace image-related attributes with WebP URLs where available.
42 41 *
43 - * @param string $html - HTML document as string
42 + * @param string $html HTML document as string.
44 43 * @return string
45 44 */
46 - public function alter_html($html) {
47 -
48 - if (!$this->is_valid_html($html)) return $html;
45 + public function alter_html(string $html): string {
46 + if (apply_filters('wpo_disable_webp_alter_html', false)) {
47 + return $html;
48 + }
49 49
50 - $this->maybe_include_simple_html_dom();
50 + // MAX_FILE_SIZE is defined in simple_html_dom — ensure it has a safe default.
51 + WP_Optimize_Utils::define_maxfile_size_constant();
51 52
52 - $dom = str_get_html($html, false, false, 'UTF-8', false);
53 + $dom = WP_Optimize_Utils::get_simple_html_dom_object($html);
53 54
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 55 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;
56 + return $this->get_skipped_html($html);
65 57 }
66 58
67 - // Replace attributes (src, srcset, data-src, etc)
68 - foreach ($this->tags as $tag) {
69 - $elems = $dom->find($tag);
70 - foreach ($elems as $elem) {
59 + $this->replace_image_attributes($dom);
60 +
61 + return $dom->save();
62 + }
63 +
64 + /**
65 + * Walks all supported tags in the DOM and replaces image attribute values
66 + * with their WebP counterparts when available.
67 + *
68 + * @param simplehtmldom\HtmlDocument $dom A simple_html_dom instance.
69 + *
70 + * @return void
71 + */
72 + private function replace_image_attributes($dom): void {
73 + foreach (self::SUPPORTED_TAGS as $tag) {
74 + foreach ($dom->find($tag) as $elem) {
71 75 $attributes = $elem->getAllAttributes();
72 76 foreach ($attributes as $attr_name => $attr_value) {
73 77 if ($this->is_image_attribute($attr_name)) {
74 78 $elem->setAttribute($attr_name, $this->handle_attribute($attr_value));
@@ -75,79 +79,49 @@
75 79 }
76 80 }
77 81 }
78 82 }
79 -
80 - return $dom->save();
81 83 }
82 84
83 85 /**
84 - * Checks whether supplied string is a valid html document or not
86 + * Returns the original HTML annotated with a comment explaining why
87 + * the WebP alteration was skipped.
85 88 *
86 - * @param string $html - HTML document as string
87 - * @return bool
89 + * @param string $html The original HTML document.
90 + * @return string
88 91 */
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';
92 + private function get_skipped_html($html): string {
93 + if (strlen($html) > MAX_FILE_SIZE) {
94 + return $html . "\n" . "<!-- Alter HTML was skipped because the HTML is too big to process! " .
95 + "(limit is set to " . MAX_FILE_SIZE . " bytes) -->";
107 96 }
97 + return $html . "\n" . "<!-- Alter HTML was skipped because the helper library refused to process the html -->";
108 98 }
109 99
110 100 /**
111 - * Append image urls with `.webp` extension
101 + * Returns the URL with a .webp extension appended if a WebP version exists on disk.
112 102 *
113 - * @param strinng $url - Image URL
103 + * @param string $url URL of the original image.
114 104 * @return string
115 105 */
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) {
106 + private function maybe_replace_url($url): string {
127 107 if ($this->is_webp_version_available($url)) {
128 - $url = $this->replace_url($url);
108 + $url .= '.webp';
129 109 }
130 110 return $url;
131 111 }
132 112
133 113 /**
134 - * Modifies src attribute value, if needed
114 + * Processes a srcset attribute value, replacing each image URL with its
115 + * WebP counterpart when available.
135 116 *
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
117 + * A srcset value is a comma-separated list of entries such as
118 + * "image.jpg 520w" or "image.jpg 2x" or just "image.jpg".
145 119 *
146 - * @param string $attr_value
120 + * @param string $attr_value The raw srcset attribute value.
147 121 * @return string
148 122 */
149 - private function handle_srcset($attr_value) {
123 + private function handle_srcset($attr_value): string {
150 124 // $attr_value is ie: <img data-x="1.jpg 1000w, 2.jpg">
151 125 $srcset_arr = explode(',', $attr_value);
152 126 foreach ($srcset_arr as $i => $srcset_entry) {
153 127 // $srcset_entry is ie "image.jpg 520w", but can also lack width, ie just "image.jpg"
@@ -153,9 +127,10 @@
153 127 // $srcset_entry is ie "image.jpg 520w", but can also lack width, ie just "image.jpg"
154 128 // it can also be ie "image.jpg 2x"
155 129 $srcset_entry = trim($srcset_entry);
156 130 $entry_parts = preg_split('/\s+/', $srcset_entry, 2);
157 - if (count($entry_parts) == 2) {
131 +
132 + if (is_array($entry_parts) && 2 === count($entry_parts)) {
158 133 list($src, $descriptors) = $entry_parts;
159 134 } else {
160 135 $src = $srcset_entry;
161 136 $descriptors = null;
@@ -167,89 +142,55 @@
167 142 return implode(', ', $srcset_arr);
168 143 }
169 144
170 145 /**
171 - * Decides whether given value is a `srcset` or not
146 + * Determines whether an attribute value resembles a srcset declaration
147 + * (contains a width or pixel-density descriptor).
172 148 *
149 + * @param string $value The attribute value to inspect.
173 150 * @return bool
174 151 */
175 - private function looks_like_srcset($value) {
176 - if (preg_match('#\s\d*(w|x)#', $value)) {
177 - return true;
178 - }
179 - return false;
152 + private function looks_like_srcset($value): bool {
153 + return (bool) preg_match('#\s\d*(w|x)#', $value);
180 154 }
181 155
182 156 /**
183 - * Handle attribute value based on attribute name, src or srcset
157 + * Dispatches attribute value handling based on whether it looks like a
158 + * srcset or a plain image source URL.
184 159 *
160 + * @param string $value The attribute value.
185 161 * @return string
186 162 */
187 - private function handle_attribute($value) {
163 + private function handle_attribute($value): string {
188 164 if ($this->looks_like_srcset($value)) {
189 165 return $this->handle_srcset($value);
190 166 }
191 - return $this->handle_src($value);
167 + return $this->maybe_replace_url($value);
192 168 }
193 169
194 170 /**
195 - * Decide whether given attribute name is an image attribute or not
171 + * Determines whether an attribute name is image-related and should be
172 + * considered for WebP replacement.
196 173 *
174 + * @param string $attr_name The attribute name.
197 175 * @return bool
198 176 */
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);
177 + private function is_image_attribute($attr_name): bool {
178 + return (bool) preg_match(self::IMAGE_ATTRIBUTE_PATTERN, $attr_name);
201 179 }
202 180
203 181 /**
204 - * Does webp image file exists
182 + * Checks whether a WebP version of the given image URL exists on disk.
205 183 *
206 - * @param string $url
207 - * @return boolean
184 + * @param string $url The original image URL.
185 + * @return bool
208 186 */
209 - private function is_webp_version_available($url) {
210 - $filename = $this->get_file_path($url);
187 + private function is_webp_version_available($url): bool {
188 + $filename = WP_Optimize_Utils::get_file_path($url);
189 + if (empty($filename)) {
190 + return false;
191 + }
211 192 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 193 }
253 194 }
254 195
255 196 endif;