Server IP : 172.67.158.161 / Your IP : 3.142.173.89 Web Server : LiteSpeed System : Linux business53.web-hosting.com 4.18.0-553.lve.el8.x86_64 #1 SMP Mon May 27 15:27:34 UTC 2024 x86_64 User : giankuin ( 1871) PHP Version : 7.4.33 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /proc/self/root/proc/thread-self/root/proc/self/root/proc/self/root/proc/thread-self/root/home/giankuin/dietcontrungnhanh.com/wp-content/plugins/perfmatters/inc/classes/ |
Upload File : |
<?php namespace Perfmatters; use Sabberworm\CSS\CSSList\AtRuleBlockList; use Sabberworm\CSS\CSSList\CSSBlockList; use Sabberworm\CSS\CSSList\Document; use Sabberworm\CSS\OutputFormat; use Sabberworm\CSS\Parser as CSSParser; use Sabberworm\CSS\Property\Charset; use Sabberworm\CSS\RuleSet\DeclarationBlock; use Sabberworm\CSS\Settings; use Sabberworm\CSS\Value\URL; use WP_Admin_Bar; class CSS { private static $used_selectors; private static $excluded_selectors; //initialize css functions public static function init() { if(isset($_GET['perfmatterscssoff'])) { return; } if(!empty(Config::$options['assets']['remove_unused_css'])) { add_action('perfmatters_queue', array('Perfmatters\CSS', 'queue')); add_action('wp_ajax_perfmatters_clear_post_used_css', array('Perfmatters\CSS', 'clear_post_used_css')); add_action('admin_bar_menu', array('Perfmatters\CSS', 'admin_bar_menu')); add_action('admin_notices', array('Perfmatters\CSS', 'admin_notices')); add_action('admin_post_perfmatters_clear_used_css', array('Perfmatters\CSS', 'admin_bar_clear_used_css')); } add_action('wp_ajax_perfmatters_clear_used_css', array('Perfmatters\CSS', 'clear_used_css_ajax')); } //queue functions public static function queue() { add_action('perfmatters_output_buffer_template_redirect', array('Perfmatters\CSS', 'remove_unused_css')); } //remove unused css public static function remove_unused_css($html) { if(empty(apply_filters('perfmatters_remove_unused_css', true))) { return $html; } if(Utilities::get_post_meta('perfmatters_exclude_unused_css')) { return $html; } //only logged out if(is_user_logged_in()) { return $html; } //skip woocommerce if(Utilities::is_woocommerce()) { return $html; } //only known url types $type = self::get_url_type(); if(empty($type)) { return $html; } //setup file variables $used_css_path = PERFMATTERS_CACHE_DIR . 'css/' . $type . '.used.css'; $used_css_url = PERFMATTERS_CACHE_URL . 'css/' . $type . '.used.css'; $used_css_exists = file_exists($used_css_path); //match all stylesheets preg_match_all('#<link(\s[^>]*?href=[\'"]([^\'"]+?\.css.*?)[\'"][^>]*?)\/?>#i', $html, $stylesheets, PREG_SET_ORDER); if(!empty($stylesheets)) { //create our css cache directory if(!is_dir(PERFMATTERS_CACHE_DIR . 'css/')) { @mkdir(PERFMATTERS_CACHE_DIR . 'css/', 0755, true); } //populate used selectors self::get_used_selectors($html); self::get_excluded_selectors(); $used_css_string = ''; //loop through stylesheets foreach($stylesheets as $key => $stylesheet) { //stylesheet check if(!preg_match('#\srel=[\'"]stylesheet[\'"]#is', $stylesheet[0])) { continue; } //print check if(preg_match('#\smedia=[\'"]print[\'"]#is', $stylesheet[0])) { continue; } //ignore google fonts if(stripos($stylesheet[2], '//fonts.googleapis.com/css') !== false || stripos($stylesheet[2], '.google-fonts.css') !== false) { continue; } //exclude entire stylesheets $stylesheet_exclusions = array( 'dashicons.min.css', //core '/uploads/elementor/css/post-', //elementor 'animations.min.css', 'woocommerce-mobile', //woocommerce 'woocommerce-smallscreen', '/uploads/oxygen/css/', //oxygen '/uploads/bb-plugin/cache/', //beaver builder '/uploads/generateblocks/', //generateblocks '/et-cache/', //divi '/widget-google-reviews/assets/css/public-main.css' //plugin for google reviews ); if(!empty(Config::$options['assets']['rucss_excluded_stylesheets'])) { $stylesheet_exclusions = array_merge($stylesheet_exclusions, Config::$options['assets']['rucss_excluded_stylesheets']); } $stylesheet_exclusions = apply_filters('perfmatters_rucss_excluded_stylesheets', $stylesheet_exclusions); foreach($stylesheet_exclusions as $exclude) { if(strpos($stylesheet[1], $exclude) !== false) { unset($stylesheets[$key]); continue 2; } } //need to generate used css if(!$used_css_exists) { //get any custom set url $custom_url = apply_filters('perfmatters_local_stylesheet_url', !empty(Config::$options['assets']['rucss_cdn_url']) ? Config::$options['assets']['rucss_cdn_url'] : ''); //prep local url $local_url = !empty($custom_url) ? trailingslashit($custom_url) : array(trailingslashit(home_url()), trailingslashit(site_url())); //get local stylesheet path $url = str_ireplace($local_url, '', explode('?', $stylesheet[2])[0]); $file = Utilities::get_root_dir_path() . ltrim($url, '/'); //make sure local file exists if(!file_exists($file)) { continue; } //get used css from stylesheet $used_css = self::clean_stylesheet($stylesheet[2], @file_get_contents($file)); //add used stylesheet css to total used $used_css_string.= $used_css; } //delay stylesheets if(empty(Config::$options['assets']['rucss_stylesheet_behavior'])) { $new_link = preg_replace('#href=([\'"]).+?\1#', 'data-pmdelayedstyle="' . $stylesheet[2] . '"', $stylesheet[0]); $html = str_replace($stylesheet[0], $new_link, $html); } //async stylesheets elseif(Config::$options['assets']['rucss_stylesheet_behavior'] == 'async') { $new_link = preg_replace(array('#media=([\'"]).+?\1#', '#onload=([\'"]).+?\1#'), '', $stylesheet[0]); $new_link = str_replace('<link', '<link media="print" onload="this.media=\'all\';this.onload=null;"', $new_link); $html = str_replace($stylesheet[0], $new_link, $html); } //remove stylesheets elseif(Config::$options['assets']['rucss_stylesheet_behavior'] == 'remove') { $html = str_replace($stylesheet[0], '', $html); } } //store used css file if(!empty($used_css_string)) { if(file_put_contents($used_css_path, apply_filters('perfmatters_used_css', $used_css_string)) !== false) { $time = get_option('perfmatters_used_css_time', array()); $time[$type] = time(); //update stored timestamp update_option('perfmatters_used_css_time', $time); } } //print used css $tag = !apply_filters('perfmatters_used_css_below', false) ? '</title>' : '</head>'; $pos = strpos($html, $tag); if($pos !== false) { //print file if(!empty(Config::$options['assets']['rucss_method']) && Config::$options['assets']['rucss_method'] == 'file') { //grab stored timestamp for query string $time = get_option('perfmatters_used_css_time', array()); if(!empty($time[$type])) { $used_css_url = add_query_arg('ver', $time[$type], $used_css_url); } $used_css_output = "<link rel='preload' href='" . $used_css_url . "' as='style' onload=\"this.rel='stylesheet';this.removeAttribute('onload');\">"; $used_css_output.= '<link rel="stylesheet" id="perfmatters-used-css" href="' . $used_css_url . '" media="all" />'; } //print inline else { $used_css_output = '<style id="perfmatters-used-css">' . file_get_contents($used_css_path) . '</style>'; } if($tag == '</title>') { $html = substr_replace($html, '</title>' . $used_css_output, $pos, 8); } else { $html = str_replace('</head>', $used_css_output . '</head>', $html); } } //delay stylesheet script if(empty(Config::$options['assets']['rucss_stylesheet_behavior'])) { $delay_check = !empty(apply_filters('perfmatters_delay_js', !empty(Config::$options['assets']['delay_js']))) && !Utilities::get_post_meta('perfmatters_exclude_delay_js'); if(!$delay_check || isset($_GET['perfmattersjsoff'])) { $script = '<script type="text/javascript" id="perfmatters-delayed-styles-js">!function(){const e=["keydown","mousemove","wheel","touchmove","touchstart","touchend"];function t(){document.querySelectorAll("link[data-pmdelayedstyle]").forEach(function(e){e.setAttribute("href",e.getAttribute("data-pmdelayedstyle"))}),e.forEach(function(e){window.removeEventListener(e,t,{passive:!0})})}e.forEach(function(e){window.addEventListener(e,t,{passive:!0})})}();</script>'; $html = str_replace('</body>', $script . '</body>', $html); } } } return $html; } //get url type private static function get_url_type() { global $wp_query; $type = ''; if($wp_query->is_page) { $type = is_front_page() ? 'front' : 'page-' . $wp_query->post->ID; } elseif($wp_query->is_home) { $type = 'home'; } elseif($wp_query->is_single) { $type = get_post_type() !== false ? get_post_type() : 'single'; } elseif($wp_query->is_category) { $type = 'category'; } elseif($wp_query->is_tag) { $type = 'tag'; } elseif($wp_query->is_tax) { $type = 'tax'; } elseif($wp_query->is_archive) { $type = $wp_query->is_day ? 'day' : ($wp_query->is_month ? 'month' : ($wp_query->is_year ? 'year' : ($wp_query->is_author ? 'author' : 'archive'))); } elseif($wp_query->is_search) { $type = 'search'; } elseif($wp_query->is_404) { $type = '404'; } return $type; } //get used selectors in html private static function get_used_selectors($html) { if(!$html) { return; } //get dom document $libxml_previous = libxml_use_internal_errors(true); $dom = new \DOMDocument(); $result = $dom->loadHTML($html); libxml_clear_errors(); libxml_use_internal_errors($libxml_previous); if($result) { //setup used selectors array self::$used_selectors = array('tags' => array(), 'ids' => array(), 'classes' => array()); //search for used selectors in dom $classes = array(); foreach($dom->getElementsByTagName('*') as $tag) { //add tag self::$used_selectors['tags'][$tag->tagName] = 1; //add add tag id if($tag->hasAttribute('id')) { self::$used_selectors['ids'][$tag->getAttribute('id')] = 1; } //store tag classes if($tag->hasAttribute('class')) { $class = $tag->getAttribute('class'); $tag_classes = preg_split('/\s+/', $class); array_push($classes, ...$tag_classes); } } //add classes $classes = array_filter(array_unique($classes)); if($classes) { self::$used_selectors['classes'] = array_fill_keys($classes, 1); } } } //get excluded selectors private static function get_excluded_selectors() { self::$excluded_selectors = array( '.wp-embed-responsive', //core '.wp-block-embed', '.wp-block-embed__wrapper', '.wp-caption', '#elementor-device-mode', //elementor '.elementor-nav-menu', '.elementor-has-item-ratio', '.elementor-popup-modal', '.elementor-sticky--active', '.dialog-type-lightbox', '.dialog-widget-content', '.lazyloaded', '.elementor-motion-effects-container', '.elementor-motion-effects-layer', '.ast-header-break-point', //astra '.dropdown-nav-special-toggle', //kadence '.kb-splide', '.splide', '.splide-initialized', '.splide-slider', 'rs-fw-forcer', //rev slider 'div.product' //woocommerce ); if(!empty(Config::$options['assets']['rucss_excluded_selectors'])) { self::$excluded_selectors = array_merge(self::$excluded_selectors, Config::$options['assets']['rucss_excluded_selectors']); } self::$excluded_selectors = apply_filters('perfmatters_rucss_excluded_selectors', self::$excluded_selectors); } //remove unusde css from stylesheet private static function clean_stylesheet($url, $css) { //https://github.com/sabberworm/PHP-CSS-Parser/issues/150 $css = preg_replace('/^\xEF\xBB\xBF/', '', $css); //setup css parser $settings = Settings::create()->withMultibyteSupport(false); $parser = new CSSParser($css, $settings); $parsed_css = $parser->parse(); //convert relative urls to full urls self::fix_relative_urls($url, $parsed_css); $css_data = self::prep_css_data($parsed_css); return self::remove_unused_selectors($css_data); } //convert relative urls to full urls private static function fix_relative_urls($stylesheet_url, Document $data) { //get base url from stylesheet $base_url = preg_replace('#[^/]+(\?.*)?$#', '', $stylesheet_url); //search css for urls $values = $data->getAllValues(); foreach($values as $value) { if(!($value instanceof URL)) { continue; } $url = $value->getURL()->getString(); //not relative if(preg_match('/^(https?|data):/', $url)) { continue; } $parsed_url = parse_url($url); //final checks if(!empty($parsed_url['host']) || empty($parsed_url['path']) || $parsed_url['path'][0] === '/') { continue; } //create full url and replace $new_url = $base_url . $url; $value->getUrl()->setString($new_url); } } //prep parsed css for cleaning private static function prep_css_data(CSSBlockList $data) { $items = array(); foreach($data->getContents() as $content) { //remove charset objects since were printing inline if($content instanceof Charset) { continue; } if($content instanceof AtRuleBlockList) { $items[] = array( 'rulesets' => self::prep_css_data($content), 'at_rule' => "@{$content->atRuleName()} {$content->atRuleArgs()}", ); } else { $item = array('css' => $content->render(OutputFormat::createCompact())); if($content instanceof DeclarationBlock) { $item['selectors'] = self::sort_selectors($content->getSelectors()); } $items[] = $item; } } return $items; } //sort selectors into different categories we need private static function sort_selectors($selectors) { $selectors = array_map( function($sel) { return $sel->__toString(); }, $selectors ); $selectors_data = array(); foreach($selectors as $selector) { //setup selector data array $data = array( 'selector' => trim($selector), 'classes' => array(), 'ids' => array(), 'tags' => array(), 'atts' => array() ); //eliminate false negatives (:not(), pseudo, etc...) $selector = preg_replace('/(?<!\\\\)::?[a-zA-Z0-9_-]+(\(.+?\))?/', '', $selector); //atts $selector = preg_replace_callback( '/\[([A-Za-z0-9_:-]+)(\W?=[^\]]+)?\]/', function($matches) use (&$data) { $data['atts'][] = $matches[1]; return ''; }, $selector ); //classes $selector = preg_replace_callback( '/\.((?:[a-zA-Z0-9_-]+|\\\\.)+)/', function($matches) use (&$data) { $data['classes'][] = stripslashes($matches[1]); return ''; }, $selector ); //ids $selector = preg_replace_callback( '/#([a-zA-Z0-9_-]+)/', function($matches) use (&$data) { $data['ids'][] = $matches[1]; return ''; }, $selector ); //tags $selector = preg_replace_callback( '/[a-zA-Z0-9_-]+/', function($matches) use (&$data) { $data['tags'][] = $matches[0]; return ''; }, $selector ); //add selector data to main array $selectors_data[] = array_filter($data); } return array_filter($selectors_data); } //remove unused selectors from css data private static function remove_unused_selectors($data) { $rendered = []; foreach($data as $item) { //has css if(isset($item['css'])) { //need at least one selector match $should_render = !isset($item['selectors']) || 0 !== count(array_filter($item['selectors'], function($selector) { return self::is_selector_used($selector); } )); if($should_render) { $rendered[] = $item['css']; } continue; } //nested rulesets if(!empty($item['rulesets'])) { $child_rulesets = self::remove_unused_selectors($item['rulesets']); if($child_rulesets) { $rendered[] = sprintf('%s{%s}', $item['at_rule'], $child_rulesets); } } } return implode("", $rendered); } //check if selector is used private static function is_selector_used($selector) { //:root selector if($selector['selector'] === ':root') { return true; } //lone attribute selector if(!empty($selector['atts']) && (empty($selector['classes']) && empty($selector['ids']) && empty($selector['tags']))) { return true; } //search for excluded selector match if(!empty(self::$excluded_selectors)) { foreach(self::$excluded_selectors as $key => $value) { if(preg_match('#(' . preg_quote($value) . ')(?=\s|\.|\:|,|\[|$)#', $selector['selector'])) { return true; } } } //is selector used in the dom foreach(array('classes', 'ids', 'tags') as $type) { if(!empty($selector[$type])) { //cast array if needed $targets = (array)$selector[$type]; foreach($targets as $target) { //bail if a target doesn't exist if(!isset(self::$used_selectors[$type][$target])) { return false; } } } } return true; } //delete all files in the css cache directory public static function clear_used_css($site = null) { $path = ''; //add site path if specified if(is_object($site) && !empty($site->path)) { $path = ltrim($site->path, '/'); } $files = glob(PERFMATTERS_CACHE_DIR . $path . 'css/*'); foreach($files as $file) { if(is_file($file)) { unlink($file); } } delete_option('perfmatters_used_css_time'); } //clear used css file for specific post or post type public static function clear_post_used_css() { if(empty($_POST['action']) || empty($_POST['nonce']) || empty($_POST['post_id'])) { return; } if($_POST['action'] != 'perfmatters_clear_post_used_css') { return; } if(!wp_verify_nonce($_POST['nonce'], 'perfmatters_clear_post_used_css')) { return; } $post_id = (int)$_POST['post_id']; $post_type = get_post_type($post_id); $path = $post_type == 'page' ? 'page-' . $post_id : $post_type; $file = PERFMATTERS_CACHE_DIR . 'css/' . $path . '.used.css'; if(is_file($file)) { unlink($file); } wp_send_json_success(); exit; } //add admin bar menu item public static function admin_bar_menu(WP_Admin_Bar $wp_admin_bar) { if(!current_user_can('manage_options') || !perfmatters_network_access()) { return; } $type = !is_admin() ? self::get_url_type() : ''; $menu_item = array( 'parent' => 'perfmatters', 'id' => 'perfmatters-clear-used-css', 'title' => __('Clear Used CSS', 'perfmatters') . ' (' . (!empty($type) ? __('Current', 'perfmatters') : __('All', 'perfmatters')) . ')', 'href' => add_query_arg(array( 'action' => 'perfmatters_clear_used_css', '_wp_http_referer' => rawurlencode($_SERVER['REQUEST_URI']), '_wpnonce' => wp_create_nonce('perfmatters_clear_used_css'), 'type' => $type ), admin_url('admin-post.php')) ); $wp_admin_bar->add_menu($menu_item); } //display admin notices public static function admin_notices() { if(get_transient('perfmatters_used_css_cleared') === false) { return; } delete_transient('perfmatters_used_css_cleared'); echo '<div class="notice notice-success is-dismissible"><p><strong>' . __('Used CSS cleared.', 'perfmatters' ) . '</strong></p></div>'; } //clear used css from admin bar public static function admin_bar_clear_used_css() { if(!isset($_GET['_wpnonce']) || !wp_verify_nonce(sanitize_key($_GET['_wpnonce']), 'perfmatters_clear_used_css')) { wp_nonce_ays(''); } if(!empty($_GET['type'])) { //clear specific type $file = PERFMATTERS_CACHE_DIR . 'css/' . $_GET['type'] . '.used.css'; if(is_file($file)) { unlink($file); } } else { //clear all self::clear_used_css(); if(is_admin()) { set_transient('perfmatters_used_css_cleared', 1); } } //go back to url where button was pressed wp_safe_redirect(esc_url_raw(wp_get_referer())); exit; } //clear used css ajax action public static function clear_used_css_ajax() { Ajax::security_check(); self::clear_used_css(); wp_send_json_success(array( 'message' => __('Used CSS cleared.', 'perfmatters'), )); } }