D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
home
/
everqlsh
/
public_html
/
wp-admin
/
user
/
577040
/
Filename :
minify.tar
back
Copy
class-wp-optimize-minify-print.php 0000644 00000016377 15162435531 0013312 0 ustar 00 <?php if (!defined('ABSPATH')) die('No direct access allowed'); if (!class_exists('WP_Optimize_Minify_Functions')) { include WP_OPTIMIZE_MINIFY_DIR.'/class-wp-optimize-minify-functions.php'; } class WP_Optimize_Minify_Print { /** * Load the script using loadAsync JavaScript * * @param string $href * @param boolean $print * @return string|void */ public static function async_script($href, $print = true) { $wpo_minify_options = wp_optimize_minify_config()->get(); $tag = '<script>' . "\n"; $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : ''; $tag .= 'var wpo_server_info_js = ' . wp_json_encode(array("user_agent" => $user_agent)) . "\n"; $exclude_js_from_page_speed_tools = $wpo_minify_options['exclude_js_from_page_speed_tools']; $enable_defer_js = $wpo_minify_options['enable_defer_js']; $is_conditional_loading = $exclude_js_from_page_speed_tools && 'all' !== $enable_defer_js; if ($is_conditional_loading) { $tag .= 'if (!(navigator.userAgent + " " + wpo_server_info_js.user_agent).match(/' . implode("|", $wpo_minify_options['ualist']) .'/i)) {' . "\n"; } $tag .= " loadAsync('$href', null);" . "\n"; if ($is_conditional_loading) { $tag .= '}'; } $tag .= '</script>' . "\n"; if ($print) { echo $tag; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output is already escaped } else { return $tag; } } /** * Print a style that will be loaded async using 'preload' * * @param string $href * @param string $media * @return void */ public static function async_style($href, $media = 'all') { // phpcs:disable WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet -- Not applicable for this function echo '<link rel="preload" href="'.esc_url($href).'" as="style" media="'.esc_attr($media).'" onload="this.onload=null;this.rel=\'stylesheet\'">' . "\n"; // fix for firefox not supporting preload echo '<link rel="stylesheet" href="'.esc_url($href).'" media="'.esc_attr($media).'">' . "\n"; echo '<noscript><link rel="stylesheet" href="'.esc_url($href).'" media="'.esc_attr($media).'"></noscript>' . "\n"; echo '<!--[if IE]><link rel="stylesheet" href="'.esc_url($href).'" media="'.esc_attr($media).'"><![endif]-->' . "\n"; // phpcs:enable } /** * Print a style that will be loaded by JS * * @param string $href * @return void */ public static function exclude_style($href) { $wpo_minify_options = wp_optimize_minify_config()->get(); // make a stylesheet, hide from PageSpeedIndex $cssguid = 'wpo_min'.hash('adler32', $href); $tag = '<script>' . "\n"; $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : ''; $tag .= 'var wpo_server_info_css = ' . wp_json_encode(array("user_agent" => $user_agent)) . "\n"; $exclude_css_from_page_speed_tools = $wpo_minify_options['exclude_css_from_page_speed_tools']; if ($exclude_css_from_page_speed_tools) { $tag .= 'if (!(navigator.userAgent + " " + wpo_server_info_css.user_agent).match(/'.implode('|', $wpo_minify_options['ualist']).'/i)){' . "\n"; } $tag .= ' var '.$cssguid.'=document.createElement("link");'.$cssguid.'.rel="stylesheet",'.$cssguid.'.type="text/css",'.$cssguid.'.media="async",'.$cssguid.'.href="'.$href.'",'.$cssguid.'.onload=function() {'.$cssguid.'.media="all"},document.getElementsByTagName("head")[0].appendChild('.$cssguid.');' . "\n"; if ($exclude_css_from_page_speed_tools) { $tag .= '}'; } $tag .= '</script>' . "\n"; echo $tag; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output is already escaped } /** * Inline a single style * * @param string $handle * @param string $href * @return boolean */ public static function inline_style($handle, $href) { $wpo_minify_options = wp_optimize_minify_config()->get(); // font awesome processing, inline // download, minify, cache $tkey = 'css-'.hash('adler32', $href).'.css'; $json = WP_Optimize_Minify_Cache_Functions::get_transient($tkey); if (false === $json) { $json = WP_Optimize_Minify_Functions::download_and_minify($href, null, $wpo_minify_options['enable_css_minification'], 'css', $handle); if ($wpo_minify_options['debug']) { echo "<!-- wpo_min DEBUG: Uncached file processing now for " . esc_html($href) . " -->" . "\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 '<style type="text/css" media="all">' . "\n"; echo $res['code'] . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- This is already escaped echo '</style>' . "\n"; return true; } else { return false; } } /** * Print a normal style tag pointing the href * * @param string $href * @return void */ public static function style($href) { echo '<link rel="stylesheet" href="'.esc_url($href).'" media="all">' . "\n"; // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet -- Not applicable here } /** * Write header * * @param string $file * @param string $headers * @return void */ public static function write_header($file, $headers) { file_put_contents($file, $headers); WP_Optimize_Minify_Cache_Functions::fix_permission_bits($file); } /** * Write CSS * * @param string $file * @param string $code * @param string $log * @return void */ public static function write_combined_asset($file, $code, $log) { file_put_contents($file.'.json', wp_json_encode($log)); file_put_contents($file, $code); // permissions WP_Optimize_Minify_Cache_Functions::fix_permission_bits($file.'.json'); WP_Optimize_Minify_Cache_Functions::fix_permission_bits($file); if (function_exists('gzencode')) { file_put_contents($file.'.gz', gzencode(file_get_contents($file), 9)); WP_Optimize_Minify_Cache_Functions::fix_permission_bits($file.'.gz'); } // brotli static support if (function_exists('brotli_compress')) { file_put_contents($file.'.br', brotli_compress(file_get_contents($file), 11)); WP_Optimize_Minify_Cache_Functions::fix_permission_bits($file.'.br'); } } /** * Load async scripts with callback * * @return void */ public static function add_load_async() { $min_or_not_internal = WP_Optimize()->get_min_or_not_internal_string(); $contents = file_get_contents(trailingslashit(WPO_PLUGIN_MAIN_PATH) . "js/loadAsync$min_or_not_internal.js"); echo "<script>$contents</script>\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output is already escaped } /** * Defer CSS globally from the header (order matters) * Dev: https://www.filamentgroup.com/lab/async-css.html * * @return void */ public static function add_load_css() { $min_or_not_internal = WP_Optimize()->get_min_or_not_internal_string(); $contents = file_get_contents(trailingslashit(WPO_PLUGIN_MAIN_PATH) . "js/loadCSS$min_or_not_internal.js"); echo "<script>$contents</script>" . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output is already escaped } } class-wp-optimize-minify-load-url-task.php 0000644 00000002605 15162435532 0014623 0 ustar 00 <?php if (!defined('ABSPATH')) die('Access denied.'); if (!class_exists('Updraft_Task_1_2')) require_once(WPO_PLUGIN_MAIN_PATH . 'vendor/team-updraft/common-libs/src/updraft-tasks/class-updraft-task.php'); class WP_Optimize_Minify_Load_Url_Task extends Updraft_Task_1_2 { /** * Default options. */ public function get_default_options() { return array(); } /** * Run preload http requests with different user-agent values to cache pages for different devices. * * @return bool */ public function run() { $url = $this->get_option('url'); if (empty($url)) return; $minify_preloader = WP_Optimize_Minify_Preloader::instance(); // load pages with different user-agents values. $minify_preloader->preload_desktop($url); $minify_preloader->preload_amp($url); if (defined('WP_CLI') && WP_CLI) { WP_CLI::log($url); } /** * Action triggered after preloading a single url * * @param string $url The url to preload * @param object $minify_preloader Minify preloader instance */ do_action('wpoptimize_after_minify_preload_url', $url, $minify_preloader); /** * Allows to change the delay between each URL preload, to reduce server load. * * @param integer $preload_delay The delay between each request in microseconds (1000000 = 1 second). */ usleep(apply_filters('wpoptimize_minify_preload_delay', 500000)); return true; } } class-wp-optimize-minify-config.php 0000644 00000012753 15162435533 0013417 0 ustar 00 <?php if (!defined('ABSPATH')) die('No direct access allowed'); /** * Handles cache configuration and related I/O */ if (!class_exists('WP_Optimize_Minify_Config')) : class WP_Optimize_Minify_Config { static protected $_instance = null; private $_options = array(); /** * Construct * * @return void */ private function __construct() { } /** * Getter to see if Minify is enabled * * @return bool */ public function is_enabled() { return $this->get('enabled'); } /** * Removes all Minify settings from the Database if 'preserve_settings_on_uninstall' is false * * @return void */ public function purge() { if (!$this->get('preserve_settings_on_uninstall')) { if (is_multisite()) { delete_site_option('wpo_minify_config'); } else { delete_option('wpo_minify_config'); } } } /** * Get config from file or cache * * @param string $option - An option name * @param mixed $default - A default for the option * @return array|string */ public function get($option = null, $default = false) { if (empty($this->_options)) { if (is_multisite()) { $config = get_site_option('wpo_minify_config', array()); } else { $config = get_option('wpo_minify_config', array()); } $this->_options = wp_parse_args($config, $this->get_defaults()); } if ($option && isset($this->_options[$option])) { return $this->_options[$option]; } elseif ($option) { return $default; } return $this->_options; } /** * Update the config * * @param array $config - The new configuration array * @return boolean */ public function update($config) { $prev_settings = $this->get(); $prev_settings = wp_parse_args($prev_settings, $this->get_defaults()); $new_settings = wp_parse_args($config, $prev_settings); $this->_options = $new_settings; if (is_multisite()) { update_site_option('wpo_minify_config', $new_settings); } else { update_option('wpo_minify_config', $new_settings); } return true; } /** * Return defaults * * @return array */ public function get_defaults() { $defaults = array( // dev tab checkboxes 'debug' => false, 'enabled_css_preload' => false, 'enabled_js_preload' => false, 'hpreconnect' => '', 'hpreload' => '', 'loadcss' => false, 'remove_css' => false, 'critical_path_css' => '', 'critical_path_css_is_front_page' => '', // settings tab checkboxes 'preserve_settings_on_uninstall' => true, 'disable_when_logged_in' => false, 'default_protocol' => 'dynamic', // dynamic, http, https 'html_minification' => true, 'clean_header_one' => false, 'emoji_removal' => true, 'merge_google_fonts' => true, 'enable_display_swap' => true, 'remove_googlefonts' => false, 'disable_google_fonts_processing' => false, 'gfonts_method' => 'inherit', // inline, async, exclude 'fawesome_method' => 'inherit', // inline, async, exclude 'enable_css' => true, 'enable_css_minification' => true, 'enable_merging_of_css' => true, 'remove_print_mediatypes' => false, 'inline_css' => false, 'enable_js' => true, 'enable_js_minification' => true, 'enable_merging_of_js' => true, 'enable_defer_js' => 'individual', 'defer_js_type' => 'defer', 'defer_jquery' => true, 'enable_js_trycatch' => false, 'exclude_defer_login' => true, 'cdn_url' => '', 'cdn_force' => false, 'enable_delay_js' => false, 'enable_preload_js' => false, 'exclude_delay_js' => '', 'async_css' => '', 'async_js' => '', 'disable_css_inline_merge' => true, /** * UA list compiled from these resources: * https://explore.whatismybrowser.com/useragents/explore/software_name/gtmetrix-analyser/ * https://explore.whatismybrowser.com/useragents/explore/software_name/pingdom-bot/ * https://explore.whatismybrowser.com/useragents/explore/software_name/google-lighthouse/ * https://explore.whatismybrowser.com/useragents/explore/software_name/googlebot/ * https://explore.whatismybrowser.com/useragents/explore/software_name/headless-chrome/ */ 'ualist' => array('Googlebot', 'Chrome-Lighthouse', 'GTmetrix', 'HeadlessChrome', 'Pingdom'), 'exclude_js_from_page_speed_tools' => false, 'exclude_css_from_page_speed_tools' => false, 'blacklist' => array(), 'ignore_list' => array(), 'exclude_js' => '', 'exclude_css' => '', 'edit_default_exclutions' => false, 'merge_allowed_urls' => '', // internal 'enabled' => false, 'last-cache-update' => 0, 'plugin_version' => '0.0.0', 'cache_lifespan' => 30, 'merge_inline_extra_css_js' => true, 'enable_analytics' => false, 'analytics_method' => 'gtagv4', 'tracking_id' => '', ); return apply_filters('wpo_minify_defaults', $defaults); } /** * Check whether everything should be purged instead of just incremented * * @return boolean */ public function always_purge_everything() { /** * Filters the result of always_purge_everything * * @return boolean */ return apply_filters('wpo_minify_always_purge_everything', 0 === intval($this->get('cache_lifespan')) || (defined('WPO_ADVANCED_CACHE') && defined('WP_CACHE') && WP_CACHE)); } /** * Get the instance * * @return WP_Optimize_Minify_Config */ public static function get_instance() { if (!self::$_instance) { self::$_instance = new self(); } return self::$_instance; } } /** * Get WPO Minify instance * * @return WP_Optimize_Minify_Config */ function wp_optimize_minify_config() { return WP_Optimize_Minify_Config::get_instance(); } endif; class-wp-optimize-detect-minify-plugins.php 0000644 00000003432 15162435534 0015074 0 ustar 00 <?php if (!defined('ABSPATH')) die('No direct access allowed'); class WP_Optimize_Detect_Minify_Plugins { /** * Detect list of active most popular WordPress minify plugins. * * @return array */ public function get_active_minify_plugins() { $active_minify_plugins = array(); foreach ($this->get_plugins() as $plugin_slug => $plugin_title) { if ($this->is_plugin_active($plugin_slug) && $this->is_minify_active($plugin_slug)) { $active_minify_plugins[$plugin_slug] = $plugin_title; } } return $active_minify_plugins; } /** * Get the plugins list * * @return array */ protected function get_plugins() { return array( 'w3-total-cache' => 'W3 Total Cache', 'autoptimize' => 'Autoptimize', 'fast-velocity-minify' => 'Fast Velocity Minify', ); } /** * Check if $plugin is active. * * @param string $plugin - plugin slug * * @return bool */ private function is_plugin_active($plugin) { $status = WP_Optimize()->get_db_info()->get_plugin_status($plugin); return $status['active']; } /** * Check if minify feature is active * * @return bool */ public function is_minify_active($plugin_slug) { switch ($plugin_slug) { case 'w3-total-cache': return (function_exists('w3tc_config') && w3tc_config()->get_boolean('minify.enabled')); case 'autoptimize': return ('on' == get_option('autoptimize_js', false) || 'on' == get_option('autoptimize_css', false) || 'on' == get_option('autoptimize_html', false)); case 'fast-velocity-minify': return true; } } /** * Instance of WP_Optimize_Detect_Minify_Plugins. * * @return WP_Optimize_Detect_Minify_Plugins */ public static function get_instance() { static $instance = null; if (null === $instance) { $instance = new self(); } return $instance; } } class-wp-optimize-minify-preloader.php 0000644 00000011215 15162435535 0014121 0 ustar 00 <?php if (!defined('ABSPATH')) die('No direct access allowed'); if (!class_exists('WP_Optimize_Minify_Load_Url_Task')) { require_once(WPO_PLUGIN_MAIN_PATH . 'minify/class-wp-optimize-minify-load-url-task.php'); } class WP_Optimize_Minify_Preloader extends WP_Optimize_Preloader { protected $preload_type = 'minify'; protected $task_type = 'minify-load-url-task'; static protected $_instance = null; /** * WP_Optimize_Page_Cache_Preloader constructor. */ public function __construct() { parent::__construct(); add_filter('cron_schedules', array($this, 'cron_add_intervals')); } /** * Check if minify is active. * * @return bool */ public function is_option_active() { if (!function_exists('wp_optimize_minify_config')) { include_once WPO_PLUGIN_MAIN_PATH . 'minify/class-wp-optimize-minify-config.php'; } return wp_optimize_minify_config()->get('enabled'); } /** * Add intervals to cron schedules. * * @param array $schedules * * @return array */ public function cron_add_intervals($schedules) { $interval = $this->get_continue_preload_cron_interval(); $schedules['wpo_minify_preload_continue_interval'] = array( 'interval' => $interval, 'display' => round($interval / 60, 1).' minutes' ); return $schedules; } /** * Create tasks (WP_Optimize_Load_Url_Task) for preload all urls from site. * * @param string $type The preload type (currently: scheduled, manual) * @return void */ public function create_tasks_for_preload_site_urls($type = 'manual') { $urls = $this->get_site_urls(); if (!empty($urls)) { $this->log(__('Minify: Creating tasks for preload site urls.', 'wp-optimize')); foreach ($urls as $url) { if (wpo_url_in_exceptions($url)) continue; // this description is being used for internal purposes. $description = 'Preload - '.$url; $options = array('url' => $url, 'preload_type' => $type, 'anonymous_user_allowed' => (defined('DOING_CRON') && DOING_CRON) || (defined('WP_CLI') && WP_CLI)); WP_Optimize_Minify_Load_Url_Task::create_task($this->task_type, $description, $options, 'WP_Optimize_Minify_Load_Url_Task'); } $this->log(__('Minify: Tasks for preload site urls created.', 'wp-optimize')); } } /** * Instance of WP_Optimize_Minify_Preloader. * * @return WP_Optimize_Minify_Preloader */ public static function instance() { if (empty(self::$_instance)) { self::$_instance = new WP_Optimize_Minify_Preloader(); } return self::$_instance; } /** * Option disabled error message * * @return array */ protected function get_option_disabled_error() { return array( 'success' => false, 'error' => __('Minify is disabled.', 'wp-optimize') ); } /** * Get preload already running error message * * @return array */ protected function get_preload_already_running_error() { return array( 'success' => false, 'error' => __('Probably minify preload is running already.', 'wp-optimize') ); } protected function get_preload_data() { $cache_path = WP_Optimize_Minify_Cache_Functions::cache_path(); $cache_dir = $cache_path['cachedir']; $minify_cache_data = array(); $minify_cache_data['size'] = esc_html(WP_Optimize_Minify_Cache_Functions::get_cachestats($cache_dir)); $minify_cache_data['total_size'] = esc_html(WP_Optimize_Minify_Cache_Functions::get_cachestats(WPO_CACHE_MIN_FILES_DIR)); return $minify_cache_data; } protected function get_preloading_message($minify_cache_data) { return array( 'done' => false, 'message' => __('Loading URLs...', 'wp-optimize'), 'size' => WP_Optimize()->format_size($minify_cache_data['size']), 'total_size' => $minify_cache_data['total_size'] ); } protected function get_last_preload_message($minify_cache_data, $last_preload_time_str) { return array( 'done' => true, // translators: %s: last preload time 'message' => sprintf(__('Last preload finished at %s', 'wp-optimize'), $last_preload_time_str), 'size' => WP_Optimize()->format_size($minify_cache_data['size']), 'total_size' => $minify_cache_data['total_size'] ); } protected function get_preload_success_message($minify_cache_data) { return array( 'done' => true, 'size' => WP_Optimize()->format_size($minify_cache_data['size']), 'total_size' => $minify_cache_data['total_size'] ); } protected function get_preload_progress_message($minify_cache_data, $preloaded_message, $preload_resuming_in) { return array( 'done' => false, 'message' => $preloaded_message, 'size' => WP_Optimize()->format_size($minify_cache_data['size']), 'total_size' => $minify_cache_data['total_size'], 'resume_in' => $preload_resuming_in ); } } WP_Optimize_Minify_Preloader::instance(); class-wp-optimize-minify-fonts.php 0000644 00000022465 15162435537 0013310 0 ustar 00 <?php if (!defined('ABSPATH')) die('No direct access allowed'); class WP_Optimize_Minify_Fonts { private static $fonts = array(); private static $subsets = array(); /** * Get a list of Google fonts * * @return Array */ public static function get_google_fonts() { // https://www.googleapis.com/webfonts/v1/webfonts?sort=alpha $google_fonts_file = WPO_PLUGIN_MAIN_PATH.'google-fonts.json'; if (is_file($google_fonts_file) && is_readable($google_fonts_file)) { return json_decode(file_get_contents($google_fonts_file), true); } return array(); } /** * Check if the google font exist or not * * @param string $font * @return boolean */ public static function concatenate_google_fonts_allowed($font) { $gfonts_whitelist = self::get_google_fonts(); // normalize $font = str_ireplace('+', ' ', strtolower($font)); return in_array($font, $gfonts_whitelist); } /** * Concatenates Google Fonts tags (http://fonts.googleapis.com/css?...) * * @param array $gfonts_array * @return string|boolean */ public static function concatenate_google_fonts($gfonts_array) { // Loop through fonts array foreach ($gfonts_array as $font) { self::parse_font_url($font); } self::convert_v1_font_specs_to_v2(); $merge = self::build(); $config = wp_optimize_minify_config(); /** * Filters whether to add display=swap to Google fonts urls * * @param boolean $display - Default to true */ if (apply_filters('wpo_minify_gfont_display_swap', $config->get('enable_display_swap'))) { /** * Filters the value of the display parameter. * * @param string $display_value - Default to 'swap'. https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display */ $merge.= '&display='.apply_filters('wpo_minify_gfont_display_type', 'swap'); } if (!empty($merge)) return 'https://fonts.googleapis.com/css2?' . $merge; return false; } /** * Parses font url based on whether it is API version 1 or 2 */ private static function parse_font_url($font) { if (false !== strpos($font, 'css?')) { self::parse_font_api1_url($font); } else { self::parse_font_api2_url($font); } } /** * Parses google font api version 1 url */ private static function parse_font_api1_url($font) { parse_str(wp_parse_url(rtrim($font, '|'), PHP_URL_QUERY), $font_elements); // Process each font family foreach (explode('|', $font_elements['family']) as $font_family) { // Separate font and sizes $font_family = explode(':', $font_family); // if the family wasn't added yet if (!in_array($font_family[0], array_keys(self::$fonts))) { self::$fonts[$font_family[0]]['specs'] = isset($font_family[1]) ? explode(',', rtrim($font_family[1], ',')) : array(); } else { // if the family was already added, and this new one has weights, merge with previous if (isset($font_family[1])) { if (isset(self::$fonts[$font_family[0]]['version']) && 'V2' == self::$fonts[$font_family[0]]['version']) { self::$fonts[$font_family[0]]['specs'] = explode(',', rtrim($font_family[1], ',')); } else { self::$fonts[$font_family[0]]['specs'] = array_merge(self::$fonts[$font_family[0]]['specs'], explode(',', rtrim($font_family[1], ','))); } } } self::$fonts[$font_family[0]]['version'] = 'V1'; } // Add subsets if (isset($font_elements['subset'])) { self::$subsets = array_merge(self::$subsets, explode(',', $font_elements['subset'])); } } /** * Parses google font api version 2 url */ private static function parse_font_api2_url($font) { $parsed_url = wp_parse_url($font, PHP_URL_QUERY); $query_elements = explode('&', $parsed_url); foreach ($query_elements as $element) { $family_str = str_replace('family=', '', $element); $family = explode(':', $family_str); if (!empty($family)) { $font_name = $family[0]; $font_elements = isset($family[1]) ? explode('@', $family[1]) : ''; if (!empty($font_elements) && !empty($font_elements[0]) && !empty($font_elements[1])) { $font_styles = $font_elements[0]; $font_units = explode(',', $font_elements[1]); } } else { $font_name = $family_str; continue; } if (!isset(self::$fonts[$font_name])) { self::$fonts[$font_name]['specs'] = array( 'wght' => array(), 'ital' => array(), 'ital,wght' => array(), ); } if (!isset(self::$fonts[$font_name]['version'])) { self::$fonts[$font_name]['version'] = 'V2'; } if (isset($font_styles) && isset($font_units) && isset($font_elements[1])) { $font_units = explode(';', $font_elements[1]); switch ($font_styles) { case 'wght': foreach ($font_units as $font_unit) { if (!in_array($font_unit, self::$fonts[$font_name]['specs']['wght'])) { array_push(self::$fonts[$font_name]['specs']['wght'], $font_unit); } } break; case 'ital': foreach ($font_units as $font_unit) { if (!in_array($font_unit, self::$fonts[$font_name]['specs']['ital'])) { array_push(self::$fonts[$font_name]['specs']['ital'], $font_unit); } } break; case 'ital,wght': foreach ($font_units as $font_unit) { if (!in_array($font_unit, self::$fonts[$font_name]['specs']['ital,wght'])) { array_push(self::$fonts[$font_name]['specs']['ital,wght'], $font_unit); } } break; } } } } /** * Converts google font api version 1 font specification into API V2 */ private static function convert_v1_font_specs_to_v2() { foreach (self::$fonts as $font_name => $font_details) { if ('V2' == $font_details['version']) continue; if (0 == count($font_details['specs'])) { self::$fonts[$font_name]['specs'] = array( 'wght' => array(), 'ital' => array(), 'ital,wght' => array(), ); } else { foreach ($font_details['specs'] as $key => $detail) { if (is_array($detail)) $detail = implode('', $detail); switch ($detail) { case 'i': unset(self::$fonts[$font_name]['specs'][$key]); self::$fonts[$font_name]['specs']['ital'] = array(1); break; case 'b': unset(self::$fonts[$font_name]['specs'][$key]); self::$fonts[$font_name]['specs']['wght'] = array(); break; case 'bi': unset(self::$fonts[$font_name]['specs'][$key]); self::$fonts[$font_name]['specs']['ital'] = array('0;1'); break; default: unset(self::$fonts[$font_name]['specs'][$key]); if (!isset(self::$fonts[$font_name]['specs']['ital,wght'])) { self::$fonts[$font_name]['specs']['ital,wght'] = array(); } if ('inherit' === $detail) { array_push(self::$fonts[$font_name]['specs']['ital,wght'], '0,400'); array_push(self::$fonts[$font_name]['specs']['ital,wght'], '1,400'); } elseif ('regular' === $detail) { $detail = 'regular' === $detail ? 400 : $detail; array_push(self::$fonts[$font_name]['specs']['ital,wght'], '0,' . $detail); } elseif (false !== strpos($detail, 'i')) { $detail = str_replace(array('italic', 'i'), '', $detail); $detail = '' === $detail ? 400 : $detail; array_push(self::$fonts[$font_name]['specs']['ital,wght'], '1,' . $detail); } else { $detail = str_replace(array('normal'), '', $detail); $detail = '' === $detail ? 400 : $detail; array_push(self::$fonts[$font_name]['specs']['ital,wght'], '0,' . $detail); } break; } } } } } /** * Build valid Google font api 2 url string * * @return string $result Url string */ private static function build() { $result = ''; foreach (self::$fonts as $font_name => $font_details) { if ('display=swap' == $font_name) continue; if ('' != $result) { $result .= '&'; } $result .= 'family=' . str_replace(' ', '+', $font_name); $result .= self::specs_to_string($font_details['specs']); } return $result; } /** * Converts font specifications into a valid google font api2 url string * * @param array $font_specs Font style and weight specifications * * @return string */ private static function specs_to_string($font_specs) { $result = array(); $weights = isset($font_specs['wght']) && count($font_specs['wght']); $italic_weights = isset($font_specs['ital']) && count($font_specs['ital']); $all_weights = isset($font_specs['ital,wght']) && count($font_specs['ital,wght']); // Nothing is set, return if (!$weights && !$italic_weights && !$all_weights) { return ''; } // Italic only if ($italic_weights && !$weights && !$all_weights) { if ('1' == $font_specs['ital'][0]) { return ':ital@1'; } elseif ('0;1' == $font_specs['ital'][0]) { return ':ital@0;1'; } } foreach ($font_specs as $style => $units) { switch ($style) { case 'wght': foreach ($units as $unit) { $multiple_units = explode(',', $unit); if (count($multiple_units) > 0) { foreach ($multiple_units as $single_unit) { array_push($result, '0,' . $single_unit); } } else { array_push($result, '0,' . $unit); } } break; case 'ital': foreach ($units as $unit) { array_push($result, 1 == $unit ? '1,400' : $unit); } break; case 'ital,wght': foreach ($units as $unit) { array_push($result, $unit); } break; } } sort($result); return ':ital,wght@' . implode(';', array_unique($result)); } } class-wp-optimize-minify-commands.php 0000644 00000026272 15162435540 0013752 0 ustar 00 <?php if (!defined('ABSPATH')) die('No direct access allowed'); if (!class_exists('WP_Optimize_Minify_Config')) require_once(dirname(__FILE__) . '/class-wp-optimize-minify-config.php'); if (!class_exists('WP_Optimize_Minify_Preloader')) require_once(dirname(__FILE__) . '/class-wpo-minify-preloader.php'); /** * All cache commands that are intended to be available for calling from any sort of control interface (e.g. wp-admin, UpdraftCentral) go in here. All public methods should either return the data to be returned, or a WP_Error with associated error code, message and error data. */ class WP_Optimize_Minify_Commands { /** * List all cache files * * @param array $data - The $_POST data * @return array */ public function get_minify_cached_files($data = array()) { $stamp = isset($data['stamp']) ? $data['stamp'] : 0; $files = WP_Optimize_Minify_Cache_Functions::get_cached_files($stamp, false); $files['js'] = array_map(array('WP_Optimize_Minify_Cache_Functions', 'format_file_logs'), $files['js']); $files['css'] = array_map(array('WP_Optimize_Minify_Cache_Functions', 'format_file_logs'), $files['css']); return $files; } /** * Removes the entire cache dir. * Use with caution, as cached html may still reference those files. * * @return array */ public function purge_all_minify_cache() { WP_Optimize_Minify_Cache_Functions::purge(); WP_Optimize_Minify_Cache_Functions::cache_increment(); $others = WP_Optimize_Minify_Cache_Functions::purge_others(); $files = $this->get_minify_cached_files(); $message = array_merge(array(esc_html__('The minification cache was deleted.', 'wp-optimize')), $others); $message = WP_Optimize_Minify_Functions::apply_strip_tags_for_messages_array($message, ''); return array( 'success' => true, 'message' => implode("\n", $message), 'files' => $files ); } /** * Forces a new Cache to be built * * @return array */ public function minify_increment_cache() { WP_Optimize_Minify_Cache_Functions::cache_increment(); $files = $this->get_minify_cached_files(); return array( 'success' => true, 'files' => $files ); } /** * Purge the cache * * @return array */ public function purge_minify_cache() { if (!WP_Optimize()->get_minify()->can_purge_cache() && !(defined('WP_CLI') && WP_CLI)) { return array( 'error' => __('You do not have permission to purge the cache', 'wp-optimize') ); } // deletes temp files and old caches incase CRON isn't working WP_Optimize_Minify_Cache_Functions::cache_increment(); if (wp_optimize_minify_config()->always_purge_everything()) { WP_Optimize_Minify_Cache_Functions::purge(); $state = array(); $old = array(); } else { $state = WP_Optimize_Minify_Cache_Functions::purge_temp_files(); $old = WP_Optimize_Minify_Cache_Functions::purge_old(); } $others = WP_Optimize_Minify_Cache_Functions::purge_others(); $files = $this->get_minify_cached_files(); $notice = array_merge(array(esc_html__('All caches from WP-Optimize Minify have been purged.', 'wp-optimize')), $others); $notice = WP_Optimize_Minify_Functions::apply_strip_tags_for_messages_array($notice, ''); $notice = wp_json_encode($notice); // encode return array( 'result' => 'caches cleared', 'others' => implode("\n", $others), 'state' => $state, 'message' => $notice, 'old' => $old, 'files' => $files ); } /** * Delete minify cache file * * @param string $filename * @return array */ public function delete_minify_cache_file($filename) { if (!WP_Optimize()->get_minify()->can_purge_cache()) return array('error' => __('You do not have permission to purge the cache', 'wp-optimize'), 'result' => '', 'files' => ''); $response = array( 'result' => __('Cache file was not found.', 'wp-optimize'), 'files' => '', 'error' => '', ); if (empty($filename)) return $response; $is_valid_filename = $this->is_valid_filename($filename); if (!$is_valid_filename) { return $response; } else { $filename = pathinfo($filename, PATHINFO_BASENAME); $data = $this->get_file_data($filename); if (empty($data)) return $response; $this->fetch_and_remove_temp_minify_cache_files($data); $files = $this->get_minify_cached_files(); return array( 'result' => __('Cache deleted.', 'wp-optimize'), 'files' => $files, 'error' => '', ); } } /** * Fetch and remove the temp and minify js/css cache files. * * @param Array $data file data */ public function fetch_and_remove_temp_minify_cache_files($data) { $filename = $data['filename']; $log = $data['log']; // get cache directories and urls $cache_path = WP_Optimize_Minify_Cache_Functions::cache_path(); $tmp_dir = $cache_path['tmpdir']; $cache_dir = $cache_path['cachedir']; $ext_to_delete = array('', '.json', '.gz'); foreach ($ext_to_delete as $ext) { $this->check_and_delete($cache_dir . '/' . $filename . $ext); } if (empty($log->files)) return; foreach ($log->files as $key => $value) { $transient_file = $this->get_transient_file($key, $value, $filename); $this->check_and_delete($tmp_dir.'/'.$transient_file); } } /** * Save options to the config * * @param array $data * @return array */ public function save_minify_settings($data) { $new_data = array(); foreach ($data as $key => $value) { if ('true' === $value) { $new_data[$key] = true; } elseif ('false' === $value) { $new_data[$key] = false; } else { $new_data[$key] = trim($value); } } if (isset($data['minify_advanced_tab'])) { // Make sure that empty settings are still saved if (!isset($new_data['ignore_list'])) $new_data['ignore_list'] = array(); if (!isset($new_data['blacklist'])) $new_data['blacklist'] = array(); } /** * Filters the data before saving it * * @param array $new_data - The original data * @return array The data, altered or not */ $new_data = apply_filters('wpo_save_minify_settings', $new_data); if (!class_exists('WP_Optimize_Minify_Config')) return array( 'success' => false, 'message' => "WP_Optimize_Minify_Config class doesn't exist", ); $working = wp_optimize_minify_config()->update($new_data); if (!$working) { return array( 'success' => false, 'error' => 'failed to save minify settings' ); } $purged = $this->purge_minify_cache(); return array( 'success' => true, 'files' => isset($purged['files']) ? $purged['files'] : array(), ); } /** * Hide the information notice for the current user * * @return array */ public function hide_minify_notice() { return array( 'success' => update_user_meta(get_current_user_id(), 'wpo-hide-minify-information-notice', true) ); } /** * Get the current status * * @return array */ public function get_status() { $config = wp_optimize_minify_config()->get(); return array( 'enabled' => $config['enabled'], 'js' => $config['enable_js'], 'css' => $config['enable_css'], 'html' => $config['html_minification'], 'stats' => $this->get_minify_cached_files() ); } /** * Run minify preload action. * * @return void|array - Doesn't return anything if run() is successful (Run() prints a JSON object and closed browser connection) or an array if failed. */ public function run_minify_preload() { return WP_Optimize_Minify_Preloader::instance()->run('manual'); } /** * Cancel minify preload action. * * @return array */ public function cancel_minify_preload() { WP_Optimize_Minify_Preloader::instance()->cancel_preload(); return WP_Optimize_Minify_Preloader::instance()->get_status_info(); } /** * Get status of minify preload. * * @return array */ public function get_minify_preload_status() { return WP_Optimize_Minify_Preloader::instance()->get_status_info(); } /** * Returns the combined json of all available meta.json files * * @return array */ public function get_minify_meta_files() { $enabled = wp_optimize_minify_config()->get('enabled'); if (!$enabled) return array( 'success' => false, 'error' => __('Minify not enabled', 'wp-optimize'), ); $combined_metas = array( 'meta_logs' => array() ); // loop through wpo-minify cache directory and get the meta.json files, combine into a single json file if (is_dir(WPO_CACHE_MIN_FILES_DIR) && wp_is_writable(dirname(WPO_CACHE_MIN_FILES_DIR))) { if ($handle = opendir(WPO_CACHE_MIN_FILES_DIR)) { while (false !== ($d = readdir($handle))) { if (0 === strcmp($d, '.') || 0 === strcmp($d, '..') || !is_numeric($d)) { continue; } $cache_min_folder = WPO_CACHE_MIN_FILES_DIR.'/'.$d; if ($cache_min_folder_handle = opendir($cache_min_folder)) { while (false !== ($maybe_file = readdir($cache_min_folder_handle))) { if (0 === strcmp($maybe_file, '.') || 0 === strcmp($maybe_file, '..')) { continue; } $maybe_file_path = $cache_min_folder . '/' . $maybe_file; if (is_file($maybe_file_path) && 'meta.json' === basename($maybe_file_path)) { $combined_metas['meta_logs'][$d] = json_decode(file_get_contents($maybe_file_path)); } } closedir($cache_min_folder_handle); } } closedir($handle); } } if (0 === count($combined_metas['meta_logs'])) { return array( 'success' => false, 'error' => __('No file was found', 'wp-optimize'), ); } else { return array( 'success' => true, 'combined_metas' => $combined_metas ); } } /** * Get minify file data. * * @param string $filename * @return array */ private function get_file_data($filename) { $log = false; $data = array(); $cache_path = WP_Optimize_Minify_Cache_Functions::cache_path(); $cache_dir = $cache_path['cachedir']; $file = $cache_dir.'/'.$filename; if (file_exists($file.'.json')) { $log = json_decode(file_get_contents($file.'.json')); $data['filename'] = $filename; $data['log'] = $log; } return $data; } /** * Check minify file exists and delete it. * * @param string $file */ private function check_and_delete($file) { if (file_exists($file)) { wp_delete_file($file); } } /** * Get transient file. * * @param string $key * @param string $value * @param string $filename * @return string */ private function get_transient_file($key, $value, $filename) { $url = $value->url; $file_url = site_url().$url; $file_ext = $this->get_file_extension($filename); $href = WP_Optimize_Minify_Functions::get_hurl($file_url); $transient_key = $file_ext.'-'.hash('adler32', $key.$href).'.'.$file_ext; return $transient_key.'.transient'; } /** * Get file extension. * * @param string $filename * @return string */ private function get_file_extension($filename) { $file_info = pathinfo($filename); return $file_info['extension']; } /** * Check for valid filename. * For file name pattern see here, the function 'process_header_css' in the file 'class-wp-optimize-minify-front-end.php' where the file name is constructed. You can find' WP_Optimize_Minify_Print::write_combined_asset' and go to the passed $file variable where it's defined or constructed. * * @param string $filename * @return bool */ private function is_valid_filename($filename) { if (preg_match('/^[a-zA-Z0-9.-]*$/', $filename)) { return true; } return false; } } class-wp-optimize-minify-functions.php 0000644 00000113500 15162435541 0014151 0 ustar 00 <?php if (!defined('ABSPATH')) die('No direct access allowed'); // handle better utf-8 and unicode encoding if (function_exists('mb_internal_encoding')) { mb_internal_encoding('UTF-8'); } // must have // phpcs:disable // Squiz.PHP.DiscouragedFunctions.Discouraged -- Not applicable here ini_set('pcre.backtrack_limit', 5000000); ini_set('pcre.recursion_limit', 5000000); // phpcs:enable use MatthiasMullie\Minify; class WP_Optimize_Minify_Functions { /** * Applies `strip_tags` function for given array of messages * * @param array $messages Array of messages * @param string $allowed_tags Tags to retain in message (optional) * * @return array */ public static function apply_strip_tags_for_messages_array($messages, $allowed_tags = '<strong>') { return array_map(function($message) use ($allowed_tags) { return strip_tags($message, $allowed_tags); }, $messages); } /** * Detect external or internal scripts * * @param string $src * @return boolean */ public static function is_local_domain($src) { $wpo_minify_options = wp_optimize_minify_config()->get(); $locations = array(home_url(), site_url(), network_home_url(), network_site_url()); $async_using_js = 'all' === $wpo_minify_options['enable_defer_js'] && 'async_using_js' === $wpo_minify_options['defer_js_type']; // excluded from cdn because of https://www.chromestatus.com/feature/5718547946799104 (we use document.write to preserve render blocking) if (!empty($wpo_minify_options['cdn_url']) && (!$async_using_js || $wpo_minify_options['cdn_force']) ) { array_push($locations, $wpo_minify_options['cdn_url']); } // cleanup locations $locations = array_filter(array_unique($locations)); // external or not? $ret = false; foreach ($locations as $l) { $l = preg_replace('/^https?:\/\//i', '', trim($l)); $l = trim(trim(preg_replace('/^www./', '', $l), '/')); if (stripos($src, $l) !== false && false === $ret) { $ret = true; } } // response return $ret; } /** * Functions, get hurl info * * @param mixed $src * * @return string */ public static function get_hurl($src) { $wp_home = site_url(); $wp_domain = trim(str_ireplace(array('http://', 'https://'), '', trim($wp_home, '/'))); // preserve empty source handles $hurl = null === $src ? '' : trim($src); if (empty($hurl)) { return $hurl; } // some fixes $hurl = str_ireplace(array('&', '&'), '&', $hurl); if (is_ssl()) { $protocol = 'https://'; } else { $protocol = 'http://'; } // make sure wp_home doesn't have a forward slash $wp_home = rtrim($wp_home, '/'); // apply some filters if (substr($hurl, 0, 2) === "//") { $hurl = $protocol.ltrim($hurl, "/"); }//end if if (substr($hurl, 0, 4) === "http" && stripos($hurl, $wp_domain) === false) { return $hurl; }//end if if (substr($hurl, 0, 4) !== "http" && stripos($hurl, $wp_domain) !== false) { $hurl = $wp_home.'/'.ltrim($hurl, "/"); }//end if // prevent double forward slashes in the middle $hurl = str_ireplace('###', '://', str_ireplace('//', '/', str_ireplace('://', '###', $hurl))); // consider different wp-content directory $proceed = 0; if (!empty($wp_home)) { $alt_wp_content = basename($wp_home); if (substr($hurl, 0, strlen($alt_wp_content)) === $alt_wp_content) { $proceed = 1; } } // Get the name of the WP-CONTENT folder. Default is wp-content, but can be changed by the user. $wp_content_folder = str_replace(ABSPATH, '', WP_CONTENT_DIR); // protocol + home for relative paths if ("/".WPINC === substr($hurl, 0, 12) || "/wp-admin" === substr($hurl, 0, 9) || "/$wp_content_folder" === substr($hurl, 0, 11) || 1 == $proceed ) { $hurl = $wp_home.'/'.ltrim($hurl, "/"); } // make sure there is a protocol prefix as required $hurl = $protocol.preg_replace('/^https?:\/\//i', '', $hurl); // enforce protocol // no query strings if (stripos($hurl, '.js?v') !== false) { $hurl = stristr($hurl, '.js?v', true).'.js'; }//end if if (stripos($hurl, '.css?v') !== false) { $hurl = stristr($hurl, '.css?v', true).'.css'; }//end if return $hurl; } /** * Check if it's an internal url or not * * @param string $hurl * @param string $wp_home * @param mixed $noxtra * @return boolean */ public static function internal_url($hurl, $wp_home, $noxtra = null) { $wpo_minify_options = wp_optimize_minify_config()->get(); if (substr($hurl, 0, strlen($wp_home)) === $wp_home) { return true; } if (stripos($hurl, $wp_home) !== false) { return true; } if (isset($_SERVER['HTTP_HOST']) && stripos($hurl, preg_replace('/:\d+$/', '', sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST'])))) !== false) { return true; } if (isset($_SERVER['SERVER_NAME']) && stripos($hurl, preg_replace('/:\d+$/', '', sanitize_text_field(wp_unslash($_SERVER['SERVER_NAME'])))) !== false) { return true; } if (isset($_SERVER['SERVER_ADDR']) && '::1' != sanitize_text_field(wp_unslash($_SERVER['SERVER_ADDR'])) && stripos($hurl, preg_replace('/:\d+$/', '', sanitize_text_field(wp_unslash($_SERVER['SERVER_ADDR'])))) !== false) { return true; } // allow specific external urls to be merged if (null === $noxtra) { $merge_allowed_urls = array_map('trim', explode("\n", $wpo_minify_options['merge_allowed_urls'])); if (is_array($merge_allowed_urls) && strlen(implode($merge_allowed_urls)) > 0) { foreach ($merge_allowed_urls as $e) { if (stripos($hurl, $e) !== false && !empty($e)) { return true; } } } } return false; } /** * Case-insensitive in_array() wrapper * * @param string $hurl * @param array $ignore * @return boolean */ public static function in_arrayi($hurl, $ignore) { $hurl = preg_replace('/^https?:\/\//i', '//', $hurl); // better compatibility $hurl = strtok(urldecode(rawurldecode($hurl)), '?'); // no query string, decode entities if (!empty($hurl) && is_array($ignore)) { foreach ($ignore as $i) { $i = preg_replace('/^https?:\/\//i', '//', $i); // better compatibility $i = strtok(urldecode(rawurldecode($i)), '?'); // no query string, decode entities $i = trim(trim(trim(rtrim($i, '/')), '*')); // wildcard char removal if (!empty($i) && false !== stripos($hurl, $i)) { return true; } } } return false; } /** * Check if selected url is point to already minifiled css/js file * * @param string $url * @return bool */ public static function is_minified_css_js_filename($url) { $parts = wp_parse_url($url); if (empty($parts['path']) || !is_string(basename($parts['path']))) return false; return 1 === preg_match('/\.min\.(js|css)$/i', basename($parts['path'])); } /** * Better compatibility urls + fix w3.org NamespaceAndDTDIdentifiers * * @param string $code * @return string * */ private static function compat_urls($code) { $wpo_minify_options = wp_optimize_minify_config()->get(); $default_protocol = $wpo_minify_options['default_protocol']; if ('dynamic' == $default_protocol) { if ((isset($_SERVER['HTTPS']) && ('on' == $_SERVER['HTTPS'] || 1 == $_SERVER['HTTPS'])) || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && 'https' == $_SERVER['HTTP_X_FORWARDED_PROTO']) ) { $default_protocol = 'https://'; } else { $default_protocol = 'http://'; } } else { $default_protocol = $default_protocol.'://'; } $code = preg_replace('/^https?:\/\//i', $default_protocol, $code); $code = str_ireplace($default_protocol.'www.w3.org', 'http://www.w3.org', $code); return $code; } /** * Minify css string with PHP Minify * * @param string $css * @return string */ public static function minify_css_string($css) { $css = apply_filters('wpo_minify_css_string', $css); $minifier = new Minify\CSS($css); $minifier->setMaxImportSize(15); // [css only] embed assets up to 15 Kb (default 5Kb) - processes gif, png, jpg, jpeg, svg & woff $min = $minifier->minify(); if (false !== $min) { return self::compat_urls($min); } return self::compat_urls($css); } /** * Minify js on demand (one file at one time, for compatibility) * * @param string $url * @param string $js * @param boolean $enable_js_minification * @return string */ public static function get_js($url, $js, $enable_js_minification) { $wpo_minify_options = wp_optimize_minify_config()->get(); // exclude minification on already minified files + jquery (because minification might break those) $excl = array('jquery.js', '.min.js', '-min.js', '/uploads/fusion-scripts/', '/min/', '.packed.js', '/includes/builder/scripts/'); foreach ($excl as $e) { if (stripos(basename($url), $e) !== false) { $enable_js_minification = false; break; } } $encoding = mb_detect_encoding($js); // remove BOM $js = self::remove_utf8_bom($js); self::maybe_log_error_message($url, $encoding, $js); // minify JS if ($enable_js_minification) { $js = self::minify_js_string($js); } else { $js = self::compat_urls($js); } // Remove source mapping files $js = preg_replace('/(\/\/\s*[#]\s*sourceMappingURL\s*[=]\s*)(.+)\s*/ui', '', $js); // needed when merging js files $js = trim($js); if (substr($js, -1) != ';') { $js = $js.';'; } if ($wpo_minify_options['debug']) { $js = '/* info: ' . $url . ' */' . "\n" . $js; } /** * Filters the imported JavaScript * * @param string $js - The imported JS * @param string $url - The imported url * @param boolean $enable_js_minification - Whether to minify or not */ return apply_filters('wpo_minify_get_js', $js . "\n", $url, $enable_js_minification); } /** * Minify JS string with PHP Minify or YUI Compressors * * @param string $js * @return string */ public static function minify_js_string($js) { $js = apply_filters('wpo_minify_js_string', $js); // PHP Minify from https://github.com/matthiasmullie/minify $minifier = new Minify\JS($js); $min = $minifier->minify(); if (false !== $min && (strlen(trim($js)) == strlen(trim($min)) || strlen(trim($min)) > 0)) { return self::compat_urls($min); } // if we are here, something went wrong and minification didn't work $js = "\n/*! wpo_min: Minification of the following section failed, so it has been merged instead. */\n".$js; return self::compat_urls($js); } /** * Wrapper to minify the inlined JS string - Adds an extra check for JSON * * @param string $js * @return string */ public static function minify_inline_js($js) { $js = apply_filters('wpo_minify_inline_js_string', $js); // Do not minify JSON (JS minification will minify `true` to `!0`, which will break the JSON) if (json_decode($js)) return $js; return self::minify_js_string($js, true); } /** * Functions, minify html * * @param string $html * @return string */ public static function minify_html($html) { $minify_css = wp_optimize_minify_config()->get('enable_css_minification'); $minify_js = wp_optimize_minify_config()->get('enable_js_minification'); $options = array(); if ($minify_css && apply_filters('wpo_minify_inline_css', true)) { $options['cssMinifier'] = array('WP_Optimize_Minify_Functions', 'minify_css_string'); } if ($minify_js && apply_filters('wpo_minify_inline_js', true)) { $options['jsMinifier'] = array('WP_Optimize_Minify_Functions', 'minify_inline_js'); } return Minify_HTML::minify($html, $options); } /** * Functions to minify HTML * * @param string $html * @return string */ public static function html_compression_finish($html) { return self::minify_html($html); } /** * Start the compression * * @return void */ public static function html_compression_start() { if (self::exclude_contents() == true) { return; } ob_start(array(__CLASS__, 'html_compression_finish')); } /** * Remove default HTTP headers * * @return void */ public static function remove_redundant_shortlink() { remove_action('wp_head', 'wp_shortlink_wp_head', 10); remove_action('template_redirect', 'wp_shortlink_header', 11); } /** * Minify css on demand (one file at one time, for compatibility) * * @param string $url * @param string $css * @param boolean $enable_css_minification * @return string */ public static function get_css($url, $css, $enable_css_minification) { $wpo_minify_options = wp_optimize_minify_config()->get(); $encoding = mb_detect_encoding($css); // remove BOM $css = self::remove_utf8_bom($css); self::maybe_log_error_message($url, $encoding, $css); // fix url paths if (!empty($url)) { $css = self::make_css_urls_absolute($css, $url); } $css = str_ireplace('@charset "UTF-8";', '', $css); // remove query strings from fonts (for better seo, but add a small cache buster based on most recent updates) // last update or zero $cache_time = $wpo_minify_options['last-cache-update']; // fonts cache buster $css = preg_replace('/(.eot|.woff2|.woff|.ttf)+[?+](.+?)(\)|\'|\")/ui', "$1"."#".$cache_time."$3", $css); // Remove Sourcemappingurls $css = preg_replace('/(\/\*\s*[#]\s*sourceMappingURL\s*[=]\s*)(.[^*]+)\s*\*\//ui', '', $css); // If @import is found, process it/them if (false !== strpos($css, '@import')) $css = self::replace_css_import($css, $url); // minify CSS if ($enable_css_minification) { $css = self::minify_css_string($css); } else { $css = self::compat_urls($css); } // cdn urls $cdn_url = $wpo_minify_options['cdn_url']; if (!empty($cdn_url)) { $wp_domain = trim(preg_replace('/^https?:\/\//i', '', trim(site_url(), '/'))); $cdn_url = trim(trim(preg_replace('/^https?:\/\//i', '', trim($cdn_url, '/'))), '/'); $css = str_ireplace($wp_domain, $cdn_url, $css); } // add css comment $css = trim($css); if ($wpo_minify_options['debug']) { $css = '/* info: ' . $url . ' */' . "\n" . trim($css); } /** * Filters the imported CSS * * @param string $css - The imported CSS * @param string $url - The imported url * @param boolean $enable_css_minification - Whether to minify or not */ return apply_filters('wpo_minify_get_css', $css, $url, $enable_css_minification); } /** * Adds full path to relative url() rules * * @param string $css - The CSS to process * @param string $url - The URL or the CSS being processed * @return string */ public static function make_css_urls_absolute($css, $url) { $matches = array(); preg_match_all("/url\(\s*['\"]?(?!data:)(?!http)(?![\/'\"])(.+?)['\"]?\s*\)/ui", $css, $matches); foreach ($matches[1] as $a) { $b = trim($a); if ($b != $a) { $css = str_replace($a, $b, $css); } } return preg_replace("/url\(\s*['\"]?(?!data:)(?!http)(?![\/'\"])(.+?)['\"]?\s*\)/ui", "url(".dirname($url)."/$1)", $css); } /** * Include @import[ed] files - The @import statement can only be used at the top of a file, which breaks when merging everything. * * @param string $css - The original CSS containing the @import statement * @param string $file_url - The original CSS' URL * @return string */ public static function replace_css_import($css, $file_url) { $remove_print_mediatypes = wp_optimize_minify_config()->get('remove_print_mediatypes'); $debug = wp_optimize_minify_config()->get('debug'); return preg_replace_callback('/(?:@import)\s(?:url\()?\s?["\'](.*?)["\']\s?\)?(?:[^;]*);?/im', function($matches) use ($file_url, $remove_print_mediatypes, $debug) { // @import contains url() if (preg_match('/url\s*\((.[^\)]*)[\)*?](.*);/', $matches[0], $url_matches)) { $url = trim(str_replace(array('"', "'"), '', $url_matches[1])); $media_query = trim($url_matches[2]); // @import uses quotes only } elseif (preg_match('/["\'](.*)["\'](.*);?/', $matches[0], $no_url_matches)) { $url = trim($no_url_matches[1]); $media_query = trim($no_url_matches[2], ") ;"); } // If $media_query contains print, and $remove_print_mediatypes is true, return empty string if ($remove_print_mediatypes && false !== strpos($media_query, 'print') && apply_filters('wpo_minfy_remove_print_mediatypes_import', true, $url, $media_query, $matches[0], $file_url)) return ($debug ? '/*! Info: the import of "'.$url.'" was removed because the setting remove_print_mediatypes is enabled. */' : ''); $purl = wp_parse_url($url); // If there's no host, the url is relative to $file_url, so prepend with the base url. if (!isset($purl['host'])) { $url = dirname($file_url).'/'.$url; } // Download @import $asset_content = WP_Optimize_Minify_Functions::get_asset_content($url); $content = $asset_content['content']; if (!$content) return ''; // Fix the URLs $content = WP_Optimize_Minify_Functions::make_css_urls_absolute($content, $url); if ($media_query) { // Wrap the code with the media query $content = "@media $media_query {\n$content\n}"; } if ($debug) { $content = "/*! CSS import Information: code imported from $url */\n$content\n/*! END CSS import Information */"; } // If the code contains its own @import, recursively include it. if (false !== strpos($content, '@import')) { return WP_Optimize_Minify_Functions::replace_css_import($content, $url); } return $content; }, $css); } /** * Download and cache css and js files * * @param string $hurl * @param string $inline * @param boolean $enable_minification * @param string $type * @param string $handle * @return boolean|string */ public static function download_and_minify($hurl, $inline, $enable_minification, $type, $handle, $version = '') { // must have if (is_null($hurl) || empty($hurl)) { return false; } if (!in_array($type, array('js', 'css'))) { return false; } $wpo_minify_options = wp_optimize_minify_config()->get(); // filters and defaults $print_url = str_ireplace(array(site_url(), home_url(), 'http:', 'https:'), '', $hurl); $log = array( 'url' => $print_url, ); // defaults if (false != $enable_minification) { $enable_minification = true; } if (is_null($inline) || empty($inline)) { $inline = ''; } $print_handle = ''; if (is_null($handle) || empty($handle)) { $handle = ''; } else { $print_handle = "[$handle]"; } // debug request $dreq = array( 'hurl' => $hurl, 'inline' => $inline, 'enable_minification' => $enable_minification, 'type' => $type, 'handle' => $handle, 'version' => $version ); $asset_content = self::get_asset_content($hurl); $code = $asset_content['content']; // If $code is empty: if (!$code) { $log['success'] = false; if ($wpo_minify_options['debug']) { $log['debug'] = "$print_handle failed. Tried wp_remote_get and local file_get_contents."; } $return = array('request' => $dreq, 'log' => $log, 'code' => '', 'status' => false); return wp_json_encode($return); } if ('js' == $type) { $code = self::get_js($hurl, $code, $enable_minification); } else { $code = self::get_css($hurl, $code.$inline, $enable_minification); } // log, save and return if ($wpo_minify_options['debug']) { $version_msg = ('' != $version) ? "[Version: $version]" : ""; $log['debug'] = $print_handle . $version_msg . ' was '.('local' === $asset_content['method'] ? 'opened' : 'fetched').' from '.$hurl; } $log['success'] = true; $return = array('request' => $dreq, 'log' => $log, 'code' => $code, 'status' => true); return wp_json_encode($return); } /** * Get the content of an asset, whether local or remote * * @param string $url * @return array */ public static function get_asset_content($url) { $wp_home = site_url(); $wp_domain = wp_parse_url($wp_home, PHP_URL_HOST); // If the file is local. if (false !== stripos($url, $wp_domain)) { // default $f = str_ireplace(rtrim($wp_home, '/'), rtrim(ABSPATH, '/'), $url); // failover when home_url != site_url if (!file_exists($f)) { $nhurl = str_ireplace(site_url(), home_url(), $url); $f = str_ireplace(rtrim($wp_home, '/'), rtrim(ABSPATH, '/'), $nhurl); } clearstatcache(); if (file_exists($f)) { $content = file_get_contents($f); // check for php code, skip if found if ("<?php" != strtolower(substr($content, 0, 5)) && false === stripos($content, "<?php")) { return array('content' => $content, 'method' => 'local'); } } } // else, fallback to remote urls (or windows) $content = self::download_remote($url); if (false !== $content && !empty($content) && strtolower(substr($content, 0, 9)) != "<!doctype" ) { // check if we got HTML instead of js or css code return array('content' => $content, 'method' => 'remote'); } // fallback when home_url != site_url if (stripos($url, $wp_domain) !== false && home_url() != site_url()) { $nhurl = str_ireplace(site_url(), home_url(), $url); $content = self::download_remote($nhurl); if (false !== $content && !empty($content) && '<!doctype' != strtolower(substr($content, 0, 9))) { return array('content' => $content, 'method' => 'remote'); } } return array('content' => '', 'method' => 'none'); } /** * Remove emoji support * * @return void */ public static function disable_wp_emojicons() { 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('wp_enqueue_scripts', 'wp_enqueue_emoji_styles'); remove_action('admin_print_styles', 'print_emoji_styles'); remove_action('admin_enqueue_scripts', 'wp_enqueue_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'); } /** * Remove from tinymce * * @return array */ public static function disable_emojis_tinymce($plugins) { if (is_array($plugins)) { return array_diff($plugins, array('wpemoji')); } else { return array(); } } /** * Remove UTF8 BOM. * Returns BOM removed string or null when `$string` does not have a recognised encoding * * @param string $string * @return string|null */ public static function remove_utf8_bom($string) { $bom = pack('H*', 'EFBBBF'); return preg_replace("/^$bom/ui", '', $string); } /** * Remove query string from static css files * * @param string $src * @return string */ public static function remove_cssjs_ver($src) { if (stripos($src, '?ver=')) { $src = remove_query_arg('ver', $src); } return $src; } /** * Rewrite cache files to http, https or dynamic * * @param string $url * @return string */ public static function get_protocol($url) { $wp_domain = trim(preg_replace('/^https?:\/\//i', '', trim(site_url(), '/'))); $wpo_minify_options = wp_optimize_minify_config()->get(); $default_protocol = $wpo_minify_options['default_protocol']; $url = ltrim(preg_replace('/^https?:\/\//i', '', $url), '/'); // better compatibility // cdn support $cdn_url = $wpo_minify_options['cdn_url']; $cdn_url = trim(trim(preg_replace('/^https?:\/\//i', '', trim($cdn_url, '/'))), '/'); // process cdn rewrite if (!empty($cdn_url) && self::is_local_domain($url) !== false) { // for js files, we need to consider thew defer for insights option if (substr($url, -3) == '.js') { $async_using_js = 'all' === $wpo_minify_options['enable_defer_js'] && 'async_using_js' === $wpo_minify_options['defer_js_type']; if (!$async_using_js || $wpo_minify_options['cdn_force'] ) { $url = str_ireplace($wp_domain, $cdn_url, $url); } } else { $url = str_ireplace($wp_domain, $cdn_url, $url); } } // enforce protocol if needed if ('dynamic' == $default_protocol) { if ((isset($_SERVER['HTTPS']) && ('on' == $_SERVER['HTTPS'] || 1 == $_SERVER['HTTPS'])) || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && 'https' == $_SERVER['HTTP_X_FORWARDED_PROTO']) ) { $default_protocol = 'https://'; } else { $default_protocol = 'http://'; } } else { $default_protocol = $default_protocol.'://'; } // return return $default_protocol . $url; } /** * Exclude processing from some pages / posts / contents * * @return boolean */ public static function exclude_contents() { // prevent execution for specific urls if (isset($_SERVER['REQUEST_URI']) && !empty($_SERVER['REQUEST_URI'])) { $disable_on_url = array_filter(array_map('trim', explode("\n", get_option('wpo_min_disable_on_url', '')))); foreach ($disable_on_url as $url) { if (wp_parse_url(esc_url_raw(wp_unslash($_SERVER['REQUEST_URI'])), PHP_URL_PATH) == $url) { return true; } } } // for compatibility, let's always skip the checkout page if (function_exists('is_checkout') && is_checkout() === true) { return true; } // exclude processing here if (is_feed() || is_admin() || is_preview() || (function_exists('is_customize_preview') && is_customize_preview()) || (defined('DOING_AJAX') && DOING_AJAX) || (function_exists('wp_doing_ajax') && wp_doing_ajax()) || (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) || (defined('WP_BLOG_ADMIN') && WP_BLOG_ADMIN) || (defined('WP_NETWORK_ADMIN') && WP_NETWORK_ADMIN) || (defined('WP_INSTALLING') && WP_INSTALLING) || (defined('WP_IMPORTING') && WP_IMPORTING) || (defined('WP_REPAIRING') && WP_REPAIRING) || (defined('XMLRPC_REQUEST') && XMLRPC_REQUEST) || (defined('SHORTINIT') && SHORTINIT) || (defined('REST_REQUEST') && REST_REQUEST) || (isset($_SERVER['REQUEST_METHOD']) && 'POST' === $_SERVER['REQUEST_METHOD']) || (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower(sanitize_text_field(wp_unslash($_SERVER['HTTP_X_REQUESTED_WITH']))) == 'xmlhttprequest') || (isset($_SERVER['REQUEST_URI']) && (strtolower(substr(esc_url_raw(wp_unslash($_SERVER['REQUEST_URI'])), -4)) == '.txt' || strtolower(substr(esc_url_raw(wp_unslash($_SERVER['REQUEST_URI'])), -4)) == '.xml')) ) { return true; } // phpcs:disable // WordPress.Security.NonceVerification.Recommended -- Using $_GET element only to compare, returns boolean // Thrive plugins and other post_types $arr = array('tve_form_type', 'tve_lead_shortcode', 'tqb_splash'); foreach ($arr as $a) { if (isset($_GET['post_type']) && $a === $_GET['post_type']) { return true; } } // Thrive architect if (isset($_GET['tve']) && 'true' === $_GET['tve']) return true; if (is_array($_GET)) { foreach ($_GET as $k => $v) { if (is_string($v) && is_string($k)) { if (stripos($k, 'elementor') !== false || stripos($v, 'elementor') !== false) { return true; } } } } // Other _GET parameters if (is_array($_GET)) { $get_params = array_keys($_GET); $excluded_params = array( // customizer preview, visual composer 'customize_theme', 'preview_id', 'preview', // Elementor 'elementor-preview', // Divi builder 'et_fb', 'PageSpeed', // Brizy Editor 'brizy-edit', 'brizy-edit-iframe', // Beaver builder 'fl_builder', 'trp-edit-translation', ); return (bool) count(array_intersect($excluded_params, $get_params)); } // phpcs:enable /** * Whether to exclude the content or not from the minifying process. */ return apply_filters('wpo_minify_exclude_contents', false); } /** * Get the default files which are ignored / excluded from processing * * @return array */ public static function get_default_ignore() { /** * Filters the default exclusions * * @param array The exclusions * @return array */ return apply_filters('wp-optimize-minify-default-exclusions', array( '/genericons.css', '/Avada/assets/js/main.min.js', '/woocommerce-product-search/js/product-search.js', '/includes/builder/scripts/frontend-builder-scripts.js', '/assets/js/jquery.themepunch.tools.min.js', '/js/TweenMax.min.js', '/jupiter/assets/js/min/full-scripts', '/wp-content/themes/Divi/core/admin/js/react-dom.production.min.js', '/LayerSlider/static/layerslider/js/greensock.js', '/themes/kalium/assets/js/main.min.js', '/wp-includes/js/mediaelement/wp-mediaelement.min.js', 'elementor-admin-bar', 'pdfjs-dist', 'wordpress-popular-posts', 'uploads/bb-plugin/cache', // Beaver builder page specific pages )); } /** * Know files that should always be ignored * * @param array $ignore * @return array */ public static function compile_ignore_list($ignore) { $ignore_list = self::get_default_ignore(); $user_excluded_ignore_items = wp_optimize_minify_config()->get('ignore_list'); if (is_array($user_excluded_ignore_items)) $ignore_list = array_diff($ignore_list, $user_excluded_ignore_items); if (is_array($ignore)) { $master_ignore = array_merge(array_map('strtolower', $ignore), array_map('strtolower', $ignore_list)); return array_unique($master_ignore); } else { return $ignore_list; } } /** * Get the files excluded for IE compatibility * * @return array */ public static function get_default_ie_blacklist() { /** * Filters the default IE specific / blacklisted items * * @param array The blacklist * @return array */ return apply_filters('wp-optimize-minify-blacklist', array( '/html5shiv.js', '/html5shiv-printshiv.min.js', '/excanvas.js', '/avada-ie9.js', '/respond.js', '/respond.min.js', '/selectivizr.js', '/Avada/assets/css/ie.css', '/html5.js', '/IE9.js', '/fusion-ie9.js', '/vc_lte_ie9.min.css', '/old-ie.css', '/ie.css', '/vc-ie8.min.css', '/mailchimp-for-wp/assets/js/third-party/placeholders.min.js', '/assets/js/plugins/wp-enqueue/min/webfontloader.js', '/a.optnmstr.com/app/js/api.min.js', '/pixelyoursite/js/public.js', '/assets/js/wcdrip-drip.js', '/instantpage.js', )); } /** * Get the files excluded for IE compatibility * * @return array */ public static function get_ie_blacklist() { $user_excluded_blacklist_items = wp_optimize_minify_config()->get('blacklist'); $default_blacklist = self::get_default_ie_blacklist(); if (!is_array($user_excluded_blacklist_items)) return $default_blacklist; return array_diff($default_blacklist, $user_excluded_blacklist_items); } /** * IE only files that should always be ignored, without incrementing our groups * * @param string $url * @return boolean */ public static function is_url_in_ie_blacklist($url) { // from the database $blacklist = self::get_ie_blacklist(); // must have $blacklist[] = '/wpo_min/cache/'; // is the url on our list and return $res = self::in_arrayi($url, $blacklist); if (true == $res) { return true; } else { return false; } } /** * Download function with fallback * * @param string $url * @return boolean */ public static function download_remote($url) { $args = array( // info (needed for google fonts woff files + hinted fonts) as well as to bypass some security filters 'user-agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36', 'timeout' => 7 ); // fetch via wordpress functions $response = wp_remote_get( $url, /** * Filters the arguments passed to wp_remote_get when downloading the scripts. * * @param array $args - The arguments filtered * @param string $url - The URL of the downloaded asset * @return array */ apply_filters('wpo_minify_download_request_args', $args, $url) ); $res_code = wp_remote_retrieve_response_code($response); if (200 == $res_code) { $data = wp_remote_retrieve_body($response); if (strlen($data) > 1) { return $data; } } return false; } /** * Prepares the merged JavaScript * * @param string $script * @param string $merged_url * @return string */ public static function prepare_merged_js($script, $merged_url) { $enable_js_trycatch = wp_optimize_minify_config()->get('enable_js_trycatch'); if ($enable_js_trycatch) { return 'try{'."\n".$script."\n".'}'."\n".'catch(e){console.error("WP-Optimize Minify: An error has occurred in the minified code. \n\n- Original script: '.esc_attr($merged_url).'\n- Error message: "+ e.message);}'."\n"; } return $script; } /** * Checks if an URL is a font-awesome resource (checks if it contains font-awesome or fontawesome) * * @param string $href * @return boolean */ public static function is_font_awesome($href) { return (boolean) preg_match('/font[-_]?awesome/i', $href); } /** * Checks if an URL is a google font resource * * @param string $href * @return boolean */ public static function is_google_font($href) { return 'fonts.googleapis.com' === strtolower(wp_parse_url($href, PHP_URL_HOST)); } /** * Get the content of an asset, whether local or remote * * @param string $url * @return int|false */ public static function get_file_size($url) { $original_url = $url; if (is_multisite()) { if (function_exists('get_main_site_id')) { $site_id = get_main_site_id(); } else { $network = get_network(); $site_id = $network->site_id; } switch_to_blog($site_id); } $upload_dir = wp_upload_dir(); $uploads_url = trailingslashit($upload_dir['baseurl']); $uploads_dir = trailingslashit($upload_dir['basedir']); if (is_multisite()) { restore_current_blog(); } $possible_urls = array( WP_CONTENT_URL => WP_CONTENT_DIR, WP_PLUGIN_URL => WP_PLUGIN_DIR, $uploads_url => $uploads_dir, get_template_directory_uri() => get_template_directory(), untrailingslashit(includes_url()) => ABSPATH . WPINC, ); $file = false; foreach ($possible_urls as $possible_url => $path) { $pos = strpos($url, $possible_url); if (false !== $pos) { $file = substr_replace($url, $path, $pos, strlen($possible_url)); break; } } if (is_string($file) && file_exists($file)) { return filesize($file); } return self::get_remote_file_size($original_url); } /** * Get the file size of a file hosting in other servers * * @param string $url * @return int|false */ public static function get_remote_file_size($url) { $args = array( // info (needed for google fonts woff files + hinted fonts) as well as to bypass some security filters 'user-agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36', 'timeout' => 7 ); // fetch via wordpress functions $response = wp_remote_get($url, $args); if (is_wp_error($response)) return false; if (!empty($response) && is_array($response) && isset($response['headers']['Content-Length'])) { return $response['headers']['Content-Length']; } return false; } /** * Check if it is 'flatsome' handle * * @param string $handle Handle of enqueued css file * * @return bool */ public static function is_flatsome_handle($handle) { return 0 === strcmp('flatsome-googlefonts', $handle); } /** * Fix google fonts url for 'flatsome' theme * * @param string $href Enqueued google fonts url * * @return string Fixed google fonts url */ public static function fix_flatsome_google_fonts_url($href) { // Get query from $href $query = wp_parse_url($href, PHP_URL_QUERY); $query_arr = explode('&', $query); // Separate 'family and display' arguments in query $family = str_replace('family=', '', $query_arr[0]); $display_type = $query_arr[1]; // Remove empty fonts $font_families = explode('|', $family); $font_families = array_diff($font_families, array(':regular', 'initial', 'inherit', '')); $system_fonts = array('+apple+system', '-apple-system', 'BlinkMacSystemFont', 'Segoe+UI', 'Helvetica+Neue', 'sans+serif', 'sans-serif', 'Georgia', 'Times', 'Times+New+Roman', 'serif'); $google_fonts = array(); // URL may look like following, fix it // https://fonts.googleapis.com/css?family=-apple-system,+BlinkMacSystemFont,+%22Segoe+UI%22,+Roboto,+Oxygen-Sans,+Ubuntu,+Cantarell,+%22Helvetica+Neue%22,+sans-serif:regular,700,regular,700|Bebas+Neue:regular,regular&display=block // http://fonts.googleapis.com/css?family=Bellota:regular|:regular|Lato:regular|Creepster:regular&display=block // http://fonts.googleapis.com/css?family=Lora:regular,regular|initial|Lato:regular,regular|&display=swap foreach ($font_families as $font) { $font_variant = explode(':', $font); // Remove beginning '+' $font = str_replace(',+', ',', $font_variant[0]); // Replace '-' with '+ $font = str_replace(array('-', ' '), '+', $font); // Remove '"' or '%22' $font = str_replace(array('%22', '"'), '', $font); $font_arr = explode(',', $font); $font_arr = array_diff($font_arr, $system_fonts); $variant = ''; if (!empty($font_variant[1])) { $variant = implode(',', array_unique(explode(',', $font_variant[1]))); } if (empty($variant)) { $variant = 'regular'; } foreach ($font_arr as $font) { $google_fonts[] = $font . ':' . $variant; } } $protocol = is_ssl() ? 'https:' : 'http:'; return $protocol . '//fonts.googleapis.com/css?family=' . implode('|', $google_fonts) . '&' . $display_type; } /** * Get the file modification time * * @param string $asset_src * @return string */ public static function get_modification_time($asset_src) { $hurl = self::get_hurl($asset_src); $abs_file_path = WP_Optimize_Utils::get_file_path($hurl); if (empty($abs_file_path)) return ''; $modification_time = @filemtime($abs_file_path); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Suppress E-Warning on failure return strval($modification_time); } /** * When BOM removed code is null (due to unrecognised character encoding), logs error message * * @param string $url URL of the script/stylesheet * @param string|false $encoding Character encoding * @param string|null $code Script/Stylesheet code * * @return void */ private static function maybe_log_error_message($url, $encoding, $code) { if (null === $code) { $message = "Minify: Could not process {$url}, it contains invalid characters. "; if (false === $encoding) { $message .= "Could not determine its character encoding."; } error_log($message); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Using for debugging purpose } } } class-wp-optimize-minify.php 0000644 00000020620 15162435543 0012145 0 ustar 00 <?php if (!defined('ABSPATH')) die('No direct access allowed'); define('WP_OPTIMIZE_MINIFY_VERSION', '2.6.5'); define('WP_OPTIMIZE_MINIFY_DIR', dirname(__FILE__)); if (!defined('WP_OPTIMIZE_SHOW_MINIFY_ADVANCED')) define('WP_OPTIMIZE_SHOW_MINIFY_ADVANCED', false); class WP_Optimize_Minify { /** * Minify commands object * * @var WP_Optimize_Minify_Commands */ public $minify_commands; /** * @var bool */ private $enabled; /** * Constructor - Initialize actions and filters * * @return void */ public function __construct() { $this->minify_commands = new WP_Optimize_Minify_Commands(); if (!class_exists('WP_Optimize_Minify_Config')) { include WP_OPTIMIZE_MINIFY_DIR.'/class-wp-optimize-minify-config.php'; } $this->enabled = wp_optimize_minify_config()->is_enabled(); $this->load_admin(); add_filter('wpo_cache_admin_bar_menu_items', array($this, 'admin_bar_menu'), 30, 1); if (WP_Optimize::is_premium()) { $this->load_premium(); } /** * Directory that stores the cache, including gzipped files and mobile specific cache */ if (!defined('WPO_CACHE_MIN_FILES_DIR')) define('WPO_CACHE_MIN_FILES_DIR', untrailingslashit(WP_CONTENT_DIR).'/cache/wpo-minify'); if (!defined('WPO_CACHE_MIN_FILES_URL')) define('WPO_CACHE_MIN_FILES_URL', untrailingslashit(WP_CONTENT_URL).'/cache/wpo-minify'); if (!class_exists('WP_Optimize_Minify_Cache_Functions')) { include WP_OPTIMIZE_MINIFY_DIR.'/class-wp-optimize-minify-cache-functions.php'; } $this->load_frontend(); // cron job to delete old wpo_min cache add_action('wpo_minify_purge_old_cache', array('WP_Optimize_Minify_Cache_Functions', 'purge_old')); add_action('init', array($this, 'schedule_or_unschedule_purge_old_cache_event')); // Handle minify cache purging. add_action('wp_loaded', array($this, 'handle_purge_minify_cache')); } /** * Schedule or unschedule purge old cache event. * * @return void */ public function schedule_or_unschedule_purge_old_cache_event() { if ($this->enabled) { $this->schedule_purge_old_cache_event(); } else { $this->unschedule_purge_old_cache_event(); } } /** * Returns singleton instance object * * @return WP_Optimize_Minify Returns `WP_Optimize_Minify` object */ public static function instance() { static $_instance = null; if (null === $_instance) { $_instance = new self(); } return $_instance; } /** * Admin toolbar processing * * @param array $menu_items * @return array */ public function admin_bar_menu($menu_items) { $wpo_minify_options = wp_optimize_minify_config()->get(); if (!$wpo_minify_options['enabled'] || !current_user_can('manage_options') || !($wpo_minify_options['enable_css'] || $wpo_minify_options['enable_js'])) return $menu_items; $act_url = remove_query_arg('wpo_minify_cache_purged'); $cache_path = WP_Optimize_Minify_Cache_Functions::cache_path(); $cache_size_info = '<h4>'.__('Minify cache', 'wp-optimize').'</h4><span><span class="label">'.__('Cache size:', 'wp-optimize').'</span> <span class="stats">'.esc_html(WP_Optimize_Minify_Cache_Functions::get_cachestats($cache_path['cachedir'])).'</span></span>'; $menu_items[] = array( 'id' => 'wpo_minify_cache_stats', 'title' => $cache_size_info, 'meta' => array( 'class' => 'wpo-cache-stats', ), 'parent' => 'wpo_purge_cache', ); $menu_items[] = array( 'parent' => 'wpo_purge_cache', 'id' => 'purge_minify_cache', 'title' => __('Purge minify cache', 'wp-optimize'), 'href' => add_query_arg('_wpo_purge_minify_cache', wp_create_nonce('wpo_purge_minify_cache'), $act_url), ); return $menu_items; } /** * Check if purge single page action sent and purge cache. */ public function handle_purge_minify_cache() { $wpo_minify_options = wp_optimize_minify_config()->get(); if (!$wpo_minify_options['enabled'] || !current_user_can('manage_options')) return; if (isset($_GET['wpo_minify_cache_purged'])) { if (is_admin()) { add_action('admin_notices', array($this, 'notice_purge_minify_cache_success')); return; } else { $message = __('Minify cache purged', 'wp-optimize'); printf('<script>alert("%s");</script>', esc_html($message)); return; } } if (!isset($_GET['_wpo_purge_minify_cache'])) return; if (wp_verify_nonce($_GET['_wpo_purge_minify_cache'], 'wpo_purge_minify_cache')) { $success = false; // Purge minify $results = $this->minify_commands->purge_minify_cache(); if ("caches cleared" == $results['result']) $success = true; // remove nonce from url and reload page. wp_redirect(add_query_arg('wpo_minify_cache_purged', $success, remove_query_arg('_wpo_purge_minify_cache'))); exit; } } /** * Load the admin class * * @return void */ private function load_admin() { if (!is_admin()) return; new WP_Optimize_Minify_Admin(); } /** * Load the frontend class * * @return void */ private function load_frontend() { if ($this->enabled) { new WP_Optimize_Minify_Front_End(); } } /** * Load the premium class * * @return void */ private function load_premium() { new WP_Optimize_Minify_Premium(); } /** * Run during activation * Increment cache first as it will save files to that dir * * @return void */ public function plugin_activate() { // increment cache time if (class_exists('WP_Optimize_Minify_Cache_Functions')) { WP_Optimize_Minify_Cache_Functions::cache_increment(); } } /** * If the WP cron event for scheduling purging of the minify cache does not exist, then create it */ private function schedule_purge_old_cache_event() { if (!wp_next_scheduled('wpo_minify_purge_old_cache')) { wp_schedule_event(time() + 43200, 'daily', 'wpo_minify_purge_old_cache'); } } /** * Unschedule purging of the minify cache * * @return void */ private function unschedule_purge_old_cache_event() { // old cache purge event cron wp_clear_scheduled_hook('wpo_minify_purge_old_cache'); } /** * Run during plugin deactivation * * @return void */ public function plugin_deactivate() { if (class_exists('WP_Optimize_Minify_Cache_Functions') && WP_Optimize()->get_page_cache()->should_purge) { WP_Optimize_Minify_Cache_Functions::purge_temp_files(); WP_Optimize_Minify_Cache_Functions::purge_old(); WP_Optimize_Minify_Cache_Functions::purge_others(); } $this->unschedule_purge_old_cache_event(); } /** * Run during plugin uninstall * * @return void */ public function plugin_uninstall() { // remove options from DB if (!function_exists('wp_optimize_minify_config')) { include WP_OPTIMIZE_MINIFY_DIR.'/class-wp-optimize-minify-config.php'; } wp_optimize_minify_config()->purge(); // remove minified files if (class_exists('WP_Optimize_Minify_Cache_Functions')) { WP_Optimize_Minify_Cache_Functions::purge(); WP_Optimize_Minify_Cache_Functions::purge_others(); } } /** * Shows success notice for purge minify cache */ public function notice_purge_minify_cache_success() { $this->show_notice(__('The minify cache was successfully purged.', 'wp-optimize'), 'success'); } /** * Show notification in WordPress admin. * * @param string $message HTML (no further escaping is performed) * @param string $type error, warning, success, or info */ public function show_notice($message, $type) { global $current_screen; if ($current_screen && is_callable(array($current_screen, 'is_block_editor')) && $current_screen->is_block_editor()) : ?> <script> window.addEventListener('load', function() { (function(wp) { if (window.wp && wp.hasOwnProperty('data') && 'function' == typeof wp.data.dispatch) { wp.data.dispatch('core/notices').createNotice( '<?php echo esc_js($type); ?>', '<?php echo wp_kses_post($message); ?>', { isDismissible: true, } ); } })(window.wp); }); </script> <?php else : ?> <div class="notice wpo-notice notice-<?php echo esc_attr($type); ?> is-dismissible"> <p><?php echo wp_kses_post($message); ?></p> </div> <?php endif; } /** * Check if current user can purge cache. * * @return bool */ public function can_purge_cache() { $required_capability = is_multisite() ? 'manage_network_options' : 'manage_options'; if (WP_Optimize::is_premium()) { return current_user_can($required_capability) || WP_Optimize_Premium()->can_purge_the_cache(); } else { return current_user_can($required_capability); } } } class-wp-optimize-minify-admin.php 0000644 00000022745 15162435544 0013246 0 ustar 00 <?php if (!defined('ABSPATH')) die('No direct access allowed'); class WP_Optimize_Minify_Admin { /** * Initialize, add actions and filters * * @return void */ public function __construct() { // exclude processing for editors and administrators (fix editors) add_action('wp_optimize_admin_page_wpo_minify_status', array($this, 'check_permissions_admin_notices')); add_action('wp_optimize_admin_page_wpo_minify_status', array($this, 'admin_notices_activation_error')); add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts')); // This function runs when WordPress updates or installs/remove something. Forces new cache add_action('upgrader_process_complete', array('WP_Optimize_Minify_Cache_Functions', 'cache_increment')); // This function runs when an active theme or plugin is updated add_action('wpo_active_plugin_or_theme_updated', array('WP_Optimize_Minify_Cache_Functions', 'reset')); add_action('upgrader_overwrote_package', array('WP_Optimize_Minify_Cache_Functions', 'reset')); add_action('after_switch_theme', array('WP_Optimize_Minify_Cache_Functions', 'cache_increment')); add_action('updraftcentral_version_updated', array('WP_Optimize_Minify_Cache_Functions', 'reset')); add_action('elementor/editor/after_save', array('WP_Optimize_Minify_Cache_Functions', 'reset')); add_action('fusion_cache_reset_after', array('WP_Optimize_Minify_Cache_Functions', 'reset')); // Output asset preload placeholder, replaced by premium add_action('wpo_minify_settings_tabs', array($this, 'output_assets_preload_placeholder'), 10, 1); add_action('wp_optimize_register_admin_content', array($this, 'register_content')); } /** * Register the content * * @return void */ public function register_content() { add_action('wp_optimize_admin_page_wpo_minify_status', array($this, 'output_status'), 20); add_action('wp_optimize_admin_page_wpo_minify_settings', array($this, 'output_settings'), 20); add_action('wp_optimize_admin_page_wpo_minify_advanced', array($this, 'output_advanced'), 20); add_action('wp_optimize_admin_page_wpo_minify_font', array($this, 'output_font_settings'), 20); add_action('wp_optimize_admin_page_wpo_minify_analytics', array($this, 'output_analytics_settings'), 20); add_action('wp_optimize_admin_page_wpo_minify_css', array($this, 'output_css_settings'), 20); add_action('wp_optimize_admin_page_wpo_minify_js', array($this, 'output_js_settings'), 20); add_action('wp_optimize_admin_page_wpo_minify_preload', array($this, 'output_preload_settings'), 20); } /** * Load scripts for controlling the admin pages * * @param string $hook * @return void */ public function admin_enqueue_scripts($hook) { $enqueue_version = WP_Optimize()->get_enqueue_version(); $min_or_not_internal = WP_Optimize()->get_min_or_not_internal_string(); if (preg_match('/wp\-optimize/i', $hook)) { wp_enqueue_script('wp-optimize-min-js', WPO_PLUGIN_URL.'js/minify' . $min_or_not_internal . '.js', array('jquery', 'wp-optimize-admin-js'), $enqueue_version, true); } } /** * Conditionally runs upon the WP action admin_notices to display error * * @return void */ public function admin_notices_activation_error() { if (!extension_loaded('mbstring')) { echo '<div class="notice notice-error wpo-warning">'; echo '<p>' . esc_html__('WP-Optimize Minify requires the PHP mbstring module to be installed on the server; please ask your web hosting company for advice on how to enable it on your server.', 'wp-optimize') . '</p>'; echo '</div>'; } } /** * Display an admin notice if the user has inadequate filesystem permissions * * @return void */ public function check_permissions_admin_notices() { // get cache path $cache_path = WP_Optimize_Minify_Cache_Functions::cache_path(); $cache_dir = $cache_path['cachedir']; if (is_dir($cache_dir) && !wp_is_writable($cache_dir)) { $chmod = substr(sprintf('%o', fileperms($cache_dir)), -4); ?> <div class="notice notice-error wpo-warning"> <p> <?php // translators: %s is a directory echo wp_kses_post(sprintf(__('WP-Optimize Minify needs write permissions on the folder %s.', 'wp-optimize'), "<strong>". esc_html($cache_dir)."</strong>")); ?> </p> </div> <div class="notice notice-error wpo-warning"> <p> <?php // translators: %s is a file permissions code echo wp_kses_post(sprintf(__('The current permissions for WP-Optimize Minify are chmod %s.', 'wp-optimize'), "<strong>" . esc_html($chmod) . "</strong>")); ?> </p> </div> <div class="notice notice-error wpo-warning"> <p> <?php // translators: %s is a file permissions code echo wp_kses_post(sprintf(__('If you need something more than %s for it to work, then your server is probably misconfigured.', 'wp-optimize'), '<strong>775</strong>')); echo " "; esc_html_e('Please contact your hosting provider.', 'wp-optimize'); ?> </p> </div> <?php } } /** * Minify - Outputs the status tab * * @return void */ public function output_status() { $found_incompatible_plugins = WP_Optimize_Detect_Minify_Plugins::get_instance()->get_active_minify_plugins(); $wpo_minify_options = wp_optimize_minify_config()->get(); $cache_path = WP_Optimize_Minify_Cache_Functions::cache_path(); WP_Optimize()->include_template( 'minify/status-tab.php', false, array( 'wpo_minify_options' => $wpo_minify_options, 'show_information_notice' => !get_user_meta(get_current_user_id(), 'wpo-hide-minify-information-notice', true), 'cache_dir' => $cache_path['cachedir'], 'can_purge_the_cache' => WP_Optimize()->get_minify()->can_purge_cache(), 'active_minify_plugins' => apply_filters('wpo_minify_found_incompatible_plugins', $found_incompatible_plugins), ) ); } /** * Minify - Outputs the font settings tab * * @return void */ public function output_font_settings() { $wpo_minify_options = wp_optimize_minify_config()->get(); WP_Optimize()->include_template( 'minify/font-settings-tab.php', false, array( 'wpo_minify_options' => $wpo_minify_options ) ); } /** * Minify - Outputs the analytics settings tab * * @return void */ public function output_analytics_settings() { $config = wp_optimize_minify_config()->get(); $id = isset($config['tracking_id']) ? $config['tracking_id'] : ''; $method = isset($config['analytics_method']) ? $config['analytics_method'] : ''; $is_enabled = isset($config['enable_analytics']) ? $config['enable_analytics'] : false; WP_Optimize()->include_template( 'minify/analytics-settings-tab.php', false, array( 'id' => $id, 'method'=> $method, 'is_enabled'=> $is_enabled ) ); } /** * Minify - Outputs the CSS settings tab * * @return void */ public function output_css_settings() { $wpo_minify_options = wp_optimize_minify_config()->get(); WP_Optimize()->include_template( 'minify/css-settings-tab.php', false, array( 'wpo_minify_options' => $wpo_minify_options ) ); } /** * Minify - Outputs the JS settings tab * * @return void */ public function output_js_settings() { $wpo_minify_options = wp_optimize_minify_config()->get(); WP_Optimize()->include_template( 'minify/js-settings-tab.php', false, array( 'wpo_minify_options' => $wpo_minify_options ) ); } /** * Minify - Outputs the settings tab * * @return void */ public function output_settings() { $wpo_minify_options = wp_optimize_minify_config()->get(); $url = wp_parse_url(get_home_url()); WP_Optimize()->include_template( 'minify/settings-tab.php', false, array( 'wpo_minify_options' => $wpo_minify_options, 'default_protocol' => $url['scheme'] ) ); } /** * Minify - Outputs the settings tab * * @return void */ public function output_assets_preload_placeholder($wpo_minify_options) { WP_Optimize()->include_template( 'minify/asset-preload.php', false, array( 'wpo_minify_options' => $wpo_minify_options ) ); } /** * Minify - Outputs the preload tab * * @return void */ public function output_preload_settings() { $wpo_minify_preloader = WP_Optimize_Minify_Preloader::instance(); $is_running = $wpo_minify_preloader->is_running(); $status = $wpo_minify_preloader->get_status_info(); $cache_config = WPO_Cache_Config::instance(); WP_Optimize()->include_template( 'minify/preload-tab.php', false, array( 'is_cache_enabled' => $cache_config->get_option('enable_page_caching'), 'is_running' => $is_running, 'status_message' => isset($status['message']) ? $status['message'] : '', ) ); } /** * Minify - Outputs the advanced tab * * @return void */ public function output_advanced() { $wpo_minify_options = wp_optimize_minify_config()->get(); $files = false; if (apply_filters('wpo_minify_status_show_files_on_load', true)) { $files = WP_Optimize_Minify_Cache_Functions::get_cached_files(); } // WP_Optimize_Minify_Functions is only loaded when Minify is active if (class_exists('WP_Optimize_Minify_Functions')) { $default_ignore = WP_Optimize_Minify_Functions::get_default_ignore(); $default_ie_blacklist = WP_Optimize_Minify_Functions::get_default_ie_blacklist(); } else { $default_ignore = array(); $default_ie_blacklist = array(); } WP_Optimize()->include_template( 'minify/advanced-tab.php', false, array( 'wpo_minify_options' => $wpo_minify_options, 'files' => $files, 'default_ignore' => $default_ignore, 'default_ie_blacklist' => $default_ie_blacklist ) ); } } class-wp-optimize-minify-front-end.php 0000644 00000300421 15162435546 0014042 0 ustar 00 <?php if (!defined('ABSPATH')) die('No direct access allowed'); class WP_Optimize_Minify_Front_End { private $collect_preload_js = array(); private $collect_preload_css = array(); private $collect_google_fonts = array(); private $minify_cache_incremented = false; private $options = array(); /** * Initialize actions and filters * * @return void */ public function __construct() { $this->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')); if (!is_admin()) { add_action('wp_headers', array($this, 'add_caching_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')); } 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(sanitize_text_field(wp_unslash($_SERVER['HTTP_X_WP_OPTIMIZE_CACHE_PRELOAD'])), 'Yes'); } /** * Set initial values for the meta log object * * @param string $header - Info on time the log was processed * @return array */ private function meta_log_initialization($header) { return array( 'header' => $header, 'invalidation_reason' => "", 'files_that_changed' => array() ); } /** * Whether 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 whether 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 = $this->get_excluded_css_list(); $ignore_list = WP_Optimize_Minify_Functions::compile_ignore_list($exclude_css); $blacklist = WP_Optimize_Minify_Functions::get_ie_blacklist(); $async_css = $this->get_async_css_list(); $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 "<!-- wpo_min DEBUG: Inline CSS processing start ". esc_html($handle) . " / " . esc_html($href) . " -->\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; } // return if already minified if (WP_Optimize_Minify_Functions::is_minified_css_js_filename($href)) { 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' == wp_parse_url($href, PHP_URL_HOST)) { // if option disabled google fonts processing if ($this->options['disable_google_fonts_processing']) return $html; // 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 "<!-- wpo_min DEBUG: Uncached file processing now for " . esc_html($handle) . " / " . esc_html($href) . " -->\n"; } WP_Optimize_Minify_Cache_Functions::set_transient($tkey, $json); } // decode $res = json_decode($json, true); // inline css or fail if (null !== $res && false != $res['status']) { // 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']); echo '<style class="optimize_css_1" type="text/css" media="all">'; echo $res['code']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- This is already escaped echo '</style>' . "\n"; return false; } else { if ($this->options['debug']) { echo "<!-- wpo_min DEBUG: Google fonts request failed for " . esc_html($href) . " -->\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 "<!-- wpo_min DEBUG: Skipped the next external enqueued CSS -->\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 "<!-- wpo_min DEBUG: Uncached file processing now for " . esc_html($handle) . " / " . esc_html($href) . " -->" . "\n"; } WP_Optimize_Minify_Cache_Functions::set_transient($tkey, $json); } // decode $res = json_decode($json, true); // inline it + other inlined children styles if (null !== $res && false != $res['status']) { echo '<style class="optimize_css_2" type="text/css" media="'.esc_attr($media).'">'; echo $res['code']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- This is already escaped echo '</style>' . "\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 '<style class="optimize_css_3" type="text/css" media="'.esc_attr($media).'">'; echo $code; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- This is already escaped echo '</style>' . "\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 '<style class="optimize_css_4" type="text/css" media="'.esc_attr($media).'">'; echo $code; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- This is already escaped echo '</style>' . "\n"; } } } } // prevent default return false; } else { if ($this->options['debug']) { echo "<!-- wpo_min DEBUG: " . esc_html($handle) . " / " . esc_html($href) . " returned an empty from minification -->" . "\n"; } return $html; } echo "<!-- ERROR: WPO-Minify couldn't catch the CSS file below. Please report this on https://wordpress.org/support/plugin/wp-optimize/ -->\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 = $this->get_excluded_js_list(); $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); $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'] && isset($_SERVER['SCRIPT_NAME']) && false !== stripos(sanitize_text_field(wp_unslash($_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 <script> tag * @param string $handle - The WP handle for the script * @param string $src - The SRC for the script * @return string - 'defer' or 'async' */ $defer_or_async = apply_filters('wpo_minify_defer_or_async', 'defer', $tag, $handle, $src); if ('defer' === $defer_or_async) { return str_ireplace('<script ', '<script defer ', $tag); } else { return str_ireplace('<script ', '<script async ', $tag); } } } // fallback return $tag; } /** * Add inline CSS code / Critical Path * * @return void */ public function add_critical_path() { if (is_front_page() && !empty($this->options['critical_path_css_is_front_page'])) { echo '<style id="critical-path-is-front-page" type="text/css" media="all">' . "\n"; echo $this->options['critical_path_css_is_front_page'] . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- This is already escaped echo '</style>' . "\n"; } elseif (!empty($this->options['critical_path_css'])) { echo '<style id="critical-path-global" type="text/css" media="all">' . "\n"; echo $this->options['critical_path_css'] . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- This is already escaped echo '</style>' . "\n"; } } /** * Add critical assets preload * * @return void */ public function add_assets_preload() { $preload = json_decode($this->options['hpreload']); if (is_array($preload)) { foreach ($preload as $asset) { if (!empty($asset)) { echo '<link rel="preload" href="'.esc_url($asset->href).'" as="'.esc_attr($asset->type).'"'.($asset->crossorigin ? ' crossorigin' : '').'>'; } } } } /** * Process header CSS * * @return boolean */ public function process_header_css() { global $wp_styles; if (!is_object($wp_styles)) return false; $cache_path = WP_Optimize_Minify_Cache_Functions::cache_path(); $cache_base_dir = $cache_path['cachebasedir']; $cache_dir = $cache_path['cachedir']; $cache_dir_url = $cache_path['cachedirurl']; $exclude_css = $this->get_excluded_css_list(); $ignore_list = WP_Optimize_Minify_Functions::compile_ignore_list($exclude_css); $async_css = $this->get_async_css_list(); $minify_css = $this->options['enable_css_minification']; $merge_css = $this->options['enable_merging_of_css']; $merge_inline_extra_css_js = $this->options['merge_inline_extra_css_js']; $process_css = $minify_css || $merge_css; $disable_google_fonts_processing = $this->options['disable_google_fonts_processing']; $styles = clone $wp_styles; $styles->all_deps($styles->queue); $done = $styles->done; $header = array(); $google_fonts = array(); $process = array(); $inline_css = array(); $log = ''; // dequeue all styles if (isset($this->options['remove_css']) && $this->options['remove_css']) { foreach ($styles->to_do as $handle) { $done = array_merge($done, array($handle)); } // remove from queue $wp_styles->done = $done; return false; } // get list of handles to process, dequeue duplicate css urls and keep empty source handles (for dependencies) $uniq = array(); foreach ($styles->to_do as $handle) { // conditionals $conditional = null; if (isset($wp_styles->registered[$handle]->extra["conditional"])) { $conditional = $wp_styles->registered[$handle]->extra["conditional"]; // such as ie7, ie8, ie9, etc } // mediatype $mt = isset($wp_styles->registered[$handle]->args) ? $wp_styles->registered[$handle]->args : 'all'; if ('screen' == $mt || 'screen, print' == $mt || empty($mt) || is_null($mt) || false == $mt) { $mt = 'all'; } $mediatype = $mt; // full url or empty $href = WP_Optimize_Minify_Functions::get_hurl($wp_styles->registered[$handle]->src); $version = $wp_styles->registered[$handle]->ver; // inlined scripts without file if (empty($href)) continue; // mark duplicates as done and remove from the queue if (!empty($href)) { $version = is_array($version) ? '' : $version; $key = hash('adler32', $href . $version); if (isset($uniq[$key])) { $done = array_merge($done, array($handle)); continue; } else { $uniq[$key] = $handle; } } // Exclude specific CSS files from PageSpeedIndex? if (WP_Optimize_Minify_Functions::in_arrayi($href, $async_css)) { WP_Optimize_Minify_Print::exclude_style($href); $done = array_merge($done, array($handle)); continue; } // Fonts Awesome Processing if (WP_Optimize_Minify_Functions::is_font_awesome($href)) { if ('inline' === $this->options['fawesome_method']) { WP_Optimize_Minify_Print::inline_style($handle, $href); $done = array_merge($done, array($handle)); continue; } elseif ('async' === $this->options['fawesome_method']) { WP_Optimize_Minify_Print::async_style($href, $mediatype); $done = array_merge($done, array($handle)); continue; } elseif ('exclude' === $this->options['fawesome_method']) { WP_Optimize_Minify_Print::exclude_style($href); $done = array_merge($done, array($handle)); continue; } } // Exclude Print mediatype if (!empty($this->options['remove_print_mediatypes']) && 'print' === $mediatype) { $done = array_merge($done, array($handle)); continue; } // array of info to save $arr = array( 'handle' => $handle, 'url' => $href, 'conditional' => $conditional, 'mediatype' => $mediatype ); // google fonts to the top (collect and skip process array) if (!$disable_google_fonts_processing && WP_Optimize_Minify_Functions::is_google_font($href)) { if ($this->options['remove_googlefonts']) { $done = array_merge($done, array($handle)); continue; } if (WP_Optimize_Minify_Functions::is_flatsome_handle($handle)) { $href = WP_Optimize_Minify_Functions::fix_flatsome_google_fonts_url($href); $google_fonts[$handle] = $href; } else { $google_fonts[$handle] = $href; } } $process[$handle] = $arr; } // Process Google fonts if (count($google_fonts) > 0) { // merge google fonts if force inlining is enabled? $nfonts = array(); if ($this->options['merge_google_fonts']) { $nfonts[] = WP_Optimize_Minify_Fonts::concatenate_google_fonts($google_fonts); // mark the google fonts as done so they don't get processed if ('inherit' !== $this->options['gfonts_method']) { $done = array_merge($done, array_keys($google_fonts)); } } else { foreach ($google_fonts as $h => $a) { if (!empty($a)) { $nfonts[$h] = $a; } } } // foreach google font (will be one if merged is not disabled) foreach ($nfonts as $handle => $href) { if ('inline' === $this->options['gfonts_method']) { // download, minify, cache $tkey = 'css-'.hash('adler32', $href).'.css'; // this returns false if the cache is empty! but it doesn't check for failed $json = WP_Optimize_Minify_Cache_Functions::get_transient($tkey); $json = json_decode($json, true); // check if the cache is empty or has no code if (null === $json || false === $json || empty($json['code'])) { $res = WP_Optimize_Minify_Functions::download_and_minify($href, null, $minify_css, 'css', null); if ($this->options['debug']) { echo "<!-- wpo_min DEBUG: Uncached file processing now for " . esc_html($href) . " -->\n"; } WP_Optimize_Minify_Cache_Functions::set_transient($tkey, $res); // decode $json = json_decode($res, true); } // inline css or fail if (null !== $json && !empty($json['code'])) { // add font-display // https://developers.google.com/web/updates/2016/02/font-display $json['code'] = str_ireplace('font-style:normal;', 'font-display:block;font-style:normal;', $json['code']); echo '<style type="text/css" media="all">'; echo $json['code']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- This is already escaped echo '</style>' . "\n"; $done = array_merge($done, array($handle)); } else { echo "<!-- GOOGLE FONTS REQUEST FAILED for " . esc_html($href) . " -->" . "\n"; // inlining failed, so enqueue again wp_enqueue_style($handle, $href, array(), null); } } elseif ('async' === $this->options['gfonts_method']) { WP_Optimize_Minify_Print::async_style($href); $done = array_merge($done, array($handle)); } elseif ('exclude' === $this->options['gfonts_method']) { // make a stylesheet, hide from PageSpeedIndex WP_Optimize_Minify_Print::exclude_style($href); $done = array_merge($done, array($handle)); } } } // get groups of handles foreach ($styles->to_do as $handle) { // skip already processed google fonts and empty dependencies if (isset($google_fonts[$handle]) && 'inherit' !== $this->options['gfonts_method']) { continue; } if (empty($wp_styles->registered[$handle]->src)) { continue; } if (WP_Optimize_Minify_Functions::in_arrayi($handle, $done)) { continue; } if (!isset($process[$handle])) { continue; } // get full url $href = $process[$handle]['url']; $conditional = $process[$handle]['conditional']; $mediatype = $process[$handle]['mediatype']; // IE only files don't increment things $ieonly = WP_Optimize_Minify_Functions::is_url_in_ie_blacklist($href); if ($ieonly) { continue; } $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 || !$this->options['inline_css'], $file_size, $href)) continue; // check if the current URL points to a minified file. $exclude_minified_css = WP_Optimize_Minify_Functions::is_minified_css_js_filename($href); $normal_processing = $exclude_minified_css && !$merge_css; // skip ignore list, conditional css, external css, font-awesome merge and already minified js if (($process_css && !$normal_processing && !WP_Optimize_Minify_Functions::in_arrayi($href, $ignore_list) && !isset($conditional) && WP_Optimize_Minify_Functions::internal_url($href, site_url())) || empty($href) || ($process_css && 'inherit' == $this->options['fawesome_method'] && WP_Optimize_Minify_Functions::is_font_awesome($href)) || ($process_css && !$disable_google_fonts_processing && 'inherit' == $this->options['gfonts_method'] && WP_Optimize_Minify_Functions::is_google_font($href)) ) { // collect inline css for this handle if (isset($wp_styles->registered[$handle]->extra['after']) && is_array($wp_styles->registered[$handle]->extra['after'])) { $inline_css[$handle] = WP_Optimize_Minify_Functions::minify_css_string(implode('', $wp_styles->registered[$handle]->extra['after'])); // save $wp_styles->registered[$handle]->extra['after'] = null; // dequeue } // process if (isset($header[count($header)-1]['handle']) || count($header) == 0 || $header[count($header)-1]['media'] != $mediatype || !$merge_css) { array_push($header, array('handles' => array(), 'media' => $mediatype, 'versions' => array())); } // push it to the array array_push($header[count($header)-1]['handles'], $handle); // We need this because some plugins/theme use empty array as version // https://plugins.trac.wordpress.org/browser/mailin/tags/3.1.54/sendinblue.php#L456 $version_str = is_array($wp_styles->registered[$handle]->ver) ? $this->array_to_string_conversion($wp_styles->registered[$handle]->ver) : $wp_styles->registered[$handle]->ver; array_push($header[count($header)-1]['versions'], $version_str); $header[count($header) - 1]['last_modified'][] = WP_Optimize_Minify_Functions::get_modification_time($wp_styles->registered[$handle]->src); // external and ignored css } else { // normal enqueuing array_push($header, array('handle' => $handle)); } } /** * Filters the array of stylesheets before processing them * * @param array $list - The list of items filtered * @param string $location - The location of the list (footer or header) * @return array */ $header = apply_filters('wpo_minify_stylesheets', $header, 'header'); // loop through header css and merge for ($i=0,$l=count($header); $i<$l; $i++) { if (!isset($header[$i]['handle'])) { $handle_str = is_array($header[$i]['handles']) ? $this->array_to_string_conversion($header[$i]['handles']) : strval($header[$i]['handles']); $last_modified_str = empty($header[$i]['last_modified']) ? '' : (is_array($header[$i]['last_modified']) ? $this->array_to_string_conversion($header[$i]['last_modified']) : strval($header[$i]['last_modified'])); if ($merge_css) { // get hash for the inline css in this group $inline_css_group = array(); if (is_array($header[$i]['handles'])) { foreach ($header[$i]['handles'] as $h) { if (!empty($inline_css) && isset($inline_css[$h]) && !empty($inline_css[$h])) { $inline_css_group[] = $inline_css[$h]; } } } $inline_css_hash = md5(implode('', $inline_css_group)); $hash = hash('adler32', $handle_str . $inline_css_hash . $last_modified_str); } else { $hash = $handle_str . $last_modified_str; } // static cache file info $file_name = 'wpo-minify-header-'.$hash.($minify_css ? '.min' : ''); // create cache files and urls $file = $cache_dir.'/'.$file_name.'.css'; $meta_json_file = $cache_base_dir.'/'.'meta.json'; $file_url = WP_Optimize_Minify_Functions::get_protocol("$cache_dir_url/$file_name.css"); // generate a new cache file clearstatcache(); if (!file_exists($file)) { // code and log initialization $log_header = "PROCESSED on ".gmdate('r')." from ".home_url(add_query_arg(null, null)); $log = array( 'header' => $log_header, 'files' => array() ); $meta_log = $this->meta_log_initialization($log_header); $code = ''; // minify and write to file foreach ($header[$i]['handles'] as $handle) { if (!empty($wp_styles->registered[$handle]->src)) { // get href per handle $handle_src = $wp_styles->registered[$handle]->src; $href = WP_Optimize_Minify_Functions::get_hurl($handle_src); $version = $wp_styles->registered[$handle]->ver; $last_modified = WP_Optimize_Minify_Functions::get_modification_time($handle_src); // inlined scripts without file if (empty($href)) continue; // download, minify, cache $tkey = 'css-'.hash('adler32', $handle . $href).'.css'; $json = false; $json = WP_Optimize_Minify_Cache_Functions::get_transient($tkey); if (false === $json) { $enable_minification = $this->options['enable_css_minification'] && !WP_Optimize_Minify_Functions::is_minified_css_js_filename($href); $json = WP_Optimize_Minify_Functions::download_and_minify($href, null, $enable_minification, 'css', $handle, $version); if ($this->options['debug']) { echo "<!-- wpo_min DEBUG: Uncached file processing now for " . esc_html($handle) . " / " . esc_html($href) . " / " . esc_html($version) . " -->" . "\n"; } WP_Optimize_Minify_Cache_Functions::set_transient($tkey, $json); } // decode $res = json_decode($json, true); if (null === $res) continue; if ($this->should_reset_minify_assets($res, $href, 'css', $handle, $version)) { WP_Optimize_Minify_Cache_Functions::reset(); $this->minify_cache_incremented = true; $meta_log['invalidation_reason'] = 'Checksum Mismatch and version changed from ' . $res['request']['version'] . ' to ' . $version; $meta_log['files_that_changed'][$handle] = $res['log']; } // response has failed if (true != $res['status']) { $log['files'][$handle] = $res['log']; $log['files'][$handle]['version'] = $version; $log['files'][$handle]['last_modified'] = $last_modified; continue; } // Only add the $handle to $done if it was successfully downloaded $done[] = $handle; // append code to merged file $code .= isset($res['code']) ? $res['code'] : ''; $log['files'][$handle] = $res['log']; $log['files'][$handle]['version'] = $version; $log['files'][$handle]['last_modified'] = $last_modified; // append inlined styles if ($merge_inline_extra_css_js && isset($inline_css[$handle]) && !empty($inline_css[$handle])) { $code.= $inline_css[$handle]; } // consider dependencies on handles with an empty src } else { wp_dequeue_script($handle); wp_enqueue_script($handle); } }; // generate cache, write log if (!empty($code)) { // Only generate meta.json file if cache is reset if (!empty($meta_log['invalidation_reason'])) { file_put_contents($meta_json_file, wp_json_encode($meta_log)); } WP_Optimize_Minify_Print::write_combined_asset($file, $code, $log); } } else { $log_file = $file.'.json'; if (file_exists($log_file)) { $saved_log = json_decode(file_get_contents($log_file)); if (is_object($saved_log) && property_exists($saved_log, 'files')) { $files = (array) $saved_log->files; foreach ($header[$i]['handles'] as $handle) { $handle = is_array($handle) ? '' : $handle; if (isset($files[$handle]) && $files[$handle]->success) { $done[] = $handle; } } } else { // The merged file already exists, so add all files to $done. $done = array_merge($done, $header[$i]['handles']); } } else { // The merged file already exists, so add all files to $done. $done = array_merge($done, $header[$i]['handles']); } } // the developers tab, takes precedence // Async CSS with loadCSS ? if ($this->options['loadcss'] && empty($this->options['remove_css'])) { $mt = $header[$i]['media']; WP_Optimize_Minify_Print::async_style($file_url, $mt); // enqueue file, if not empty } else { if (file_exists($file) && filesize($file) > 0) { // enqueue it wp_enqueue_style("wpo_min-header-$i", $file_url, array(), 'mycoolversion', $header[$i]['media']); foreach ($header[$i]['handles'] as $h) { if (!$merge_inline_extra_css_js && isset($inline_css[$h]) && !empty($inline_css[$h])) { wp_add_inline_style("wpo_min-header-$i", $inline_css[$h]); } } } else { // file could not be generated, output something meaningful echo "<!-- ERROR: WP-Optimize Minify was not allowed to save its cache on - ".esc_html(str_replace(ABSPATH, '', $file))." -->"; echo "<!-- Please check if the path above is correct and ensure your server has write permission there! -->"; } } // other css need to be requeued for the order of files to be kept } else { wp_dequeue_style($header[$i]['handle']); wp_enqueue_style($header[$i]['handle']); } } // remove from queue $wp_styles->done = $done; return true; } /** * Process JS in the footer * * @return void */ public function process_footer_scripts() { global $wp_scripts; if (!is_object($wp_scripts)) { return; } $cache_path = WP_Optimize_Minify_Cache_Functions::cache_path(); $cache_base_dir = $cache_path['cachebasedir']; $cache_dir = $cache_path['cachedir']; $cache_dir_url = $cache_path['cachedirurl']; $exclude_js = $this->get_excluded_js_list(); $ignore_list = WP_Optimize_Minify_Functions::compile_ignore_list($exclude_js); $async_js = $this->get_async_js_list(); $scripts = clone $wp_scripts; $scripts->all_deps($scripts->queue); $footer = array(); $minify_js = $this->options['enable_js_minification']; $merge_js = $this->options['enable_merging_of_js']; $process_js = $minify_js || $merge_js; $merge_inline_extra_css_js = $this->options['merge_inline_extra_css_js']; // mark as done (as we go) $done = $scripts->done; $previous_load_strategy = null; // get groups of handles foreach ($scripts->to_do as $handle) : // get full url $href = WP_Optimize_Minify_Functions::get_hurl($wp_scripts->registered[$handle]->src); // inlined scripts without file if (empty($href)) { continue; } // Exclude JS files from PageSpeedIndex (Async) takes priority over the ignore list if (false != $async_js || is_array($async_js)) { // check for string match $skipjs = false; foreach ($async_js as $l) { if (stripos($href, $l) !== false) { // print code if there are no linebreaks, or return WP_Optimize_Minify_Print::async_script($href); $skipjs = true; $done = array_merge($done, array($handle)); break; } } if (false != $skipjs) { continue; } } // IE only files don't increment things $ieonly = WP_Optimize_Minify_Functions::is_url_in_ie_blacklist($href); if ($ieonly) { continue; } // check if the current URL points to a minified file. $exclude_minified_js = WP_Optimize_Minify_Functions::is_minified_css_js_filename($href); $normal_processing = $exclude_minified_js && !$merge_js; // skip ignore list, scripts with conditionals, external scripts if (($process_js && !$normal_processing && !WP_Optimize_Minify_Functions::in_arrayi($href, $ignore_list) && !isset($wp_scripts->registered[$handle]->extra["conditional"]) && WP_Optimize_Minify_Functions::internal_url($href, site_url())) || empty($href) ) { // process if (isset($footer[count($footer)-1]['handle']) || !count($footer) || !$merge_js || (isset($scripts->registered[$handle]->extra['strategy']) && $previous_load_strategy != $scripts->registered[$handle]->extra['strategy']) || (!isset($scripts->registered[$handle]->extra['strategy']) && null != $previous_load_strategy) ) { array_push($footer, array('handles' => array(), 'versions' => array())); } if (isset($wp_scripts->registered[$handle]->extra['before'])) { if (!empty($footer[count($footer)-1]['handles'])) { array_push($footer, array('handles' => array(), 'versions' => array())); } } // push it to the array array_push($footer[count($footer)-1]['handles'], $handle); array_push($footer[count($footer)-1]['versions'], $wp_scripts->registered[$handle]->ver); // Register strategy if it is used if (isset($scripts->registered[$handle]->extra['strategy'])) $footer[count($footer)-1]['strategy'] = $scripts->registered[$handle]->extra['strategy']; $footer[count($footer) - 1]['last_modified'][] = WP_Optimize_Minify_Functions::get_modification_time($wp_scripts->registered[$handle]->src); // external and ignored scripts if (isset($scripts->registered[$handle]->extra['strategy'])) { $previous_load_strategy = $scripts->registered[$handle]->extra['strategy']; } else { $previous_load_strategy = null; } } else { array_push($footer, array('handle' => $handle)); } endforeach; // loop through footer scripts and merge for ($i=0,$l=count($footer); $i<$l; $i++) { if (!isset($footer[$i]['handle'])) { $handles_str = is_array($footer[$i]['handles']) ? $this->array_to_string_conversion($footer[$i]['handles']) : strval($footer[$i]['handles']); $last_modified_str = empty($footer[$i]['last_modified']) ? '' : (is_array($footer[$i]['last_modified']) ? $this->array_to_string_conversion($footer[$i]['last_modified']) : strval($footer[$i]['last_modified'])); if ($merge_js) { // Change the hash based on last modified timestamp $hash = hash('adler32', $handles_str . $last_modified_str); } else { $hash = $handles_str . $last_modified_str; } // static cache file info $file_name = 'wpo-minify-footer-'.$hash.($minify_js ? '.min' : ''); // create cache files and urls $file = $cache_dir.'/'.$file_name.'.js'; $meta_json_file = $cache_base_dir.'/'.'meta.json'; $file_url = WP_Optimize_Minify_Functions::get_protocol($cache_dir_url.'/'.$file_name.'.js'); // generate a new cache file clearstatcache(); if (!file_exists($file)) { // code and log initialization $log_header = "PROCESSED on ".gmdate('r')." from ".home_url(add_query_arg(null, null)); $log = array( 'header' => $log_header, 'files' => array() ); $meta_log = $this->meta_log_initialization($log_header); $before_code = ''; $code = ''; $after_code = ''; // minify and write to file foreach ($footer[$i]['handles'] as $handle) : if (!empty($wp_scripts->registered[$handle]->src)) { // only add minified scripts once to the bundle if (in_array($handle, $done)) continue; // get href per handle $handle_src = $wp_scripts->registered[$handle]->src; $href = WP_Optimize_Minify_Functions::get_hurl($handle_src); $version = $wp_scripts->registered[$handle]->ver; $last_modified = WP_Optimize_Minify_Functions::get_modification_time($handle_src); // inlined scripts without file if (empty($href)) { continue; } // download, minify, cache $tkey = 'js-'.hash('adler32', $handle . $href).'.js'; $json = false; $json = WP_Optimize_Minify_Cache_Functions::get_transient($tkey); if (false === $json) { $enable_minification = $minify_js && !WP_Optimize_Minify_Functions::is_minified_css_js_filename($href); $json = WP_Optimize_Minify_Functions::download_and_minify($href, null, $enable_minification, 'js', $handle, $version); if ($this->options['debug']) { echo "<!-- wpo_min DEBUG: Uncached file processing now for " . esc_html($handle) . " / " . esc_html($href) . " / " . esc_html($version) . " -->\n"; } WP_Optimize_Minify_Cache_Functions::set_transient($tkey, $json); } // decode $res = json_decode($json, true); if (null === $res) continue; if ($this->should_reset_minify_assets($res, $href, 'js', $handle, $version)) { WP_Optimize_Minify_Cache_Functions::reset(); $this->minify_cache_incremented = true; $meta_log['invalidation_reason'] = 'Checksum Mismatch and version changed from ' . $res['request']['version'] . ' to ' . $version; $meta_log['files_that_changed'][$handle] = $res['log']; } // response has failed if (true != $res['status']) { $log['files'][$handle] = $res['log']; $log['files'][$handle]['uses_loading_strategy'] = isset($footer[$i]['strategy']); $log['files'][$handle]['version'] = $version; $log['files'][$handle]['last_modified'] = $last_modified; continue; } $done[] = $handle; // Add extra data from wp_add_inline_script before if (!empty($wp_scripts->registered[$handle]->extra)) { if (!empty($wp_scripts->registered[$handle]->extra['before']) && is_array($wp_scripts->registered[$handle]->extra['before'])) { if ($merge_inline_extra_css_js) { $before_code .= "\n" . implode("\n", array_filter($wp_scripts->registered[$handle]->extra['before'])); } } } // Add translation if (!empty($wp_scripts->registered[$handle]->textdomain)) { $code .= "\n" . $wp_scripts->print_translations($handle, false); } // Add extra data from wp_add_inline_script after if (!empty($wp_scripts->registered[$handle]->extra)) { if (!empty($wp_scripts->registered[$handle]->extra['after']) && is_array($wp_scripts->registered[$handle]->extra['after'])) { if ($merge_inline_extra_css_js) { $after_code .= "\n" . implode("\n", array_filter($wp_scripts->registered[$handle]->extra['after'])); } } } // append code to merged file $code .= $before_code . "\n"; $code .= isset($res['code']) ? $res['code'] : ''; $code .= "\n" . $after_code . "\n"; $code = WP_Optimize_Minify_Functions::prepare_merged_js($code, $href); $log['files'][$handle] = $res['log']; $log['files'][$handle]['uses_loading_strategy'] = isset($footer[$i]['strategy']); $log['files'][$handle]['version'] = $version; $log['files'][$handle]['last_modified'] = $last_modified; // consider dependencies on handles with an empty src } else { wp_dequeue_script($handle); wp_enqueue_script($handle); } endforeach; // generate cache, write log if (!empty($code)) { // Only generate meta.json file if cache is reset if (!empty($meta_log['invalidation_reason'])) { file_put_contents($meta_json_file, wp_json_encode($meta_log)); } WP_Optimize_Minify_Print::write_combined_asset($file, $code, $log); } } else { $log_file = $file.'.json'; if (file_exists($log_file)) { $saved_log = json_decode(file_get_contents($log_file)); if (is_object($saved_log) && property_exists($saved_log, 'files')) { $files = (array) $saved_log->files; foreach ($footer[$i]['handles'] as $handle) { if (isset($files[$handle]) && $files[$handle]->success) { $done[] = $handle; } } } else { // The merged file already exists, so add all files to $done. $done = array_merge($done, $footer[$i]['handles']); } } else { // The merged file already exists, so add all files to $done. $done = array_merge($done, $footer[$i]['handles']); } } // register minified file wp_register_script("wpo_min-footer-$i", $file_url, array(), null, false); // add all extra data from wp_localize_script $before_code = ''; $data = array(); $after_code = ''; foreach ($footer[$i]['handles'] as $handle) { if (isset($wp_scripts->registered[$handle]->extra['data'])) { $data[] = $wp_scripts->registered[$handle]->extra['data']; } // Add extra data from wp_add_inline_script before if (!empty($wp_scripts->registered[$handle]->extra)) { if (!empty($wp_scripts->registered[$handle]->extra['before']) && is_array($wp_scripts->registered[$handle]->extra['before'])) { if (!$merge_inline_extra_css_js) { $before_code.= "\n" . implode("\n", array_filter($wp_scripts->registered[$handle]->extra['before'])) . "\n"; } } if (!empty($wp_scripts->registered[$handle]->extra['after']) && is_array($wp_scripts->registered[$handle]->extra['after'])) { if (!$merge_inline_extra_css_js) { $after_code.= "\n" . implode("\n", array_filter($wp_scripts->registered[$handle]->extra['after'])) . "\n"; } } } } if (count($data) > 0) { $wp_scripts->registered["wpo_min-footer-$i"]->extra['data'] = implode("\n", $data); } // enqueue file, if not empty if (file_exists($file) && (filesize($file) > 0 || count($data) > 0)) { if (!empty($before_code)) { wp_add_inline_script("wpo_min-footer-$i", $before_code, 'before'); } wp_enqueue_script("wpo_min-footer-$i"); if (!empty($after_code)) { wp_add_inline_script("wpo_min-footer-$i", $after_code, 'after'); } if (isset($footer[$i]['strategy'])) { // Backward compatible way of doing this wp_script_add_data( "wpo_min-footer-$i", 'strategy', $footer[$i]['strategy']); } } else { // file could not be generated, output something meaningful echo "<!-- ERROR: WP-Optimize Minify was not allowed to save its cache on - ".esc_html(str_replace(ABSPATH, '', $file))." -->"; echo "<!-- Please check if the path above is correct and ensure your server has write permission there! -->"; } // other scripts need to be requeued for the order of files to be kept } else { wp_dequeue_script($footer[$i]['handle']); wp_enqueue_script($footer[$i]['handle']); } } // remove from queue $wp_scripts->done = $done; } /** * Process header JavaScript * Dependant on 'enable_js' option * * @return void */ public function process_header_scripts() { global $wp_scripts; if (!is_object($wp_scripts)) return; $cache_path = WP_Optimize_Minify_Cache_Functions::cache_path(); $cache_base_dir = $cache_path['cachebasedir']; $cache_dir = $cache_path['cachedir']; $cache_dir_url = $cache_path['cachedirurl']; $exclude_js = $this->get_excluded_js_list(); $ignore_list = WP_Optimize_Minify_Functions::compile_ignore_list($exclude_js); $async_js = $this->get_async_js_list(); $scripts = clone $wp_scripts; $scripts->all_deps($scripts->queue); $minify_js = $this->options['enable_js_minification']; $merge_js = $this->options['enable_merging_of_js']; $process_js = $minify_js || $merge_js; $merge_inline_extra_css_js = $this->options['merge_inline_extra_css_js']; $header = array(); // mark as done (as we go) $done = $scripts->done; $excluded_dependencies = array(); $previous_load_strategy = null; // Prepare and separate assets (get groups of handles) foreach ($scripts->to_do as $handle) { // get full url $href = WP_Optimize_Minify_Functions::get_hurl($wp_scripts->registered[$handle]->src); // inlined scripts without file if (empty($href)) { wp_enqueue_script($handle, false); continue; } // Only go through items without a group if (!$scripts->groups[$handle]) { // Exclude JS files from PageSpeedIndex (Async) takes priority over the ignore list // check for string match $skipjs = false; foreach ($async_js as $l) { if (stripos($href, $l) !== false) { WP_Optimize_Minify_Print::async_script($href); $skipjs = true; $done = array_merge($done, array($handle)); break; } } // IE only files don't increment things if ($skipjs || WP_Optimize_Minify_Functions::is_url_in_ie_blacklist($href) ) { continue; } // Skip jQuery from the merged files if deferring is ENABLED and defer_jquery is DISABLED if ('all' === $this->options['enable_defer_js'] && !$this->options['defer_jquery'] && (false !== stripos($href, '/jquery.js') || false !== stripos($href, '/jquery.min.js') || (false !== stripos($href, '/jquery-') && false !== stripos($href, '.js'))) ) { continue; } // check if the current URL points to a minified file. $exclude_minified_js = WP_Optimize_Minify_Functions::is_minified_css_js_filename($href); $normal_processing = $exclude_minified_js && !$merge_js; // Group handles - skip ignore list, scripts with conditionals, external scripts if (($process_js && !$normal_processing && !WP_Optimize_Minify_Functions::in_arrayi($href, $ignore_list) && !isset($wp_scripts->registered[$handle]->extra["conditional"]) && WP_Optimize_Minify_Functions::internal_url($href, site_url())) || empty($href) ) { // process if (isset($header[count($header)-1]['handle']) || !count($header) || !$merge_js || (isset($scripts->registered[$handle]->extra['strategy']) && $previous_load_strategy != $scripts->registered[$handle]->extra['strategy']) || (!isset($scripts->registered[$handle]->extra['strategy']) && null != $previous_load_strategy) ) { array_push($header, array('handles' => array(), 'versions' => array())); } // Force loading of dependencies foreach ($wp_scripts->registered[$handle]->deps as $dep) { // If the handle is not present in $done yet, or excluded, enqueue it. $dep_href = WP_Optimize_Minify_Functions::get_hurl($wp_scripts->registered[$dep]->src); if (!in_array($dep, $done) && !WP_Optimize_Minify_Functions::in_arrayi($dep_href, $ignore_list)) { // Include any dependency array_push($header[count($header)-1]['handles'], $dep); array_push($header[count($header)-1]['versions'], $wp_scripts->registered[$dep]->ver); } elseif (!in_array($dep, $done) && WP_Optimize_Minify_Functions::in_arrayi($dep_href, $ignore_list)) { // The dependency is in the exclude list array_push($header, array('handle' => $dep)); // Record dependency to be added to the minified script as dependency array if (isset($excluded_dependencies[count($header)])) { $excluded_dependencies[count($header)][] = $dep; } else { $excluded_dependencies[count($header)] = array($dep); } // Adds the 'handles' record for the main script array_push($header, array('handles' => array(), 'versions' => array())); } } if (isset($wp_scripts->registered[$handle]->extra['before'])) { if (!empty($header[count($header)-1]['handles'])) { array_push($header, array('handles' => array(), 'versions' => array())); } } // push it to the array array_push($header[count($header)-1]['handles'], $handle); array_push($header[count($header)-1]['versions'], $scripts->registered[$handle]->ver); $header[count($header) - 1]['last_modified'][] = WP_Optimize_Minify_Functions::get_modification_time($wp_scripts->registered[$handle]->src); // Register strategy if it is used if (isset($scripts->registered[$handle]->extra['strategy'])) $header[count($header)-1]['strategy'] = $scripts->registered[$handle]->extra['strategy']; if (isset($scripts->registered[$handle]->extra['strategy'])) { $previous_load_strategy = $scripts->registered[$handle]->extra['strategy']; } else { $previous_load_strategy = null; } // external and ignored scripts } else { // add the ignored assets array_push($header, array('handle' => $handle)); } // make sure that the scripts skipped here, show up in the footer } else { wp_enqueue_script($handle, $href, array(), null, true); } } // loop through header scripts and merge for ($i=0,$l=count($header); $i < $l; $i++) { if (!isset($header[$i]['handle'])) { $handles_str = is_array($header[$i]['handles']) ? $this->array_to_string_conversion($header[$i]['handles']) : strval($header[$i]['handles']); $last_modified_str = empty($header[$i]['last_modified']) ? '' : (is_array($header[$i]['last_modified']) ? $this->array_to_string_conversion($header[$i]['last_modified']) : strval($header[$i]['last_modified'])); if ($merge_js) { $hash = hash('adler32', $handles_str . $last_modified_str); } else { $hash = $handles_str . $last_modified_str; } // static cache file info $file_name = 'wpo-minify-header-'.$hash.($minify_js ? '.min' : ''); // create cache files and urls $file = $cache_dir.'/'.$file_name.'.js'; $meta_json_file = $cache_base_dir.'/'.'meta.json'; $file_url = WP_Optimize_Minify_Functions::get_protocol($cache_dir_url.'/'.$file_name.'.js'); // generate a new cache file clearstatcache(); if (!file_exists($file)) { // code and log initialization $log_header = "PROCESSED on ".gmdate('r')." from ".home_url(add_query_arg(null, null)); $log = array( 'header' => $log_header, 'files' => array() ); $meta_log = $this->meta_log_initialization($log_header); $before_code = ''; $code = ''; $after_code = ''; // minify and write to file foreach ($header[$i]['handles'] as $handle) { if (!empty($wp_scripts->registered[$handle]->src)) { // only add minified scripts once to the bundle if (in_array($handle, $done)) continue; // get href per handle $handle_src = $wp_scripts->registered[$handle]->src; $href = WP_Optimize_Minify_Functions::get_hurl($handle_src); $version = $wp_scripts->registered[$handle]->ver; $last_modified = WP_Optimize_Minify_Functions::get_modification_time($handle_src); if (empty($href)) continue; // download, minify, cache $tkey = 'js-'.hash('adler32', $handle . $href).'.js'; $json = false; $json = WP_Optimize_Minify_Cache_Functions::get_transient($tkey); if (false === $json) { $enable_minification = $minify_js && !WP_Optimize_Minify_Functions::is_minified_css_js_filename($href); $json = WP_Optimize_Minify_Functions::download_and_minify($href, null, $enable_minification, 'js', $handle, $version); if ($this->options['debug']) { echo "<!-- wpo_min DEBUG: Uncached file processing now for " . esc_html($handle) . " / " . esc_html($href) . " / " . esc_html($version) . " -->" . "\n"; } WP_Optimize_Minify_Cache_Functions::set_transient($tkey, $json); } // decode $res = json_decode($json, true); if (null === $res) continue; if ($this->should_reset_minify_assets($res, $href, 'js', $handle, $version)) { WP_Optimize_Minify_Cache_Functions::reset(); $this->minify_cache_incremented = true; $meta_log['invalidation_reason'] = 'Checksum Mismatch and version changed from ' . $res['request']['version'] . ' to ' . $version; $meta_log['files_that_changed'][$handle] = $res['log']; } // response has failed if (true != $res['status']) { $log['files'][$handle] = $res['log']; $log['files'][$handle]['uses_loading_strategy'] = isset($header[$i]['strategy']); $log['files'][$handle]['version'] = $version; $log['files'][$handle]['last_modified'] = $last_modified; continue; } // Add extra data from wp_add_inline_script before if (!empty($wp_scripts->registered[$handle]->extra)) { if (!empty($wp_scripts->registered[$handle]->extra['before']) && is_array($wp_scripts->registered[$handle]->extra['before'])) { if ($merge_inline_extra_css_js) { $before_code .= "\n" . implode("\n", array_filter($wp_scripts->registered[$handle]->extra['before'])); } } } // Only add the $handle to $done if it was successfully downloaded $done[] = $handle; // Add translations if (!empty($wp_scripts->registered[$handle]->textdomain)) { $code .= "\n" . $wp_scripts->print_translations($handle, false); } // Add extra data from wp_add_inline_script after if (!empty($wp_scripts->registered[$handle]->extra)) { if (!empty($wp_scripts->registered[$handle]->extra['after']) && is_array($wp_scripts->registered[$handle]->extra['after'])) { if ($merge_inline_extra_css_js) { $after_code.= "\n" . implode("\n", array_filter($wp_scripts->registered[$handle]->extra['after'])); } } } // append code to merged file $code .= $before_code . "\n"; $code .= isset($res['code']) ? $res['code'] : ''; $code .= "\n" . $after_code . "\n"; $code = WP_Optimize_Minify_Functions::prepare_merged_js($code, $href); $log['files'][$handle] = $res['log']; $log['files'][$handle]['uses_loading_strategy'] = isset($header[$i]['strategy']); $log['files'][$handle]['version'] = $version; $log['files'][$handle]['last_modified'] = $last_modified; // consider dependencies on handles with an empty src } else { wp_dequeue_script($handle); wp_enqueue_script($handle); } } // generate cache, write log if (!empty($code)) { // Only generate meta.json file if cache is reset if (!empty($meta_log['invalidation_reason'])) { file_put_contents($meta_json_file, wp_json_encode($meta_log)); } WP_Optimize_Minify_Print::write_combined_asset($file, $code, $log); } } else { $log_file = $file.'.json'; if (file_exists($log_file)) { $saved_log = json_decode(file_get_contents($log_file)); if (is_object($saved_log) && property_exists($saved_log, 'files')) { $files = (array) $saved_log->files; foreach ($header[$i]['handles'] as $handle) { $handle = is_array($handle) ? '' : $handle; if (isset($files[$handle]) && $files[$handle]->success) { $done[] = $handle; } } } else { // The merged file already exists, so add all files to $done. $done = array_merge($done, $header[$i]['handles']); } } else { // The merged file already exists, so add all files to $done. $done = array_merge($done, $header[$i]['handles']); } } // register minified file $dependencies = isset($excluded_dependencies[$i]) ? $excluded_dependencies[$i] : array(); wp_register_script("wpo_min-header-$i", $file_url, $dependencies, null, false); // add all extra data from wp_localize_script $before_code = ''; $data = array(); $after_code = ''; foreach ($header[$i]['handles'] as $handle) { if (isset($wp_scripts->registered[$handle]->extra['data'])) { $data[] = $wp_scripts->registered[$handle]->extra['data']; } // Add extra data from wp_add_inline_script before if (!empty($wp_scripts->registered[$handle]->extra)) { if (!empty($wp_scripts->registered[$handle]->extra['before']) && is_array($wp_scripts->registered[$handle]->extra['before'])) { if (!$merge_inline_extra_css_js) { $before_code.= "\n" . implode("\n", array_filter($wp_scripts->registered[$handle]->extra['before'])) . "\n"; } } if (!empty($wp_scripts->registered[$handle]->extra['after']) && is_array($wp_scripts->registered[$handle]->extra['after'])) { if (!$merge_inline_extra_css_js) { $after_code.= "\n" . implode("\n", array_filter($wp_scripts->registered[$handle]->extra['after'])) . "\n"; } } } } if (count($data) > 0) { $wp_scripts->registered["wpo_min-header-$i"]->extra['data'] = implode("\n", $data); } // enqueue file, if not empty if (file_exists($file) && (filesize($file) > 0 || count($data) > 0)) { if (!empty($before_code)) { wp_add_inline_script("wpo_min-header-$i", $before_code, 'before'); } wp_enqueue_script("wpo_min-header-$i"); if (!empty($after_code)) { wp_add_inline_script("wpo_min-header-$i", $after_code, 'after'); } if (isset($header[$i]['strategy'])) { // Backward compatible way of doing this wp_script_add_data( "wpo_min-header-$i", 'strategy', $header[$i]['strategy']); } } else { // file could not be generated, output something meaningful echo "<!-- ERROR: WP-Optimize minify was not allowed to save its cache on - ".esc_html(str_replace(ABSPATH, '', $file))." -->"; echo "<!-- Please check if the path above is correct and ensure your server has write permission there! -->"; echo "<!-- If you found a bug, please report this on https://wordpress.org/support/plugin/wp-optimize/ -->"; } // other scripts need to be requeued for the order of files to be kept } else { wp_dequeue_script($header[$i]['handle']); wp_enqueue_script($header[$i]['handle']); } } // remove from queue $wp_scripts->done = $done; } /** * Process CSS in the footer * * @return void */ public function process_footer_css() { global $wp_styles; if (!is_object($wp_styles)) return; $cache_path = WP_Optimize_Minify_Cache_Functions::cache_path(); $cache_base_dir = $cache_path['cachebasedir']; $cache_dir = $cache_path['cachedir']; $cache_dir_url = $cache_path['cachedirurl']; $exclude_css = $this->get_excluded_css_list(); $ignore_list = WP_Optimize_Minify_Functions::compile_ignore_list($exclude_css); $async_css = $this->get_async_css_list(); $minify_css = $this->options['enable_css_minification']; $merge_css = $this->options['enable_merging_of_css']; $process_css = $minify_css || $merge_css; $disable_google_fonts_processing = $this->options['disable_google_fonts_processing']; $log = ""; $code = ""; $styles = clone $wp_styles; $styles->all_deps($styles->queue); $done = $styles->done; $footer = array(); $google_fonts = array(); $inline_css = array(); // dequeue all styles if (isset($this->options['remove_css']) && $this->options['remove_css']) { foreach ($styles->to_do as $handle) : $done = array_merge($done, array($handle)); endforeach; // remove from queue $wp_styles->done = $done; return; } // dequeue and get a list of google fonts, or requeue external foreach ($styles->to_do as $handle) { $href = WP_Optimize_Minify_Functions::get_hurl($wp_styles->registered[$handle]->src); // inlined scripts without file if (empty($href)) continue; if (!$disable_google_fonts_processing && WP_Optimize_Minify_Functions::is_google_font($href)) { wp_dequeue_style($handle); if ($this->options['remove_googlefonts']) { $done = array_merge($done, array($handle)); continue; } // mark as done if to be removed if ($this->options['merge_google_fonts'] || '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); $google_fonts[$handle] = $href; } else { $google_fonts[$handle] = $href; } } else { // skip google fonts optimization? wp_enqueue_style($handle); } } else { // failsafe wp_dequeue_style($handle); wp_enqueue_style($handle); } } // concat google fonts, if enabled if ($this->options['merge_google_fonts'] && count($google_fonts) > 0 || ('inline' === $this->options['gfonts_method'] && count($google_fonts) > 0) ) { // merge google fonts if force inlining is enabled? $nfonts = array(); if ($this->options['merge_google_fonts']) { $nfonts[] = WP_Optimize_Minify_Fonts::concatenate_google_fonts($google_fonts); } else { foreach ($google_fonts as $h => $a) { if (!empty($a)) { $nfonts[$h] = $a; } } } // foreach google font (will be one if merged is not disabled) if (count($nfonts) > 0) { foreach ($nfonts as $handle => $href) { // hide from PageSpeedIndex, async, inline, or default if ('exclude' === $this->options['gfonts_method']) { WP_Optimize_Minify_Print::exclude_style($href); $done = array_merge($done, array($handle)); } elseif ('async' === $this->options['gfonts_method']) { // async CSS WP_Optimize_Minify_Print::async_style($href); $done = array_merge($done, array($handle)); } elseif ('inline' === $this->options['gfonts_method']) { // inline css WP_Optimize_Minify_Print::inline_style($handle, $href); $done = array_merge($done, array($handle)); } else { // fallback, enqueue google fonts wp_enqueue_style($handle, $href, array(), null, 'all'); } } } } // get groups of handles $uniq = array(); foreach ($styles->to_do as $handle) { // skip already processed google fonts if (isset($google_fonts[$handle])) { continue; } // conditionals $conditional = null; if (isset($wp_styles->registered[$handle]->extra["conditional"])) { $conditional = $wp_styles->registered[$handle]->extra["conditional"]; // such as ie7, ie8, ie9, etc } // mediatype $mt = isset($wp_styles->registered[$handle]->args) ? $wp_styles->registered[$handle]->args : 'all'; if ('screen' == $mt || 'screen, print' == $mt || empty($mt) || is_null($mt) || false == $mt) { $mt = 'all'; } $mediatype = $mt; // get full url $href = WP_Optimize_Minify_Functions::get_hurl($wp_styles->registered[$handle]->src); $version = $wp_styles->registered[$handle]->ver; // inlined scripts without file if (empty($href)) { continue; } // mark duplicates as done and remove from the queue if (!empty($href)) { $version = is_array($version) ? '' : $version; $key = hash('adler32', $href . $version); if (isset($uniq[$key])) { $done = array_merge($done, array($handle)); continue; } else { $uniq[$key] = $handle; } } // IE only files don't increment things $ieonly = WP_Optimize_Minify_Functions::is_url_in_ie_blacklist($href); if ($ieonly) { continue; } // Exclude specific CSS files from PageSpeedIndex? if (false != $async_css && is_array($async_css) && WP_Optimize_Minify_Functions::in_arrayi($href, $async_css)) { WP_Optimize_Minify_Print::exclude_style($href); $done = array_merge($done, array($handle)); continue; } if (WP_Optimize_Minify_Functions::is_font_awesome($href)) { if ('inline' === $this->options['fawesome_method']) { WP_Optimize_Minify_Print::inline_style($handle, $href); $done = array_merge($done, array($handle)); continue; } elseif ('async' === $this->options['fawesome_method']) { WP_Optimize_Minify_Print::async_style($href, $mediatype); $done = array_merge($done, array($handle)); continue; } elseif ('exclude' === $this->options['fawesome_method']) { WP_Optimize_Minify_Print::exclude_style($href); $done = array_merge($done, array($handle)); continue; } } // Exclude Print mediatype if ($this->options['remove_print_mediatypes'] && 'print' === $mediatype) { $done = array_merge($done, array($handle)); continue; } // check if the current URL points to a minified file. $exclude_minified_css = WP_Optimize_Minify_Functions::is_minified_css_js_filename($href); $normal_processing = $exclude_minified_css && !$merge_css; // skip ignore list, conditional css, external css, font-awesome merge and already minified css if (($process_css && !$normal_processing && !WP_Optimize_Minify_Functions::in_arrayi($href, $ignore_list) && !isset($conditional) && WP_Optimize_Minify_Functions::internal_url($href, site_url())) || empty($href) ) { // collect inline css for this handle if (isset($wp_styles->registered[$handle]->extra['after']) && is_array($wp_styles->registered[$handle]->extra['after'])) { $inline_css[$handle] = WP_Optimize_Minify_Functions::minify_css_string(implode('', $wp_styles->registered[$handle]->extra['after'])); // save $wp_styles->registered[$handle]->extra['after'] = null; // dequeue } // process if (isset($footer[count($footer)-1]['handle']) || !count($footer) || $footer[count($footer)-1]['media'] != $wp_styles->registered[$handle]->args || !$merge_css) { array_push($footer, array('handles' => array(), 'media' => $mediatype, 'versions' => array())); } // push it to the array get latest modified time array_push($footer[count($footer)-1]['handles'], $handle); array_push($footer[count($footer)-1]['versions'], $version); $footer[count($footer) - 1]['last_modified'][] = WP_Optimize_Minify_Functions::get_modification_time($wp_styles->registered[$handle]->src); // external and ignored css } else { // normal enqueueing array_push($footer, array('handle' => $handle)); } } /** * Filters the array of stylesheets before processing them * * @param array $list - The list of items filtered * @param string $location - The location of the list (footer or header) * @return array */ $footer = apply_filters('wpo_minify_stylesheets', $footer, 'footer'); // loop through footer css and merge for ($i=0,$l=count($footer); $i<$l; $i++) { if (!isset($footer[$i]['handle'])) { $handles_str = is_array($footer[$i]['handles']) ? $this->array_to_string_conversion($footer[$i]['handles']) : strval($footer[$i]['handles']); $last_modified_str = empty($footer[$i]['last_modified']) ? '' : (is_array($footer[$i]['last_modified']) ? $this->array_to_string_conversion($footer[$i]['last_modified']) : strval($footer[$i]['last_modified'])); if ($merge_css) { // get hash for the inline css in this group $inline_css_group = array(); if (is_array($footer[$i]['handles'])) { foreach ($footer[$i]['handles'] as $h) { if (isset($inline_css[$h]) && !empty($inline_css[$h])) { $inline_css_group[] = $inline_css[$h]; } } } $inline_css_hash = md5(implode('', $inline_css_group)); $hash = hash('adler32', $handles_str . $inline_css_hash . $last_modified_str); } else { $hash = $handles_str . $last_modified_str; } // static cache file info $file_name = 'wpo-minify-footer-'.$hash.($minify_css ? '.min' : ''); // create cache files and urls $file = $cache_dir.'/'.$file_name.'.css'; $meta_json_file = $cache_base_dir.'/'.'meta.json'; $file_url = WP_Optimize_Minify_Functions::get_protocol($cache_dir_url.'/'.$file_name.'.css'); // generate a new cache file clearstatcache(); if (!file_exists($file)) { // code and log initialization $log_header = "PROCESSED on ".gmdate('r')." from ".home_url(add_query_arg(null, null)); $log = array( 'header' => $log_header, 'files' => array() ); $meta_log = $this->meta_log_initialization($log_header); $code = ''; // minify and write to file foreach ($footer[$i]['handles'] as $handle) { if (!empty($wp_styles->registered[$handle]->src)) { // get href per handle $handle_src = $wp_styles->registered[$handle]->src; $href = WP_Optimize_Minify_Functions::get_hurl($handle_src); $version = $wp_styles->registered[$handle]->ver; $last_modified = WP_Optimize_Minify_Functions::get_modification_time($handle_src); // inlined scripts without hreffile if (empty($href)) continue; // download, minify, cache $tkey = 'css-'.hash('adler32', $handle . $href).'.css'; $json = false; $json = WP_Optimize_Minify_Cache_Functions::get_transient($tkey); if (false === $json) { $enable_minification = $minify_css && !WP_Optimize_Minify_Functions::is_minified_css_js_filename($href); $json = WP_Optimize_Minify_Functions::download_and_minify($href, null, $enable_minification, 'css', $handle, $version); if ($this->options['debug']) { echo "<!-- wpo_min DEBUG: Uncached file processing now for " . esc_html($handle) . " / " . esc_html($href) . " / " . esc_html($version) . " -->" . "\n"; } WP_Optimize_Minify_Cache_Functions::set_transient($tkey, $json); } // decode $res = json_decode($json, true); if (null === $res) continue; if ($this->should_reset_minify_assets($res, $href, 'css', $handle, $version)) { WP_Optimize_Minify_Cache_Functions::reset(); $this->minify_cache_incremented = true; $meta_log['invalidation_reason'] = 'Checksum Mismatch and version changed from ' . $res['request']['version'] . ' to ' . $version; $meta_log['files_that_changed'][$handle] = $res['log']; } // response has failed if (true != $res['status']) { $log['files'][$handle] = $res['log']; $log['files'][$handle]['version'] = $version; $log['files'][$handle]['last_modified'] = $last_modified; continue; } $done[] = $handle; // append code to merged file $code .= isset($res['code']) ? $res['code'] : ''; $log['files'][$handle] = $res['log']; $log['files'][$handle]['version'] = $version; $log['files'][$handle]['last_modified'] = $last_modified; // append inlined styles if (isset($inline_css[$handle]) && !empty($inline_css[$handle])) { $code.= $inline_css[$handle]; } // consider dependencies on handles with an empty src } else { wp_dequeue_script($handle); wp_enqueue_script($handle); } }; // generate cache, add inline css, write log if (!empty($code)) { // Only generate meta.json file if cache is reset if (!empty($meta_log['invalidation_reason'])) { file_put_contents($meta_json_file, wp_json_encode($meta_log)); } WP_Optimize_Minify_Print::write_combined_asset($file, $code, $log); } } else { $log_file = $file.'.json'; if (file_exists($log_file)) { $saved_log = json_decode(file_get_contents($log_file)); if (is_object($saved_log) && property_exists($saved_log, 'files')) { $files = (array) $saved_log->files; foreach ($footer[$i]['handles'] as $handle) { if (isset($files[$handle]) && $files[$handle]->success) { $done[] = $handle; } } } else { // The merged file already exists, so add all files to $done. $done = array_merge($done, $footer[$i]['handles']); } } else { // The merged file already exists, so add all files to $done. $done = array_merge($done, $footer[$i]['handles']); } } // Async CSS with loadCSS ? if ($this->options['loadcss'] && !$this->options['remove_css']) { $mt = $footer[$i]['media']; WP_Optimize_Minify_Print::async_style($file_url, $mt); // enqueue file, if not empty } else { if (file_exists($file) && filesize($file) > 0) { // inline if the file is smaller than 20KB or option has been enabled if (filesize($file) < 20000 && $this->options['inline_css']) { $this->inline_css(file_get_contents($file), $handle, $file_url, $footer[$i]['media']); } else { // enqueue it wp_enqueue_style("wpo_min-footer-$i", $file_url, array(), null, $footer[$i]['media']); } } else { // file could not be generated, output something meaningful echo "<!-- ERROR: WP-Optimize Minify was not allowed to save its cache on - ".esc_html(str_replace(ABSPATH, '', $file))." -->"; echo "<!-- Please check if the path above is correct and ensure your server has write permission there! -->"; } } // other css need to be requeued for the order of files to be kept } else { wp_dequeue_style($footer[$i]['handle']); wp_enqueue_style($footer[$i]['handle']); } } // remove from queue $wp_styles->done = $done; } /** * Process JS Scripts of type module * * @param String $buffer Page HTML. * * @return String */ public function process_script_modules($buffer) { if ('' === $buffer) return ''; if (!WP_Optimize_Utils::is_valid_html($buffer)) return $buffer; $minify_js = $this->options['enable_js_minification']; $cache_path = WP_Optimize_Minify_Cache_Functions::cache_path(); $cache_dir = $cache_path['cachedir']; $cache_dir_url = $cache_path['cachedirurl']; $cache_base_dir = $cache_path['cachebasedir']; $scripts = $this->get_scripts($buffer); if (empty($scripts)) return $buffer; foreach ($scripts as $script) { if ("module" !== $script['type']) continue; $href = WP_Optimize_Minify_Functions::get_hurl($script['url']); if (WP_Optimize_Minify_Functions::is_minified_css_js_filename($href)) continue; $last_modified = WP_Optimize_Minify_Functions::get_modification_time($script['url']); $handle = $script['handle']; $version = $script['version']; // Module script handles use slashes and @ symbols, // replace them with '-' and empty string, break if handle is still '' if (!empty($handle)) { $handle_no_slash = str_replace(array('/', '@'), array('-', ''), $handle); } else { continue; } // code and log initialization $log_header = "PROCESSED on ".gmdate('r')." from ".home_url(add_query_arg(null, null)); $log = array( 'header' => $log_header, 'files' => array() ); $meta_log = $this->meta_log_initialization($log_header); $code = ''; // create cache files and urls $file_name = 'wpo-minify-'.$handle_no_slash.($minify_js ? '.min' : ''); $file = $cache_dir.'/'.$file_name.'.js'; $meta_json_file = $cache_base_dir.'/'.'meta.json'; $file_url = WP_Optimize_Minify_Functions::get_protocol($cache_dir_url.'/'.$file_name.'.js'); // download, minify, cache $tkey = 'js-'.hash('adler32', $handle_no_slash . $href).'.js'; $json = WP_Optimize_Minify_Cache_Functions::get_transient($tkey); if (false === $json) { $enable_minification = $minify_js && !WP_Optimize_Minify_Functions::is_minified_css_js_filename($href); $json = WP_Optimize_Minify_Functions::download_and_minify($href, null, $enable_minification, 'js', $handle_no_slash, $version); if ($this->options['debug']) { echo "<!-- wpo_min DEBUG: Uncached file processing now for " . esc_html($handle) . " / " . esc_html($href) . " / " . esc_html($version) . " -->\n"; } WP_Optimize_Minify_Cache_Functions::set_transient($tkey, $json); } // decode $res = json_decode($json, true); if (null === $res) continue; // Reset minified script module if ($this->should_reset_minify_assets($res, $href, 'js', $handle, $version)) { WP_Optimize_Minify_Cache_Functions::reset(); $this->minify_cache_incremented = true; $meta_log['invalidation_reason'] = 'Checksum Mismatch and version changed from ' . $res['request']['version'] . ' to ' . $version; $meta_log['files_that_changed'][$handle] = $res['log']; } if (!$res['status']) continue; $code .= isset($res['code']) ? $res['code'] : ''; $code = WP_Optimize_Minify_Functions::prepare_merged_js($code, $href); $log['files'][$handle] = $res['log']; $log['files'][$handle]['version'] = $version; $log['files'][$handle]['last_modified'] = $last_modified; // If code is empty (nothing to minify) break loop if (empty($code)) continue; // Only generate meta.json file if cache is reset if (!empty($meta_log['invalidation_reason'])) { file_put_contents($meta_json_file, wp_json_encode($meta_log)); } WP_Optimize_Minify_Print::write_combined_asset($file, $code, $log); // Extract importmap script tag $json_map = $this->get_script_module_importmap($buffer); $json_map_update = $json_map; // Update module source file and importmap with new file url (if necessary) $buffer = str_replace($script['url'], $file_url, $buffer); if (!empty($json_map) && isset($json_map['imports'][$handle])) { $json_map_update['imports'][$handle] = $file_url; $buffer = str_replace(wp_json_encode($json_map), wp_json_encode($json_map_update), $buffer); } } return $buffer; } /** * Orders the CSS per media type * * @param array $list - The list of assets * @return array */ public function order_stylesheets_per_media_type($list) { // get unique mediatypes $allmedia = array(); foreach ($list as $array) { if (isset($array['media'])) { $allmedia[$array['media']] = ''; } } // extract handles by mediatype $grouphandles = array(); foreach ($allmedia as $md => $var) { foreach ($list as $array) { if (isset($array['media']) && $array['media'] === $md) { foreach ($array['handles'] as $h) { $grouphandles[$md][] = $h; } } } } // reset and reorder list by mediatypes $newlist = array(); foreach ($allmedia as $md => $var) { $newlist[] = array('handles' => $grouphandles[$md], 'media' => $md); } if (count($newlist) > 0) { $list = $newlist; } return $list; } /** * Merged google font * * @return void; */ public function add_google_fonts_merged() { // must have something to do if (count($this->collect_google_fonts) == 0) return; // merge google fonts $href = WP_Optimize_Minify_Fonts::concatenate_google_fonts($this->collect_google_fonts); if (empty($href)) { return; } // hide google fonts from PageSpeedIndex if ('exclude' === $this->options['gfonts_method']) { // make a stylesheet, hide from PageSpeedIndex WP_Optimize_Minify_Print::exclude_style($href); } elseif ('async' === $this->options['gfonts_method']) { // load google fonts async WP_Optimize_Minify_Print::async_style($href); } else { // fallback to normal inline WP_Optimize_Minify_Print::style($href); } // unset per hook foreach ($this->collect_google_fonts as $k => $v) { unset($this->collect_google_fonts[$k]); } } /** * Check if the string given contains 'wpo-minify' * * @param string $var * * @return boolean */ public function check_wpo($var) { return (false === strpos($var, 'assets/wpo-minify')); } /** * Generate preload headers file * * @return boolean */ public function generate_preload_headers() { // always false for admin pages if (is_admin()) return false; // get host with multisite support and query strings $host = isset($_SERVER['SERVER_NAME']) ? sanitize_text_field(wp_unslash($_SERVER['SERVER_NAME'])) : ''; if (empty($host)) { $host = isset($_SERVER['HTTP_HOST']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST'])) : ''; } $request_query = ''; $request_uri = isset($_SERVER['REQUEST_URI']) ? esc_url_raw(wp_unslash($_SERVER['REQUEST_URI'])) : ''; if (!empty($request_uri)) { $request_query = wp_parse_url($request_uri, PHP_URL_QUERY); $request_uri = wp_parse_url($request_uri, PHP_URL_PATH); } // initialize headers $headers = array(); // css headers if ($this->options['enabled_css_preload'] && count($this->collect_preload_css) > 0 ) { foreach ($this->collect_preload_css as $u) { // filter out footer footer files, because they are not in the critical path if (strpos($u, '/assets/wpo-minify-footer-') !== false) { continue; } // add headers $headers[] = "Link: <$u>; rel=preload; as=style"; } } // js headers if ($this->options['enabled_js_preload'] && count($this->collect_preload_js) > 0 ) { foreach ($this->collect_preload_js as $u) { // filter out footer footer files, because they are not in the critical path if (false !== strpos($u, '/assets/wpo-minify-footer-')) { continue; } // add headers $headers[] = "Link: <$u>; rel=preload; as=script"; } } // must have something if (count($headers) == 0) { return false; } else { $headers = implode("\n", $headers); } // get cache path $cache_path = WP_Optimize_Minify_Cache_Functions::cache_path(); $header_dir = $cache_path['headerdir']; // possible cache file locations $b = $header_dir . '/' . md5($host.'-'.$request_uri) . '.header'; $a = $header_dir . '/' . md5($host.'-'.$request_uri . $request_query).'.header'; // reset file cache clearstatcache(); // if there are no query strings if ($b == $a) { if (!file_exists($a)) { WP_Optimize_Minify_Print::write_header($a, $headers); } return false; } elseif (!file_exists($b)) { WP_Optimize_Minify_Print::write_header($b, $headers); } return false; } /** * Collect all wpo_min JS files and save them to a headers file * * @param String $html * @param String $handle * @param String $src * * @return String */ public function collect_js_preload_headers($html, $handle, $src) { if (false !== strpos($src, 'assets/wpo-minify')) { if (!in_array($src, $this->collect_preload_js)) { $this->collect_preload_js[] = $src; } } return $html . "\n"; } /** * Collect all wpo_min CSS files and save them to a headers file * this function intercepts the styles that are enqueued * take note that if the css is loaded ASYNC, it's not enqueued * * @param string $html - Html string * @param string $handle - Handle * @param string $src - The CSS file path * * @return string */ public function collect_css_preload_headers($html, $handle, $src) { if (false !== strpos($src, 'assets/wpo-minify')) { if (!in_array($src, $this->collect_preload_css)) { $this->collect_preload_css[] = $src; } } return $html . "\n"; } /** * Get current headers file for the url * * @return bool|string */ private function get_preload_headers() { // always false for admin pages if (is_admin()) return false; $enabled_css_preload = $this->options['enabled_css_preload']; $enabled_js_preload = $this->options['enabled_js_preload']; if (!$enabled_css_preload && !$enabled_js_preload) { return false; } // get host with multisite support and query strings $host = isset($_SERVER['SERVER_NAME']) ? sanitize_text_field(wp_unslash($_SERVER['SERVER_NAME'])) : ''; if (empty($host)) { $host = isset($_SERVER['HTTP_HOST']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST'])) : ''; } $request_query = ''; $request_uri = isset($_SERVER['REQUEST_URI']) ? esc_url_raw(wp_unslash($_SERVER['REQUEST_URI'])) : ''; if (!empty($request_uri)) { $request_query = wp_parse_url($request_uri, PHP_URL_QUERY); $request_uri = wp_parse_url($request_uri, PHP_URL_PATH); } // get cache path $cache_path = WP_Optimize_Minify_Cache_Functions::cache_path(); $header_dir = $cache_path['headerdir']; $cache_file_base = $header_dir . '/'; // possible cache file locations $b = $cache_file_base . md5("$host-$request_uri").'.header'; $a = $cache_file_base . md5("$host-$request_uri$request_query").'.header'; // reset file cache clearstatcache(); // return header files or fallback if ($b == $a && file_exists($a)) { return file_get_contents($a); } if ($b != $a && file_exists($b)) { return file_get_contents($b); } return false; } /** * Add pre-connect and pre-load headers * * @return void */ public function extra_preload_headers() { if (!$this->run_on_page('extra_preload_headers')) return; // fetch headers $pre_connect = $this->get_pre_connect_list(); // preload if (is_array($pre_connect) && count($pre_connect) > 0) { foreach ($pre_connect as $url) { if (!empty($url) && filter_var($url, FILTER_VALIDATE_URL)) { header("Link: <$url>; rel=preconnect", false); } } } $headers = $this->get_preload_headers(); if (false != $headers) { $nh = array_map('trim', explode("\n", $headers)); foreach ($nh as $h) { if (!empty($h)) { header($h, false); } } } } /** * Add caching headers * * @param array $headers An array of headers * * @return array An array of modified headers */ public function add_caching_headers($headers) { $cache = WP_Optimize()->get_page_cache(); if ($cache->is_enabled()) return $headers; if (!isset($headers['Cache-Control'])) { $headers['Cache-Control'] = 'must-revalidate'; } if (!isset($headers['Last-Modified'])) { $headers['Last-Modified'] = gmdate('D, d M Y H:i:s', time()) . ' GMT'; } return $headers; } /** * Includes dependency files */ private function include_dependencies() { if (!class_exists('WP_Optimize_Minify_Functions')) { include WP_OPTIMIZE_MINIFY_DIR.'/class-wp-optimize-minify-functions.php'; } if (!class_exists('WP_Optimize_Minify_Print')) { include WP_OPTIMIZE_MINIFY_DIR.'/class-wp-optimize-minify-print.php'; } if (!class_exists('WP_Optimize_Minify_Fonts')) { include WP_OPTIMIZE_MINIFY_DIR.'/class-wp-optimize-minify-fonts.php'; } } /** * Handles emoji removal */ private function disable_emojis() { WP_Optimize_Minify_Functions::disable_wp_emojicons(); add_filter('tiny_mce_plugins', array('WP_Optimize_Minify_Functions', 'disable_emojis_tinymce' )); } /** * Handles header meta information removal */ private function remove_header_meta_info() { // no resource hints, generator tag, shortlinks, manifest link, etc remove_action('wp_head', 'wp_resource_hints', 2); remove_action('wp_head', 'wp_generator'); remove_action('template_redirect', 'wp_shortlink_header', 11); remove_action('wp_head', 'wlwmanifest_link'); remove_action('wp_head', 'rsd_link'); remove_action('wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0); remove_action('wp_head', 'feed_links', 2); remove_action('wp_head', 'feed_links_extra', 3); WP_Optimize_Minify_Functions::remove_redundant_shortlink(); } /** * Handles javascript processing */ private function process_js() { add_action('wp_print_scripts', array($this, 'process_header_scripts'), PHP_INT_MAX); add_action('wp_print_footer_scripts', array($this, 'process_footer_scripts'), 9); // Defer JS add_filter('script_loader_tag', array($this, 'defer_js'), 10, 3); // Preloading if ($this->options['enabled_js_preload']) { add_filter('script_loader_tag', array($this, 'collect_js_preload_headers'), PHP_INT_MAX, 3); } // add the LoadAsync JavaScript function $async_js = $this->get_async_js_list(); if (count($async_js) > 0 || ( 'all' === $this->options['enable_defer_js'] && 'async_using_js' === $this->options['defer_js_type'] ) ) { add_action('wp_head', array('WP_Optimize_Minify_Print', 'add_load_async'), 0); } // Process script module from generated html ob_start(array($this, "process_script_modules")); } /** * Extract scripts tags from html buffer using regex * * @param string $html - HTML document as string * @return array */ private function get_scripts($html) { preg_match_all('/<script([^>]*)>/Umi', $html, $script_matches, PREG_SET_ORDER); $modified_script_matches = array(); foreach ($script_matches as $script) { $script['url'] = ''; $script['handle'] = ''; $script['version'] = ''; $script['type'] = ''; $attributes = WP_Optimize_Utils::parse_attributes($script[1]); if (isset($attributes['src'])) { $script['url'] = $attributes['src']; $url_query = wp_parse_url($script['url'], PHP_URL_QUERY); $output = array(); wp_parse_str($url_query, $output); if (isset($output['ver'])) $script['version'] = $output['ver']; } // Extract the id/handle from attribute's array if present if (isset($attributes['id'])) $script['handle'] = $attributes['id']; // Extract the type from attribute's array if present if (isset($attributes['type'])) $script['type'] = $attributes['type']; $modified_script_matches[] = $script; } return $modified_script_matches; } /** * Extract `importmap` json object from html buffer using regex * * @param string $html - HTML document as string * @return array */ private function get_script_module_importmap($html) { preg_match('/<script\s+type\s*=\s*[\'"]importmap.*>(?<importmap>.*)<\/script>/Umsi', $html, $match); if (!empty($match) && isset($match['importmap'])) return json_decode($match['importmap'], true); return array(); } /** * Handles css processing */ private function process_css() { add_action('wp_head', array($this, 'add_critical_path'), 2); // merge, if inline is selected but prevent optimization for these locations if ($this->options['inline_css']) { // this prints the styles (not fonts) and checks the 'colllect google fonts' add_filter('style_loader_tag', array($this, 'inline_css'), PHP_INT_MAX, 4); // this prints the google fonts add_action('wp_print_styles', array($this, 'add_google_fonts_merged'), PHP_INT_MAX); add_action('wp_print_footer_scripts', array($this, 'add_google_fonts_merged'), PHP_INT_MAX); } // Preloading if ($this->options['enabled_css_preload']) { add_filter('style_loader_tag', array($this, 'collect_css_preload_headers'), PHP_INT_MAX, 3); } // Optimize the css and collect the google fonts for merging add_action('wp_print_styles', array($this, 'process_header_css'), PHP_INT_MAX); add_action('wp_print_footer_scripts', array($this, 'process_footer_css'), 9); /** * Filters whether or not to ignore the order of the CSS files, and group them by media type * * @param boolean $maintain_css_order * @return boolean * @default true */ if (!apply_filters('wpo_minify_maintain_css_order', true)) { // Reorder stylesheets add_filter('wpo_minify_stylesheets', array($this, 'order_stylesheets_per_media_type'), 10); } } /** * Determines whether we should minify html or not */ private function should_process_html() { return $this->options['html_minification'] && !is_admin() && $this->is_cache_preload(); } /** * Determines whether to use loadCSS polyfill or not */ private function should_use_loadCSS() { return $this->options['loadcss'] || 'async' === $this->options['fawesome_method'] || 'async' === $this->options['gfonts_method']; } /** * Removes query string from static assets */ private function remove_query_string_from_static_assets() { add_filter('style_loader_src', array('WP_Optimize_Minify_Functions', 'remove_cssjs_ver'), 10, 2); add_filter('script_loader_src', array('WP_Optimize_Minify_Functions', 'remove_cssjs_ver'), 10, 2); } /** * Converts the lines in a textarea option value into an array * * @param string $option * @return array */ private function convert_option_to_array($option) { return trim($option) ? array_map('trim', explode("\n", trim($option))) : array(); } /** * Retrieves excluded css URLs as an array * * @return array */ private function get_excluded_css_list() { $excluded_css = $this->options['exclude_css']; return $this->convert_option_to_array($excluded_css); } /** * Retrieves async css URLs as an array * * @return array */ private function get_async_css_list() { $async_css = $this->options['async_css']; return $this->convert_option_to_array($async_css); } /** * Retrieves excluded javascript URLs as an array * * @return array */ private function get_excluded_js_list() { $excluded_js = $this->options['exclude_js']; return $this->convert_option_to_array($excluded_js); } /** * Retrieves async javascript URLs as an array * * @return array */ private function get_async_js_list() { $async_js = $this->options['async_js']; return $this->convert_option_to_array($async_js); } /** * Retrieves pre connect headers as an array * * @return array */ private function get_pre_connect_list() { $pre_connect = $this->options['hpreconnect']; return $this->convert_option_to_array($pre_connect); } /** * Array to string conversion * * @param array $arr * @return string */ private function array_to_string_conversion($arr) { $str = ''; // implode won't work, because element can be an array foreach ($arr as $value) { $str .= is_array($value) ? '' : $value; } return $str; } /** * Decides whether minify cache should be incremented or not * * @param string $res * @param string $href * @param string $type * @param string $handle * @param string $version * * @return bool true when cache is invalid, false otherwise */ private function should_reset_minify_assets($res, $href, $type, $handle, $version) { if (isset($res['request']['version']) && $res['request']['version'] != $version && !$this->minify_cache_incremented) { $enable_minification = $this->options['enable_'.$type.'_minification'] && !WP_Optimize_Minify_Functions::is_minified_css_js_filename($href); $new_json = WP_Optimize_Minify_Functions::download_and_minify($href, null, $enable_minification, $type, $handle, $version); $new_res = json_decode($new_json, true); /** * In case `download_and_minify` fails because of * Network issues, Syntax errors in updated code, * Wrong use of `wpo_minify_get_js` and `wpo_minify_get_css` filters * We don't want to invalidate current assets, so bail out */ if (null === $new_res) return false; $hash = hash('sha256', $res['code']); $new_hash = hash('sha256', $new_res['code']); return strcmp($hash, $new_hash); } } } class-wp-optimize-minify-cache-functions.php 0000644 00000043556 15162435547 0015235 0 ustar 00 <?php if (!defined('ABSPATH')) die('No direct access allowed'); if (!defined('WP_OPTIMIZE_MINIFY_DIR')) { die('No direct access.'); } if (!function_exists('wpo_delete_files')) { include WPO_PLUGIN_MAIN_PATH.'cache/file-based-page-cache-functions.php'; } class WP_Optimize_Minify_Cache_Functions { /** * Fix the permission bits on generated files * * @param String $file - full path to a file */ public static function fix_permission_bits($file) { if (function_exists('stat')) { if ($stat = stat(dirname($file))) { $perms = $stat['mode'] & 0007777; chmod($file, $perms); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_chmod -- N/A clearstatcache(); return true; } } // Get permissions from parent directory $perms = 0777; if (function_exists('stat')) { if ($stat = stat(dirname($file))) { $perms = $stat['mode'] & 0007777; } } if (file_exists($file)) { if (($perms & ~umask() != $perms)) { $folder_parts = explode('/', substr($file, strlen(dirname($file)) + 1)); for ($i = 1, $c = count($folder_parts); $i <= $c; $i++) { chmod(dirname($file) . '/' . implode('/', array_slice($folder_parts, 0, $i)), $perms); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_chmod -- N/A } } } return true; } /** * Get cache directories and urls * * @return Array */ public static function cache_path() { // get latest time stamp $cache_time = wp_optimize_minify_config()->get('last-cache-update'); $cache_base_dir = WPO_CACHE_MIN_FILES_DIR . "/$cache_time"; $cache_dir_url = WPO_CACHE_MIN_FILES_URL . "/$cache_time/assets"; $tmp_dir = WPO_CACHE_MIN_FILES_DIR . "/tmp"; $header_dir = WPO_CACHE_MIN_FILES_DIR . "/$cache_time/header"; $cache_dir = WPO_CACHE_MIN_FILES_DIR . "/$cache_time/assets"; // Create directories $dirs = array($cache_dir, $tmp_dir, $header_dir); foreach ($dirs as $target) { $enabled = wp_optimize_minify_config()->get('enabled'); if (false === $enabled) break; if (!is_dir($target) && !wp_mkdir_p($target)) { error_log('WP_Optimize_Minify_Cache_Functions::cache_path(): The folder "'.$target.'" could not be created.'); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Used for debugging } } return array( 'tmpdir' => $tmp_dir, 'cachedir' => $cache_dir, 'cachedirurl' => $cache_dir_url, 'headerdir' => $header_dir, 'cachebasedir' => $cache_base_dir ); } /** * Increment file names */ public static function cache_increment() { $stamp = time(); wp_optimize_minify_config()->update(array( 'last-cache-update' => $stamp )); return $stamp; } /** * Reset the cache (Increment + purge temp files) */ public static function reset() { self::cache_increment(); self::purge_temp_files(); } /** * Will delete temporary intermediate stuff but leave final css/js alone for compatibility * * @return array */ public static function purge_temp_files() { // get cache directories and urls $cache_path = self::cache_path(); $tmp_dir = $cache_path['tmpdir']; $header_dir = $cache_path['headerdir']; // delete temporary directories only if (is_dir($tmp_dir)) { wpo_delete_files($tmp_dir, true); } if (is_dir($header_dir)) { wpo_delete_files($header_dir, true); } /** * Action triggered after purging temporary files */ do_action('wpo_min_after_purge_temp_files'); return array( 'tmpdir' => $tmp_dir, 'headerdir' => $header_dir, ); } /** * Purge supported hosting and plugins * * @return array An array of caches purged message */ public static function purge_others() { /** * Action triggered before purging other plugins cache */ do_action('wpo_min_before_purge_others'); // WordPress default cache if (function_exists('wp_cache_flush')) { wp_cache_flush(); } // Purge WP-Optimize $is_cache_purged = WP_Optimize()->get_page_cache()->purge(); if ($is_cache_purged) WP_Optimize()->get_page_cache()->file_log("Full Cache Purge triggered by: ". __METHOD__); // Store the messages of purged cache if it was successful $result = array(); // When plugins have a simple method, add them to the array ('Plugin Name' => 'method_name') $others = array( 'WP Super Cache' => 'wp_cache_clear_cache', 'W3 Total Cache' => 'w3tc_pgcache_flush', 'WP Fastest Cache' => 'wpfc_clear_all_cache', 'WP Rocket' => 'rocket_clean_domain', 'Cachify' => 'cachify_flush_cache', 'Comet Cache' => array('comet_cache', 'clear'), 'SG Optimizer' => 'sg_cachepress_purge_cache', 'Pantheon' => 'pantheon_wp_clear_edge_all', 'Zen Cache' => array('zencache', 'clear'), 'Breeze' => array('Breeze_PurgeCache', 'breeze_cache_flush'), 'Swift Performance' => array('Swift_Performance_Cache', 'clear_all_cache'), ); foreach ($others as $plugin => $method) { if (is_callable($method)) { call_user_func($method); $result[] = self::get_caches_purged_message($plugin); } } // Purge LiteSpeed Cache if (is_callable(array('LiteSpeed_Cache_Tags', 'add_purge_tag'))) { LiteSpeed_Cache_Tags::add_purge_tag('*'); $result[] = self::get_caches_purged_message('LiteSpeed Cache'); } // Purge Hyper Cache if (class_exists('HyperCache')) { do_action('autoptimize_action_cachepurged'); $result[] = self::get_caches_purged_message('Hyper Cache'); } // Purge Godaddy Managed WordPress Hosting (Varnish + APC) if (class_exists('WPaaS\Plugin')) { self::godaddy_request('BAN'); // translators: %s is a remote cache system name `Go Daddy Varnish` $result[] = sprintf(__('A cache purge request has been sent to %s.', 'wp-optimize'), '<strong>Go Daddy Varnish</strong>') . ' ' . __('Please note that it may not work every time, due to cache rate limiting by your host.', 'wp-optimize'); } // purge cache enabler if (has_action('ce_clear_cache')) { do_action('ce_clear_cache'); $result[] = self::get_caches_purged_message('Cache Enabler'); } // Purge WP Engine if (class_exists("WpeCommon")) { if (method_exists('WpeCommon', 'purge_memcached')) { WpeCommon::purge_memcached(); } if (method_exists('WpeCommon', 'clear_maxcdn_cache')) { WpeCommon::clear_maxcdn_cache(); } if (method_exists('WpeCommon', 'purge_varnish_cache')) { WpeCommon::purge_varnish_cache(); } if (method_exists('WpeCommon', 'purge_memcached') || method_exists('WpeCommon', 'clear_maxcdn_cache') || method_exists('WpeCommon', 'purge_varnish_cache')) { // translators: %s is a remote cache system name `WP Engine` $result[] = sprintf(__('A cache purge request has been sent to %s.', 'wp-optimize'), '<strong>WP Engine</strong>') . ' ' . __('Please note that it may not work every time, due to cache rate limiting by your host.', 'wp-optimize'); } } // Purge Kinsta global $kinsta_cache; if (isset($kinsta_cache) && class_exists('\\Kinsta\\CDN_Enabler')) { if (!empty($kinsta_cache->kinsta_cache_purge) && is_callable(array($kinsta_cache->kinsta_cache_purge, 'purge_complete_caches'))) { $kinsta_cache->kinsta_cache_purge->purge_complete_caches(); $result[] = self::get_remote_caches_purged_message('Kinsta'); } } // Purge Pagely if (class_exists('PagelyCachePurge')) { $purge_pagely = new PagelyCachePurge(); if (is_callable(array($purge_pagely, 'purgeAll'))) { $purge_pagely->purgeAll(); $result[] = self::get_remote_caches_purged_message('Pagely'); } } // Purge Pressidum if (defined('WP_NINUKIS_WP_NAME') && class_exists('Ninukis_Plugin') && is_callable(array('Ninukis_Plugin', 'get_instance'))) { $purge_pressidum = Ninukis_Plugin::get_instance(); if (is_callable(array($purge_pressidum, 'purgeAllCaches'))) { $purge_pressidum->purgeAllCaches(); $result[] = self::get_remote_caches_purged_message('Pressidium'); } } // Purge Savvii if (defined('\Savvii\CacheFlusherPlugin::NAME_DOMAINFLUSH_NOW')) { $purge_savvii = new \Savvii\CacheFlusherPlugin(); if (is_callable(array($purge_savvii, 'domainflush'))) { $purge_savvii->domainflush(); $result[] = self::get_remote_caches_purged_message('Savvii'); } } /** * Action triggered when purging other plugins cache, and nothing was triggered */ do_action('wpo_min_after_purge_others'); return $result; } /** * Returns the purged cache message for the given plugin name * * @param string $plugin_name Name of the plugin * * @return string */ public static function get_caches_purged_message($plugin_name) { $message = sprintf( // translators: %s is a plugin name __('All caches from %s have also been purged.', 'wp-optimize'), '<strong>' . esc_html($plugin_name) . '</strong>' ); return $message; } /** * Returns remote purged cache message for given host name * * @param string $host_name * * @return string */ public static function get_remote_caches_purged_message($host_name) { $message = sprintf( // translators: %s is a remote cache system name __('A cache purge request has been sent to %s.', 'wp-optimize'), '<strong>' . esc_html($host_name) . '</strong>' ); return $message; } /** * Purge all public files on uninstallation * This will break cached pages that ref minified JS/CSS * * @return Boolean */ public static function purge() { $log = ''; if (is_dir(WPO_CACHE_MIN_FILES_DIR)) { if (wpo_delete_files(WPO_CACHE_MIN_FILES_DIR, true)) { $log = "[Minify] files and folders are deleted recursively"; } else { $log = "[Minify] recursive files and folders deletion unsuccessful"; } if (wp_optimize_minify_config()->get('debug')) { error_log($log); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Used for debugging } } return true; } /** * Purge cache files older than 30 days * * @return array */ public static function purge_old() { if (!class_exists('WP_Optimize_Minify_Config')) { include_once WPO_PLUGIN_MAIN_PATH . 'minify/class-wp-optimize-minify-config.php'; } $cache_time = wp_optimize_minify_config()->get('last-cache-update'); $cache_lifespan = wp_optimize_minify_config()->get('cache_lifespan'); /** * Minify cache lifespan * * @param int The minify cache expiry timestamp */ $expires = apply_filters('wp_optimize_minify_cache_expiry_time', time() - 86400 * $cache_lifespan); $log = array(); // get all directories that are a direct child of current directory if (is_dir(WPO_CACHE_MIN_FILES_DIR) && wp_is_writable(dirname(WPO_CACHE_MIN_FILES_DIR))) { if ($handle = opendir(WPO_CACHE_MIN_FILES_DIR)) { while (false !== ($d = readdir($handle))) { if (strcmp($d, '.')==0 || strcmp($d, '..')==0) { continue; } $log[] = "cache expiration time - $expires"; $log[] = "checking if cache has expired - $d"; if ($d != $cache_time && (is_numeric($d) && $d <= $expires)) { $dir = WPO_CACHE_MIN_FILES_DIR.'/'.$d; if (is_dir($dir)) { $log[] = "deleting cache in $dir"; if (wpo_delete_files($dir, true)) { $log[] = "files and folders are deleted recursively - $dir"; } else { $log[] = "recursive files and folders deletion unsuccessful - $dir"; } if (file_exists($dir)) { if (rmdir($dir)) { // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_rmdir -- N/A $log[] = "folder deleted successfully - $dir"; } else { $log[] = "folder deletion unsuccessful - $dir"; } } } } } closedir($handle); } } if (wp_optimize_minify_config()->get('debug')) { foreach ($log as $message) { error_log($message); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Used for debugging } } return $log; } /** * Get transients from the disk * * @return String|Boolean */ public static function get_transient($key) { $cache_path = self::cache_path(); $tmp_dir = $cache_path['tmpdir']; $f = $tmp_dir.'/'.$key.'.transient'; clearstatcache(); if (file_exists($f)) { return file_get_contents($f); } else { return false; } } /** * Set cache on disk * * @param String $key * @param Mixed $code * * @return Boolean */ public static function set_transient($key, $code) { if (is_null($code) || empty($code)) { return false; } $cache_path = self::cache_path(); $tmp_dir = $cache_path['tmpdir']; $f = $tmp_dir.'/'.$key.'.transient'; file_put_contents($f, $code); self::fix_permission_bits($f); return true; } /** * Get the cache size and count * * @param string $folder * @return String */ public static function get_cachestats($folder) { clearstatcache(); if (is_dir($folder)) { $dir = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($folder, FilesystemIterator::SKIP_DOTS)); $size = 0; $file_count = 0; foreach ($dir as $file) { $size += $file->getSize(); $file_count++; } return WP_Optimize()->format_size($size) . ' ('.$file_count.' files)'; } else { // translators: %s is a folder path return sprintf(__('Error: %s is not a directory!', 'wp-optimize'), $folder); } } /** * Purge GoDaddy Managed WordPress Hosting (Varnish) * * Source: https://github.com/wp-media/wp-rocket/blob/master/inc/3rd-party/hosting/godaddy.php * * @param String $method * @param String|Null $url */ public static function godaddy_request($method, $url = null) { $url = empty($url) ? home_url() : $url; $host = wp_parse_url($url, PHP_URL_HOST); $url = set_url_scheme(str_replace($host, WPaas\Plugin::vip(), $url), 'http'); wp_cache_flush(); update_option('gd_system_last_cache_flush', time()); // purge apc wp_remote_request(esc_url_raw($url), array('method' => $method, 'blocking' => false, 'headers' => array('Host' => $host))); } /** * List all cache files * * @param integer $stamp A timestamp * @param boolean $use_cache If true, do not use transient value * @return array */ public static function get_cached_files($stamp = 0, $use_cache = true) { if ($use_cache && $files = get_transient('wpo_minify_get_cached_files')) { return $files; } $cache_path = self::cache_path(); $cache_dir = $cache_path['cachedir']; $size = self::get_cachestats($cache_dir); $total_size = self::get_cachestats(WPO_CACHE_MIN_FILES_DIR); $o = wp_optimize_minify_config()->get(); $cache_time = (0 == $o['last-cache-update']) ? __('Never.', 'wp-optimize') : self::format_date_time($o['last-cache-update']); $return = array( 'js' => array(), 'css' => array(), 'stamp' => $stamp, 'cachesize' => esc_html($size), 'total_cache_size' => esc_html($total_size), 'cacheTime' => $cache_time, 'cachePath' => $cache_path['cachedir'] ); // Inspect directory with opendir, since glob might not be available in some systems clearstatcache(); if (is_dir($cache_dir.'/') && $handle = opendir($cache_dir.'/')) { while (false !== ($file = readdir($handle))) { $file = $cache_dir.'/'.$file; $ext = pathinfo($file, PATHINFO_EXTENSION); if (in_array($ext, array('js', 'css'))) { $log = self::generate_log($file.'.json' ); $min_css = substr($file, 0, -4).'.min.css'; $minjs = substr($file, 0, -3).'.min.js'; $file_name = basename($file); $file_url = trailingslashit($cache_path['cachedirurl']).$file_name; if ('css' == $ext && file_exists($min_css)) { $file_name = basename($min_css); } if ('js' == $ext && file_exists($minjs)) { $file_name = basename($minjs); } $file_size = WP_Optimize()->format_size(filesize($file)); $uid = hash('adler32', $file_name); array_push($return[$ext], array('uid' => $uid, 'filename' => $file_name, 'file_url' => $file_url, 'log' => $log, 'fsize' => $file_size)); } } closedir($handle); } set_transient('wpo_minify_get_cached_files', $return, DAY_IN_SECONDS); return $return; } /** * Generate log information from a json file. * * @param string $file Full path of log file. * * @return object Could be either a 'json_decode' object upon successful parsing of the JSON file, or a stdClass object * upon failure. In the case of stdClass object, $obj->error will contain the error message. */ public static function generate_log($file) { $error_log = new stdClass(); $cache_path = self::cache_path(); $file_name = basename($file); $file_url = trailingslashit($cache_path['cachedirurl']) . $file_name; $file_link_html = '<a href="' . esc_url($file_url) . '" target="_blank">' . $file_name . '</a>'; if (!file_exists($file)) { // translators: %s is a log file link $error_log->error = sprintf(__('Log file %s is missing', 'wp-optimize'), $file_link_html); return $error_log; } $log = json_decode(file_get_contents($file)); $is_valid_json = json_last_error() === JSON_ERROR_NONE ? true : false; if (!$is_valid_json) { // translators: %1$s is a log file link, %2$s is the error message $error_log->error = sprintf(__('JSON error in file %1$s | Error details: %2$s', 'wp-optimize'), $file_link_html, json_last_error_msg()); return $error_log; } if (!isset($log->header) || !isset($log->files)) { // translators: %s is a log file link $error_log->error = sprintf(__('Some data is missing in the log file %s', 'wp-optimize'), $file_link_html); return $error_log; } return $log; } /** * Format a timestamp using WP's date_format and time_format * * @param integer $timestamp - The timestamp * @return string */ public static function format_date_time($timestamp) { return WP_Optimize()->format_date_time($timestamp); } /** * Format the log created when merging assets. Called via array_map * * @param array $files The files array, containing the 'log' object or array. * @return array */ public static function format_file_logs($files) { $files['log'] = WP_Optimize()->include_template( 'minify/cached-file-log.php', true, array( 'log' => $files['log'], 'minify_config' => wp_optimize_minify_config()->get(), ) ); return $files; } }