Last active
July 13, 2026 11:39
-
-
Save Ibochkarev/d21bb00d57df7b241afad899486f2909 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| defined('MODX_CORE_PATH') || exit; | |
| /** | |
| * Pull script/style/noscript/textarea out before DOMDocument so markup inside | |
| * JS/CSS strings is not rewritten (can inject literal </script> and break pages). | |
| * | |
| * @return array{0: string, 1: array<string, string>} | |
| */ | |
| function imageoptimizer_extract_raw_blocks(string $html): array | |
| { | |
| $blocks = []; | |
| $replaced = preg_replace_callback( | |
| '#<(script|style|noscript|textarea)\b[^>]*>.*?</\1>#is', | |
| static function (array $match) use (&$blocks): string { | |
| $token = '<!--IMAGEOPTIMIZER_RAW_' . count($blocks) . '-->'; | |
| $blocks[$token] = $match[0]; | |
| return $token; | |
| }, | |
| $html | |
| ); | |
| return [$replaced ?? $html, $blocks]; | |
| } | |
| /** | |
| * @param array<string, string> $blocks | |
| */ | |
| function imageoptimizer_restore_raw_blocks(string $html, array $blocks): string | |
| { | |
| if ($blocks === []) { | |
| return $html; | |
| } | |
| return str_replace(array_keys($blocks), array_values($blocks), $html); | |
| } | |
| /** | |
| * Full page document (has doctype or html root) vs HTML fragment. | |
| */ | |
| function imageoptimizer_is_full_html_document(string $html): bool | |
| { | |
| $sample = ltrim(substr($html, 0, 2048)); | |
| if ($sample === '') { | |
| return false; | |
| } | |
| return (bool) preg_match('/^(?:<!DOCTYPE\b|<html\b)/i', $sample); | |
| } | |
| function imageoptimizer_load_html_document(string $html): ?DOMDocument | |
| { | |
| $doc = new DOMDocument(); | |
| $flags = LIBXML_NOERROR | LIBXML_NOWARNING; | |
| if (defined('LIBXML_HTML_NODEFDTD')) { | |
| $flags |= LIBXML_HTML_NODEFDTD; | |
| } | |
| if (imageoptimizer_is_full_html_document($html)) { | |
| // Keep <html>/<head>/<body>; do not wrap in #imageoptimizer-root. | |
| $payload = '<?xml encoding="UTF-8">' . $html; | |
| if (!$doc->loadHTML($payload, $flags)) { | |
| return null; | |
| } | |
| return $doc; | |
| } | |
| if (defined('LIBXML_HTML_NOIMPLIED')) { | |
| $flags |= LIBXML_HTML_NOIMPLIED; | |
| } | |
| $wrapped = '<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body><div id="imageoptimizer-root">' | |
| . $html . '</div></body></html>'; | |
| if (!$doc->loadHTML($wrapped, $flags)) { | |
| return null; | |
| } | |
| return $doc; | |
| } | |
| /** | |
| * @return DOMElement[] | |
| */ | |
| function imageoptimizer_extract_img_nodes(DOMDocument $doc): array | |
| { | |
| $images = []; | |
| $nodes = $doc->getElementsByTagName('img'); | |
| foreach ($nodes as $node) { | |
| if ($node instanceof DOMElement) { | |
| $images[] = $node; | |
| } | |
| } | |
| return $images; | |
| } | |
| function imageoptimizer_serialize_document(DOMDocument $doc): string | |
| { | |
| $root = $doc->getElementById('imageoptimizer-root'); | |
| if ($root) { | |
| $html = ''; | |
| foreach ($root->childNodes as $child) { | |
| $html .= $doc->saveHTML($child); | |
| } | |
| return imageoptimizer_normalize_html_output($html); | |
| } | |
| $html = (string) $doc->saveHTML(); | |
| // loadHTML UTF-8 hint may serialize as PI or as HTML comment after doctype. | |
| $html = preg_replace('/<\?xml[^?]*\?>\s*/i', '', $html) ?? $html; | |
| $html = preg_replace('/<!--\?xml[^>]*-->\s*/i', '', $html) ?? $html; | |
| return imageoptimizer_normalize_html_output($html); | |
| } | |
| function imageoptimizer_normalize_html_output(string $html): string | |
| { | |
| $voidTags = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']; | |
| foreach ($voidTags as $tag) { | |
| $html = preg_replace('#(<' . $tag . '\b[^>]*>)\s*</' . $tag . '>#i', '$1', $html) ?? $html; | |
| } | |
| $html = preg_replace('#</source>\s*</picture>#i', '</picture>', $html) ?? $html; | |
| return $html; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment