'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( '#