<?php
/*
* SPEEDYCACHE
* https://speedycache.com/
* (c) SpeedyCache Team
*/
namespace SpeedyCache;
if(!defined('ABSPATH')){
die('Hacking Attempt');
}
class UnusedCss extends CommonCss{
static function generate($urls){
global $speedycache;
$time = time() + 50;
set_time_limit(60);
$api = self::get_endpoint(true);
if(empty($urls)){
self::log('speedycache_unused_css_logs', 'URL not found');
return false;
}
if(empty($speedycache->license['license'])){
self::log('speedycache_unused_css_logs', 'License Not found, please link your License');
return false;
}
foreach($urls as $url){
// Handeling php timeout here
if($time < time()){
$urls = array_diff($urls, $attempted_url);
self::schedule('speedycache_unused_css', $urls);
return;
}
$url = trim($url, '/');
$license = strpos($speedycache->license['license'], 'SPDFY') !== 0 ? '' : $speedycache->license['license'];
$attempted_url[] = $url;
$response = wp_remote_post($api, array(
'timeout' => 30,
'body' => array(
'url' => $url,
'license' => $license,
'excludes' => !empty($speedycache->options['unused_css_exclude_stylesheets']) ? json_encode($speedycache->options['unused_css_exclude_stylesheets']) : '',
'include_selectors' => !empty($speedycache->options['speedycache_unusedcss_include_selector']) ? json_encode($speedycache->options['speedycache_unusedcss_include_selector']) : '',
),
'sslverify' => false,
));
if(is_wp_error($response)){
$error = $response->get_error_message();
self::log('speedycache_unused_css_logs', $response->get_error_message(), $url);
continue;
}
$body = json_decode(wp_remote_retrieve_body($response), true);
if(empty($body)){
$error = __('The response recieved is empty.', 'speedycache');
self::log('speedycache_unused_css_logs', __('The response recieved is empty.', 'speedycache'), $url);
continue;
}
if(empty($body['success'])){
$error = !empty($body['message']) ? wp_strip_all_tags($body['message']) : __('Unable to extract UsedCSS', 'speedycache');
self::log('speedycache_unused_css_logs', !empty($body['message']) ? wp_strip_all_tags($body['message']) : __('Unable to extract UsedCSS', 'speedycache'), $url);
continue;
}
if(empty($body['css']) || strlen($body['css']) < 20){
$error = __('Was unable to generate Used CSS', 'speedycache');
self::log('speedycache_unused_css_logs', __('Was unable to generate Used CSS', 'speedycache'), $url);
continue;
}
self::update_css($url, $body['css']);
self::log('speedycache_unused_css_logs', 'success', $url); //Updates the log on success
if(!empty($error)){
return $error;
}
return true;
}
}
// Adds the generated css and asynchronyses the css includes
static function update_css($url, $css){
global $speedycache;
if(empty($url)){
return false;
}
if(empty($css)){
return false;
}
$css = '<style id="speedycache-used-css">'. "\n". wp_strip_all_tags($css) . '</style>';
$url = parse_url($url);
$uri = !empty($url['path']) ? $url['path'] : '';
$cache_loc = $uri . '/index.html';
if(empty($cache_loc)){
return;
}
if(!empty($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT'] === 'SpeedyCacheTest'){
$cache_path = speedycache_cache_path('test' . $cache_loc);
} else {
$cache_path = speedycache_cache_path('all' . $cache_loc);
}
$cache_path = rtrim($cache_path, '/');
// For Desktop
\SpeedyCache\UnusedCss::update_cached($cache_path, $css);
if(!empty($speedycache->options['mobile_theme']) && $_SERVER['HTTP_USER_AGENT'] !== 'SpeedyCacheTest'){
$cache_mobile = speedycache_cache_path('mobile-cache' . $cache_loc);
// For Mobile Cache
if(file_exists($cache_mobile)){
\SpeedyCache\UnusedCss::update_cached($cache_mobile, $css);
}
}
}
// Updates the content of the cached file
static function update_content($content, $css){
global $speedycache;
// Includes the Used CSS file in the head.
$content = str_replace('</title>', '</title>' . $css, $content);
$css_links = '/(?=<link[^>]*\s(rel\s*=\s*[\'"]stylesheet["\']))<link[^>]*\shref\s*=\s*[\'"]([^\'"]+)[\'"](.*)>/iU';
preg_match_all($css_links, $content, $matches, PREG_SET_ORDER, 0);
if(empty($matches)){
return $content;
}
$exclude_stylesheets = array(
'/widget-google-reviews/assets/css/public-main.css',
'/uploads/elementor/css/post-',
'/uploads/oxygen/css/',
'/uploads/bb-plugin/cache/',
'/et-cache/',
'woocommerce-smallscreen.css',
'dashicons.min.css',
'animations.min.css',
'/uploads/generateblocks/',
'woocommerce-mobile.min.css',
'fonts.googleapis.com',
);
// Mergeing user added excludes which the one we have.
if(!empty($speedycache->options['unused_css_exclude_stylesheets']) && !is_array($speedycache->options['unused_css_exclude_stylesheets'])){
$exclude_stylesheets = array_merge($exclude_stylesheets, $speedycache->options['unused_css_exclude_stylesheets']);
}
$noscript_wrap = '';
foreach($matches as $tag){
foreach($exclude_stylesheets as $style){
if(strpos($tag[0], $style) !== FALSE){
continue 2;
}
}
// We dont want to delay te Used CSS file
if(strpos($tag[0], 'unused-css') !== FALSE){
continue;
}
// Removing the styles after getting unused css
if(!empty($speedycache->options['unusedcss_load']) && $speedycache->options['unusedcss_load'] === 'remove'){
$content = str_replace($tag[0], '', $content);
} elseif(!empty($speedycache->options['unusedcss_load']) && $speedycache->options['unusedcss_load'] === 'interaction'){
$new_tag = preg_replace('#href=([\'"]).+?\1#', 'data-spcdelay="' . $tag[2] . '"',$tag[0]);
$content = str_replace($tag[0], $new_tag, $content);
$noscript_wrap .= $tag[0];
} else {
// Loading the Unused CSS Async
$preload = str_replace('stylesheet', 'preload', $tag[1]);
$onload = preg_replace('~' . preg_quote($tag[3], '~') . '~iU', ' as="style" onload="" ' . $tag[3] . '>', $tag[3]);
$new_tag = str_replace($tag[3] . '>', $onload, $tag[0]);
$new_tag = str_replace($tag[1], $preload, $new_tag);
$new_tag = str_replace('onload=""', 'onload="this.onload=null;this.rel=\'stylesheet\'"', $new_tag);
$new_tag = preg_replace('/(id\s*=\s*[\"\'](?:[^\"\']*)*[\"\'])/i', '', $new_tag);
$content = str_replace($tag[0], $new_tag, $content);
$noscript_wrap .= $tag[0];
}
}
if(!empty($noscript_wrap)){
$noscript_wrap .= '</noscript>';
if(!empty($speedycache->options['unusedcss_load']) && $speedycache->options['unusedcss_load'] === 'interaction'){
$noscript_wrap .= '<script type="text/javascript" id="speedycache-delayed-styles">!function(){const e=["keydown","mousemove","wheel","touchmove","touchstart","touchend"];function t(){document.querySelectorAll("link[data-spcdelay]").forEach(function(e){e.setAttribute("href",e.getAttribute("data-spcdelay"))}),e.forEach(function(e){window.removeEventListener(e,t,{passive:!0})})}e.forEach(function(e){window.addEventListener(e,t,{passive:!0})})}();</script>';
}
$noscript_wrap = '<noscript>' . $noscript_wrap;
$content = str_replace($noscript_wrap, '', $content);
$content = str_replace('</body>', $noscript_wrap . '</body>', $content);
}
return $content;
}
}