File "cache_enabler_disk.class.php"
Full Path: /home/ycoalition/public_html/blog/wp-admin/js/widgets/plugins/cache-enabler/inc/cache_enabler_disk.class.php
File size: 64.46 KB
MIME-type: text/x-php
Charset: utf-8
<?php
/**
* Class used for handling disk-related operations.
*
* @since 1.0.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
final class Cache_Enabler_Disk {
/**
* Plugin cache directory (deprecated).
*
* @since 1.5.0
* @deprecated 1.8.0
*/
public static $cache_dir = WP_CONTENT_DIR . '/cache/cache-enabler';
/**
* File path to the cached page for the current request.
*
* @since 1.8.0
*
* @var string
*/
private static $cache_file;
/**
* Add and configure files required by plugin.
*
* @since 1.5.0
* @change 1.8.0
*/
public static function setup() {
self::create_advanced_cache_file();
self::set_wp_cache_constant();
}
/**
* Delete and unconfigure files required by plugin.
*
* @since 1.5.0
* @change 1.8.0
*/
public static function clean() {
self::delete_settings_file();
if ( ! is_dir( CACHE_ENABLER_SETTINGS_DIR ) ) {
array_map( 'unlink', glob( WP_CONTENT_DIR . '/cache/cache-enabler-advcache-*.json' ) ); // < 1.4.0
array_map( 'unlink', glob( ABSPATH . 'CE_SETTINGS_PATH-*.json' ) ); // = 1.4.0
@unlink( WP_CONTENT_DIR . '/advanced-cache.php' );
self::set_wp_cache_constant( false );
}
}
/**
* Create a static HTML file from the page contents received from the cache engine.
*
* @since 1.5.0
* @change 1.8.6
*
* @param string $page_contents Page contents from the cache engine as raw HTML.
*/
public static function cache_page( $page_contents ) {
/**
* Filters the page contents before a static HTML file is created.
*
* @since 1.6.0
*
* @param string $page_contents Page contents from the cache engine as raw HTML.
*/
$page_contents = (string) apply_filters( 'cache_enabler_page_contents_before_store', $page_contents );
$page_contents = (string) apply_filters_deprecated( 'cache_enabler_before_store', array( $page_contents ), '1.6.0', 'cache_enabler_page_contents_before_store' );
self::create_cache_file( $page_contents );
}
/**
* Whether a cached page exists.
*
* @since 1.5.0
* @change 1.7.0
*
* @param string $cache_file File path to a cached page.
* @return bool True if the cached page exists and is readable, false otherwise.
*/
public static function cache_exists( $cache_file ) {
return is_readable( $cache_file );
}
/**
* Whether an existing cached page is expired.
*
* @since 1.5.0
* @change 1.8.0
*
* @param string $cache_file File path to an existing cached page.
* @return bool True if the cached page is expired, false otherwise.
*/
public static function cache_expired( $cache_file ) {
if ( ! Cache_Enabler_Engine::$settings['cache_expires'] || Cache_Enabler_Engine::$settings['cache_expiry_time'] === 0 ) {
return false;
}
$expires_seconds = 3600 * Cache_Enabler_Engine::$settings['cache_expiry_time'];
if ( ( filemtime( $cache_file ) + $expires_seconds ) <= time() ) {
return true;
}
return false;
}
/**
* Iterate over cache objects to perform actions and/or gather data.
*
* The $args parameter either takes an associative array of arguments or a
* template string. The templates 'pagination' and 'subpages' are mainly for
* backward compatibility but are also helpful shortcuts.
*
* Array of arguments for iterating over cache objects:
*
* @type int $clear Whether to clear the cache files iterated over.
* Default 0.
* @type int $expired Whether to only iterate over expired cache files.
* Default 0.
* @type int|string[]|array[] $hooks The cache hooks to fire.
* Default 0.
* @type int|string[]|array[] $keys The cache file versions to iterate over.
* Default 0.
* @type string $root The root path all cache files iterated over must have.
* Default ''.
* @type int|string[]|array[] $subpages The subpages to iterate over.
*
* Until this can be improved, see PR #237 for more information.
*
* @since 1.8.0
* @access private
*
* @param string $url URL to a cached page (with or without scheme, wildcard path, and query string).
* @param array|string $args See description.
* @return array Cache data.
*/
public static function cache_iterator( $url, $args = array() ) {
$cache = array(
'index' => array(),
'size' => 0,
);
if ( ! is_string( $url ) || empty( $url ) ) {
return $cache;
}
$url = esc_url_raw( $url, array( 'http', 'https' ) );
$cache_dir = self::get_cache_dir( $url );
if ( ! is_dir( $cache_dir ) ) {
return $cache;
}
$switched = false;
if ( is_multisite() && ! ms_is_switched() ) {
$blog_domain = (string) parse_url( $url, PHP_URL_HOST );
$blog_path = is_subdomain_install() ? '/' : Cache_Enabler::get_blog_path_from_url( $url );
$blog_id = get_blog_id_from_url( $blog_domain, $blog_path );
if ( $blog_id !== 0 ) {
$switched = Cache_Enabler::switch_to_blog( $blog_id, true );
}
}
$args = self::get_cache_iterator_args( $url, $args );
$recursive = ( $args['subpages'] === 1 || ! empty( $args['subpages']['include'] ) || isset( $args['subpages']['exclude'] ) );
$filter = ( $recursive && $args['subpages'] !== 1 ) ? $args['subpages'] : null;
$cache_objects = self::get_dir_objects( $cache_dir, $recursive, $filter );
$cache_keys_regex = self::get_cache_keys_regex( $args['keys'] );
foreach ( $cache_objects as $cache_object ) {
if ( is_file( $cache_object ) ) {
if ( $args['root'] && strpos( $cache_object, $args['root'] ) !== 0 ) {
// Skip to the next object because the file does not start with the provided root path.
continue;
}
$cache_object_name = basename( $cache_object );
if ( $cache_keys_regex && ! preg_match( $cache_keys_regex, $cache_object_name ) ) {
// Skip to the next object because the file name does not match the provided cache keys.
continue;
}
if ( $args['expired'] && ! self::cache_expired( $cache_object ) ) {
// Skip to the next object because the file is not expired.
continue;
}
$cache_object_dir = dirname( $cache_object );
$cache_object_size = (int) @filesize( $cache_object );
if ( $args['clear'] ) {
if ( ! @unlink( $cache_object ) ) {
// Skip to the next object because the file deletion failed.
continue;
}
// The cache size is negative when cleared.
$cache_object_size = -$cache_object_size;
// Remove the containing directory if empty along with any of its empty parents.
self::rmdir( $cache_object_dir, true );
}
if ( strpos( $cache_object_name, 'index' ) === false ) {
// Skip to the next object because the file is not a cache version and no longer
// needs to be handled, such as a hidden file.
continue;
}
if ( ! isset( $cache['index'][ $cache_object_dir ]['url'] ) ) {
$cache['index'][ $cache_object_dir ]['url'] = self::get_cache_url( $cache_object_dir );
$cache['index'][ $cache_object_dir ]['id'] = url_to_postid( $cache['index'][ $cache_object_dir ]['url'] );
}
$cache['index'][ $cache_object_dir ]['versions'][ $cache_object_name ] = $cache_object_size;
$cache['size'] += $cache_object_size;
}
}
// Sort the cache index by forward slashes from the lowest to highest.
uksort( $cache['index'], self::class . '::sort_dir_objects' );
if ( $args['clear'] ) {
self::fire_cache_cleared_hooks( $cache['index'], $args['hooks'] );
}
if ( $switched ) {
Cache_Enabler::restore_current_blog( true );
}
return $cache;
}
/**
* Get the cache size (deprecated).
*
* @since 1.0.0
* @deprecated 1.7.0
*/
public static function cache_size( $dir = null ) {
return self::get_cache_size( $dir );
}
/**
* Clear the cache (deprecated).
*
* @since 1.0.0
* @deprecated 1.8.0
*/
public static function clear_cache( $clear_url = null, $clear_type = 'page' ) {
Cache_Enabler::clear_page_cache_by_url( $clear_url, $clear_type );
}
/**
* Create the advanced-cache.php drop-in file.
*
* @since 1.8.0
* @change 1.8.6
*
* @return string|bool Path to the created file, false on failure.
*/
public static function create_advanced_cache_file() {
if ( ! is_writable( WP_CONTENT_DIR ) ) {
return false;
}
$advanced_cache_sample_file = CACHE_ENABLER_DIR . '/advanced-cache.php';
if ( ! is_readable( $advanced_cache_sample_file ) ) {
return false;
}
$advanced_cache_file = WP_CONTENT_DIR . '/advanced-cache.php';
$advanced_cache_file_contents = file_get_contents( $advanced_cache_sample_file );
$search = "realpath(__DIR__) . '/constants.php'";
$replace = "'" . CACHE_ENABLER_CONSTANTS_FILE . "'";
$advanced_cache_file_contents = str_replace( $search, $replace, $advanced_cache_file_contents );
$advanced_cache_file_created = file_put_contents( $advanced_cache_file, $advanced_cache_file_contents, LOCK_EX );
return ( $advanced_cache_file_created === false ) ? false : $advanced_cache_file;
}
/**
* Create a static HTML file.
*
* @since 1.5.0
* @change 1.8.6
*
* @param string $page_contents Page contents from the cache engine as raw HTML.
*/
private static function create_cache_file( $page_contents ) {
if ( ! is_string( $page_contents ) || strlen( $page_contents ) === 0 ) {
return;
}
$new_cache_file = self::get_cache_file();
$new_cache_file_dir = dirname( $new_cache_file );
$new_cache_file_name = basename( $new_cache_file );
if ( Cache_Enabler_Engine::$settings['minify_html'] ) {
$page_contents = self::minify_html( $page_contents );
}
$page_contents = $page_contents . self::get_cache_signature( $new_cache_file_name );
if ( strpos( $new_cache_file_name, 'webp' ) !== false ) {
$page_contents = self::converter( $page_contents );
}
if ( ! Cache_Enabler_Engine::is_cacheable( $page_contents ) ) {
return; // Filter, HTML minification, or WebP conversion failed.
}
switch ( substr( $new_cache_file_name, -2, 2 ) ) {
case 'br':
$page_contents = brotli_compress( $page_contents );
break;
case 'gz':
$page_contents = gzencode( $page_contents, 9 );
break;
}
if ( $page_contents === false ) {
return; // Compression failed.
}
if ( ! self::mkdir_p( $new_cache_file_dir ) ) {
return;
}
$new_cache_file_created = file_put_contents( $new_cache_file, $page_contents, LOCK_EX );
if ( $new_cache_file_created !== false ) {
$page_created_url = self::get_cache_url( $new_cache_file_dir );
$page_created_id = url_to_postid( $page_created_url );
$cache_created_index[ $new_cache_file_dir ]['url'] = $page_created_url;
$cache_created_index[ $new_cache_file_dir ]['id'] = $page_created_id;
$cache_created_index[ $new_cache_file_dir ]['versions'][ $new_cache_file_name ] = $new_cache_file_created;
/**
* Fires after the page cache has been created.
*
* @since 1.8.0
*
* @param string $page_created_url Full URL of the page created.
* @param int $page_created_id Post ID of the page created.
* @param array[] $cache_created_index Index of the cache created.
*/
do_action( 'cache_enabler_page_cache_created', $page_created_url, $page_created_id, $cache_created_index );
}
}
/**
* Create a settings file.
*
* @since 1.5.0
* @change 1.8.0
*
* @param array $settings Plugin settings from the database.
* @return string|bool Path to the created file, false on failure.
*/
public static function create_settings_file( $settings ) {
if ( ! is_array( $settings ) || ! function_exists( 'home_url' ) ) {
return false;
}
$new_settings_file = self::get_settings_file();
$new_settings_file_contents = '<?php' . PHP_EOL;
$new_settings_file_contents .= '/**' . PHP_EOL;
$new_settings_file_contents .= ' * The settings file for Cache Enabler.' . PHP_EOL;
$new_settings_file_contents .= ' *' . PHP_EOL;
$new_settings_file_contents .= ' * This file is automatically created, mirroring the plugin settings saved in the' . PHP_EOL;
$new_settings_file_contents .= ' * database. It is used to cache and deliver pages.' . PHP_EOL;
$new_settings_file_contents .= ' *' . PHP_EOL;
$new_settings_file_contents .= ' * @site ' . home_url() . PHP_EOL;
$new_settings_file_contents .= ' * @time ' . self::get_current_time() . PHP_EOL;
$new_settings_file_contents .= ' *' . PHP_EOL;
$new_settings_file_contents .= ' * @since 1.5.0' . PHP_EOL;
$new_settings_file_contents .= ' * @since 1.6.0 The `clear_site_cache_on_saved_post` setting was added.' . PHP_EOL;
$new_settings_file_contents .= ' * @since 1.6.0 The `clear_complete_cache_on_saved_post` setting was removed.' . PHP_EOL;
$new_settings_file_contents .= ' * @since 1.6.0 The `clear_site_cache_on_new_comment` setting was added.' . PHP_EOL;
$new_settings_file_contents .= ' * @since 1.6.0 The `clear_complete_cache_on_new_comment` setting was removed.' . PHP_EOL;
$new_settings_file_contents .= ' * @since 1.6.0 The `clear_site_cache_on_changed_plugin` setting was added.' . PHP_EOL;
$new_settings_file_contents .= ' * @since 1.6.0 The `clear_complete_cache_on_changed_plugin` setting was removed.' . PHP_EOL;
$new_settings_file_contents .= ' * @since 1.6.1 The `clear_site_cache_on_saved_comment` setting was added.' . PHP_EOL;
$new_settings_file_contents .= ' * @since 1.6.1 The `clear_site_cache_on_new_comment` setting was removed.' . PHP_EOL;
$new_settings_file_contents .= ' * @since 1.7.0 The `mobile_cache` setting was added.' . PHP_EOL;
$new_settings_file_contents .= ' * @since 1.8.0 The `use_trailing_slashes` setting was added.' . PHP_EOL;
$new_settings_file_contents .= ' * @since 1.8.0 The `permalink_structure` setting was deprecated.' . PHP_EOL;
$new_settings_file_contents .= ' */' . PHP_EOL;
$new_settings_file_contents .= PHP_EOL;
$new_settings_file_contents .= 'return ' . var_export( $settings, true ) . ';';
if ( ! self::mkdir_p( dirname( $new_settings_file ) ) ) {
return false;
}
$new_settings_file_created = file_put_contents( $new_settings_file, $new_settings_file_contents, LOCK_EX );
return ( $new_settings_file_created === false ) ? false : $new_settings_file;
}
/**
* Fire the cache cleared hooks.
*
* @since 1.8.0
*
* @param array[] $cache_cleared_index Index of the cache cleared.
* @param array[] $hooks Cache cleared hooks to 'include' and/or 'exclude' from being fired.
*/
private static function fire_cache_cleared_hooks( $cache_cleared_index, $hooks ) {
if ( empty( $cache_cleared_index ) || empty( $hooks ) ) {
return;
}
if ( isset( $hooks['include'] ) ) {
$hooks_to_fire = $hooks['include'];
} else {
$hooks_to_fire = array( 'cache_enabler_complete_cache_cleared', 'cache_enabler_site_cache_cleared', 'cache_enabler_page_cache_cleared' );
}
if ( ! empty( $hooks['exclude'] ) ) {
$hooks_to_fire = array_diff( $hooks_to_fire, $hooks['exclude'] );
}
if ( empty( $hooks_to_fire ) ) {
return;
}
if ( in_array( 'cache_enabler_page_cache_cleared', $hooks_to_fire, true ) ) {
foreach ( $cache_cleared_index as $cache_cleared_dir => $cache_cleared_data ) {
$page_cleared_url = $cache_cleared_data['url'];
$page_cleared_id = $cache_cleared_data['id'];
/**
* Fires after the page cache has been cleared.
*
* @since 1.6.0
* @since 1.8.0 The `$cache_cleared_index` parameter was added.
*
* @param string $page_cleared_url Full URL of the page cleared.
* @param int $page_cleared_id Post ID of the page cleared.
* @param array[] $cache_cleared_index Index of the cache cleared.
*/
do_action( 'cache_enabler_page_cache_cleared', $page_cleared_url, $page_cleared_id, $cache_cleared_index );
do_action( 'ce_action_cache_by_url_cleared', $page_cleared_url ); // Deprecated in 1.6.0.
}
}
if ( in_array( 'cache_enabler_site_cache_cleared', $hooks_to_fire, true ) && empty( Cache_Enabler::get_cache_index() ) ) {
$site_cleared_url = user_trailingslashit( home_url() );
$site_cleared_id = get_current_blog_id();
/**
* Fires after the site cache has been cleared.
*
* @since 1.6.0
* @since 1.8.0 The `$cache_cleared_index` parameter was added.
*
* @param string $site_cleared_url Full URL of the site cleared.
* @param int $site_cleared_id Post ID of the site cleared.
* @param array[] $cache_cleared_index Index of the cache cleared.
*/
do_action( 'cache_enabler_site_cache_cleared', $site_cleared_url, $site_cleared_id, $cache_cleared_index );
}
if ( in_array( 'cache_enabler_complete_cache_cleared', $hooks_to_fire, true ) && ! is_dir( CACHE_ENABLER_CACHE_DIR ) ) {
/**
* Fires after the complete cache has been cleared.
*
* @since 1.6.0
*/
do_action( 'cache_enabler_complete_cache_cleared' );
do_action( 'ce_action_cache_cleared' ); // Deprecated in 1.6.0.
}
}
/**
* Filters whether a file or directory should be included or excluded.
*
* @since 1.8.0
*
* @param string $dir_object File or directory path to filter (without trailing slash).
* @param array[] $filter File or directory path(s) to 'include' and/or 'exclude' (without trailing slash).
* @return bool True if directory object should be included, false if excluded.
*/
private static function filter_dir_object( $dir_object, $filter ) {
if ( isset( $filter['exclude'] ) ) {
$match = in_array( $dir_object, $filter['exclude'], true );
if ( $match ) {
return false;
}
}
if ( isset( $filter['include'] ) ) {
$match = in_array( $dir_object, $filter['include'], true );
if ( $match ) {
return true;
}
}
if ( ! isset( $match ) ) {
return true;
}
ksort( $filter ); // Sort the keys in alphabetical order to check for an exclusion first.
if ( is_dir( $dir_object ) ) {
$dir_object = $dir_object . '/'; // Append a trailing slash to prevent a false match.
}
foreach ( $filter as $filter_type => $filter_value ) {
if ( $filter_type !== 'include' && $filter_type !== 'exclude' ) {
continue;
}
foreach ( $filter_value as $filter_object ) {
// If a trailing asterisk exists remove it to allow a wildcard match.
if ( substr( $filter_object, -1, 1 ) === '*' ) {
$filter_object = substr( $filter_object, 0, -1 );
// Otherwise, maybe append a trailing slash to force a strict match.
} elseif ( is_dir( $dir_object ) ) {
$filter_object = $filter_object . '/';
}
if ( str_replace( $filter_object, '', $dir_object ) !== $dir_object ) {
switch ( $filter_type ) {
case 'include':
return true; // Past inclusion or present wildcard inclusion.
case 'exclude':
return false; // Present wildcard exclusion.
}
}
if ( strpos( $filter_object, $dir_object ) === 0 && $filter_type === 'include' ) {
return true; // Future strict or wildcard inclusion.
}
}
}
if ( isset( $filter['include'] ) ) {
return false; // Match not found.
}
return true;
}
/**
* Get the cache directory path for the current URL or from a given URL.
*
* This does not check whether the returned cache directory path exists. The
* untrailingslashit() function is not being used to remove the trailing slash
* because it is not available when the cache engine is started early.
*
* @since 1.8.0
*
* @param string $url (Optional) Full URL to a cached page (with or without wildcard path). Default
* is the current URL.
* @return string Cache directory path (without trailing slash), empty string if the URL is invalid.
*/
private static function get_cache_dir( $url = null ) {
if ( empty ( $url ) ) {
$url = 'http://' . Cache_Enabler_Engine::$request_headers['Host'] . Cache_Enabler_Engine::sanitize_server_input( $_SERVER['REQUEST_URI'], false );
}
$url_host = parse_url( $url, PHP_URL_HOST );
if ( ! is_string( $url_host ) ) {
return CACHE_ENABLER_CACHE_DIR;
}
$url_path = parse_url( $url, PHP_URL_PATH );
if ( ! is_string( $url_path ) ) {
$url_path = '';
} elseif ( substr( $url_path, -1, 1 ) === '*' ) {
$url_path = dirname( $url_path );
}
$cache_dir = sprintf(
'%s/%s%s',
CACHE_ENABLER_CACHE_DIR,
strtolower( $url_host ),
$url_path
);
$cache_dir = rtrim( $cache_dir, '/\\' );
return $cache_dir;
}
/**
* Get the cache iterator arguments.
*
* @since 1.8.0
*
* @global WP_Rewrite $wp_rewrite WordPress rewrite component.
*
* @param string $url (Optional) Full URL to a cached page (with or without wildcard path and query
* string). Default null.
* @param array|string $args (Optional) Cache iterator arguments or an arguments template. Default empty array.
* @return array Cache iterator arguments.
*/
private static function get_cache_iterator_args( $url = null, $args = array() ) {
$default_args = array(
'clear' => 0,
'expired' => 0,
'hooks' => 0,
'keys' => 0,
'root' => '',
'subpages' => 0,
);
if ( ! is_array( $args ) ) {
$args_template = $args;
$args = array(
'clear' => 1,
'hooks' => array( 'include' => 'cache_enabler_page_cache_cleared' ),
);
switch ( $args_template ) {
case 'pagination':
global $wp_rewrite;
$included_subpages[] = isset( $wp_rewrite->pagination_base ) ? $wp_rewrite->pagination_base : '';
$included_subpages[] = isset( $wp_rewrite->comments_pagination_base ) ? $wp_rewrite->comments_pagination_base . '-*' : '';
$args['subpages']['include'] = $included_subpages;
break;
case 'subpages':
$args['subpages'] = 1;
break;
default:
$args = array();
}
}
$url_path = (string) parse_url( $url, PHP_URL_PATH );
if ( substr( $url_path, -1, 1 ) === '*' ) {
$args['root'] = CACHE_ENABLER_CACHE_DIR . '/' . substr( (string) parse_url( $url, PHP_URL_HOST ) . $url_path, 0, -1 );
$args['subpages']['include'] = basename( $url_path );
}
// Merge query string arguments into the parameter arguments and then the default arguments.
wp_parse_str( (string) parse_url( $url, PHP_URL_QUERY ), $query_string_args );
$args = wp_parse_args( $query_string_args, $args );
$args = wp_parse_args( $args, $default_args );
$args = self::validate_cache_iterator_args( $args );
return $args;
}
/**
* Get the path to the cache file for the current request.
*
* This does not check whether the returned cache file exists. It sets the
* $cache_file property to prevent different paths being returned on the same
* request. This can occur because the $_SERVER['REQUEST_URI'] superglobal can be
* updated, like by another plugin, between trying to deliver a cached page and
* then actually creating it.
*
* @since 1.7.0
* @change 1.8.0
*
* @return string Path to the cache file.
*/
public static function get_cache_file() {
if ( ! empty( self::$cache_file ) ) {
return self::$cache_file;
}
self::$cache_file = sprintf(
'%s/%s',
self::get_cache_dir(),
self::get_cache_file_name()
);
return self::$cache_file;
}
/**
* Get the name of the cache file for the current request.
*
* @since 1.7.0
*
* @return string Name of the cache file.
*/
private static function get_cache_file_name() {
$cache_keys = self::get_cache_keys();
$cache_file_name = $cache_keys['scheme'] . 'index' . $cache_keys['device'] . $cache_keys['webp'] . '.html' . $cache_keys['compression'];
return $cache_file_name;
}
/**
* Get the cache keys from the request headers for the cache file name.
*
* This has some functionality copied from is_ssl() and wp_is_mobile().
*
* @since 1.7.0
* @change 1.8.0
*
* @return string[] An array of cache keys with names as the keys and keys as the values.
*/
private static function get_cache_keys() {
$cache_keys = array(
'scheme' => 'http-',
'device' => '',
'webp' => '',
'compression' => '',
);
if ( isset( $_SERVER['HTTPS'] ) && ( strtolower( $_SERVER['HTTPS'] ) === 'on' || $_SERVER['HTTPS'] == '1' ) ) {
$cache_keys['scheme'] = 'https-';
} elseif ( isset( $_SERVER['SERVER_PORT'] ) && $_SERVER['SERVER_PORT'] == '443' ) {
$cache_keys['scheme'] = 'https-';
} elseif ( Cache_Enabler_Engine::$request_headers['X-Forwarded-Proto'] === 'https'
|| Cache_Enabler_Engine::$request_headers['X-Forwarded-Scheme'] === 'https'
) {
$cache_keys['scheme'] = 'https-';
}
if ( Cache_Enabler_Engine::$settings['mobile_cache'] ) {
if ( strpos( Cache_Enabler_Engine::$request_headers['User-Agent'], 'Mobile' ) !== false
|| strpos( Cache_Enabler_Engine::$request_headers['User-Agent'], 'Android' ) !== false
|| strpos( Cache_Enabler_Engine::$request_headers['User-Agent'], 'Silk/' ) !== false
|| strpos( Cache_Enabler_Engine::$request_headers['User-Agent'], 'Kindle' ) !== false
|| strpos( Cache_Enabler_Engine::$request_headers['User-Agent'], 'BlackBerry' ) !== false
|| strpos( Cache_Enabler_Engine::$request_headers['User-Agent'], 'Opera Mini' ) !== false
|| strpos( Cache_Enabler_Engine::$request_headers['User-Agent'], 'Opera Mobi' ) !== false
) {
$cache_keys['device'] = '-mobile';
}
}
if ( Cache_Enabler_Engine::$settings['convert_image_urls_to_webp'] ) {
if ( strpos( Cache_Enabler_Engine::$request_headers['Accept'], 'image/webp' ) !== false ) {
$cache_keys['webp'] = '-webp';
}
}
if ( Cache_Enabler_Engine::$settings['compress_cache'] ) {
if ( function_exists( 'brotli_compress' )
&& $cache_keys['scheme'] === 'https-'
&& strpos( Cache_Enabler_Engine::$request_headers['Accept-Encoding'], 'br' ) !== false
) {
$cache_keys['compression'] = '.br';
} elseif ( strpos( Cache_Enabler_Engine::$request_headers['Accept-Encoding'], 'gzip' ) !== false ) {
$cache_keys['compression'] = '.gz';
}
}
return $cache_keys;
}
/**
* Get the cache keys regex for the cache iterator.
*
* This uses positive and negative lookaheads to create a regex that will be used
* to check the name of the cache file in the cache iterator, for example:
* * #^(?=.*https)(?=.*webp).+$#
* * #^(?=.*https)(?!.*webp).+$#
* * #^.+$#
*
* @since 1.8.0
*
* @param array[] $cache_keys Cache keys to 'include' and/or 'exclude'.
* @return string Cache keys regex, false on failure.
*/
private static function get_cache_keys_regex( $cache_keys ) {
if ( ! is_array( $cache_keys ) ) {
return false;
}
$cache_keys_regex = '#^';
foreach ( $cache_keys as $filter_type => $filter_value ) {
switch ( $filter_type ) {
case 'include':
$lookahead = '?=';
break;
case 'exclude':
$lookahead = '?!';
break;
default:
continue 2; // Skip to the next filter value.
}
foreach ( $filter_value as $cache_key ) {
$cache_keys_regex .= '(' . $lookahead . '.*' . preg_quote( $cache_key ) . ')';
}
}
$cache_keys_regex .= '.+$#';
return $cache_keys_regex;
}
/**
* Get the cache signature.
*
* This gets the HTML comment that is inserted at the bottom of a new cache file.
*
* @since 1.7.0
*
* @param string $cache_file_name Name of the new cache file.
* @return string HTML comment with the current time in HTTP-date format and the new cache file name.
*/
private static function get_cache_signature( $cache_file_name ) {
$cache_signature = sprintf(
'<!-- %s @ %s (%s) -->',
'Cache Enabler by KeyCDN',
self::get_current_time(),
$cache_file_name
);
return $cache_signature;
}
/**
* Get the cache size from the disk (deprecated).
*
* @since 1.7.0
* @deprecated 1.8.0
*/
public static function get_cache_size( $dir = null ) {
if ( empty( $dir ) ) {
$cache_size = Cache_Enabler::get_cache_size();
} else {
$url = self::get_cache_url( $dir );
$cache = self::cache_iterator( $url, array( 'subpages' => 1 ) );
$cache_size = $cache['size'];
}
return $cache_size;
}
/**
* Get the cache URL for a given directory path.
*
* This only checks if the given directory path is in the plugin cache directory. It
* does not check whether the URL returned is from a cache directory that exists.
*
* @since 1.8.0
*
* @param string $dir Directory path to a cached page.
* @return string Full cache URL (with trailing slash if set), empty string if the directory path
* is invalid.
*/
private static function get_cache_url( $dir ) {
if ( strpos( $dir, CACHE_ENABLER_CACHE_DIR ) !== 0 ) {
return '';
}
$cache_url = parse_url( home_url(), PHP_URL_SCHEME ) . '://' . str_replace( CACHE_ENABLER_CACHE_DIR . '/', '', $dir );
$cache_url = user_trailingslashit( $cache_url );
return $cache_url;
}
/**
* Get the path to the settings file for the current site.
*
* @since 1.4.0
* @change 1.8.0
*
* @param bool $fallback (Optional) Whether the fallback settings file should be returned. Default false.
* @return string Path to the settings file.
*/
private static function get_settings_file( $fallback = false ) {
$settings_file = sprintf(
'%s/%s',
CACHE_ENABLER_SETTINGS_DIR,
self::get_settings_file_name( $fallback )
);
return $settings_file;
}
/**
* Get the name of the settings file for the current site.
*
* This uses home_url() in the late cache engine start to get the settings file
* name when creating and deleting the settings file or when getting the plugin
* settings from the settings file. Otherwise, it finds the name of the settings
* file in the settings directory when the cache engine is started early.
*
* @since 1.5.5
* @change 1.8.0
*
* @param bool $fallback (Optional) Whether the fallback settings file name should be returned. Default false.
* @param bool $skip_blog_path (Optional) Whether the blog path should be included in the settings file name.
* Default false.
* @return string Name of the settings file.
*/
private static function get_settings_file_name( $fallback = false, $skip_blog_path = false ) {
$settings_file_name = '';
if ( function_exists( 'home_url' ) ) {
$settings_file_name = parse_url( home_url(), PHP_URL_HOST );
if ( is_multisite() && defined( 'SUBDOMAIN_INSTALL' ) && ! SUBDOMAIN_INSTALL ) {
$blog_path = Cache_Enabler::get_blog_path();
$settings_file_name .= ( ! empty( $blog_path ) ) ? '.' . trim( $blog_path, '/' ) : '';
}
$settings_file_name .= '.php';
} elseif ( is_dir( CACHE_ENABLER_SETTINGS_DIR ) ) {
if ( $fallback ) {
$settings_files = array_map( 'basename', self::get_dir_objects( CACHE_ENABLER_SETTINGS_DIR ) );
$settings_file_regex = '/\.php$/';
if ( is_multisite() ) {
$settings_file_regex = '/^' . strtolower( Cache_Enabler_Engine::$request_headers['Host'] );
$settings_file_regex = str_replace( '.', '\.', $settings_file_regex );
if ( defined( 'SUBDOMAIN_INSTALL' ) && ! SUBDOMAIN_INSTALL && ! $skip_blog_path ) {
$url_path = trim( parse_url( Cache_Enabler_Engine::sanitize_server_input( $_SERVER['REQUEST_URI'], false ), PHP_URL_PATH ), '/' );
if ( ! empty( $url_path ) ) {
$url_path_regex = str_replace( '/', '|', $url_path );
$url_path_regex = '\.(' . $url_path_regex . ')';
$settings_file_regex .= $url_path_regex;
}
}
$settings_file_regex .= '\.php$/';
}
$filtered_settings_files = preg_grep( $settings_file_regex, $settings_files );
if ( ! empty( $filtered_settings_files ) ) {
$settings_file_name = current( $filtered_settings_files );
} elseif ( is_multisite() && defined( 'SUBDOMAIN_INSTALL' ) && ! SUBDOMAIN_INSTALL && ! $skip_blog_path ) {
$fallback = true;
$skip_blog_path = true;
$settings_file_name = self::get_settings_file_name( $fallback, $skip_blog_path );
}
} else {
$settings_file_name = strtolower( Cache_Enabler_Engine::$request_headers['Host'] );
if ( is_multisite() && defined( 'SUBDOMAIN_INSTALL' ) && ! SUBDOMAIN_INSTALL && ! $skip_blog_path ) {
$url_path = Cache_Enabler_Engine::sanitize_server_input( $_SERVER['REQUEST_URI'], false );
$url_path_pieces = explode( '/', $url_path, 3 );
$blog_path = $url_path_pieces[1];
if ( ! empty( $blog_path ) ) {
$settings_file_name .= '.' . $blog_path;
}
$settings_file_name .= '.php';
// Check if the main site in a subdirectory network.
if ( ! is_file( CACHE_ENABLER_SETTINGS_DIR . '/' . $settings_file_name ) ) {
$fallback = false;
$skip_blog_path = true;
$settings_file_name = self::get_settings_file_name( $fallback, $skip_blog_path );
}
}
$settings_file_name .= ( strpos( $settings_file_name, '.php' ) === false ) ? '.php' : '';
}
}
return $settings_file_name;
}
/**
* Get the plugin settings from the settings file for the current site.
*
* This will create the settings file if it does not exist and the cache engine
* was started late. If that occurs, the settings from the new settings file will
* be returned. Before it is created, checking if the settings file exists after
* retrieving the database settings is done in case an update occurred, which
* would have resulted in a new settings file being created.
*
* This can update the disk and backend requirements and then clear the site cache
* if the settings are outdated. If that occurs, a new settings file will be
* created and an empty array returned.
*
* @since 1.5.0
* @since 1.8.0 The `$update` parameter was added.
* @change 1.8.7
*
* @param bool $update Whether to update the disk and backend requirements if the settings are
* outdated. Default true.
* @return array Plugin settings from the settings file, empty array when outdated or on failure.
*/
public static function get_settings( $update = true ) {
$settings = array();
$settings_file = self::get_settings_file();
if ( is_file( $settings_file ) ) {
$settings = include $settings_file;
} else {
$fallback = true;
$settings_file = self::get_settings_file( $fallback );
if ( is_file( $settings_file ) ) {
$settings = include $settings_file;
}
}
$outdated_settings = ( ! empty( $settings ) && ( ! defined( 'CACHE_ENABLER_VERSION' ) || ! isset( $settings['version'] ) || $settings['version'] !== CACHE_ENABLER_VERSION ) );
if ( $outdated_settings ) {
$settings = array();
}
if ( empty( $settings ) && class_exists( 'Cache_Enabler' ) ) {
if ( $outdated_settings ) {
if ( $update ) {
Cache_Enabler::update();
}
} else {
$_settings = Cache_Enabler::get_settings();
$settings_file = self::get_settings_file();
if ( is_file( $settings_file ) ) {
$settings = include $settings_file;
} else {
$settings_file = self::create_settings_file( $_settings );
if ( $settings_file !== false ) {
$settings = include $settings_file;
}
}
}
}
return $settings;
}
/**
* Get the files and directories inside of a given directory.
*
* @since 1.4.7
* @since 1.8.0 The `$recursive` parameter was added.
* @since 1.8.0 The `$filter` parameter was added.
* @change 1.8.0
*
* @param string $dir Directory path to scan (without trailing slash).
* @param bool $recursive (Optional) Whether to recursively include directory objects in nested
* directories. Default false.
* @param array[] $filter (Optional) Directory paths relative to $dir (without leading and/or trailing
* slashes) to 'include' and/or 'exclude'. Default null.
* @return string[] File and directory paths to objects found, empty array if the directory path is invalid.
*/
private static function get_dir_objects( $dir, $recursive = false, $filter = null ) {
$dir_objects = array();
if ( ! is_dir( $dir ) ) {
return $dir_objects;
}
$dir_object_names = scandir( $dir ); // The sorted order is alphabetical in ascending order.
if ( is_array( $filter ) && empty( $filter['full_path'] ) ) {
$filter['full_path'] = 1;
foreach ( $filter as $filter_type => &$filter_value ) {
if ( $filter_type === 'include' || $filter_type === 'exclude' ) {
foreach ( $filter_value as &$filter_object ) {
$filter_object = $dir . '/' . $filter_object;
}
}
}
}
foreach ( $dir_object_names as $dir_object_name ) {
if ( $dir_object_name === '.' || $dir_object_name === '..' ) {
continue; // Skip object because it is the current or parent folder link.
}
$dir_object = $dir . '/' . $dir_object_name;
if ( is_dir( $dir_object ) ) {
if ( ! empty( $filter['full_path'] ) && ! self::filter_dir_object( $dir_object, $filter ) ) {
continue; // Skip object because it is excluded.
}
if ( $recursive ) {
$dir_objects = array_merge( $dir_objects, self::get_dir_objects( $dir_object, $recursive, $filter ) );
}
}
$dir_objects[] = $dir_object;
}
return $dir_objects;
}
/**
* Get the site objects (deprecated).
*
* @since 1.6.0
* @deprecated 1.8.0
*/
public static function get_site_objects( $site_url ) {
$site_objects = array();
$dir = self::get_cache_dir( $site_url );
if ( ! is_dir( $dir ) ) {
return $site_objects;
}
$site_objects = array_map( 'basename', self::get_dir_objects( $dir ) );
// Maybe filter the site objects.
if ( is_multisite() && ! is_subdomain_install() ) {
$blog_path = Cache_Enabler::get_blog_path();
$blog_paths = Cache_Enabler::get_blog_paths();
// Check if the main site in a subdirectory network.
if ( ! in_array( $blog_path, $blog_paths, true ) ) {
foreach ( $site_objects as $key => $site_object ) {
// Delete the site object if it does not belong to the main site.
if ( in_array( '/' . $site_object . '/', $blog_paths, true ) ) {
unset( $site_objects[ $key ] );
}
}
}
}
return $site_objects;
}
/**
* Get the current time.
*
* @since 1.7.0
*
* @return string Current time in HTTP-date format.
*/
private static function get_current_time() {
$current_time = current_time( 'D, d M Y H:i:s', true ) . ' GMT';
return $current_time;
}
/**
* Get the image path from an image URL.
*
* This does not check whether the returned image exists.
*
* @since 1.4.8
* @change 1.8.0
*
* @param string $image_url Full or relative URL maybe with an intrinsic width or density descriptor.
* @return string File path to the image.
*/
private static function get_image_path( $image_url ) {
// In case there is an intrinsic width or density descriptor.
$image_pieces = explode( ' ', $image_url );
$image_url = $image_pieces[0];
// In case installation is in a subdirectory.
$image_url_path = ltrim( parse_url( $image_url, PHP_URL_PATH ), '/' );
$installation_dir = ltrim( parse_url( site_url( '/' ), PHP_URL_PATH ), '/' );
$image_path = str_replace( $installation_dir, '', ABSPATH ) . $image_url_path;
return $image_path;
}
/**
* Get the current WordPress filesystem instance.
*
* This will initialize the WordPress filesystem if it has not yet been and will
* cache the result afterward.
*
* @since 1.7.0
* @change 1.7.1
*
* @throws \RuntimeException If the WordPress filesystem could not be initialized.
*
* @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
*
* @return WP_Filesystem_Base WordPress filesystem.
*/
public static function get_filesystem() {
global $wp_filesystem;
if ( $wp_filesystem instanceof WP_Filesystem_Base ) {
return $wp_filesystem;
}
try {
require_once ABSPATH . 'wp-admin/includes/file.php';
$filesystem = WP_Filesystem();
if ( $filesystem === null ) {
throw new \RuntimeException( 'The provided filesystem method is unavailable.' );
}
if ( $filesystem === false ) {
if ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) {
throw new \RuntimeException(
$wp_filesystem->errors->get_error_message(),
is_numeric( $wp_filesystem->errors->get_error_code() ) ? (int) $wp_filesystem->errors->get_error_code() : 0
);
}
throw new \RuntimeException( 'Unspecified failure.' );
}
if ( ! is_object( $wp_filesystem ) || ! $wp_filesystem instanceof WP_Filesystem_Base ) {
throw new \RuntimeException( '$wp_filesystem is not an instance of WP_Filesystem_Base.' );
}
} catch ( \Exception $e ) {
throw new \RuntimeException(
sprintf( 'There was an error initializing the WP_Filesystem class: %1$s', $e->getMessage() ),
$e->getCode(),
$e
);
}
return $wp_filesystem;
}
/**
* Make a directory recursively based on the directory path.
*
* This assumes that the directory (and its parent) should have 755 permissions,
* and will attempt to update any existing directories accordingly.
*
* @since 1.7.0
* @change 1.8.12
*
* @param string $dir Directory path to create.
* @return bool True if the directory either already exists or was created *and* has the
* correct permissions, false otherwise.
*/
private static function mkdir_p( $dir ) {
/**
* Filters the mode assigned to directories on creation.
*
* @since 1.7.2
*
* @param int $mode Mode that defines the access permissions for the created directory. The mode
* must be an octal number, which means it should have a leading zero. Default is 0755.
*/
$mode_octal = (int) apply_filters( 'cache_enabler_mkdir_mode', 0755 );
$mode_string = decoct( $mode_octal ); // Get the last three digits (e.g. '755').
$parent_dir = dirname( $dir );
$fs = self::get_filesystem();
if ( $fs->is_dir( $dir ) && $fs->getchmod( $dir ) === $mode_string && $fs->getchmod( $parent_dir ) === $mode_string ) {
return true;
}
// Directory validation
$valid = false;
if ( ! empty( CACHE_ENABLER_CACHE_DIR ) && strpos( $dir, CACHE_ENABLER_CACHE_DIR ) === 0 ) {
$valid = true;
}
if ( ! empty( CACHE_ENABLER_SETTINGS_DIR ) && strpos( $dir, CACHE_ENABLER_SETTINGS_DIR ) === 0 ) {
$valid = true;
}
if ( ! $valid || strpos( $dir, '../' ) !== false ) {
return false;
}
if ( ! wp_mkdir_p( $dir ) ) {
return false;
}
return true;
}
/**
* Remove an empty directory based on the directory path.
*
* This is a wrapper for rmdir() that can delete empty parent directories and will
* call clearstatcache() when necessary. It suppresses errors on failure.
*
* @since 1.8.0
*
* @param string $dir Directory path to remove.
* @param bool $parents (Optional) Whether empty parent directories should also be removed. Default false.
* @return array[]|bool An array of removed directories with paths as the keys and objects as the
* values. There are no directory objects because a directory has to be empty to
* be removed, which is why it will always be an empty array. False if no
* directories were removed.
*/
private static function rmdir( $dir, $parents = false ) {
$removed_dir = @rmdir( $dir );
clearstatcache();
if ( $removed_dir ) {
$removed_dir = array( $dir => array() );
if ( $parents ) {
$parent_dir = dirname( $dir );
while ( @rmdir( $parent_dir ) ) {
clearstatcache();
$removed_dir[ $parent_dir ] = array();
$parent_dir = dirname( $parent_dir );
}
}
}
return $removed_dir;
}
/**
* Set or unset the WP_CACHE constant in the wp-config.php file.
*
* This has some functionality copied from wp-load.php when trying to find the
* wp-config.php file. It will only set the WP_CACHE constant if the wp-config.php
* file is considered to be default and it is not already set. It will only unset
* the WP_CACHE constant if previously set by the plugin.
*
* @since 1.5.0
* @since 1.8.7 The return value was updated.
* @change 1.8.7
*
* @param bool $set (Optional) True to set the WP_CACHE constant, false to unset. Default true.
* @return string|bool Path to the updated wp-config.php file, false otherwise.
*/
private static function set_wp_cache_constant( $set = true ) {
if ( file_exists( ABSPATH . 'wp-config.php' ) ) {
// The config file resides in ABSPATH.
$wp_config_file = ABSPATH . 'wp-config.php';
} elseif ( @file_exists( dirname( ABSPATH ) . '/wp-config.php' ) && ! @file_exists( dirname( ABSPATH ) . '/wp-settings.php' ) ) {
// The config file resides one level above ABSPATH but is not part of another installation.
$wp_config_file = dirname( ABSPATH ) . '/wp-config.php';
} else {
// The config file could not be found.
return false;
}
if ( ! is_writable( $wp_config_file ) ) {
return false;
}
$wp_config_file_contents = file_get_contents( $wp_config_file );
if ( ! is_string( $wp_config_file_contents ) ) {
return false;
}
if ( $set ) {
$default_wp_config_file = ( strpos( $wp_config_file_contents, '/** Sets up WordPress vars and included files. */' ) !== false );
if ( ! $default_wp_config_file ) {
return false;
}
$found_wp_cache_constant = preg_match( '#define\s*\(\s*[\'\"]WP_CACHE[\'\"]\s*,.+\);#', $wp_config_file_contents );
if ( $found_wp_cache_constant ) {
return false;
}
$new_wp_config_lines = '/** Enables page caching for Cache Enabler. */' . PHP_EOL;
$new_wp_config_lines .= "if ( ! defined( 'WP_CACHE' ) ) {" . PHP_EOL;
$new_wp_config_lines .= "\tdefine( 'WP_CACHE', true );" . PHP_EOL;
$new_wp_config_lines .= '}' . PHP_EOL;
$new_wp_config_lines .= PHP_EOL;
$new_wp_config_file_contents = preg_replace( '#(/\*\* Sets up WordPress vars and included files\. \*/)#', $new_wp_config_lines . '$1', $wp_config_file_contents );
} else { // Unset.
if ( strpos( $wp_config_file_contents, '/** Enables page caching for Cache Enabler. */' ) !== false ) {
$new_wp_config_file_contents = preg_replace( '#/\*\* Enables page caching for Cache Enabler\. \*/' . PHP_EOL . '.+' . PHP_EOL . '.+' . PHP_EOL . '\}' . PHP_EOL . PHP_EOL . '#', '', $wp_config_file_contents );
} elseif ( strpos( $wp_config_file_contents, '// Added by Cache Enabler' ) !== false ) { // < 1.5.0
$new_wp_config_file_contents = preg_replace( '#.+Added by Cache Enabler\r\n#', '', $wp_config_file_contents );
} else {
return false; // Not previously set by the plugin.
}
}
if ( ! is_string( $new_wp_config_file_contents ) || empty( $new_wp_config_file_contents ) ) {
return false;
}
$wp_config_file_updated = file_put_contents( $wp_config_file, $new_wp_config_file_contents, LOCK_EX );
return ( $wp_config_file_updated === false ) ? false : $wp_config_file;
}
/**
* Sort file and directory paths by the number of forward slashes.
*
* This sorts paths by the lowest amount of forward slashes to the highest.
*
* @since 1.8.0
*
* @param string $a File or directory path to compare in sort.
* @param string $b File or directory path to compare in sort.
* @return int 1 if $a has more slashes than $b, 0 if equal, and -1 if less.
*/
private static function sort_dir_objects( $a, $b ) {
$a = substr_count( $a, '/' );
$b = substr_count( $b, '/' );
if ( $a === $b ) {
return 0;
}
return ( $a > $b ) ? 1 : -1;
}
/**
* Convert the page contents.
*
* This handles converting inline image URLs for the WebP cache version.
*
* @since 1.7.0
* @change 1.8.6
*
* @param string $page_contents Page contents from the cache engine as raw HTML.
* @return string Page contents after maybe being converted.
*/
private static function converter( $page_contents ) {
/**
* Filters the HTML attributes to convert during the WebP conversion.
*
* @since 1.6.1
*
* @param string[] $attributes HTML attributes to convert during the WebP conversion. Default are 'src',
* 'srcset', and 'data-*'.
*/
$attributes = (array) apply_filters( 'cache_enabler_convert_webp_attributes', array( 'src', 'srcset', 'data-[^=]+' ) );
$attributes_regex = implode( '|', $attributes );
/**
* Filters whether inline image URLs with query strings should be ignored during the WebP conversion.
*
* @since 1.6.1
*
* @param bool $ignore_query_strings True if inline image URLs with query strings should be ignored during the WebP
* conversion, false if not. Default true.
*/
if ( apply_filters( 'cache_enabler_convert_webp_ignore_query_strings', true ) ) {
$image_urls_regex = '#(?:(?:(' . $attributes_regex . ')\s*=|(url)\()\s*[\'\"]?\s*)\K(?:[^\?\"\'\s>]+)(?:\.jpe?g|\.png)(?:\s\d+[wx][^\"\'>]*)?(?=\/?[\"\'\s\)>])(?=[^<{]*(?:\)[^<{]*\}|>))#i';
} else {
$image_urls_regex = '#(?:(?:(' . $attributes_regex . ')\s*=|(url)\()\s*[\'\"]?\s*)\K(?:[^\"\'\s>]+)(?:\.jpe?g|\.png)(?:\s\d+[wx][^\"\'>]*)?(?=\/?[\?\"\'\s\)>])(?=[^<{]*(?:\)[^<{]*\}|>))#i';
}
/**
* Filters the page contents after the inline image URLs were maybe converted to WebP.
*
* @since 1.6.0
*
* @param string $page_contents Page contents from the cache engine as raw HTML.
*/
$converted_page_contents = (string) apply_filters( 'cache_enabler_page_contents_after_webp_conversion', preg_replace_callback( $image_urls_regex, self::class . '::convert_webp', $page_contents ) );
$converted_page_contents = (string) apply_filters_deprecated( 'cache_enabler_disk_webp_converted_data', array( $converted_page_contents ), '1.6.0', 'cache_enabler_page_contents_after_webp_conversion' );
return $converted_page_contents;
}
/**
* Convert image URL(s) to WebP.
*
* @since 1.5.0
* @change 1.8.0
*
* @param string[] $matches Pattern matches from parsed page contents.
* @return string The image URL(s) after maybe being converted to WebP.
*/
private static function convert_webp( $matches ) {
$full_match = $matches[0];
$image_extension_regex = '/(\.jpe?g|\.png)/i';
$image_found = preg_match( $image_extension_regex, $full_match );
if ( ! $image_found ) {
return $full_match;
}
$image_urls = explode( ',', $full_match );
foreach ( $image_urls as &$image_url ) {
$image_url = trim( $image_url, ' ' );
$image_url_webp = preg_replace( $image_extension_regex, '$1.webp', $image_url ); // Append the .webp extension.
$image_path_webp = self::get_image_path( $image_url_webp );
if ( is_file( $image_path_webp ) ) {
$image_url = $image_url_webp;
} else {
$image_url_webp = preg_replace( $image_extension_regex, '', $image_url_webp ); // Remove the default extension.
$image_path_webp = self::get_image_path( $image_url_webp );
if ( is_file( $image_path_webp ) ) {
$image_url = $image_url_webp;
}
}
}
$conversion = implode( ', ', $image_urls );
return $conversion;
}
/**
* Minify HTML.
*
* This removes HTML, CSS, and JavaScript comments. Whitespaces of any size are
* replaced with a single space.
*
* @since 1.5.0
* @change 1.7.0
*
* @param string $html Page contents from the cache engine as raw HTML.
* @return string Page contents after maybe being minified.
*/
private static function minify_html( $html ) {
if ( strlen( $html ) > 700000 ) {
return $html;
}
/**
* Filters the HTML tags to ignore during HTML minification.
*
* @since 1.6.0
*
* @param string[] $ignore_tags The names of HTML tags to ignore. Default are 'textarea', 'pre', and 'code'.
*/
$ignore_tags = (array) apply_filters( 'cache_enabler_minify_html_ignore_tags', array( 'textarea', 'pre', 'code' ) );
$ignore_tags = (array) apply_filters_deprecated( 'cache_minify_ignore_tags', array( $ignore_tags ), '1.6.0', 'cache_enabler_minify_html_ignore_tags' );
if ( ! Cache_Enabler_Engine::$settings['minify_inline_css_js'] ) {
array_push( $ignore_tags, 'style', 'script' );
}
if ( ! $ignore_tags ) {
return $html; // At least one HTML tag is required.
}
$ignore_tags_regex = implode( '|', $ignore_tags );
// Remove HTML comments.
$minified_html = preg_replace( '#<!--[^\[><].*?-->#s', '', $html );
if ( Cache_Enabler_Engine::$settings['minify_inline_css_js'] ) {
// Remove CSS and JavaScript comments.
$minified_html = preg_replace(
'#/\*(?!!)[\s\S]*?\*/|(?:^[ \t]*)//.*$|((?<!\()[ \t>;,{}[\]])//[^;\n]*$#m',
'$1',
$minified_html
);
}
// Replace whitespaces of any size with a single space.
$minified_html = preg_replace(
'#(?>[^\S ]\s*|\s{2,})(?=[^<]*+(?:<(?!/?(?:' . $ignore_tags_regex . ')\b)[^<]*+)*+(?:<(?>' . $ignore_tags_regex . ')\b|\z))#ix',
' ',
$minified_html
);
if ( strlen( $minified_html ) <= 1 ) {
return $html; // HTML minification failed.
}
return $minified_html;
}
/**
* Delete a settings file based on a given settings file path.
*
* This will try to remove the settings file directory and any of its empty parent
* directories. It suppresses errors on failure.
*
* @since 1.5.0
* @since 1.8.0 The `$settings_file` parameter was added.
* @change 1.8.0
*
* @param string (Optional) Path to the settings file. Default is the settings file for the
* current site.
*/
public static function delete_settings_file( $settings_file = null ) {
if ( empty( $settings_file ) ) {
$settings_file = self::get_settings_file();
}
if ( @unlink( $settings_file ) ) {
self::rmdir( CACHE_ENABLER_SETTINGS_DIR, true );
}
}
/**
* Delete an asset (deprecated).
*
* @since 1.0.0
* @deprecated 1.5.0
*/
public static function delete_asset( $url ) {
Cache_Enabler::clear_page_cache_by_url( $url, 'subpages' );
}
/**
* Validate the cache iterator arguments.
*
* @since 1.8.0
*
* @param array $args Cache iterator arguments.
* @return array Validated cache iterator arguments.
*/
private static function validate_cache_iterator_args( $args ) {
$validated_args = array();
foreach ( $args as $arg_name => $arg_value ) {
if ( $arg_name === 'root' ) {
$validated_args[ $arg_name ] = (string) $arg_value;
} elseif ( is_array( $arg_value ) ) {
foreach ( $arg_value as $filter_type => $filter_value ) {
if ( is_string( $filter_value ) ) {
$filter_value = ( substr_count( $filter_value, '|' ) > 0 ) ? explode( '|', $filter_value ) : explode( ',', $filter_value );
} elseif ( ! is_array( $filter_value ) ) {
$filter_value = array(); // The type is not being converting to avoid unwanted values.
}
foreach ( $filter_value as $filter_value_key => &$filter_value_item ) {
$filter_value_item = trim( $filter_value_item, '/- ' );
if ( empty( $filter_value_item ) ) {
unset( $filter_value[ $filter_value_key ] );
}
}
if ( $filter_type !== 'include' || $filter_type !== 'exclude' ) {
unset( $arg_value[ $filter_type ] );
if ( $filter_type === 0 || $filter_type === 'i' ) {
$filter_type = 'include';
} elseif ( $filter_type === 1 || $filter_type === 'e' ) {
$filter_type = 'exclude';
}
}
$arg_value[ $filter_type ] = $filter_value;
}
$validated_args[ $arg_name ] = $arg_value;
} else {
$validated_args[ $arg_name ] = (int) $arg_value;
}
}
return $validated_args;
}
}