options = get_option( 'aio_options', array() ); $min_css = ! empty( $this->options['optEnableMinifyCss'] ); $min_js = ! empty( $this->options['optEnableMinifyJs'] ); if ( ! $min_css && ! $min_js ) { return; } if ( ! empty( $this->options['optMinifyExcludes'] ) ) { foreach ( preg_split( '/[\r\n]+/', (string) $this->options['optMinifyExcludes'] ) as $ex ) { $ex = trim( $ex ); if ( $ex !== '' ) { $this->excludes[] = $ex; } } } $uploads = wp_upload_dir(); $this->assets_dir = trailingslashit( wp_normalize_path( $uploads['basedir'] ) ) . 'aio-assets/'; $this->assets_url = trailingslashit( $uploads['baseurl'] ) . 'aio-assets/'; $this->site_host = strtolower( (string) wp_parse_url( home_url(), PHP_URL_HOST ) ); if ( $min_css ) { add_filter( 'style_loader_src', array( $this, 'optimize_css_src' ), 999, 2 ); } if ( $min_js ) { add_filter( 'script_loader_src', array( $this, 'optimize_js_src' ), 999, 2 ); } } private function is_excluded_asset( $src, $handle ) { if ( empty( $this->excludes ) ) { return false; } $haystack = (string) $handle . ' ' . (string) $src; foreach ( $this->excludes as $ex ) { if ( $ex !== '' && stripos( $haystack, $ex ) !== false ) { return true; } } return false; } /** * Map a local WordPress asset URL to its filesystem path safely. */ private function url_to_local_path( $url ) { $clean = strtok( (string) $url, '?#' ); if ( $clean === '' ) { return ''; } if ( strpos( $clean, '//' ) === 0 ) { $clean = ( is_ssl() ? 'https:' : 'http:' ) . $clean; } elseif ( strpos( $clean, '/' ) === 0 ) { $clean = home_url( $clean ); } $host = strtolower( (string) wp_parse_url( $clean, PHP_URL_HOST ) ); if ( $host === '' || $host !== $this->site_host ) { return ''; } $home_path = rtrim( (string) wp_parse_url( home_url( '/' ), PHP_URL_PATH ), '/' ); $url_path = rawurldecode( (string) wp_parse_url( $clean, PHP_URL_PATH ) ); if ( $home_path !== '' && strpos( $url_path, $home_path . '/' ) === 0 ) { $url_path = substr( $url_path, strlen( $home_path ) ); } if ( strpos( $url_path, '..' ) !== false || strpos( $url_path, "\0" ) !== false ) { return ''; } // Never touch already cached aio-assets or wp-includes core bundles if ( strpos( $url_path, '/aio-assets/' ) !== false || strpos( $url_path, '/wp-includes/' ) !== false || strpos( $url_path, '/wp-admin/' ) !== false ) { return ''; } $abs_root = trailingslashit( wp_normalize_path( ABSPATH ) ); $candidate = wp_normalize_path( $abs_root . ltrim( $url_path, '/' ) ); if ( strpos( $candidate, $abs_root ) === 0 && is_file( $candidate ) && is_readable( $candidate ) ) { return $candidate; } return ''; } private function ensure_assets_dir() { if ( ! file_exists( $this->assets_dir ) ) { wp_mkdir_p( $this->assets_dir ); @file_put_contents( $this->assets_dir . 'index.php', "assets_dir ) && is_writable( $this->assets_dir ); } /** * Minify local non-.min.css files and rewrite relative url(...) paths. */ public function optimize_css_src( $src, $handle ) { if ( is_user_logged_in() || empty( $src ) || $this->is_excluded_asset( $src, $handle ) ) { return $src; } $clean = strtok( (string) $src, '?#' ); if ( substr( strtolower( $clean ), -8 ) === '.min.css' || substr( strtolower( $clean ), -4 ) !== '.css' ) { return $src; } $file = $this->url_to_local_path( $src ); if ( $file === '' || filesize( $file ) > 600000 ) { return $src; } $mtime = (int) filemtime( $file ); $hash = substr( md5( $file . '|' . $mtime . '|' . AIO_VERSION . '|v2' ), 0, 16 ); $out_name = sanitize_key( $handle ) . '-' . $hash . '.min.css'; $out_path = $this->assets_dir . $out_name; if ( ! file_exists( $out_path ) ) { if ( ! $this->ensure_assets_dir() ) { return $src; } $raw = (string) @file_get_contents( $file ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged if ( $raw === '' ) { return $src; } $base_dir_url = trailingslashit( dirname( $clean ) ); $min = $this->minify_css_content( $raw, $base_dir_url ); if ( @file_put_contents( $out_path, $min ) === false ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged return $src; } } return $this->assets_url . $out_name; } /** * Minify local non-.min.js files using a safe string/regex-preserving comment & whitespace stripper. */ public function optimize_js_src( $src, $handle ) { if ( is_user_logged_in() || empty( $src ) || $this->is_excluded_asset( $src, $handle ) ) { return $src; } $clean = strtok( (string) $src, '?#' ); if ( substr( strtolower( $clean ), -7 ) === '.min.js' || substr( strtolower( $clean ), -3 ) !== '.js' ) { return $src; } $file = $this->url_to_local_path( $src ); if ( $file === '' || filesize( $file ) > 500000 ) { return $src; } $mtime = (int) filemtime( $file ); $hash = substr( md5( $file . '|' . $mtime . '|' . AIO_VERSION ), 0, 16 ); $clean_handle = preg_replace( '/^gt_widget_script_\d+$/', 'gt_widget_script', sanitize_key( $handle ) ); $out_name = $clean_handle . '-' . $hash . '.min.js'; $out_path = $this->assets_dir . $out_name; if ( ! file_exists( $out_path ) ) { if ( ! $this->ensure_assets_dir() ) { return $src; } $raw = (string) @file_get_contents( $file ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged if ( $raw === '' ) { return $src; } $min = $this->minify_js_safe( $raw ); if ( @file_put_contents( $out_path, $min ) === false ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged return $src; } } return $this->assets_url . $out_name; } private function minify_css_content( $css, $base_dir_url ) { // Strip CSS block comments first so commented-out @import / url() rules are ignored $css = preg_replace( '#/\*(?!\!)[^*]*\*+(?:[^/*][^*]*\*+)*/#', '', $css ) ?? $css; // Rewrite relative @import "..." (non-url() form) to original stylesheet directory $css = preg_replace_callback( '/@import\s+(["\'])([^"\']+)\1/i', function ( $m ) use ( $base_dir_url ) { $quote = $m[1]; $u = trim( $m[2] ); if ( $u === '' || preg_match( '#^(?:https?:|data:|//|/)#i', $u ) ) { return $m[0]; } return '@import ' . $quote . $base_dir_url . $u . $quote; }, $css ) ?? $css; // Rewrite relative url(...) references (including @import url(...)) to original stylesheet directory $css = preg_replace_callback( '/url\(\s*(["\']?)([^"\')]+)\1\s*\)/i', function ( $m ) use ( $base_dir_url ) { $quote = $m[1]; $u = trim( $m[2] ); if ( $u === '' || preg_match( '#^(?:https?:|data:|blob:|//|\#|/)#i', $u ) ) { return $m[0]; } return 'url(' . $quote . $base_dir_url . $u . $quote . ')'; }, $css ) ?? $css; $css = preg_replace( '/\s+/', ' ', $css ) ?? $css; $css = preg_replace( '/\s*([{};,>~])\s*/', '$1', $css ) ?? $css; $css = preg_replace( '/:\s+/', ':', $css ) ?? $css; $css = str_replace( ';}', '}', $css ); return trim( $css ); } /** * Conservative JS minifier: strips block/line comments outside strings/templates/regexes * and trims blank lines & leading indentation while preserving newlines so ASI never breaks! */ private function minify_js_safe( $js ) { $len = strlen( $js ); $out = ''; $i = 0; $in_str = null; while ( $i < $len ) { $ch = $js[ $i ]; $next = ( $i + 1 < $len ) ? $js[ $i + 1 ] : ''; if ( $in_str !== null ) { $out .= $ch; if ( $ch === '\\' && $i + 1 < $len ) { $out .= $js[ $i + 1 ]; $i += 2; continue; } if ( $ch === $in_str ) { $in_str = null; } $i++; continue; } if ( $ch === '"' || $ch === "'" || $ch === '`' ) { $in_str = $ch; $out .= $ch; $i++; continue; } // Block comment /* ... */ if ( $ch === '/' && $next === '*' ) { $end = strpos( $js, '*/', $i + 2 ); if ( $end === false ) { break; } $has_nl = strpos( substr( $js, $i, $end - $i + 2 ), "\n" ) !== false; $out .= $has_nl ? "\n" : ' '; $i = $end + 2; continue; } // Single-line comment // ... (only when at start of line or preceded by whitespace/;/{/}/(/)/,) if ( $ch === '/' && $next === '/' ) { $prev = $out !== '' ? substr( $out, -1 ) : "\n"; if ( ctype_space( $prev ) || in_array( $prev, array( ';', '{', '}', '(', ')', ',', '[' ), true ) ) { $nl = strpos( $js, "\n", $i + 2 ); if ( $nl === false ) { break; } $out .= "\n"; $i = $nl + 1; continue; } } $out .= $ch; $i++; } // Trim per-line leading/trailing horizontal spaces & collapse empty lines $lines = explode( "\n", str_replace( "\r", '', $out ) ); $clean = array(); foreach ( $lines as $line ) { $t = trim( $line, " \t" ); if ( $t !== '' ) { $clean[] = $t; } } return implode( "\n", $clean ); } /** * Delete all generated .min.css / .min.js cached files. */ public static function clear_minified_assets() { $uploads = wp_upload_dir(); $dir = trailingslashit( wp_normalize_path( $uploads['basedir'] ) ) . 'aio-assets/'; if ( ! is_dir( $dir ) ) { return; } $files = glob( $dir . '*' ); if ( is_array( $files ) ) { foreach ( $files as $f ) { if ( is_file( $f ) && basename( $f ) !== 'index.php' ) { @unlink( $f ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged } } } } }