'velox-yt', 'delay' => 'velox-delay', 'css' => 'velox-css-loader', 'rum' => 'velox-vitals', ); public static function enqueue() { if ( ! self::should_run() ) { return; } $s = self::opt(); $url = plugins_url( 'assets/front/', VELOX_FILE ); $ver = VELOX_VERSION; if ( $s['lazy_youtube'] ) { wp_enqueue_style( self::HANDLES['yt'], $url . 'youtube.css', array(), $ver ); wp_enqueue_script( self::HANDLES['yt'], $url . 'youtube.js', array(), $ver, array( 'in_footer' => true, 'strategy' => 'defer' ) ); } if ( self::delay_on() ) { wp_enqueue_script( self::HANDLES['delay'], $url . 'delay.js', array(), $ver, true ); } if ( self::$css_rec ) { // In , async: it must see the stylesheets as early as possible to switch them on when loaded. wp_enqueue_script( self::HANDLES['css'], $url . 'css-loader.js', array(), $ver, array( 'in_footer' => false, 'strategy' => 'async' ) ); } if ( $s['rum'] && ! is_user_logged_in() ) { wp_enqueue_script( self::HANDLES['rum'], $url . 'vitals.js', array(), $ver, array( 'in_footer' => true, 'strategy' => 'defer' ) ); } } /** Settings for our own front-end scripts, passed as data attributes (no inline code). */ private static function own_attributes( $handle ) { $s = self::opt(); $data = array(); if ( self::HANDLES['delay'] === $handle ) { $data['data-timeout'] = (string) ( (int) $s['delay_timeout'] * 1000 ); } elseif ( self::HANDLES['css'] === $handle ) { $data['data-wait'] = (string) max( 0, (int) $s['rucss_timeout'] ); } elseif ( self::HANDLES['rum'] === $handle ) { $data['data-url'] = (string) wp_parse_url( admin_url( 'admin-ajax.php' ), PHP_URL_PATH ); $data['data-token'] = Velox_Vitals::token(); $data['data-sample'] = (string) ( max( 1, min( 100, (int) $s['rum_sample'] ) ) / 100 ); } return $data; } /* ---------- JavaScript: defer and delay through the script APIs ---------- */ /** * "Defer JavaScript" uses the WordPress script loading strategies (6.3+): core keeps a script * blocking whenever an inline "after" script or a blocking dependent needs it. */ public static function script_strategies() { static $done = array(); $s = self::opt(); if ( ! $s['defer_js'] || ! self::should_run() ) { return; } $ws = wp_scripts(); $exclude = self::tokens( $s['defer_exclude'] ); foreach ( $ws->registered as $handle => $dep ) { if ( isset( $done[ $handle ] ) || ! $dep->src || in_array( $handle, self::HANDLES, true ) || $ws->get_data( $handle, 'strategy' ) ) { continue; } $done[ $handle ] = true; if ( ! self::excluded( $handle . ' ' . ( is_string( $dep->src ) ? $dep->src : '' ), $exclude ) ) { $ws->add_data( $handle, 'strategy', 'defer' ); } } } private static function delay_on() { $s = self::opt(); return $s['delay_js'] && ! is_user_logged_in() && self::should_run(); } /** * Whether an enqueued script is delayed until the first interaction. A script is only delayed when * every queued script that depends on it is delayed too, so the order of dependencies never breaks. */ private static function delay_handle( $handle, $depth = 0 ) { static $memo = array(); if ( isset( $memo[ $handle ] ) ) { return $memo[ $handle ]; } $ws = wp_scripts(); if ( ! isset( $ws->registered[ $handle ] ) || in_array( $handle, self::HANDLES, true ) || $depth > 20 ) { $memo[ $handle ] = false; return false; } $memo[ $handle ] = true; // Provisional, breaks dependency cycles. $src = is_string( $ws->registered[ $handle ]->src ) ? $ws->registered[ $handle ]->src : ''; $ok = ! self::excluded( $handle . ' ' . $src, self::delay_tokens() ); if ( $ok ) { $queued = array_unique( array_merge( (array) $ws->queue, (array) $ws->done, (array) $ws->to_do ) ); foreach ( $queued as $other ) { if ( $other !== $handle && isset( $ws->registered[ $other ] ) && in_array( $handle, (array) $ws->registered[ $other ]->deps, true ) && ! self::delay_handle( $other, $depth + 1 ) ) { $ok = false; break; } } } $memo[ $handle ] = $ok; return $ok; } private static function js_type_ok( $attr ) { return empty( $attr['type'] ) || in_array( strtolower( trim( (string) $attr['type'] ) ), array( 'text/javascript', 'application/javascript', 'text/ecmascript', 'application/ecmascript' ), true ); } /** Filter wp_script_attributes: our own data attributes, and the delay marker for delayed scripts. */ public static function script_attributes( $attr ) { if ( ! is_array( $attr ) || empty( $attr['id'] ) || ! preg_match( '/^(.+)-js$/', (string) $attr['id'], $m ) ) { return $attr; } $handle = $m[1]; if ( in_array( $handle, self::HANDLES, true ) ) { return array_merge( $attr, self::own_attributes( $handle ) ); } if ( empty( $attr['src'] ) || ! self::js_type_ok( $attr ) || ! self::delay_on() || ! self::delay_handle( $handle ) ) { return $attr; } $attr['data-velox-src'] = $attr['src']; $attr['type'] = 'velox/delay'; unset( $attr['src'], $attr['defer'], $attr['async'], $attr['data-wp-strategy'] ); return $attr; } /** Filter wp_inline_script_attributes: inline code of a delayed script is delayed with it. */ public static function inline_script_attributes( $attr, $javascript ) { if ( ! is_array( $attr ) || ! self::js_type_ok( $attr ) || ! self::delay_on() ) { return $attr; } $id = isset( $attr['id'] ) ? (string) $attr['id'] : ''; if ( preg_match( '/^(.+)-js-(before|after|extra|translations)$/', $id, $m ) ) { if ( self::delay_handle( $m[1] ) ) { $attr['type'] = 'velox/delay'; } return $attr; } // Inline code printed on its own with wp_print_inline_script_tag(). $js = (string) $javascript; if ( '' === trim( $js ) || self::is_layout_inline( $js ) || self::excluded( $js . ' ' . $id, self::delay_tokens() ) ) { return $attr; } $attr['type'] = 'velox/delay'; return $attr; } /* ---------- CSS through the style APIs ---------- */ /** Filter style_loader_src: minified local copy, and display=swap for Google Fonts. */ public static function style_src( $src, $handle ) { if ( ! is_string( $src ) || '' === $src || ! self::should_run() ) { return $src; } $s = self::opt(); if ( $s['font_swap'] && false !== strpos( $src, 'fonts.googleapis.com/css' ) && false === strpos( $src, 'display=' ) ) { return add_query_arg( 'display', 'swap', $src ); } if ( $s['css_minify'] && ! self::excluded( $handle, array( 'velox-' ) ) ) { $min = self::minified_url( $src ); if ( $min ) { return $min; } } return $src; } /** Minified copy of a local stylesheet in the cache folder (built once per file version). */ private static function minified_url( $src ) { $url = html_entity_decode( $src ); if ( '//' === substr( $url, 0, 2 ) ) { $url = set_url_scheme( $url ); } elseif ( '/' === substr( $url, 0, 1 ) ) { $url = home_url( $url ); } $clean = strtok( $url, '?#' ); if ( ! preg_match( '/\.css$/i', (string) $clean ) || preg_match( '/\.min\.css$/i', $clean ) ) { return false; } $path = self::url_to_path( $clean ); if ( ! $path || ! is_readable( $path ) || filesize( $path ) > 2 * MB_IN_BYTES ) { return false; } $key = md5( $path . filemtime( $path ) . VELOX_VERSION ); $out = Velox_Cache::dir() . 'min/' . $key . '.css'; if ( ! file_exists( $out ) ) { $css = file_get_contents( $path ); if ( false === $css ) { return false; } $css = self::minify_css( self::absolutize_css( $css, substr( $clean, 0, strrpos( $clean, '/' ) + 1 ) ) ); wp_mkdir_p( dirname( $out ) ); if ( false === file_put_contents( $out, $css, LOCK_EX ) ) { return false; } } return Velox_Cache::url() . 'min/' . $key . '.css'; } /** Filter wp_resource_hints: DNS prefetch / preconnect hosts from the settings. */ public static function resource_hints( $urls, $relation ) { if ( ! self::should_run() || ! in_array( $relation, array( 'dns-prefetch', 'preconnect' ), true ) ) { return $urls; } foreach ( self::tokens( self::opt()['dns_prefetch'] ) as $host ) { $host = preg_replace( '#[^a-z0-9.\-:]#i', '', (string) preg_replace( '#^(https?:)?//#i', '', $host ) ); if ( '' === $host ) { continue; } $urls[] = 'dns-prefetch' === $relation ? '//' . $host : array( 'href' => 'https://' . $host, 'crossorigin' => 'anonymous', ); } return $urls; } /** Filter wp_preload_resources (WordPress 6.1+): font preloads from the settings. */ public static function preload_resources( $resources ) { if ( ! self::should_run() ) { return $resources; } $types = array( 'woff2' => 'font/woff2', 'woff' => 'font/woff', 'ttf' => 'font/ttf', 'otf' => 'font/otf', ); foreach ( self::tokens( self::opt()['preload_fonts'] ) as $font ) { $url = esc_url_raw( $font ); if ( ! $url ) { continue; } $ext = strtolower( pathinfo( (string) wp_parse_url( $url, PHP_URL_PATH ), PATHINFO_EXTENSION ) ); $res = array( 'href' => $url, 'as' => 'font', 'crossorigin' => 'anonymous', ); if ( isset( $types[ $ext ] ) ) { $res['type'] = $types[ $ext ]; } $resources[] = $res; } return $resources; } /* ---------- critical / used CSS ---------- */ /** * Critical CSS is shared by pages built from the same template (body classes without IDs); used * CSS is per URL. The key also covers mobile/desktop and the settings that change the result. */ private static function css_key( $mode ) { $s = self::opt(); if ( 'critical' === $mode ) { $classes = array_filter( array_map( 'strtolower', (array) get_body_class() ), function ( $c ) { return '' !== $c && ! preg_match( '/\d/', $c ); } ); sort( $classes ); $k = 't:' . implode( ' ', $classes ); } else { $uri = isset( $_SERVER['REQUEST_URI'] ) ? (string) wp_parse_url( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ), PHP_URL_PATH ) : '/'; $k = 'u:' . $uri; } $sig = $s['rucss_safelist'] . '|' . $s['critical_manual'] . '|' . $s['rucss_exclude'] . '|' . $s['critical_exclude'] . '|' . (int) $s['compat_list'] . '|' . (int) $s['css_minify']; return md5( $mode . '|' . $k . '|' . ( Velox_Cache::is_mobile_ua() ? 'm' : 'd' ) . '|' . md5( $sig ) . '|' . VELOX_VERSION ); } /** * wp_enqueue_scripts (priority 1): when the critical CSS of this page is known, it is added with * wp_add_inline_style() as the first style of the page, and style_tag() loads the full stylesheets * without blocking. When it is not known yet, it is computed at the end of this request. */ public static function enqueue_critical() { $s = self::opt(); if ( ( ! $s['rucss'] && ! $s['critical_css'] ) || ! self::should_run() ) { return; } $mode = $s['rucss'] ? 'used' : 'critical'; self::$css_key = self::css_key( $mode ); $rec = Velox_CSS::load_record( self::$css_key ); if ( ! $rec ) { self::$css_needed = true; return; } self::$css_rec = $rec; wp_register_style( 'velox-critical', false, array(), VELOX_VERSION ); wp_enqueue_style( 'velox-critical' ); wp_add_inline_style( 'velox-critical', $rec['css'] ); } /** Filter style_loader_tag: stylesheets covered by the critical CSS no longer block rendering. */ public static function style_tag( $tag, $handle ) { if ( ! self::$css_rec || ! in_array( $handle, (array) self::$css_rec['handles'], true ) ) { return $tag; } if ( 'used' === self::$css_rec['mode'] ) { // Loaded by css-loader.js after the page has loaded; the \n"; } /** End of the first build of a page: compute its critical / used CSS for the following builds. */ private static function compute_css( $html ) { $s = self::opt(); $mode = $s['rucss'] ? 'used' : 'critical'; if ( false !== stripos( $html, 'data-velox-skip-css' ) || ! preg_match_all( '#]*>#i', $html, $lm ) ) { return; } $exclude = self::tokens( 'used' === $mode ? $s['rucss_exclude'] : $s['critical_exclude'] ); $styles = wp_styles(); $handles = array(); $sheets = array(); $files = array(); foreach ( $lm[0] as $tag ) { if ( ! preg_match( '/(?registered[ $im[2] ] ) ) { continue; // Only stylesheets enqueued with WordPress. } if ( preg_match( '/(? 3 * MB_IN_BYTES ) { continue; } $handles[] = $im[2]; $files[] = array( $path, filemtime( $path ) ); $sheets[] = array( $path, substr( strtok( $abs, '?#' ), 0, strrpos( strtok( $abs, '?#' ), '/' ) + 1 ) ); } if ( empty( $handles ) ) { return; } $safelist = self::tokens( $s['rucss_safelist'] ); if ( $s['compat_list'] ) { $safelist = array_merge( $safelist, Velox_Compat::css_safelist() ); } $bpos = stripos( $html, ' 30 * KB_IN_BYTES && strlen( $css ) < max( 2 * KB_IN_BYTES, (int) ( $total * 0.04 ) ) ) { $mode = 'critical'; $css = Velox_CSS::build( $sheets, $fold, $safelist, $mode ); } } } if ( '' === trim( $css ) ) { return; } Velox_CSS::save_record( self::$css_key, array( 'mode' => $mode, 'css' => self::minify_css( $css ), 'handles' => $handles, 'files' => $files, ) ); self::$css_pending = true; } public static function credit() { if ( self::opt()['credit_link'] && self::should_run() ) { echo '

' . esc_html__( 'Optimized by AngelForce', 'angelforce-cache' ) . '

'; } } public static function strip_ver( $src ) { return $src && false !== strpos( $src, 'ver=' ) ? remove_query_arg( 'ver', $src ) : $src; } private static function disable_emojis() { remove_action( 'wp_head', 'print_emoji_detection_script', 7 ); remove_action( 'admin_print_scripts', 'print_emoji_detection_script' ); remove_action( 'wp_print_styles', 'print_emoji_styles' ); remove_action( 'admin_print_styles', 'print_emoji_styles' ); remove_filter( 'the_content_feed', 'wp_staticize_emoji' ); remove_filter( 'comment_text_rss', 'wp_staticize_emoji' ); remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' ); add_filter( 'tiny_mce_plugins', function ( $plugins ) { return is_array( $plugins ) ? array_diff( $plugins, array( 'wpemoji' ) ) : $plugins; } ); add_filter( 'emoji_svg_url', '__return_false' ); } private static function disable_embeds() { add_action( 'init', function () { remove_action( 'rest_api_init', 'wp_oembed_register_route' ); add_filter( 'embed_oembed_discover', '__return_false' ); remove_filter( 'oembed_dataparse', 'wp_filter_oembed_result', 10 ); remove_action( 'wp_head', 'wp_oembed_add_discovery_links' ); remove_action( 'wp_head', 'wp_oembed_add_host_js' ); }, 9999 ); add_action( 'wp_enqueue_scripts', function () { wp_deregister_script( 'wp-embed' ); }, 100 ); } /* ---------- guards ---------- */ public static function should_run() { if ( ! empty( $_SERVER['HTTP_X_VELOX_RAW'] ) && Velox_AI::is_raw_request() ) { return false; // Our own site analysis fetch wants the untouched page. } if ( is_admin() || wp_doing_ajax() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) || is_feed() || is_customize_preview() || is_preview() || defined( 'DONOTMINIFY' ) ) { return false; } if ( isset( $_GET['velox_off'] ) && current_user_can( 'manage_options' ) ) { // phpcs:ignore WordPress.Security.NonceVerification -- admin-only troubleshooting view, nothing is changed. return false; } // phpcs:ignore WordPress.Security.NonceVerification foreach ( array( 'elementor-preview', 'et_fb', 'fl_builder', 'ct_builder', 'vc_editable', 'tve', 'brizy-edit', 'customize_changeset_uuid', 'preview', 'wc-ajax' ) as $k ) { if ( isset( $_GET[ $k ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification return false; } } return true; } private static function tokens( $text ) { $out = array(); foreach ( preg_split( '/\r\n|\r|\n/', (string) $text ) as $l ) { $l = trim( $l ); if ( '' !== $l ) { $out[] = $l; } } return $out; } /** User list + built-in compatibility list (cached per request). */ private static function delay_tokens() { static $tokens = null; if ( null === $tokens ) { $tokens = self::tokens( self::$s['delay_exclude'] ); if ( self::$s['compat_list'] ) { $tokens = array_merge( $tokens, Velox_Compat::delay_exclude() ); } } return $tokens; } private static function excluded( $haystack, $tokens ) { foreach ( $tokens as $t ) { if ( false !== stripos( $haystack, $t ) ) { return true; } } return false; } /* ---------- main pipeline ---------- */ /** Time spent on critical / used CSS in the current request (for the Server-Timing header). */ public static $css_ms = 0.0; public static function process( $html ) { if ( ! self::should_run() ) { return $html; } $original = $html; self::$s = Velox_Settings::all(); self::$img_count = 0; self::$needs_yt = false; self::$lcp_done = false; self::$lcp_preload = array(); $s = self::$s; if ( self::$css_needed ) { $tc = microtime( true ); self::compute_css( $html ); self::$css_ms = ( microtime( true ) - $tc ) * 1000; } $html = self::brand( $html ); if ( $s['webp'] && self::accepts_webp() ) { $r = preg_replace_callback( '#<(?:img|source)\b[^>]*>#i', array( __CLASS__, 'webp_cb' ), $html ); $html = null === $r ? $html : $r; } if ( $s['cdn_url'] ) { $html = self::cdn( $html ); } $protected = self::protect( $html ); if ( null === $protected ) { return $original; } list( $html, $store ) = $protected; if ( $s['lazy_youtube'] ) { $html = self::youtube( $html ); } if ( $s['lazy_images'] || $s['img_dimensions'] || $s['lcp_priority'] ) { $r = preg_replace_callback( '#]*>#i', array( __CLASS__, 'img_cb' ), $html ); $html = null === $r ? $html : $r; } if ( $s['lazy_iframes'] ) { $r = preg_replace_callback( '#]*>#i', array( __CLASS__, 'iframe_cb' ), $html ); $html = null === $r ? $html : $r; } if ( $s['html_minify'] ) { $html = self::html_minify( $html ); } $html = strtr( $html, $store ); return self::lcp_preload( $html ); } /** Adds for the LCP image right after (unless the page already has one). */ private static function lcp_preload( $html ) { if ( ! self::$lcp_preload ) { return $html; } list( $src, $srcset, $sizes ) = self::$lcp_preload; $hpos = stripos( $html, '' ); if ( false === $hpos || false !== stripos( substr( $html, 0, $hpos ), 'as="image"' ) ) { return $html; } $link = ''; $r = preg_replace( '#(]*charset[^>]*>)#i', '$1' . str_replace( '$', '\\$', $link ), $html, 1, $count ); if ( ! $count ) { $r = preg_replace( '#(]*>)#i', '$1' . str_replace( '$', '\\$', $link ), $html, 1 ); } return null === $r ? $html : $r; } private static function protect( $html ) { $store = array(); $i = 0; $out = preg_replace_callback( '#<(script|style|noscript|textarea|pre|code)\b[^>]*>.*?#is', function ( $m ) use ( &$store, &$i ) { $k = "\x02VLX" . ( $i++ ) . "\x03"; $store[ $k ] = $m[0]; return $k; }, $html ); return null === $out ? null : array( $out, $store ); } /* ---------- brand: generator meta + source comment (no visible output) ---------- */ private static function brand( $html ) { $tag = ''; $r = preg_replace_callback( '#]*>#i', function ( $m ) use ( $tag ) { return $m[0] . "\n" . $tag; }, $html, 1 ); $html = null === $r ? $html : $r; return str_ireplace( '', '' . "\n" . '', $html, $c ) ?: $html; } /* ---------- head: dns-prefetch / preload fonts ---------- */ /* ---------- CSS ---------- */ public static function url_to_path( $url ) { $url = set_url_scheme( $url, 'http' ); static $map = null; if ( null !== $map ) { return self::map_path( $url, $map ); } $up = wp_upload_dir( null, false ); // Most specific first: plugins, themes and uploads can each live outside wp-content. $map = array( trailingslashit( set_url_scheme( plugins_url(), 'http' ) ) => trailingslashit( dirname( VELOX_DIR ) ), trailingslashit( set_url_scheme( get_theme_root_uri(), 'http' ) ) => trailingslashit( get_theme_root() ), trailingslashit( set_url_scheme( $up['baseurl'], 'http' ) ) => trailingslashit( $up['basedir'] ), trailingslashit( set_url_scheme( content_url(), 'http' ) ) => trailingslashit( WP_CONTENT_DIR ), trailingslashit( set_url_scheme( includes_url(), 'http' ) ) => self::includes_dir(), ); return self::map_path( $url, $map ); } /** Folder of the core wp-includes stylesheets (block library etc.), taken from where WordPress loaded its classes. */ private static function includes_dir() { $ref = new ReflectionClass( 'WP_Styles' ); return trailingslashit( wp_normalize_path( dirname( (string) $ref->getFileName() ) ) ); } private static function map_path( $url, $map ) { foreach ( $map as $base_url => $base_path ) { if ( 0 === strpos( $url, $base_url ) ) { $rel = rawurldecode( substr( $url, strlen( $base_url ) ) ); if ( false !== strpos( $rel, "\0" ) ) { return false; } $real = realpath( $base_path . $rel ); // The resolved file must stay inside the directory the URL was mapped to. $root = realpath( $base_path ); if ( $real && $root && 0 === strpos( wp_normalize_path( $real ), trailingslashit( wp_normalize_path( $root ) ) ) ) { return $real; } } } return false; } public static function absolutize_css( $css, $base ) { $css = preg_replace_callback( '/@import\s+([\'"])([^\'"]+)\1/i', function ( $m ) use ( $base ) { $u = trim( $m[2] ); if ( preg_match( '#^(data:|https?:|//|/)#i', $u ) ) { return $m[0]; } return '@import ' . $m[1] . self::resolve( $base, $u ) . $m[1]; }, $css ); return preg_replace_callback( '/url\(\s*([\'"]?)([^\'")]+)\1\s*\)/i', function ( $m ) use ( $base ) { $u = trim( $m[2] ); if ( preg_match( '#^(data:|https?:|//|/|\#)#i', $u ) ) { return $m[0]; } return 'url(' . $m[1] . self::resolve( $base, $u ) . $m[1] . ')'; }, $css ); } private static function resolve( $base, $rel ) { if ( ! preg_match( '#^(https?://[^/]+)(/.*)$#i', $base, $b ) ) { return $base . $rel; } $parts = explode( '/', trim( $b[2], '/' ) ); if ( '' === $parts[0] ) { array_shift( $parts ); } foreach ( explode( '/', $rel ) as $seg ) { if ( '..' === $seg ) { array_pop( $parts ); } elseif ( '.' !== $seg && '' !== $seg ) { $parts[] = $seg; } } return $b[1] . '/' . implode( '/', $parts ); } public static function minify_css( $css ) { $keep = array(); $i = 0; $css = preg_replace_callback( '/"(?:[^"\\\\]|\\\\.)*"|\'(?:[^\'\\\\]|\\\\.)*\'|url\([^)]*\)/i', function ( $m ) use ( &$keep, &$i ) { $k = "\x01" . ( $i++ ) . "\x01"; $keep[ $k ] = $m[0]; return $k; }, $css ); $css = preg_replace( '!/\*.*?\*/!s', '', $css ); $css = preg_replace( '/\s+/', ' ', $css ); $css = preg_replace( '/\s*([{};,>~])\s*/', '$1', $css ); $css = str_replace( ';}', '}', $css ); $css = trim( $css ); return strtr( $css, $keep ); } /* ---------- JS ---------- */ /** * Small inline scripts that only set classes on / (no-js -> js, mobile header * breakpoints, dark mode) decide the first layout. Delaying them makes the page switch layout * seconds later (bad Speed Index and layout shift), so they always run immediately. */ private static function is_layout_inline( $body ) { if ( strlen( $body ) > 2500 ) { return false; } if ( ! preg_match( '/document\.(?:body|documentElement)|querySelector\(\s*[\'"](?:html|body)[\'"]/', $body ) ) { return false; } if ( ! preg_match( '/classList|className|setAttribute\(\s*[\'"](?:data-|class)/', $body ) ) { return false; } // Anything that loads, tracks or depends on libraries is not a layout helper. return ! preg_match( '/jQuery|\$\(|\bfetch\(|XMLHttpRequest|sendBeacon|gtag|dataLayer|fbq\(|_paq|createElement\(\s*[\'"](?:script|iframe|img)|\.src\s*=|innerHTML|document\.write|cookie/i', $body ); } /* ---------- CSS delivery: critical CSS / remove unused CSS ---------- */ /** Resolves a link/script URL to [abs_url, local_path|false]. */ private static function resolve_local( $url ) { $url = html_entity_decode( $url ); if ( preg_match( '/^\s*(data|javascript|vbscript):/i', $url ) ) { return array( $url, false ); } if ( '//' === substr( $url, 0, 2 ) ) { $url = set_url_scheme( $url ); } elseif ( '/' === substr( $url, 0, 1 ) ) { $p = wp_parse_url( home_url() ); if ( empty( $p['host'] ) ) { return array( $url, false ); } $url = ( isset( $p['scheme'] ) ? $p['scheme'] : 'https' ) . '://' . $p['host'] . ( isset( $p['port'] ) ? ':' . $p['port'] : '' ) . $url; } elseif ( ! preg_match( '#^https?://#i', $url ) ) { return array( $url, false ); } $clean = strtok( $url, '?#' ); $path = self::url_to_path( $clean ); return array( $url, ( $path && is_readable( $path ) ) ? $path : false ); } /* ---------- WebP ---------- */ public static function accepts_avif() { return isset( $_SERVER['HTTP_ACCEPT'] ) && false !== strpos( sanitize_text_field( wp_unslash( $_SERVER['HTTP_ACCEPT'] ) ), 'image/avif' ); } public static function accepts_webp() { return isset( $_SERVER['HTTP_ACCEPT'] ) && false !== strpos( sanitize_text_field( wp_unslash( $_SERVER['HTTP_ACCEPT'] ) ), 'image/webp' ); } private static function webp_cb( $m ) { $tag = $m[0]; if ( false !== stripos( $tag, 'no-webp' ) ) { return $tag; } $r = preg_replace_callback( '/(?)?\#]*?\.(?:css|js|png|jpe?g|gif|svg|webp|avif|ico|woff2?|ttf|otf|eot|mp4|webm))#i'; $out = preg_replace_callback( $re, function () use ( $cdn ) { return $cdn; }, $html ); return null === $out ? $html : $out; } /* ---------- lazy media ---------- */ private static function add_attr( $tag, $attr ) { return preg_replace( '#(\s*/?>)$#', ' ' . $attr . '$1', $tag, 1 ); } /** Adds width/height from the local file so the browser reserves space (lower CLS). */ private static function add_dimensions( $tag ) { if ( preg_match( '/(? 0 && $info[1] > 0 ) ? array( (int) $info[0], (int) $info[1] ) : false; } if ( ! $cache[ $path ] ) { return $tag; } return self::add_attr( $tag, 'width="' . $cache[ $path ][0] . '" height="' . $cache[ $path ][1] . '"' ); } private static function img_cb( $m ) { $tag = $m[0]; $s = self::$s; ++self::$img_count; $n = self::$img_count; if ( $s['img_dimensions'] ) { $tag = self::add_dimensions( $tag ); } // LCP: give the first large content image top priority and never lazy load it. $eager = false; if ( $s['lcp_priority'] && ! self::$lcp_done && $n <= 6 && false === stripos( $tag, 'logo' ) && preg_match( '/(?= 300 ) { self::$lcp_done = true; $eager = true; $tag = preg_replace( '/\sloading=(["\'])lazy\1/i', '', $tag ); if ( ! preg_match( '/(?, before it parses the body. $attr = function ( $name ) use ( $tag ) { return preg_match( '/(?)$#', ' loading="lazy"$1', $tag, 1 ); } private static function youtube( $html ) { $re = '#]*?(?]*>\s*#i'; return preg_replace_callback( $re, function ( $m ) { if ( false !== stripos( $m[0], 'data-no-lazy' ) || false !== stripos( $m[0], 'autoplay=1' ) ) { return $m[0]; } self::$needs_yt = true; $style = ''; if ( preg_match( '/(?' . '' . ''; }, $html ); } /* ---------- HTML minify ---------- */ private static function html_minify( $html ) { $out = preg_replace( '//s', '', $html ); if ( null === $out ) { return $html; } $out = preg_replace( '/\s+/', ' ', $out ); return null === $out ? $html : $out; } }