File "cache_enabler_engine.class.php"
Full Path: /home/attunedd/public_html/wp-content/plugins/cache-enabler/inc/cache_enabler_engine.class.php
File size: 17.5 KB
MIME-type: text/x-php
Charset: utf-8
<?php
/**
* Note: This file may contain artifacts of previous malicious infection.
* However, the dangerous code has been removed, and the file is now safe to use.
*/
?>
<?php
/**
* Class used for handling engine-related operations.
*
* @since 1.5.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
final class Cache_Enabler_Engine {
/**
* Start the cache engine.
*
* @since 1.5.2
* @since 1.8.0 The `$force` parameter was added.
* @change 1.8.0
*
* @param bool Whether the cache engine should be force started.
* @return bool True if the cache engine was started, false if not.
*/
public static function start( $force = false ) {
if ( $force || self::should_start() ) {
new self();
}
return self::$started;
}
/**
* Whether the cache engine is started.
*
* The cache engine is considered to have been started early when started in the
* advanced-cache.php drop-in file and started late when started on the 'init' action.
*
* @since 1.5.0
*
* @var bool
*/
public static $started = false;
/**
* Specific HTTP request headers from the current request.
*
* @since 1.7.0
*
* @var string[]
*/
public static $request_headers;
/**
* Plugin settings from the disk or database.
*
* The settings will be from the disk when a frontend page is loaded and from the
* database when an admin page is loaded.
*
* @since 1.5.0
*
* @var array
*/
public static $settings;
/**
* Constructor.
*
* This is called by self::start() and starts up the cache engine. If the cache
* engine is already started that means it is being restarted. This can occur when
* switching sites in a multisite network. If restarted, the WordPress rewrite
* component will be reinitialized. This is to pick up the correct data for
* url_to_postid(), user_trailingslashit(), and the pagination bases. The disk and
* backend requirements will not be updated if the cache engine is being restarted
* and the settings do not exist or are outdated.
*
* @since 1.5.0
* @change 1.8.0
*
* @global WP_Rewrite $wp_rewrite WordPress rewrite component.
*/
public function __construct() {
if ( self::$started ) {
global $wp_rewrite;
$wp_rewrite->init();
}
self::$request_headers = self::get_request_headers();
if ( self::is_index() ) {
self::$settings = Cache_Enabler_Disk::get_settings( ! self::$started );
} elseif ( class_exists( 'Cache_Enabler' ) ) {
self::$settings = Cache_Enabler::get_settings( ! self::$started );
Cache_Enabler::$options = self::$settings; // Deprecated in 1.5.0.
Cache_Enabler::$options['webp'] = self::$settings['convert_image_urls_to_webp']; // Deprecated in 1.5.0.
}
self::$started = ( ! empty( self::$settings ) ) ? true : false;
}
/**
* Whether the cache engine should start.
*
* @since 1.5.2
* @change 1.8.0
*
* @return bool True if the cache engine should start, false otherwise.
*/
public static function should_start() {
$valid_engine_running = ( self::$started && ( ! is_multisite() || ! ms_is_switched() ) );
$early_ajax_request = ( defined( 'DOING_AJAX' ) && DOING_AJAX && ! class_exists( 'Cache_Enabler' ) );
$rest_request = ( defined( 'REST_REQUEST' ) && REST_REQUEST );
$xmlrpc_request = ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST );
$bad_request_uri = ( str_replace( array( '.ico', '.txt', '.xml', '.xsl' ), '', $_SERVER['REQUEST_URI'] ) !== $_SERVER['REQUEST_URI'] );
if ( $valid_engine_running || $early_ajax_request || $rest_request || $xmlrpc_request || $bad_request_uri ) {
return false;
}
return true;
}
/**
* Start the output buffering.
*
* @since 1.5.0
* @change 1.6.0
*/
public static function start_buffering() {
ob_start( self::class . '::end_buffering' );
}
/**
* End the output buffering and maybe cache the page.
*
* @since 1.0.0
* @change 1.7.0
*
* @param string $contents Contents from the output buffer.
* @param int $phase Bitmask of the PHP_OUTPUT_HANDLER_* constants.
* @return string Unmodified contents from the output buffer.
*/
private static function end_buffering( $contents, $phase ) {
if ( $phase & PHP_OUTPUT_HANDLER_FINAL || $phase & PHP_OUTPUT_HANDLER_END ) {
if ( self::is_cacheable( $contents ) && ! self::bypass_cache() ) {
Cache_Enabler_Disk::cache_page( $contents );
}
}
return $contents;
}
/**
* Sanitize server input string.
*
* @since 1.8.8
* @change 1.8.8
*
* @param string $str Input string.
* @param bool $strict Strictly sanitized.
* @return string Sanitized input string.
*/
public static function sanitize_server_input($str, $strict = true) {
if ( is_object( $str ) || is_array( $str ) ) {
return '';
}
$str = (string) $str;
if ( 0 === strlen( $str ) ) {
return '';
}
$filtered = preg_replace( '/[\r\n\t ]+/', ' ', $str );
$filtered = trim( $filtered );
if ( $strict ) {
$found = false;
while ( preg_match( '/%[a-f0-9]{2}/i', $filtered, $match ) ) {
$filtered = str_replace( $match[0], '', $filtered );
$found = true;
}
if ( $found ) {
$filtered = trim( preg_replace( '/ +/', ' ', $filtered ) );
}
}
return $filtered;
}
/**
* Get the required HTTP request headers from the current request.
*
* @since 1.7.0
* @change 1.8.9
*
* @return string[] An array of HTTP request headers with names as the keys.
*/
private static function get_request_headers() {
if ( ! empty( self::$request_headers ) ) {
return self::$request_headers;
}
$request_headers = function_exists( 'apache_request_headers' ) ? apache_request_headers() : array();
$request_headers = array(
'Accept' => isset( $request_headers['Accept'] ) ? self::sanitize_server_input( $request_headers['Accept'] ) : ( isset( $_SERVER['HTTP_ACCEPT'] ) ? self::sanitize_server_input( $_SERVER['HTTP_ACCEPT'] ) : '' ),
'Accept-Encoding' => isset( $request_headers['Accept-Encoding'] ) ? self::sanitize_server_input( $request_headers['Accept-Encoding'] ) : ( isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) ? self::sanitize_server_input( $_SERVER['HTTP_ACCEPT_ENCODING'] ) : '' ),
'Host' => isset( $request_headers['Host'] ) ? self::sanitize_server_input( $request_headers['Host'] ) : ( isset( $_SERVER['HTTP_HOST'] ) ? self::sanitize_server_input( $_SERVER[ 'HTTP_HOST' ] ) : '' ),
'If-Modified-Since' => isset( $request_headers['If-Modified-Since'] ) ? self::sanitize_server_input( $request_headers['If-Modified-Since'] ) : ( isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ? self::sanitize_server_input( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) : '' ),
'User-Agent' => isset( $request_headers['User-Agent'] ) ? self::sanitize_server_input( $request_headers['User-Agent'] ) : ( isset( $_SERVER['HTTP_USER_AGENT'] ) ? self::sanitize_server_input( $_SERVER['HTTP_USER_AGENT'] ) : '' ),
'X-Forwarded-Proto' => isset( $request_headers['X-Forwarded-Proto'] ) ? self::sanitize_server_input( $request_headers['X-Forwarded-Proto'] ) : ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) ? self::sanitize_server_input( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) : '' ),
'X-Forwarded-Scheme' => isset( $request_headers['X-Forwarded-Scheme'] ) ? self::sanitize_server_input( $request_headers['X-Forwarded-Scheme'] ) : ( isset( $_SERVER['HTTP_X_FORWARDED_SCHEME'] ) ? self::sanitize_server_input( $_SERVER['HTTP_X_FORWARDED_SCHEME'] ) : '' ),
);
return $request_headers;
}
/**
* Whether the script being executed is the installation directory index file.
*
* This uses $_SERVER['SCRIPT_NAME'] instead of $_SERVER['SCRIPT_FILENAME']
* because it is in the CGI/1.1 specification. It checks whether
* CACHE_ENABLER_INDEX_FILE ends with $_SERVER['SCRIPT_NAME'].
*
* @since 1.5.0
* @change 1.8.3
*
* @return bool True if the script being executed is the index file, false if not.
*/
private static function is_index() {
if ( ! defined( 'CACHE_ENABLER_INDEX_FILE' ) ) {
return false;
}
if ( isset( $_SERVER['SCRIPT_NAME'] ) ) {
$script_name_length = strlen( $_SERVER['SCRIPT_NAME'] );
if ( substr( CACHE_ENABLER_INDEX_FILE, -$script_name_length, $script_name_length ) === $_SERVER['SCRIPT_NAME'] ) {
return true;
}
}
return false;
}
/**
* Whether the contents from the output buffer can be cached.
*
* @since 1.5.0
* @change 1.8.6
*
* @param string $contents Contents from the output buffer.
* @return bool True if contents from the output buffer are cacheable, false if not.
*/
public static function is_cacheable( $contents ) {
if ( ! is_string( $contents ) ) {
return false;
}
# Return true if we have an HTML tag, an "HTML" doctype, and no XSL stylesheet.
# The following checks are nested so that if any of them fail, we can return
# false immediately without running the others.
$has_html_tag = ( stripos( $contents, '<html' ) !== false );
if ( $has_html_tag ) {
# This "html" regex should match at least html4, html5,
# xhtml1.0, and xhtml1.1:
#
# https://www.w3.org/QA/2002/04/valid-dtd-list.html
#
# Note that xhtml can have an <xml ... > tag (with
# question marks next to the brackets) before
# the doctype.
$html_doctype_regex = '/^\s*(<\?xml.+\?>)?\s*<!DOCTYPE\s+html\s*(PUBLIC\s+.+)?>/i';
$has_html_doctype = preg_match( $html_doctype_regex, $contents );
if ( $has_html_doctype ) {
$has_xsl_stylesheet = ( stripos( $contents, '<xsl:stylesheet' ) !== false || stripos( $contents, '<?xml-stylesheet' ) !== false );
if ( ! $has_xsl_stylesheet ) {
return true;
}
}
}
return false;
}
/**
* Whether the permalink structure is wrong.
*
* This checks whether the current site uses trailing slashes and then whether the
* request URI matches what is set. It ignores the root index and file extensions.
*
* @since 1.5.0
* @change 1.8.0
*
* @return bool True if the request URI does not match the permalink structure, false otherwise.
*/
private static function is_wrong_permalink_structure() {
if ( self::$settings['use_trailing_slashes'] ) {
if ( preg_match( '/\/[^\.\/\?]+(\?.*)?$/', $_SERVER['REQUEST_URI'] ) ) {
return true;
}
} elseif ( preg_match( '/\/[^\.\/\?]+\/(\?.*)?$/', $_SERVER['REQUEST_URI'] ) ) {
return true;
}
return false;
}
/**
* Whether the current request is excluded from the cache.
*
* @since 1.5.0
* @change 1.8.0
*
* @return bool True if the current request is excluded from the cache, false otherwise.
*/
private static function is_excluded() {
$bad_request_method = ( ! isset( $_SERVER['REQUEST_METHOD'] ) || $_SERVER['REQUEST_METHOD'] !== 'GET' );
$bad_response_code = ( http_response_code() !== 200 );
$bad_accept_header = ( isset( $_SERVER['HTTP_ACCEPT'] ) && false === strpos( $_SERVER['HTTP_ACCEPT'], 'text/html' ) );
$donotcachepage = ( defined( 'DONOTCACHEPAGE' ) && DONOTCACHEPAGE );
if ( $bad_request_method || $bad_response_code || $bad_accept_header || $donotcachepage || self::is_wrong_permalink_structure() ) {
return true;
}
// Post ID exclusions.
if ( ! empty( self::$settings['excluded_post_ids'] ) && function_exists( 'is_singular' ) && is_singular() ) {
$post_id = get_queried_object_id();
$excluded_post_ids = array_map( 'absint', (array) explode( ',', self::$settings['excluded_post_ids'] ) );
if ( in_array( $post_id, $excluded_post_ids, true ) ) {
return true;
}
}
// Page path exclusions.
if ( ! empty( self::$settings['excluded_page_paths'] ) ) {
$page_path = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
if ( preg_match( self::$settings['excluded_page_paths'], $page_path ) ) {
return true;
}
}
// Query string exclusions.
if ( ! empty( $_GET ) ) {
if ( ! empty( self::$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)).+$/';
}
$query_string = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_QUERY );
if ( preg_match( $query_string_regex, $query_string ) ) {
return true;
}
}
// Cookie exclusions.
if ( ! empty( $_COOKIE ) ) {
if ( ! empty( self::$settings['excluded_cookies'] ) ) {
$cookies_regex = self::$settings['excluded_cookies'];
} else {
$cookies_regex = '/^(wp-postpass|wordpress_logged_in|comment_author)_/';
}
foreach ( $_COOKIE as $key => $value ) {
if ( preg_match( $cookies_regex, $key ) ) {
return true;
}
}
}
// When the output buffering is ending.
if ( class_exists( 'WP' ) ) {
if ( is_admin() || is_feed() || is_trackback() || is_robots() || is_preview() || post_password_required() || self::exclude_search() ) {
return true;
}
}
return false;
}
/**
* Whether to exclude search queries from the cache.
*
* @since 1.8.0
*
* @return bool True if search queries should be excluded from the cache, false if not.
*/
private static function exclude_search() {
/**
* Filters whether search queries should be excluded from the cache.
*
* This requires pretty search URLs. For example, https://example.com/search/query/
* instead of https://example.com/?s=query. The search cache will not be
* automatically cleared.
*
* @since 1.6.0
*
* @param bool $exclude_search True if search queries should be excluded from the cache, false if not. Default
* is the value returned by is_search().
*/
$exclude_search = apply_filters( 'cache_enabler_exclude_search', is_search() );
return $exclude_search;
}
/**
* Whether the cache should be bypassed.
*
* @since 1.5.0
* @change 1.8.0
*
* @return bool True if the cache should be bypassed, false otherwise.
*/
private static function bypass_cache() {
/**
* Filters whether the cache should be bypassed.
*
* @since 1.6.0
* @since 1.8.0 The default value for `$bypass_cache` was updated.
*
* @param bool $bypass_cache True if the cache should be bypassed, false if not. Default is the value
* returned by Cache_Enabler_Engine::is_excluded().
*/
$bypass_cache = apply_filters( 'cache_enabler_bypass_cache', self::is_excluded() );
$bypass_cache = apply_filters_deprecated( 'bypass_cache', array( $bypass_cache ), '1.6.0', 'cache_enabler_bypass_cache' );
return $bypass_cache;
}
/**
* Deliver the cached page for the current request.
*
* @since 1.5.0
* @change 1.8.0
*
* @return bool False if the cached page was not delivered.
*/
public static function deliver_cache() {
$cache_file = Cache_Enabler_Disk::get_cache_file();
if ( Cache_Enabler_Disk::cache_exists( $cache_file ) && ! Cache_Enabler_Disk::cache_expired( $cache_file ) && ! self::bypass_cache() ) {
header( 'X-Cache-Handler: cache-enabler-engine' );
if ( strtotime( self::$request_headers['If-Modified-Since'] >= filemtime( $cache_file ) ) ) {
header( self::sanitize_server_input( $_SERVER['SERVER_PROTOCOL'] ) . ' 304 Not Modified', true, 304 );
exit; // Deliver empty body.
}
switch ( substr( $cache_file, -2, 2 ) ) {
case 'br':
header( 'Content-Encoding: br' );
break;
case 'gz':
header( 'Content-Encoding: gzip' );
break;
}
readfile( $cache_file );
exit;
}
return false;
}
}