include_dependencies(); $this->options = wp_optimize_minify_config()->get(); // Main process add_action('wp', array($this, 'init')); // Beaver builder add_action('fl_builder_after_save_layout', array('WP_Optimize_Minify_Cache_Functions', 'reset')); // extra_preload_headers is currently not available to users // add_action('send_headers', array($this, 'extra_preload_headers')); } /** * Fired on wp init * * @return void */ public function init() { /** * Check whether Minify is run on the current page */ if (!$this->run_on_page()) return; if ($this->options['emoji_removal']) { $this->disable_emojis(); } if ($this->options['clean_header_one']) { $this->remove_header_meta_info(); } // Headers & Preload JS/CSS/Extra if ($this->options['enabled_css_preload'] || $this->options['enabled_js_preload']) { add_action('wp_footer', array($this, 'generate_preload_headers'), PHP_INT_MAX); } if ($this->options['enable_js']) { $this->process_js(); } if ($this->options['enable_css']) { $this->process_css(); } // Preload tags if (trim($this->options['hpreload'])) { add_action('wp_head', array($this, 'add_assets_preload'), 2); } if ($this->should_process_html()) { add_action('template_redirect', array('WP_Optimize_Minify_Functions', 'html_compression_start'), PHP_INT_MAX); } if ($this->should_use_loadCSS()) { add_action('wp_footer', array('WP_Optimize_Minify_Print', 'add_load_css'), PHP_INT_MAX); } $this->remove_query_string_from_static_assets(); } /** * Detects whether cache preloading is running or not * * @return bool */ private function is_cache_preload() { return isset($_SERVER['HTTP_X_WP_OPTIMIZE_CACHE_PRELOAD']) && 0 === strcmp($_SERVER['HTTP_X_WP_OPTIMIZE_CACHE_PRELOAD'], 'Yes'); } /** * Wether to run the feature on a page or not * * @param string $context - Optional, The context where the check is done * @return boolean */ public function run_on_page($context = 'default') { /** * Filters wether the functionality is ran on the current page. * * @param boolean $run_on_page * @param string $context - Optional, The feature where the check is done */ return apply_filters( 'wpo_minify_run_on_page', !is_admin() && (!defined('SCRIPT_DEBUG') || !SCRIPT_DEBUG) && !is_preview() && (!function_exists('is_customize_preview') || !is_customize_preview()) && !($this->options['disable_when_logged_in'] && is_user_logged_in()) && !(function_exists('is_amp_endpoint') && is_amp_endpoint()) && !WP_Optimize_Minify_Functions::exclude_contents(), $context ); } /** * Inline css in place, instead of inlining the large file * * @param String $html * @param String $handle * @param String $href * @param String $media * * @return String */ public function inline_css($html, $handle, $href, $media) { $exclude_css = array_map('trim', explode("\n", trim($this->options['exclude_css']))); $ignore_list = WP_Optimize_Minify_Functions::compile_ignore_list($exclude_css); $blacklist = WP_Optimize_Minify_Functions::get_ie_blacklist(); $async_css = array_map('trim', explode("\n", trim($this->options['async_css']))); $master_ignore = array_merge($ignore_list, $blacklist); // make sure href is complete $href = WP_Optimize_Minify_Functions::get_hurl($href); if ($this->options['debug']) { echo "\n"; } // skip all this, if the async css option is enabled if ($this->options['loadcss']) return $html; // remove all css? if ($this->options['remove_css']) return false; // leave conditionals alone if (wp_styles()->get_data($handle, 'conditional')) return $html; // mediatype fix for some plugins + remove print mediatypes if ('screen' == $media || 'screen, print' == $media || empty($media) || is_null($media) || false == $media ) { $media = 'all'; } if (!empty($this->options['remove_print_mediatypes']) && 'print' == $media) { return false; } // Exclude specific CSS files from PageSpeedInsights? if (WP_Optimize_Minify_Functions::in_arrayi($href, $async_css)) { WP_Optimize_Minify_Print::exclude_style($href); return false; } // remove wpo_min from the ignore list $ignore_list = array_filter($ignore_list, array($this, 'check_wpo')); // return if in any ignore or black list if (count($master_ignore) > 0 && WP_Optimize_Minify_Functions::in_arrayi($href, $master_ignore)) { return $html; } // check if working with a font awesom link if (WP_Optimize_Minify_Functions::is_font_awesome($href)) { // font awesome processing, async css if ('async' == $this->options['fawesome_method']) { WP_Optimize_Minify_Print::async_style($href, $media); return false; } elseif ('exclude' === $this->options['fawesome_method']) { // font awesome processing, async and exclude from PageSpeedIndex WP_Optimize_Minify_Print::exclude_style($href); return false; } elseif ('inline' == $this->options['fawesome_method']) { WP_Optimize_Minify_Print::inline_style($handle, $href); return false; } } // Check if working with google font url if ('fonts.googleapis.com' == parse_url($href, PHP_URL_HOST)) { // check if google fonts should be removed if ($this->options['remove_googlefonts']) return false; // check if google fonts should be merged if ($this->options['merge_google_fonts']) { if (WP_Optimize_Minify_Functions::is_flatsome_handle($handle)) { $href = WP_Optimize_Minify_Functions::fix_flatsome_google_fonts_url($href); $this->collect_google_fonts[$handle] = $href; } else { $this->collect_google_fonts[$handle] = $href; } return false; } else { if ('inline' === $this->options['gfonts_method']) { if (WP_Optimize_Minify_Functions::is_flatsome_handle($handle)) { $href = WP_Optimize_Minify_Functions::fix_flatsome_google_fonts_url($href); } // download, minify, cache $tkey = 'css-'.hash('adler32', $handle.$href).'.css'; $json = false; $json = WP_Optimize_Minify_Cache_Functions::get_transient($tkey); if (false === $json) { $json = WP_Optimize_Minify_Functions::download_and_minify($href, null, $this->options['enable_css_minification'], 'css', $handle); if ($this->options['debug']) { echo "\n"; } WP_Optimize_Minify_Cache_Functions::set_transient($tkey, $json); } // decode $res = json_decode($json, true); // add font-display // https://developers.google.com/web/updates/2016/02/font-display $res['code'] = str_ireplace('font-style:normal;', 'font-display:block;font-style:normal;', $res['code']); // inline css or fail if (false != $res['status']) { echo '' . "\n"; return false; } else { if ($this->options['debug']) { echo "\n"; } return $html; } } elseif ('async' === $this->options['gfonts_method']) { WP_Optimize_Minify_Print::async_style($href); return false; } elseif ('exclude' === $this->options['gfonts_method']) { WP_Optimize_Minify_Print::exclude_style($href); return false; } } } // skip external scripts that are not specifically allowed if (false === WP_Optimize_Minify_Functions::internal_url($href, site_url()) || empty($href)) { if ($this->options['debug']) { echo "\n"; } return $html; } $file_size = WP_Optimize_Minify_Functions::get_file_size($href); // If we can't determine file size, then we still need to proceed with normal minify process if (apply_filters('wp_optimize_skip_inlining', false === $file_size || $file_size > 20480, $file_size, $href)) return $html; // download, minify, cache $tkey = 'css-'.hash('adler32', $handle.$href).'.css'; $json = false; $json = WP_Optimize_Minify_Cache_Functions::get_transient($tkey); if (false === $json) { $json = WP_Optimize_Minify_Functions::download_and_minify($href, null, $this->options['enable_css_minification'], 'css', $handle); if ($this->options['debug']) { echo "" . "\n"; } WP_Optimize_Minify_Cache_Functions::set_transient($tkey, $json); } // decode $res = json_decode($json, true); // inline it + other inlined children styles if (false != $res['status']) { echo '' . "\n"; // get inline_styles for this handle, minify and print $inline_styles = array(); $inline_styles = wp_styles()->get_data($handle, 'after'); if (false != $inline_styles) { // string type if (is_string($inline_styles)) { $code = WP_Optimize_Minify_Functions::get_css($href, $inline_styles, $this->options['enable_css_minification']); if (!empty($code) && false != $code) { echo '' . "\n"; } } // array type if (is_array($inline_styles)) { foreach ($inline_styles as $st) { $code = WP_Optimize_Minify_Functions::get_css($href, $st, $this->options['enable_css_minification']); if (!empty($code) && false != $code) { echo '' . "\n"; } } } } // prevent default return false; } else { if ($this->options['debug']) { echo "" . "\n"; } return $html; } echo "\n"; return $html; } /** * Enable defer for JavaScript (WP 4.1 and above) and remove query strings for ignored files * * @param String $tag * @param String $handle * @param String $src * * @return String */ public function defer_js($tag, $handle, $src) { $wp_domain = trim(str_ireplace(array('http://', 'https://'), '', trim(site_url(), '/'))); $exclude_js = array_map('trim', explode("\n", trim($this->options['exclude_js']))); $ignore_list = WP_Optimize_Minify_Functions::compile_ignore_list($exclude_js); // Should this defer the Poly fills for IE? $blacklist = WP_Optimize_Minify_Functions::get_ie_blacklist(); // no query strings if (false !== stripos($src, '?ver')) { $srcf = stristr($src, '?ver', true); // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctionParameters.stristr_before_needleFound $tag = str_ireplace($src, $srcf, $tag); $src = $srcf; } // return if defer option is set to individual if ('individual' === $this->options['enable_defer_js']) { return $tag; } // return the tag if it's a polyfill if (count($blacklist) > 0 && WP_Optimize_Minify_Functions::in_arrayi($src, $blacklist)) { return $tag; } // Skip deferring the jQuery library option, if the defer jQuery option is disabled if (!$this->options['defer_jquery'] && (false !== stripos($tag, '/jquery.js') || false !== stripos($tag, '/jquery.min.js') || (false !== stripos($tag, '/jquery-') && false !== stripos($tag, '.js'))) ) { return $tag; } // return if external script url https://www.chromestatus.com/feature/5718547946799104 if (WP_Optimize_Minify_Functions::is_local_domain($src) !== true) { return $tag; } // bypass if already optimized if (false !== stripos($tag, 'navigator.userAgent.match')) { return $tag; } // should we exclude defer on the login page? if ($this->options['exclude_defer_login'] && false !== stripos($_SERVER["SCRIPT_NAME"], strrchr(wp_login_url(), '/')) ) { return $tag; } // add defer attribute, but only if not having async or defer already if (stripos($tag, 'defer') === false && stripos($tag, 'async') === false) { // add cdn for PageSpeedIndex if (!empty($this->options['cdn_url'])) { $cdn_url = trim(trim(str_ireplace(array('http://', 'https://'), '', trim($this->options['cdn_url'], '/'))), '/'); $src = str_ireplace($wp_domain, $cdn_url, $src); } if ('async_using_js' === $this->options['defer_js_type']) { return WP_Optimize_Minify_Print::async_script($src, false); } // remove wpo_min from the ignore list $ignore_list = array_filter($ignore_list, array($this, 'check_wpo')); if (count($ignore_list) > 0 && WP_Optimize_Minify_Functions::in_arrayi($src, $ignore_list)) { return $tag; } else { /** * Filters whether to use the defer attribute or async. * * @default string 'defer' (any other value will be set to async) * @param string $value - 'defer' or 'async' * @param string $tag - The