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