File "class-disk-cache.php"

Full Path: /home/theinspectionboy/public_html/suffolk/includes-20250622113618/class-disk-cache.php
File size: 26.8 KB
MIME-type: text/x-php
Charset: utf-8

<?php
/**
 * Class and methods for disk caching engine.
 *
 * @link https://ewww.io/swis/
 * @package SWIS_Performance
 */

namespace SWIS;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Disk Caching Engine.
 */
final class Disk_Cache {

	/**
	 * The cache directory.
	 *
	 * @var string $cache_dir
	 */
	public static $cache_dir = WP_CONTENT_DIR . '/swis/cache/html';

	/**
	 * The settings directory.
	 *
	 * @var string $settings_dir
	 */
	public static $settings_dir = WP_CONTENT_DIR . '/swis/cache/settings';

	/**
	 * List of directories that have been cleared.
	 *
	 * @var array
	 */
	private static $dir_cleared = array();

	/**
	 * Configure system files.
	 */
	public static function setup() {
		self::debug_message( __METHOD__ );
		// Add advanced-cache.php drop-in.
		copy( dirname( SWIS_PLUGIN_FILE ) . '/assets/advanced-cache.php', WP_CONTENT_DIR . '/advanced-cache.php' );

		self::set_wp_cache_constant();
	}

	/**
	 * Clean system files.
	 */
	public static function clean() {
		self::debug_message( __METHOD__ );
		// Delete settings file.
		self::delete_settings_file();

		// Check if settings directory exists.
		if ( ! is_dir( self::$settings_dir ) ) {
			// Delete old advanced cache settings file(s).
			array_map( 'unlink', glob( WP_CONTENT_DIR . '/swis/cache/advcache-*.json' ) );
			// Delete advanced-cache.php drop-in file.
			if ( is_file( WP_CONTENT_DIR . '/advanced-cache.php' ) && is_writable( WP_CONTENT_DIR . '/advanced-cache.php' ) ) {
				unlink( WP_CONTENT_DIR . '/advanced-cache.php' );
			}
			// Unset WP_CACHE constant in config file if set by SWIS.
			self::set_wp_cache_constant( false );
		}
	}

	/**
	 * Adds information to the in-memory debug log (wrapper for static class).
	 *
	 * @param string $message Debug information to add to the log.
	 */
	public static function debug_message( $message ) {
		if ( function_exists( 'swis' ) && class_exists( '\SWIS\Cache' ) ) {
			swis()->cache->debug_message( $message );
		}
	}

	/**
	 * Check if file exists, and that it is local rather than using a protocol like http:// or phar://
	 *
	 * @param string $file The path of the file to check.
	 * @return bool True if the file exists and is local, false otherwise.
	 */
	public static function is_file( $file ) {
		if ( false !== strpos( $file, '://' ) ) {
			return false;
		}
		if ( false !== strpos( $file, 'phar://' ) ) {
			return false;
		}
		$file   = realpath( $file );
		$wp_dir = realpath( ABSPATH );
		if ( function_exists( 'wp_get_upload_dir' ) ) {
			$upload_dir = wp_get_upload_dir();
			$upload_dir = realpath( $upload_dir['basedir'] );
		}

		$content_dir = realpath( WP_CONTENT_DIR );
		if ( empty( $content_dir ) ) {
			$content_dir = $wp_dir;
		}
		if ( empty( $upload_dir ) ) {
			$upload_dir = $content_dir;
		}
		if ( defined( 'SWIS_PLUGIN_FILE' ) ) {
			$plugin_dir = dirname( SWIS_PLUGIN_FILE );
		} else {
			$plugin_dir = ( ( defined( 'WP_PLUGIN_DIR' ) ) ? WP_PLUGIN_DIR : WP_CONTENT_DIR . '/plugins' ) . '/swis-performance';
		}
		if (
			false === strpos( $file, $upload_dir ) &&
			false === strpos( $file, $content_dir ) &&
			false === strpos( $file, $wp_dir ) &&
			false === strpos( $file, $plugin_dir )
		) {
			return false;
		}
		return is_file( $file );
	}

	/**
	 * A wrapper for PHP's parse_url, prepending assumed scheme for network path
	 * URLs. PHP versions 5.4.6 and earlier do not correctly parse without scheme.
	 *
	 * @param string  $url The URL to parse.
	 * @param integer $component Retrieve specific URL component.
	 * @return mixed Result of parse_url.
	 */
	public static function parse_url( $url, $component = -1 ) {
		if ( 0 === strpos( $url, '//' ) ) {
			$url = ( is_ssl() ? 'https:' : 'http:' ) . $url;
		}
		if ( false === strpos( $url, 'http' ) && '/' !== substr( $url, 0, 1 ) ) {
			$url = ( is_ssl() ? 'https://' : 'http://' ) . $url;
		}
		// Because encoded ampersands in the filename break things.
		$url = str_replace( '&#038;', '&', $url );
		return parse_url( $url, $component );
	}

	/**
	 * Store a cached page.
	 *
	 * @param string $page_contents The content of the page.
	 */
	public static function cache_page( $page_contents ) {
		self::debug_message( __METHOD__ );

		$page_contents = apply_filters( 'swis_cache_page_contents_before_store', $page_contents );

		// Save the data to disk.
		self::create_cache_file( $page_contents );
	}

	/**
	 * Check if a cached page exists.
	 *
	 * @param string $cache_file File path to potential cache page.
	 * @return bool True if page exists, false otherwise.
	 */
	public static function cache_exists( $cache_file ) {
		return is_readable( $cache_file );
	}

	/**
	 * Check if asset is expired.
	 *
	 * @param string $cache_file File path to existing cache page.
	 * @return bool True if expired, false otherwise.
	 */
	public static function cache_expired( $cache_file ) {
		// Check if an expiry time is set.
		if ( 0 === Cache_Engine::$settings['expires'] ) {
			return false;
		}

		$expires_seconds = 3600 * Cache_Engine::$settings['expires'];

		// Check if the asset is beyond the expiration time.
		if ( ( filemtime( $cache_file ) + $expires_seconds ) <= time() ) {
			self::debug_message( 'cache expired' );
			return true;
		}

		return false;
	}

	/**
	 * Clear the whole cache.
	 *
	 * @param string $clear_url Full URL to potentially cached page.
	 * @param string $clear_type Clear the `pagination` or the entire `dir` instead of only the cached `page`.
	 */
	public static function clear_cache( $clear_url = null, $clear_type = 'page' ) {
		self::debug_message( __METHOD__ );
		// Check if complete cache should be cleared.
		if ( empty( $clear_url ) ) {
			self::debug_message( 'no URL to clear' );
			return;
		}

		self::debug_message( "clearing cache for $clear_url" );
		// Get cache directory for URL.
		$dir = self::get_cache_file_dir( $clear_url );

		if ( ! is_dir( $dir ) ) {
			self::debug_message( "no dir found for $clear_url" );
			return;
		}

		if ( 'dir' === $clear_type ) {
			$clear_type = 'subpages';
		}

		self::debug_message( microtime( true ) );
		// Check if pagination needs to be cleared.
		if ( 'subpages' === $clear_type ) {
			self::clear_dir( $dir );
		} else {
			self::debug_message( "clearing all files in $dir" );
			// Delete all cache files in directory.
			$skip_child_dir = true;
			self::clear_dir( $dir, $skip_child_dir );

			if ( 'pagination' === $clear_type ) {
				// Get pagination base.
				$pagination_base = $GLOBALS['wp_rewrite']->pagination_base;
				if ( strlen( $pagination_base ) > 0 ) {
					$pagination_dir = trailingslashit( $dir ) . $pagination_base;
					// Clear pagination page(s) cache.
					self::clear_dir( $pagination_dir );
				}
			}
		}

		foreach ( self::$dir_cleared as $dir => $dir_objects ) {
			if ( false !== strpos( $dir, self::$cache_dir ) ) {
				self::debug_message( "checking for hooks to fire with $dir" );
				if ( swis()->cache->fire_page_cache_cleared_hook ) {
					self::debug_message( 'page cache cleared hook is in play' );
					if ( ! empty( preg_grep( '/index/', $dir_objects ) ) ) {
						// Run the page cache cleared hook.
						$page_cleared_url = parse_url( home_url(), PHP_URL_SCHEME ) . '://' . str_replace( self::$cache_dir . '/', '', $dir );
						$page_cleared_id  = url_to_postid( $page_cleared_url );
						do_action( 'swis_cache_by_url_cleared', $page_cleared_url, $page_cleared_id );
					}
				} else {
					// Full cache cleared hook.
					if ( $dir === self::$cache_dir ) {
						do_action( 'swis_complete_cache_cleared' );
					}
					// NOTE: kept untrailingslashit(), just in case.
					if ( untrailingslashit( self::get_cache_file_dir( home_url() ) ) === $dir ) {
						$site_cleared_url = home_url();
						$site_cleared_id  = get_current_blog_id();
						do_action( 'swis_site_cache_cleared', $site_cleared_url, $site_cleared_id );
					}
				}
				unset( self::$dir_cleared[ $dir ] );
			}
		}

		// Remove the parent dir if empty.
		self::delete_parent_dir( $dir );
	}

	/**
	 * Clear a cache directory.
	 *
	 * @param string $dir The directory path.
	 * @param bool   $skip_child_dir  Whether or not child directories should be skipped.
	 */
	private static function clear_dir( $dir, $skip_child_dir = false ) {
		self::debug_message( __METHOD__ );
		$dir = untrailingslashit( $dir );
		self::debug_message( microtime( true ) );

		if ( ! is_dir( $dir ) ) {
			return;
		}
		if ( ! is_readable( $dir ) ) {
			return;
		}
		if ( ! is_writable( $dir ) ) {
			return;
		}

		self::debug_message( "clearing cache for $dir" );

		$dir_objects = self::get_dir_objects( $dir );

		foreach ( $dir_objects as $dir_object ) {
			// Create the full path.
			$dir_object = trailingslashit( $dir ) . $dir_object;

			if ( is_dir( $dir_object ) && ! $skip_child_dir ) {
				self::debug_message( "calling clear_dir to clear $dir_object" );
				self::clear_dir( $dir_object );
			} elseif ( is_file( $dir_object ) && is_writable( $dir_object ) ) {
				self::debug_message( "removing $dir_object" );
				unlink( $dir_object );
			}
		}

		// Add this folder to the list of directories cleared.
		self::$dir_cleared[ $dir ] = $dir_objects;

		// Doing this to make sure the directory is empty before we try and delete it.
		clearstatcache();
		$dir_objects = self::get_dir_objects( $dir );

		// If the directory is empty now. No need to do error suppression here.
		if ( empty( $dir_objects ) ) {
			rmdir( $dir );
		}
		clearstatcache();
	}


	/**
	 * Save the asset to disk.
	 *
	 * @param string $page_contents The HTML content to be stored.
	 */
	private static function create_cache_file( $page_contents ) {
		self::debug_message( __METHOD__ );

		// Make sure we actually have something to cache.
		if ( ! is_string( $page_contents ) || 0 === strlen( $page_contents ) ) {
			return;
		}

		// Setup cache file.
		$new_cache_file      = self::get_cache_file();
		$new_cache_file_dir  = dirname( $new_cache_file );
		$new_cache_file_name = basename( $new_cache_file );

		// Append the cache signature.
		$page_contents .= self::get_cache_signature( $new_cache_file_name );

		// Create WebP-supported file.
		if ( false !== strpos( $new_cache_file_name, 'webp' ) && ! class_exists( 'ExactDN' ) && class_exists( '\SWIS\Cache_WebP' ) ) {
			self::debug_message( 'creating a WebP variant' );
			$cache_webp    = new Cache_WebP();
			$page_contents = $cache_webp->filter_webp_html( $page_contents );
		}

		if ( ! wp_mkdir_p( $new_cache_file_dir ) ) {
			return;
		}

		if ( ! is_readable( $new_cache_file_dir ) ) {
			self::debug_message( "Cannot access cache directory: $new_cache_file_dir" );
			return;
		}
		if ( ! is_writable( $new_cache_file_dir ) ) {
			self::debug_message( "Cannot write to directory: $new_cache_file_dir" );
			return;
		}

		self::debug_message( "caching to $new_cache_file" );
		// Write the file to disk.
		file_put_contents( $new_cache_file, $page_contents, LOCK_EX );
		clearstatcache();

		// Set permissions on the cached asset.
		$new_cache_file_stats = stat( $new_cache_file_dir );
		$new_cache_file_perms = $new_cache_file_stats['mode'] & 0007777;
		$new_cache_file_perms = $new_cache_file_perms & 0000666;
		chmod( $new_cache_file, $new_cache_file_perms );
		clearstatcache();
	}

	/**
	 * Record settings for advanced-cache.php to disk.
	 *
	 * @param array $settings Settings as an associative array.
	 */
	public static function create_settings_file( $settings ) {
		self::debug_message( __METHOD__ );
		if ( ! is_array( $settings ) || ! function_exists( 'home_url' ) ) {
			return;
		}
		// Get location of settings file.
		$new_settings_file = self::get_settings_file();

		$new_settings_file_contents  = '<?php' . PHP_EOL;
		$new_settings_file_contents .= '/**' . PHP_EOL;
		$new_settings_file_contents .= ' * SWIS Cache settings for ' . home_url() . PHP_EOL;
		$new_settings_file_contents .= ' * @generated ' . self::get_current_time() . PHP_EOL;
		$new_settings_file_contents .= ' */' . PHP_EOL;
		$new_settings_file_contents .= PHP_EOL;
		$new_settings_file_contents .= 'return ' . var_export( $settings, true ) . ';';

		// Create folder if needed.
		if ( ! wp_mkdir_p( dirname( $new_settings_file ) ) ) {
			self::debug_message( 'unable to create settings directory' );
			wp_die( 'Unable to create directory: ' . esc_html( dirname( $new_settings_file ) ) );
		}
		if ( ! is_writable( dirname( $new_settings_file ) ) ) {
			self::debug_message( 'cannot write to settings directory' );
			wp_die( 'Cannot write to directory: ' . esc_html( dirname( $new_settings_file ) ) );
		}

		self::debug_message( "saving cache settings to $new_settings_file" );
		file_put_contents( $new_settings_file, $new_settings_file_contents, LOCK_EX );

		return $new_settings_file;
	}

	/**
	 * Get the path for a cache file.
	 *
	 * @return string The file path to a new or potentially cached page
	 */
	public static function get_cache_file() {
		$cache_file = sprintf(
			'%s/%s',
			self::get_cache_file_dir(),
			self::get_cache_file_name()
		);
		return $cache_file;
	}

	/**
	 * Get the directory/path to a cached asset.
	 *
	 * @param string $url The full URL.
	 * @return string The path to a cached asset.
	 */
	public static function get_cache_file_dir( $url = null ) {
		self::debug_message( __METHOD__ );
		$cache_file_dir = '';

		// Validate the given URL.
		if ( $url && ! filter_var( $url, FILTER_VALIDATE_URL ) ) {
			return $cache_file_dir;
		}

		$cache_file_dir = sprintf(
			'%s%s%s%s',
			self::$cache_dir,
			DIRECTORY_SEPARATOR,
			( $url ) ? self::parse_url( $url, PHP_URL_HOST ) : strtolower( Cache_Engine::$request_headers['Host'] ),
			self::parse_url( ( $url ) ? $url : Cache_Engine::$request_uri, PHP_URL_PATH )
		);

		$cache_file_dir = rtrim( $cache_file_dir, '/\\' );
		self::debug_message( $cache_file_dir );
		return $cache_file_dir;
	}

	/**
	 * Get file path for a cached page.
	 *
	 * @return string Path to the cached HTML file.
	 */
	private static function get_cache_file_name() {
		self::debug_message( __METHOD__ );

		$cache_keys      = self::get_cache_keys();
		$cache_file_name = $cache_keys['scheme'] . 'index' . $cache_keys['device'] . $cache_keys['webp'] . '.html';

		return $cache_file_name;
	}

	/**
	 * Get cache keys for generating a cache filename.
	 *
	 * @return array The keys needed to build the filename.
	 */
	private static function get_cache_keys() {
		self::debug_message( __METHOD__ );
		// Set the default cache keys.
		$cache_keys = array(
			'scheme'      => 'http-',
			'device'      => '',
			'webp'        => '',
			'compression' => '',
		);

		// Set the scheme.
		if ( isset( $_SERVER['HTTPS'] ) && ( 'on' === strtolower( $_SERVER['HTTPS'] ) || '1' === (string) $_SERVER['HTTPS'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
			$cache_keys['scheme'] = 'https-';
		} elseif ( isset( $_SERVER['SERVER_PORT'] ) && '443' === (string) $_SERVER['SERVER_PORT'] ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
			$cache_keys['scheme'] = 'https-';
		} elseif ( 'https' === Cache_Engine::$request_headers['X-Forwarded-Proto'] || 'https' === Cache_Engine::$request_headers['X-Forwarded-Scheme'] ) {
			$cache_keys['scheme'] = 'https-';
		}

		if ( Cache_Engine::$settings['mobile'] ) {
			self::debug_message( 'mobile cache enabled' );
			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
			) {
				self::debug_message( 'mobile device detected' );
				$cache_keys['device'] = '-mobile';
			}
		}

		if ( Cache_Engine::$settings['webp'] ) {
			self::debug_message( 'webp cache enabled' );
			if ( false !== strpos( Cache_Engine::$request_headers['Accept'], 'image/webp' ) ) {
				self::debug_message( 'webp support detected' );
				$cache_keys['webp'] = '-webp';
			} else {
				self::debug_message( Cache_Engine::$request_headers['Accept'] );
			}
		}

		return $cache_keys;
	}

	/**
	 * Create cache signature.
	 *
	 * @param string $cache_file_name Filename of cached page.
	 * @return string Cache signature string.
	 */
	private static function get_cache_signature( $cache_file_name ) {
		$cache_signature = sprintf(
			"\n<!-- %s @ %s (%s) -->",
			'SWIS Cache',
			self::get_current_time(),
			$cache_file_name
		);
		return $cache_signature;
	}

	/**
	 * Get the size of the cache.
	 *
	 * @param string $dir Path of the cache folder.
	 * @return int Size in bytes.
	 */
	public static function get_cache_size( $dir = null ) {
		$cache_size = 0;

		// Get a list of the files in a folder.
		if ( is_dir( $dir ) && is_readable( $dir ) ) {
			$dir_objects = self::get_dir_objects( $dir );
		} else {
			$dir_objects = self::get_site_objects( home_url() );
		}

		if ( empty( $dir_objects ) ) {
			return $cache_size;
		}

		foreach ( $dir_objects as $dir_object ) {
			// Create the full path.
			$dir_object = trailingslashit( ( $dir ) ? $dir : ( self::$cache_dir . DIRECTORY_SEPARATOR . parse_url( home_url(), PHP_URL_HOST ) . parse_url( home_url(), PHP_URL_PATH ) ) ) . $dir_object;

			if ( is_dir( $dir_object ) ) {
				$cache_size += self::get_cache_size( $dir_object );
			} elseif ( is_file( $dir_object ) && is_readable( $dir_object ) ) {
				$cache_size += filesize( $object );
			}
		}

		return $cache_size;
	}

	/**
	 * Get settings file path.
	 *
	 * @param bool $fallback Whether or not fallback settings should be returned. Optional, defaults to false.
	 * @return string Path to the settings file.
	 */
	private static function get_settings_file( $fallback = false ) {
		$settings_file = sprintf(
			'%s/%s',
			self::$settings_dir,
			self::get_settings_file_name( $fallback )
		);

		return $settings_file;
	}

	/**
	 * Get settings file name.
	 *
	 * @param bool $fallback Whether or not fallback settings file name should be returned. Optional, defaults to false.
	 * @param bool $skip_blog_path Whether or not blog path should be included in settings file name. Optional, defaults to false.
	 * @return string File name for settings file.
	 */
	private static function get_settings_file_name( $fallback = false, $skip_blog_path = false ) {
		$settings_file_name = '';

		// If creating or deleting settings file.
		if ( function_exists( 'home_url' ) ) {
			$settings_file_name = self::parse_url( home_url(), PHP_URL_HOST );

			// Sub-directory network install.
			if ( is_multisite() && defined( 'SUBDOMAIN_INSTALL' ) && ! SUBDOMAIN_INSTALL ) {
				$blog_path           = swis()->cache->get_blog_path();
				$settings_file_name .= ( ! empty( $blog_path ) ) ? '.' . trim( $blog_path, '/' ) : '';
			}

			$settings_file_name .= '.php';
			// If getting settings from settings file.
		} elseif ( is_dir( self::$settings_dir ) ) {
			if ( $fallback ) {
				$settings_files      = self::get_dir_objects( self::$settings_dir );
				$settings_file_regex = '/\.php$/';

				if ( is_multisite() ) {
					$settings_file_regex = '/^' . strtolower( stripslashes( Cache_Engine::$request_headers['Host'] ) );
					$settings_file_regex = str_replace( '.', '\.', $settings_file_regex );

					// Sub-directory network install.
					if ( defined( 'SUBDOMAIN_INSTALL' ) && ! SUBDOMAIN_INSTALL && ! $skip_blog_path ) {
						$url_path = Cache_Engine::$request_uri;

						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 ) {
					$settings_file_name = self::get_settings_file_name( true, true );
				}
			} else {
				$settings_file_name = strtolower( Cache_Engine::$request_headers['Host'] );

				// Sub-directory network install.
				if ( is_multisite() && defined( 'SUBDOMAIN_INSTALL' ) && ! SUBDOMAIN_INSTALL && ! $skip_blog_path ) {
					$url_path        = Cache_Engine::$request_uri;
					$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 main site.
					if ( ! is_file( self::$settings_dir . '/' . $settings_file_name ) ) {
						$settings_file_name = self::get_settings_file_name( false, true );
					}
				}
				$settings_file_name .= ( strpos( $settings_file_name, '.php' ) === false ) ? '.php' : '';
			}
		}
		self::debug_message( "settings file at $settings_file_name" );
		return $settings_file_name;
	}

	/**
	 * Get settings from settings file.
	 *
	 * @return array Settings from the file.
	 */
	public static function get_settings() {
		$settings = array();

		$settings_file = self::get_settings_file();
		// Check if the settings file exists.
		if ( self::is_file( $settings_file ) && is_readable( $settings_file ) ) {
			$settings = include_once( $settings_file );
		} else {
			// Try to get fallback settings file otherwise.
			$fallback_settings_file = self::get_settings_file( true );

			if ( self::is_file( $fallback_settings_file ) && is_readable( $fallback_settings_file ) ) {
				$settings = include_once( $fallback_settings_file );
			}
		}

		// Create settings file if cache exists but settings file does not.
		if ( ( empty( $settings ) || ! is_array( $settings ) ) && class_exists( '\SWIS\Cache' ) ) {
			$new_settings_file = self::create_settings_file( swis()->cache->get_settings() );
			if ( self::is_file( $new_settings_file ) && is_readable( $new_settings_file ) ) {
				$settings = include_once( $new_settings_file );
			}
		}

		if ( is_array( $settings ) ) {
			return $settings;
		}
		return array();
	}

	/**
	 * Get all the files in a directory.
	 *
	 * @param string $dir The directory's path.
	 * @return array A list of files found.
	 */
	public static function get_dir_objects( $dir ) {
		self::debug_message( __METHOD__ );
		self::debug_message( microtime( true ) );
		if ( ! is_readable( $dir ) ) {
			return array();
		}
		// Scan the directory.
		$dir_objects = @scandir( $dir );

		if ( is_array( $dir_objects ) ) {
			$dir_objects = array_diff( $dir_objects, array( '..', '.' ) );
		} else {
			$dir_objects = array();
		}
		return $dir_objects;
	}

	/**
	 * Get site file system objects.
	 *
	 * @param string $site_url The site URL.
	 * @return array An array of site objects (files & dirs).
	 */
	public static function get_site_objects( $site_url ) {
		self::debug_message( __METHOD__ );
		self::debug_message( microtime( true ) );
		$site_objects = array();

		// Get the directory for the given URL.
		$dir = self::get_cache_file_dir( $site_url );

		// Check if the directory exists.
		if ( ! is_dir( $dir ) ) {
			return $site_objects;
		}

		// Get site objects from $dir.
		$site_objects = self::get_dir_objects( $dir );

		// Cleanup sub-directory network site objects.
		if ( is_multisite() && ! is_subdomain_install() ) {
			$blog_path  = swis()->cache->get_blog_path();
			$blog_paths = swis()->cache->get_blog_paths();

			// Filter objects if this is the main site in a sub-directory network.
			if ( ! in_array( $blog_path, $blog_paths, true ) ) {
				foreach ( $site_objects as $key => $site_object ) {
					// Delete a 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 (formatted).
	 *
	 * @return string The current time in HTTP-date format.
	 */
	private static function get_current_time() {
		return current_time( 'D, d M Y H:i:s', true ) . ' GMT';
	}

	/**
	 * Set or unset WP_CACHE constant.
	 *
	 * @param bool $set True to set WP_CACHE constant in wp-config.php, false to unset.
	 */
	private static function set_wp_cache_constant( $set = true ) {
		self::debug_message( __METHOD__ );
		$wp_config_file = false;
		// Get wp-config.php file.
		if ( self::is_file( ABSPATH . 'wp-config.php' ) ) {
			// Config file resides in ABSPATH.
			$wp_config_file = ABSPATH . 'wp-config.php';
		} elseif ( self::is_file( dirname( ABSPATH ) . '/wp-config.php' ) && ! self::is_file( dirname( ABSPATH ) . '/wp-settings.php' ) ) {
			// Config file resides one level above ABSPATH but is not part of another installation.
			$wp_config_file = dirname( ABSPATH ) . '/wp-config.php';
		}

		// Check if config file is writable.
		if ( ! $wp_config_file || ! is_writable( $wp_config_file ) ) {
			return;
		}

		// Get config file contents.
		$wp_config_file_contents = file_get_contents( $wp_config_file );

		// Validate config file contents.
		if ( ! is_string( $wp_config_file_contents ) ) {
			return;
		}
		$wp_cache_line = "define( 'WP_CACHE', true ); // Added by SWIS Performance\n";

		$found_wp_cache_constant = preg_match( '/define\s*\(\s*[\'\"]WP_CACHE[\'\"]\s*,.+\);/', $wp_config_file_contents );
		if ( $set && ! $found_wp_cache_constant ) {
			$wp_config_file_contents = preg_replace( '/(<\?php\r?\n?)/i', "<?php\n$wp_cache_line", $wp_config_file_contents );
		} elseif ( $set && $found_wp_cache_constant && defined( 'WP_CACHE' ) && ! WP_CACHE ) {
			$wp_config_file_contents = preg_replace( '/define\s*\(\s*[\'\"]WP_CACHE[\'\"]\s*,.+\);.*\r?\n?/', "$wp_cache_line\n", $wp_config_file_contents );
		}

		if ( ! $set ) {
			$wp_config_file_contents = preg_replace( '/define.+Added by SWIS Performance\r?\n?/', '', $wp_config_file_contents );
		}

		file_put_contents( $wp_config_file, $wp_config_file_contents, LOCK_EX );
	}

	/**
	 * Delete an empty parent directory.
	 *
	 * @param string $dir Path of a directory.
	 */
	private static function delete_parent_dir( $dir ) {
		self::debug_message( __METHOD__ );
		self::debug_message( microtime( true ) );
		self::debug_message( "maybe deleting parent of $dir" );
		$parent_dir         = dirname( $dir );
		$parent_dir_objects = self::get_dir_objects( $parent_dir );

		if ( empty( $parent_dir_objects ) && is_writable( $parent_dir ) ) {
			rmdir( $parent_dir );
			self::debug_message( "removed parent $parent_dir" );

			// Add this folder to the list of directories cleared.
			self::$dir_cleared[ $parent_dir ] = $parent_dir_objects;

			self::delete_parent_dir( $parent_dir );
		}
	}

	/**
	 * Delete settings file for advanced-cache.php.
	 */
	private static function delete_settings_file() {
		self::debug_message( __METHOD__ );
		// Get location of settings file.
		$settings_file = self::get_settings_file();

		if ( ! self::is_file( $settings_file ) || ! is_writable( $settings_file ) ) {
			return;
		}

		unlink( $settings_file );

		$dir_objects = self::get_dir_objects( self::$settings_dir );

		if ( empty( $dir_objects ) ) {
			rmdir( self::$settings_dir );
		}
		clearstatcache();
	}
}