get_skipped_html($html); } $this->replace_image_attributes($dom); return $dom->save(); } /** * Walks all supported tags in the DOM and replaces image attribute values * with their WebP counterparts when available. * * @param simplehtmldom\HtmlDocument $dom A simple_html_dom instance. * * @return void */ private function replace_image_attributes($dom): void { foreach (self::SUPPORTED_TAGS as $tag) { foreach ($dom->find($tag) as $elem) { $attributes = $elem->getAllAttributes(); foreach ($attributes as $attr_name => $attr_value) { if ($this->is_image_attribute($attr_name)) { $elem->setAttribute($attr_name, $this->handle_attribute($attr_value)); } } } } } /** * Returns the original HTML annotated with a comment explaining why * the WebP alteration was skipped. * * @param string $html The original HTML document. * @return string */ private function get_skipped_html($html): string { if (strlen($html) > MAX_FILE_SIZE) { return $html . "\n" . ""; } return $html . "\n" . ""; } /** * Returns the URL with a .webp extension appended if a WebP version exists on disk. * * @param string $url URL of the original image. * @return string */ private function maybe_replace_url($url): string { if ($this->is_webp_version_available($url)) { $url .= '.webp'; } return $url; } /** * Processes a srcset attribute value, replacing each image URL with its * WebP counterpart when available. * * A srcset value is a comma-separated list of entries such as * "image.jpg 520w" or "image.jpg 2x" or just "image.jpg". * * @param string $attr_value The raw srcset attribute value. * @return string */ private function handle_srcset($attr_value): string { // $attr_value is ie: $srcset_arr = explode(',', $attr_value); foreach ($srcset_arr as $i => $srcset_entry) { // $srcset_entry is ie "image.jpg 520w", but can also lack width, ie just "image.jpg" // it can also be ie "image.jpg 2x" $srcset_entry = trim($srcset_entry); $entry_parts = preg_split('/\s+/', $srcset_entry, 2); if (is_array($entry_parts) && 2 === count($entry_parts)) { list($src, $descriptors) = $entry_parts; } else { $src = $srcset_entry; $descriptors = null; } $url = $this->maybe_replace_url($src); $srcset_arr[$i] = $url . (isset($descriptors) ? ' ' . $descriptors : ''); } return implode(', ', $srcset_arr); } /** * Determines whether an attribute value resembles a srcset declaration * (contains a width or pixel-density descriptor). * * @param string $value The attribute value to inspect. * @return bool */ private function looks_like_srcset($value): bool { return (bool) preg_match('#\s\d*(w|x)#', $value); } /** * Dispatches attribute value handling based on whether it looks like a * srcset or a plain image source URL. * * @param string $value The attribute value. * @return string */ private function handle_attribute($value): string { if ($this->looks_like_srcset($value)) { return $this->handle_srcset($value); } return $this->maybe_replace_url($value); } /** * Determines whether an attribute name is image-related and should be * considered for WebP replacement. * * @param string $attr_name The attribute name. * @return bool */ private function is_image_attribute($attr_name): bool { return (bool) preg_match(self::IMAGE_ATTRIBUTE_PATTERN, $attr_name); } /** * Checks whether a WebP version of the given image URL exists on disk. * * @param string $url The original image URL. * @return bool */ private function is_webp_version_available($url): bool { $filename = WP_Optimize_Utils::get_file_path($url); if (empty($filename)) { return false; } return file_exists($filename . '.webp'); } } endif;