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

197 lines 5.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('ABSPATH')) die('No direct access allowed');
4
5 if (!class_exists('WPO_WebP_Alter_HTML')) :
6
7 class WPO_WebP_Alter_HTML {
8
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');
13
14 /**
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.
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 */
23 private function __construct() {
24 }
25
26 /**
27 * Returns singleton instance.
28 *
29 * @return self
30 */
31 public static function get_instance(): self {
32 static $_instance = null;
33 if (null === $_instance) {
34 $_instance = new self();
35 }
36 return $_instance;
37 }
38
39 /**
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
44 */
45 public function alter_html(string $html): string {
46 if (apply_filters('wpo_disable_webp_alter_html', false)) {
47 return $html;
48 }
49
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);
57 }
58
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) {
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
85 /**
86 * Returns the original HTML annotated with a comment explaining why
87 * the WebP alteration was skipped.
88 *
89 * @param string $html The original HTML document.
90 * @return string
91 */
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) -->";
96 }
97 return $html . "\n" . "<!-- Alter HTML was skipped because the helper library refused to process the html -->";
98 }
99
100 /**
101 * Returns the URL with a .webp extension appended if a WebP version exists on disk.
102 *
103 * @param string $url URL of the original image.
104 * @return string
105 */
106 private function maybe_replace_url($url): string {
107 if ($this->is_webp_version_available($url)) {
108 $url .= '.webp';
109 }
110 return $url;
111 }
112
113 /**
114 * Processes a srcset attribute value, replacing each image URL with its
115 * WebP counterpart when available.
116 *
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".
119 *
120 * @param string $attr_value The raw srcset attribute value.
121 * @return string
122 */
123 private function handle_srcset($attr_value): string {
124 // $attr_value is ie: <img data-x="1.jpg 1000w, 2.jpg">
125 $srcset_arr = explode(',', $attr_value);
126 foreach ($srcset_arr as $i => $srcset_entry) {
127 // $srcset_entry is ie "image.jpg 520w", but can also lack width, ie just "image.jpg"
128 // it can also be ie "image.jpg 2x"
129 $srcset_entry = trim($srcset_entry);
130 $entry_parts = preg_split('/\s+/', $srcset_entry, 2);
131
132 if (is_array($entry_parts) && 2 === count($entry_parts)) {
133 list($src, $descriptors) = $entry_parts;
134 } else {
135 $src = $srcset_entry;
136 $descriptors = null;
137 }
138
139 $url = $this->maybe_replace_url($src);
140 $srcset_arr[$i] = $url . (isset($descriptors) ? ' ' . $descriptors : '');
141 }
142 return implode(', ', $srcset_arr);
143 }
144
145 /**
146 * Determines whether an attribute value resembles a srcset declaration
147 * (contains a width or pixel-density descriptor).
148 *
149 * @param string $value The attribute value to inspect.
150 * @return bool
151 */
152 private function looks_like_srcset($value): bool {
153 return (bool) preg_match('#\s\d*(w|x)#', $value);
154 }
155
156 /**
157 * Dispatches attribute value handling based on whether it looks like a
158 * srcset or a plain image source URL.
159 *
160 * @param string $value The attribute value.
161 * @return string
162 */
163 private function handle_attribute($value): string {
164 if ($this->looks_like_srcset($value)) {
165 return $this->handle_srcset($value);
166 }
167 return $this->maybe_replace_url($value);
168 }
169
170 /**
171 * Determines whether an attribute name is image-related and should be
172 * considered for WebP replacement.
173 *
174 * @param string $attr_name The attribute name.
175 * @return bool
176 */
177 private function is_image_attribute($attr_name): bool {
178 return (bool) preg_match(self::IMAGE_ATTRIBUTE_PATTERN, $attr_name);
179 }
180
181 /**
182 * Checks whether a WebP version of the given image URL exists on disk.
183 *
184 * @param string $url The original image URL.
185 * @return bool
186 */
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 }
192 return file_exists($filename . '.webp');
193 }
194 }
195
196 endif;
197