File "class-critical-css.php"
Full Path: /home/theinspectionboy/public_html/suffolk/includes-20250622113618/class-critical-css.php
File size: 18.09 KB
MIME-type: text/x-php
Charset: utf-8
<?php
/**
* Class and methods to generate Critical CSS.
*
* @link https://ewww.io/swis/
* @package SWIS_Performance
*/
namespace SWIS;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// TODO: does it need to extend page parser?
/**
* Generates and inserts Critical CSS via an external API.
*/
final class Critical_CSS extends Page_Parser {
/**
* A list of user-defined exclusions, populated by validate_user_exclusions().
*
* @access protected
* @var array $user_exclusions
*/
protected $user_exclusions = array();
/**
* Register actions and filters for Critical CSS.
*/
function __construct() {
if ( ! $this->get_option( 'critical_css_key' ) ) {
return;
}
parent::__construct();
$this->debug_message( '<b>' . __METHOD__ . '()</b>' );
if ( $this->background_mode_enabled() ) {
// Add handler to manually start the (async) preloader.
add_action( 'admin_action_swis_generate_css_manual', array( $this, 'generate_css_manual' ) );
} else {
// TODO: do we need to stop the process when they clear the cache?
// Any time the cache is cleared, clear the preload queue.
add_action( 'swis_complete_cache_cleared', array( $this, 'stop_generate_css' ) );
add_action( 'swis_site_cache_cleared', array( $this, 'stop_generate_css' ) );
add_action( 'swis_cache_by_url_cleared', array( $this, 'stop_generate_css' ) );
}
// Actions to process preload via AJAX.
add_action( 'wp_ajax_swis_generate_css_init', array( $this, 'start_generate_css_ajax' ) );
add_action( 'wp_ajax_swis_url_generate_css', array( $this, 'url_generate_css_ajax' ) );
// Allow the user to override the preload delay with a constant.
add_filter( 'swis_generate_css_delay', array( $this, 'generate_css_delay_override' ) );
// Overrides for user exclusions.
add_filter( 'swis_skip_generate_css', array( $this, 'skip_generate_css' ), 10, 2 );
$this->validate_user_exclusions();
}
/**
* Checks to see if the user defined an override for the generate css delay.
*
* @param int $delay The current delay (defaults to 5 seconds).
* @return int The default, or a user-configured override.
*/
function generate_css_delay_override( $delay ) {
if ( defined( 'SWIS_GENERATE_CSS_DELAY' ) ) {
$delay_override = SWIS_GENERATE_CSS_DELAY;
return absint( $delay_override );
}
return $delay;
}
/**
* Validate the user-defined exclusions.
*/
function validate_user_exclusions() {
$user_exclusions = $this->get_option( 'cache_preload_exclude' );
if ( ! empty( $user_exclusions ) ) {
if ( is_string( $user_exclusions ) ) {
$user_exclusions = array( $user_exclusions );
}
if ( is_array( $user_exclusions ) ) {
foreach ( $user_exclusions as $exclusion ) {
if ( ! is_string( $exclusion ) ) {
continue;
}
$this->user_exclusions[] = $exclusion;
}
}
}
}
/**
* Exclude page from being preloaded based on user specified list.
*
* @param boolean $skip Whether SWIS should skip preloading.
* @param string $url The page URL.
* @return boolean True to skip the page, unchanged otherwise.
*/
function skip_cache_preload( $skip, $url ) {
if ( $this->user_exclusions ) {
foreach ( $this->user_exclusions as $exclusion ) {
if ( false !== strpos( $url, $exclusion ) ) {
$this->debug_message( __METHOD__ . "(); user excluded $url via $exclusion" );
return true;
}
}
}
return $skip;
}
/**
* Handle the manual preload admin action.
*/
function manual_preload_action() {
if ( false === current_user_can( 'manage_options' ) || ! check_admin_referer( 'swis_cache_preload_nonce', 'swis_cache_preload_nonce' ) ) {
wp_die( esc_html__( 'Access denied', 'swis-performance' ) );
}
if ( ! empty( $_GET['swis_stop_preload'] ) ) {
$this->stop_preload();
} else {
$this->start_preload();
}
$base_url = admin_url( 'options-general.php?page=swis-performance-options' );
wp_safe_redirect( $base_url );
exit;
}
/**
* Begin preload process.
*/
function start_preload() {
$this->debug_message( '<b>' . __METHOD__ . '()</b>' );
$this->stop_preload();
swis()->cache_preload_async->dispatch();
}
/**
* Stop preload process.
*/
function stop_preload() {
$this->debug_message( '<b>' . __METHOD__ . '()</b>' );
swis()->cache_preload_background->cancel_process();
\delete_transient( 'swis_cache_preload_total' );
}
/**
* Check if the home/front page is uncached and therefore the preloader should be launched.
*/
function check_front_page_cache() {
if ( $this->get_option( 'cache_preload_front_page_auto' ) && ! \is_user_logged_in() && \is_front_page() && ! \get_transient( 'swis_cache_preload_frontpage_triggered' ) ) {
$this->debug_message( 'front page not cached, starting preload' );
\set_transient( 'swis_cache_preload_frontpage_triggered', true, 10 * MINUTE_IN_SECONDS );
$this->start_preload();
}
}
/**
* Begin preload process.
*
* @param string $url The page to preload.
*/
function start_preload_url( $url ) {
$this->debug_message( '<b>' . __METHOD__ . '()</b>' );
$this->debug_message( "preloading $url" );
swis()->cache_preload_async->data(
array(
'swis_preload_url' => esc_url( $url ),
)
)->dispatch();
}
/**
* Begin preload process via AJAX request.
*/
function start_preload_ajax() {
$this->debug_message( '<b>' . __METHOD__ . '()</b>' );
if ( false === current_user_can( 'manage_options' ) || ! check_ajax_referer( 'swis_cache_preload_nonce', 'swis_cache_preload_nonce', false ) ) {
die( wp_json_encode( array( 'error' => esc_html__( 'Access token has expired, please reload the page.', 'swis-performance' ) ) ) );
}
$remaining_urls = (int) swis()->cache_preload_background->count_queue();
$completed = 0;
if ( empty( $remaining_urls ) ) {
$this->debug_message( 'looking for URLs to preload' );
$this->get_urls();
$total_urls = (int) swis()->cache_preload_background->count_queue();
} else {
$total_urls = (int) get_transient( 'swis_cache_preload_total' );
if ( ! $total_urls ) {
$total_urls = $remaining_urls;
set_transient( 'swis_cache_preload_total', (int) $total_urls, DAY_IN_SECONDS );
}
$completed = $total_urls - $remaining_urls;
}
/* translators: %d: number of images */
$message = sprintf( esc_html__( '%1$d / %2$d pages have been completed.', 'swis-performance' ), $completed, (int) $total_urls );
die(
wp_json_encode(
array(
'success' => $total_urls,
'message' => $message,
)
)
);
}
/**
* Preload the next URL in the queue via AJAX request.
*/
function preload_url_ajax() {
$this->debug_message( '<b>' . __METHOD__ . '()</b>' );
if ( false === current_user_can( 'manage_options' ) || ! check_ajax_referer( 'swis_cache_preload_nonce', 'swis_cache_preload_nonce', false ) ) {
die( wp_json_encode( array( 'error' => esc_html__( 'Access token has expired, please reload the page.', 'swis-performance' ) ) ) );
}
global $wpdb;
$url = $wpdb->get_row( "SELECT id,page_url FROM $wpdb->swis_queue WHERE queue_name = 'swis_cache_preload' LIMIT 1", ARRAY_A );
if ( ! $this->is_iterable( $url ) || empty( $url['page_url'] ) ) {
die( wp_json_encode( array( 'success' => 0 ) ) );
}
$this->preload( $url['page_url'] );
swis()->cache_preload_background->delete( $url['id'] );
$remaining_urls = (int) swis()->cache_preload_background->count_queue();
$total_urls = (int) get_transient( 'swis_cache_preload_total' );
$completed = $total_urls - $remaining_urls;
/* translators: %d: number of images */
$message = sprintf( esc_html__( '%1$d / %2$d pages have been completed.', 'swis-performance' ), (int) $completed, (int) $total_urls );
die(
wp_json_encode(
array(
'success' => $remaining_urls,
'message' => $message,
)
)
);
}
/**
* Gets all the URLs to preload, called via AJAX or async operation.
*/
function get_urls() {
$this->debug_message( '<b>' . __METHOD__ . '()</b>' );
$urls = array_merge( $this->get_homepage_urls(), $this->get_sitemap_urls() );
if ( $this->is_iterable( $urls ) ) {
foreach ( $urls as $url ) {
if ( empty( $url ) || ! is_string( $url ) ) {
continue;
}
$this->debug_message( "queueing $url for preload" );
swis()->cache_preload_background->push_to_queue( $url );
}
set_transient( 'swis_cache_preload_total', (int) swis()->cache_preload_background->count_queue(), DAY_IN_SECONDS );
}
}
/**
* Fetch the home page and get all links for preloading.
*
* @return array A list of URLs that should be preloaded.
*/
function get_homepage_urls() {
$this->debug_message( '<b>' . __METHOD__ . '()</b>' );
$urls = array();
$home_url = get_home_url();
$home_domain = $this->parse_url( $home_url, PHP_URL_HOST );
$args = array(
'user-agent' => 'SWIS Performance/Preload',
'sslverify' => apply_filters( 'https_local_ssl_verify', false, $home_url ),
);
$args = apply_filters( 'swis_cache_preload_homepage_request_args', $args );
$result = wp_remote_get( $home_url, $args );
if ( is_wp_error( $result ) ) {
$this->debug_message( 'cache preload error: ' . $result->get_error_message() );
return $urls;
}
$http_code = wp_remote_retrieve_response_code( $result );
if ( 200 !== (int) $http_code ) {
$this->debug_message( "cache preload error, http code $http_code" );
return $urls;
}
$content = wp_remote_retrieve_body( $result );
$links = $this->get_elements_from_html( $content, 'a' );
foreach ( $links as $link ) {
$url = $this->get_attribute( $link, 'href' );
$url = $this->should_preload( $url, $home_url, $home_domain );
if ( ! empty( $url ) ) {
$urls[] = $url;
}
}
return $urls;
}
/**
* Fetch the sitemap to get URLs for preloading.
*
* @param string $sitemap_url The sitemap URL to search through.
* @return array A list of URLs that should be preloaded.
*/
function get_sitemap_urls( $sitemap_url = '' ) {
$this->debug_message( '<b>' . __METHOD__ . '()</b>' );
$urls = array();
$sitemap_urls = false;
if ( ! $sitemap_url ) {
if ( defined( 'SWIS_CACHE_PRELOAD_SITEMAP' ) && SWIS_CACHE_PRELOAD_SITEMAP ) {
$sitemap_override = SWIS_CACHE_PRELOAD_SITEMAP;
if ( is_string( $sitemap_override ) ) {
$sitemap_urls = array( $sitemap_override );
}
}
if ( ! $this->is_iterable( $sitemap_urls ) ) {
$sitemap_urls = array(
home_url( 'sitemap_index.xml' ),
home_url( 'sitemap.xml' ),
home_url( 'wp-sitemap.xml' ),
);
}
$sitemap_urls = apply_filters( 'swis_cache_preload_default_sitemaps', $sitemap_urls );
foreach ( $sitemap_urls as $sitemap_url ) {
$sitemap_xml = $this->get_sitemap_xml( $sitemap_url );
if ( $sitemap_xml ) {
break;
}
}
} else {
$sitemap_xml = $this->get_sitemap_xml( $sitemap_url );
}
if ( $sitemap_xml && function_exists( 'simplexml_load_string' ) ) {
libxml_use_internal_errors( true );
$xml = simplexml_load_string( $sitemap_xml );
if ( false !== $xml ) {
$url_count = count( $xml->url );
$map_count = count( $xml->sitemap );
if ( $url_count ) {
foreach ( $xml->url as $xml_url ) {
if ( ! empty( $xml_url->loc ) ) {
$this->debug_message( 'found a url in sitemap: ' . $xml_url->loc );
$urls[] = (string) $xml_url->loc;
}
}
}
if ( $map_count ) {
foreach ( $xml->sitemap as $sitemap ) {
$this->debug_message( 'found a child map at ' . $sitemap->loc );
$urls = array_merge( $urls, $this->get_sitemap_urls( (string) $sitemap->loc ) );
}
}
}
}
if ( empty( $urls ) ) {
$urls = $this->get_post_urls();
}
return $urls;
}
/**
* Retrieve a sitemap for parsing.
*
* @param string $sitemap_url The sitemap URL.
* @return string The contents of the sitemap.
*/
function get_sitemap_xml( $sitemap_url ) {
$this->debug_message( '<b>' . __METHOD__ . '()</b>' );
$this->debug_message( "fetching $sitemap_url" );
$args = array(
'user-agent' => 'SWIS Performance/Preload',
'sslverify' => apply_filters( 'https_local_ssl_verify', false, $sitemap_url ),
);
$args = apply_filters( 'swis_cache_preload_sitemap_request_args', $args );
$result = wp_remote_get( esc_url_raw( $sitemap_url ), $args );
if ( is_wp_error( $result ) ) {
$this->debug_message( 'cache preload error: ' . $result->get_error_message() );
return '';
}
$http_code = wp_remote_retrieve_response_code( $result );
if ( 200 !== $http_code ) {
$this->debug_message( "cache preload error, code $http_code" );
return '';
}
$xml_content = wp_remote_retrieve_body( $result );
// Check to be sure this is a valid sitemap.
if ( false === strpos( $xml_content, '<loc>' ) ) {
$this->debug_message( 'cache preload error, no <loc> sections found!' );
return '';
}
return $xml_content;
}
/**
* Fetch posts for preloading, fallback if no sitemaps were found.
*
* @return array A list of URLs that should be preloaded.
*/
function get_post_urls() {
$this->debug_message( '<b>' . __METHOD__ . '()</b>' );
$urls = array();
$post_types = get_post_types( array( 'public' => true ) );
$post_types = array_filter( $post_types, 'is_post_type_viewable' );
$args = apply_filters(
'swis_preload_posts_args',
array(
'fields' => 'ids',
'numberposts' => 1000,
'orderby' => 'post_date',
'order' => 'DESC',
'posts_per_page' => -1,
'post_status' => 'publish',
'post_type' => $post_types,
)
);
$blog_posts = get_posts( $args );
if ( ! $this->is_iterable( $blog_posts ) ) {
return $urls;
}
foreach ( $blog_posts as $blog_post ) {
$permalink = get_permalink( $blog_post );
$this->debug_message( "found $permalink for post $blog_post" );
if ( $permalink ) {
$urls[] = $permalink;
}
}
return $urls;
}
/**
* Check if the given URL should be preloaded.
*
* @param string $url URL to check.
* @param string $home_url Homepage URL.
* @param string $home_domain Homepage domain name.
* @return bool True to preload, false otherwise.
*/
function should_preload( $url, $home_url, $home_domain ) {
$this->debug_message( '<b>' . __METHOD__ . '()</b>' );
$this->debug_message( "checking $url against $home_url with $home_domain" );
$url_parts = $this->parse_url( $url );
if ( empty( $url_parts ) ) {
$this->debug_message( 'parsing failed' );
return false;
}
if ( ! empty( $url_parts['fragment'] ) ) {
$this->debug_message( 'bookmark not necessary' );
return false;
}
if ( empty( $url_parts['host'] ) ) {
$url = home_url( $url );
$this->debug_message( "fixed $url" );
$url_parts = $this->parse_url( $url );
}
if ( 0 === strpos( $url, '//' ) && ! empty( $url_parts['scheme'] ) ) {
$url = $url_parts['scheme'] . ':' . $url;
$this->debug_message( "added scheme to $url" );
}
if ( untrailingslashit( $url ) === untrailingslashit( $home_url ) ) {
$this->debug_message( 'URL is home URL' );
return false;
}
if ( $url_parts['host'] !== $home_domain ) {
$this->debug_message( 'URL is not at home' );
return false;
}
if ( apply_filters( 'swis_skip_cache_preload', false, $url ) ) {
$this->debug_message( 'URL skipped by user/filter' );
return false;
}
if ( $this->is_file_url( $url ) ) {
$this->debug_message( 'URL is a file' );
return false;
}
$cache_settings = swis()->cache->get_settings();
if ( ! empty( $cache_settings['excluded_query_strings'] ) ) {
$query_string_regex = self::$settings['excluded_query_strings'];
} else {
$query_string_regex = '/^(?!(fbclid|ref|mc_(cid|eid)|utm_(source|medium|campaign|term|content|expid)|gclid|fb_(action_ids|action_types|source)|age-verified|usqp|cn-reloaded|_ga|_ke)).+$/';
}
if ( ! empty( $url_parts['query'] ) && preg_match( $query_string_regex, $url_parts['query'] ) ) {
$this->debug_message( 'URL has disallowed query params' );
return false;
}
return $url;
}
/**
* Check if the URL is already cached.
*
* @param string $url The URL path to check.
* @return bool True for cached, false if it ain't.
*/
function is_cached( $url ) {
$this->debug_message( '<b>' . __METHOD__ . '()</b>' );
if ( ! class_exists( '\SWIS\Disk_Cache' ) ) {
return false;
}
$cache_file_dir = Disk_Cache::get_cache_file_dir( $url );
$this->debug_message( "checking if $cache_file_dir/ exists" );
if ( \is_dir( $cache_file_dir ) ) {
$dir_objects = Disk_Cache::get_dir_objects( $cache_file_dir );
if ( $this->is_iterable( $dir_objects ) ) {
foreach ( $dir_objects as $dir_object ) {
if ( is_file( $dir_object ) ) {
$this->debug_message( 'it sure does!' );
return true;
}
}
}
}
return false;
}
/**
* Check if the URL path is to a known file type.
*
* @param string $path The URL path to check.
* @return bool True for known files, false for everything else.
*/
function is_file_url( $path ) {
$known_types = array( 'jpe', 'jpg', 'jpeg', 'png', 'gif', 'svg', 'webp', 'bmp', 'tiff', 'pdf', 'doc', 'docx', 'odt', 'txt', 'mp3', 'ogg', 'avi', 'm4v', 'mov', 'wvm', 'qt', 'webm', 'ogv', 'mp4', 'm4p', 'mpg', 'mpeg', 'mpv', 'zip', 'tar', 'bz2', 'tgz', 'rar', 'gz' );
$known_types = implode( '|', $known_types );
if ( preg_match( '#\.(?:' . $known_types . ')$#i', $path ) ) {
return true;
}
return false;
}
/**
* Preloads the given URL.
*
* @param string $url The page to preload.
*/
function preload( $url ) {
$this->debug_message( '<b>' . __METHOD__ . '()</b>' );
if ( $this->is_cached( $url ) ) {
return;
}
// Sleep first, instead of later, which gives the cache clearing time to finish.
// It also means that when we're done, that's it, and we exit right away.
if ( $this->function_exists( 'sleep' ) ) {
sleep( absint( apply_filters( 'swis_cache_preload_delay', 5 ) ) );
}
$args = array(
'timeout' => 10,
'user-agent' => 'SWIS Performance/Preload',
'sslverify' => apply_filters( 'https_local_ssl_verify', false, $url ),
);
if ( $this->get_option( 'cache_webp' ) ) {
$args['headers'] = 'Accept: image/webp';
}
$args = apply_filters( 'swis_cache_preload_url_request_args', $args );
$result = wp_remote_get( esc_url_raw( $url ), $args );
if ( is_wp_error( $result ) ) {
$this->debug_message( 'cache preload error: ' . $result->get_error_message() );
} else {
$this->debug_message( wp_remote_retrieve_response_code( $result ) );
}
}
}