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 -149 3.2.21 → 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,53 +34,45 @@
26 34 $_instance = new self();
27 35 }
28 36 return $_instance;
29 37 }
30 -
38 +
31 39 /**
32 - * Start to alter html in output buffer
40 + * Alter HTML to replace image-related attributes with WebP URLs where available.
41 + *
42 + * @param string $html HTML document as string.
43 + * @return string
33 44 */
34 - public function start() {
45 + public function alter_html(string $html): string {
35 46 if (apply_filters('wpo_disable_webp_alter_html', false)) {
36 - return;
47 + return $html;
37 48 }
38 49
39 - if (!is_admin() || (defined('DOING_AJAX') && DOING_AJAX)) {
40 - ob_start(array(__CLASS__, 'alter_html'));
50 + // MAX_FILE_SIZE is defined in simple_html_dom — ensure it has a safe default.
51 + WP_Optimize_Utils::define_maxfile_size_constant();
52 +
53 + $dom = WP_Optimize_Utils::get_simple_html_dom_object($html);
54 +
55 + if (false === $dom) {
56 + return $this->get_skipped_html($html);
41 57 }
58 +
59 + $this->replace_image_attributes($dom);
60 +
61 + return $dom->save();
42 62 }
43 63
44 64 /**
45 - * Alter html to change image related tags to specify webp images
65 + * Walks all supported tags in the DOM and replaces image attribute values
66 + * with their WebP counterparts when available.
46 67 *
47 - * @param string $html - HTML document as string
48 - * @return string
68 + * @param simplehtmldom\HtmlDocument $dom A simple_html_dom instance.
69 + *
70 + * @return void
49 71 */
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) {
72 + private function replace_image_attributes($dom): void {
73 + foreach (self::SUPPORTED_TAGS as $tag) {
74 + foreach ($dom->find($tag) as $elem) {
75 75 $attributes = $elem->getAllAttributes();
76 76 foreach ($attributes as $attr_name => $attr_value) {
77 77 if ($this->is_image_attribute($attr_name)) {
78 78 $elem->setAttribute($attr_name, $this->handle_attribute($attr_value));
@@ -79,79 +79,49 @@
79 79 }
80 80 }
81 81 }
82 82 }
83 -
84 - return $dom->save();
85 83 }
86 84
87 85 /**
88 - * 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.
89 88 *
90 - * @param string $html - HTML document as string
91 - * @return bool
89 + * @param string $html The original HTML document.
90 + * @return string
92 91 */
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';
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) -->";
111 96 }
97 + return $html . "\n" . "<!-- Alter HTML was skipped because the helper library refused to process the html -->";
112 98 }
113 99
114 100 /**
115 - * Append image urls with `.webp` extension
101 + * Returns the URL with a .webp extension appended if a WebP version exists on disk.
116 102 *
117 - * @param strinng $url - Image URL
103 + * @param string $url URL of the original image.
118 104 * @return string
119 105 */
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) {
106 + private function maybe_replace_url($url): string {
131 107 if ($this->is_webp_version_available($url)) {
132 - $url = $this->replace_url($url);
108 + $url .= '.webp';
133 109 }
134 110 return $url;
135 111 }
136 112
137 113 /**
138 - * Modifies src attribute value, if needed
114 + * Processes a srcset attribute value, replacing each image URL with its
115 + * WebP counterpart when available.
139 116 *
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
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".
149 119 *
150 - * @param string $attr_value
120 + * @param string $attr_value The raw srcset attribute value.
151 121 * @return string
152 122 */
153 - private function handle_srcset($attr_value) {
123 + private function handle_srcset($attr_value): string {
154 124 // $attr_value is ie: <img data-x="1.jpg 1000w, 2.jpg">
155 125 $srcset_arr = explode(',', $attr_value);
156 126 foreach ($srcset_arr as $i => $srcset_entry) {
157 127 // $srcset_entry is ie "image.jpg 520w", but can also lack width, ie just "image.jpg"
@@ -157,9 +127,10 @@
157 127 // $srcset_entry is ie "image.jpg 520w", but can also lack width, ie just "image.jpg"
158 128 // it can also be ie "image.jpg 2x"
159 129 $srcset_entry = trim($srcset_entry);
160 130 $entry_parts = preg_split('/\s+/', $srcset_entry, 2);
161 - if (count($entry_parts) == 2) {
131 +
132 + if (is_array($entry_parts) && 2 === count($entry_parts)) {
162 133 list($src, $descriptors) = $entry_parts;
163 134 } else {
164 135 $src = $srcset_entry;
165 136 $descriptors = null;
@@ -171,90 +142,55 @@
171 142 return implode(', ', $srcset_arr);
172 143 }
173 144
174 145 /**
175 - * 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).
176 148 *
149 + * @param string $value The attribute value to inspect.
177 150 * @return bool
178 151 */
179 - private function looks_like_srcset($value) {
180 - if (preg_match('#\s\d*(w|x)#', $value)) {
181 - return true;
182 - }
183 - return false;
152 + private function looks_like_srcset($value): bool {
153 + return (bool) preg_match('#\s\d*(w|x)#', $value);
184 154 }
185 155
186 156 /**
187 - * 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.
188 159 *
160 + * @param string $value The attribute value.
189 161 * @return string
190 162 */
191 - private function handle_attribute($value) {
163 + private function handle_attribute($value): string {
192 164 if ($this->looks_like_srcset($value)) {
193 165 return $this->handle_srcset($value);
194 166 }
195 - return $this->handle_src($value);
167 + return $this->maybe_replace_url($value);
196 168 }
197 169
198 170 /**
199 - * 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.
200 173 *
174 + * @param string $attr_name The attribute name.
201 175 * @return bool
202 176 */
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);
177 + private function is_image_attribute($attr_name): bool {
178 + return (bool) preg_match(self::IMAGE_ATTRIBUTE_PATTERN, $attr_name);
205 179 }
206 180
207 181 /**
208 - * Does webp image file exists
182 + * Checks whether a WebP version of the given image URL exists on disk.
209 183 *
210 - * @param string $url
211 - * @return boolean
184 + * @param string $url The original image URL.
185 + * @return bool
212 186 */
213 - private function is_webp_version_available($url) {
214 - $filename = $this->get_file_path($url);
215 - if (empty($filename)) return false;
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 + }
216 192 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 193 }
258 194 }
259 195
260 196 endif;