pagination_base; if ( strlen( $pagination_base ) > 0 ) { $pagination_dir = $dir . $pagination_base; self::clear_dir( $pagination_dir ); } } } // delete parent directory if empty self::delete_parent_dir( $dir ); // cache cleared hooks foreach ( self::$dir_cleared as $dir => $dir_objects ) { if ( strpos( $dir, self::$cache_dir ) !== false ) { if ( A2_Optimized_Cache::$fire_page_cache_cleared_hook ) { if ( ! empty( preg_grep( '/index/', $dir_objects ) ) ) { // 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( 'a2opt_cache_page_cache_cleared', $page_cleared_url, $page_cleared_id ); } } else { // complete cache cleared hook if ( $dir === self::$cache_dir ) { do_action( 'a2opt_cache_complete_cache_cleared' ); } // site cache cleared hook if ( $dir === untrailingslashit( self::cache_file_dir_path( home_url() ) ) ) { $site_cleared_url = home_url(); $site_cleared_id = get_current_blog_id(); do_action( 'a2opt_cache_site_cache_cleared', $site_cleared_url, $site_cleared_id ); } } unset( self::$dir_cleared[ $dir ] ); } } } /** * clear directory * * @param string $dir directory path * @param boolean $skip_child_dir whether or not child directories should be skipped */ private static function clear_dir($dir, $skip_child_dir = false) { // remove trailing slash $dir = untrailingslashit( $dir ); // check if directory exists if ( ! is_dir( $dir ) ) { return; } // get directory objects $dir_objects = self::get_dir_objects( $dir ); foreach ( $dir_objects as $dir_object ) { // get full path $dir_object = $dir . '/' . $dir_object; if ( is_dir( $dir_object ) && ! $skip_child_dir ) { self::clear_dir( $dir_object ); } elseif ( is_file( $dir_object ) ) { // clear cached page variant unlink( $dir_object ); } } // delete directory if empty @rmdir( $dir ); // clear file status cache clearstatcache(); // add cleared directory to directories cleared list self::$dir_cleared[ $dir ] = $dir_objects; } /** * create files for cache * * @param string $page_contents contents of a page from the output buffer */ private static function create_cache_files($page_contents) { // get base signature $cache_signature = self::cache_signature(); // make directory if necessary if ( ! wp_mkdir_p( self::cache_file_dir_path() ) ) { wp_die( 'Unable to create directory.' ); } // minify HTML $page_contents = self::minify_html( $page_contents ); // create default file self::create_cache_file( self::cache_file_html(), $page_contents . $cache_signature . ' (' . self::cache_file_scheme() . ' html) -->' ); // create pre-compressed file if ( A2_Optimized_Cache_Engine::$settings['compress_cache'] ) { $compressed_page_contents = gzencode( $page_contents . $cache_signature . ' (' . self::cache_file_scheme() . ' gzip) -->', 9 ); // validate compression if ( is_string( $compressed_page_contents ) ) { self::create_cache_file( self::cache_file_gzip(), $compressed_page_contents ); } } // create WebP supported files if ( A2_Optimized_Cache_Engine::$settings['convert_image_urls_to_webp'] ) { // attributes to convert during WebP conversion hook $attributes = (array) apply_filters( 'a2opt_cache_convert_webp_attributes', array( 'src', 'srcset', 'data-[^=]+' ) ); // stringify $attributes_regex = implode( '|', $attributes ); // magic regex rule $image_urls_regex = '#(?:(?:(' . $attributes_regex . ')\s*=|(url)\()\s*[\'\"]?\s*)\K(?:[^\?\"\'\s>]+)(?:\.jpe?g|\.png)(?:\s\d+[wx][^\"\'>]*)?(?=\/?[\"\'\s\)>])(?=[^<{]*(?:\)[^<{]*\}|>))#i'; // ignore query strings during WebP conversion hook if ( ! apply_filters( 'a2opt_cache_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'; } // page contents after WebP conversion hook $converted_page_contents = apply_filters( 'a2opt_cache_page_contents_after_webp_conversion', preg_replace_callback( $image_urls_regex, 'self::convert_webp', $page_contents ) ); // create default WebP file self::create_cache_file( self::cache_file_webp_html(), $converted_page_contents . $cache_signature . ' (' . self::cache_file_scheme() . ' webp html) -->' ); // create pre-compressed file if ( A2_Optimized_Cache_Engine::$settings['compress_cache'] ) { $compressed_converted_page_contents = gzencode( $converted_page_contents . $cache_signature . ' (' . self::cache_file_scheme() . ' webp gzip) -->', 9 ); // validate compression if ( is_string( $compressed_converted_page_contents ) ) { self::create_cache_file( self::cache_file_webp_gzip(), $compressed_converted_page_contents ); } } } } /** * create file for cache * * @param string $file_path file path * @param string $page_contents contents of a page from the output buffer */ private static function create_cache_file($file_path, $page_contents) { // write page contents from output buffer to file file_put_contents( $file_path, $page_contents ); // clear file status cache clearstatcache(); // set permissions $file_stats = @stat( dirname( $file_path ) ); $permissions = $file_stats['mode'] & 0007777; $permissions = $permissions & 0000666; @chmod( $file_path, $permissions ); // clear file status cache clearstatcache(); } /** * create settings file * * @param array $settings settings from database * @return string $new_settings_file new settings file */ public static function create_settings_file($settings) { // validate array if ( ! is_array( $settings ) ) { return; } // check settings file requirements if ( ! function_exists( 'home_url' ) ) { return; } $settings = A2_Optimized_Cache::validate_settings($settings); // get new settings file $new_settings_file = self::get_settings_file(); $htaccess_file = self::$settings_dir . '/.htaccess'; // make directory if necessary if ( ! wp_mkdir_p( dirname( $new_settings_file ) ) ) { wp_die( 'Unable to create directory.' ); } $home_url = parse_url(home_url()); // add new settings file contents $new_settings_file_contents = '= @filemtime( self::cache_file_html() ) ) ) { header( $_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified', true, 304 ); exit; } // check webp and deliver gzip webp file if support if ( $http_accept && ( strpos( $http_accept, 'webp' ) !== false ) ) { if ( is_readable( self::cache_file_webp_gzip() ) ) { header( 'Content-Encoding: gzip' ); return self::cache_file_webp_gzip(); } elseif ( is_readable( self::cache_file_webp_html() ) ) { return self::cache_file_webp_html(); } } // check encoding and deliver gzip file if support if ( $http_accept_encoding && ( strpos( $http_accept_encoding, 'gzip' ) !== false ) && is_readable( self::cache_file_gzip() ) ) { header( 'Content-Encoding: gzip' ); return self::cache_file_gzip(); } // get default cached file return self::cache_file_html(); } /** * get settings file * * @param boolean $fallback whether or not fallback settings file should be returned * @return string $settings_file file path to 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 boolean $fallback whether or not fallback settings file name should be returned * @param boolean $skip_blog_path whether or not blog path should be included in settings file name * @return string $settings_file_name 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 = parse_url( home_url(), PHP_URL_HOST ); // subdirectory network if ( is_multisite() && defined( 'SUBDOMAIN_INSTALL' ) && ! SUBDOMAIN_INSTALL ) { $blog_path = A2_Optimized_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 = '/^' . parse_url( 'http://' . strtolower( $_SERVER['HTTP_HOST'] ), PHP_URL_HOST ); $settings_file_regex = str_replace( '.', '\.', $settings_file_regex ); // subdirectory network if ( defined( 'SUBDOMAIN_INSTALL' ) && ! SUBDOMAIN_INSTALL && ! $skip_blog_path ) { $url_path = $_SERVER['REQUEST_URI']; $url_path = trim( $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 = parse_url( 'http://' . strtolower( $_SERVER['HTTP_HOST'] ), PHP_URL_HOST ); // subdirectory network if ( is_multisite() && defined( 'SUBDOMAIN_INSTALL' ) && ! SUBDOMAIN_INSTALL && ! $skip_blog_path ) { $url_path = $_SERVER['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 ) ) { $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 settings from settings file * * @return array $settings current settings from settings file */ public static function get_settings() { $settings = array(); // get settings file $settings_file = self::get_settings_file(); // include settings file if it exists if ( is_file( $settings_file ) ) { $settings = include_once $settings_file; // try to get fallback settings file otherwise } else { $fallback = true; $fallback_settings_file = self::get_settings_file( $fallback ); if ( is_file( $fallback_settings_file ) ) { $settings = include_once $fallback_settings_file; } } // create settings file if it does not exist and in late engine start if ( empty( $settings ) && class_exists( 'A2_Optimized_Cache' ) ) { $new_settings_file = self::create_settings_file( A2_Optimized_Cache::get_settings() ); if ( is_file( $new_settings_file ) ) { $settings = include_once $new_settings_file; } } return $settings; } /** * get directory file system objects * * @param string $dir directory path * @return array $dir_objects directory objects */ private static function get_dir_objects($dir) { $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 site URL * @return array $site_objects site objects */ public static function get_site_objects($site_url) { $site_objects = array(); // get directory $dir = self::cache_file_dir_path( $site_url ); // check if directory exists if ( ! is_dir( $dir ) ) { return $site_objects; } // get site objects $site_objects = self::get_dir_objects( $dir ); // maybe filter subdirectory network site objects if ( is_multisite() && ! is_subdomain_install() ) { $blog_path = A2_Optimized_Cache::get_blog_path(); $blog_paths = A2_Optimized_Cache::get_blog_paths(); // check if main site in subdirectory network if ( ! in_array( $blog_path, $blog_paths ) ) { foreach ( $site_objects as $key => $site_object ) { // delete site object if it does not belong to main site if ( in_array( '/' . $site_object . '/', $blog_paths ) ) { unset( $site_objects[ $key ] ); } } } } return $site_objects; } /** * set or unset WP_CACHE constant in wp-config.php * * @param boolean $set true to set WP_CACHE constant, false to unset */ private static function set_wp_cache_constant($set = true) { // get config file if ( file_exists( ABSPATH . 'wp-config.php' ) ) { // 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' ) ) { // 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 can be written to if ( ! is_writable( $wp_config_file ) ) { return; } // get config file contents $wp_config_file_contents = file_get_contents( $wp_config_file ); // validate config file if ( ! is_string( $wp_config_file_contents ) ) { return; } // search for WP_CACHE constant $found_wp_cache_constant = preg_match( '/define\s*\(\s*[\'\"]WP_CACHE[\'\"]\s*,.+\);/', $wp_config_file_contents ); // if not found set WP_CACHE constant when config file is default (must be before WordPress sets up) if ( $set && ! $found_wp_cache_constant ) { $cache_wp_config_lines = '/** Enables page caching for A2 Optimized Cache. */' . PHP_EOL; $cache_wp_config_lines .= "if ( ! defined( 'WP_CACHE' ) ) {" . PHP_EOL; $cache_wp_config_lines .= "\tdefine( 'WP_CACHE', true );" . PHP_EOL; $cache_wp_config_lines .= '}' . PHP_EOL; $cache_wp_config_lines .= PHP_EOL; $wp_config_file_contents = preg_replace( '/(\/\*\* Sets up WordPress vars and included files\. \*\/)/', $cache_wp_config_lines . '$1', $wp_config_file_contents ); } // unset WP_CACHE constant if set if ( ! $set ) { $wp_config_file_contents = preg_replace( '/.+Added by A2 Optimized\r\n/', '', $wp_config_file_contents ); // < 1.5.0 $wp_config_file_contents = preg_replace( '/\/\*\* Enables page caching for A2 Optimized Cache\. \*\/' . PHP_EOL . '.+' . PHP_EOL . '.+' . PHP_EOL . '\}' . PHP_EOL . PHP_EOL . '/', '', $wp_config_file_contents ); } // update config file file_put_contents( $wp_config_file, $wp_config_file_contents ); } /** * get image path * * @param string $image_url full or relative URL with or without intrinsic width or density descriptor * @return string $image_path path to image */ private static function image_path($image_url) { // in case image has intrinsic width or density descriptor $image_parts = explode( ' ', $image_url ); $image_url = $image_parts[0]; // in case installation is in a subdirectory $image_url_path = ltrim( parse_url( $image_url, PHP_URL_PATH ), '/' ); $installation_dir = preg_replace( '/^[^\/]+\/\K.+/', '', $image_url_path ); $image_path = str_replace( $installation_dir, '', ABSPATH ) . $image_url_path; return $image_path; } /** * convert image URL(s) to WebP * * @param array $matches pattern matches from parsed page contents * @return string $conversion converted image URL(s) to WebP if applicable, default URL(s) otherwise */ 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 ) { // set image URL(s) $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 .webp extension $image_path_webp = self::image_path( $image_url_webp ); // check if WebP image exists if ( is_file( $image_path_webp ) ) { $image_url = $image_url_webp; } else { $image_url_webp = preg_replace( $image_extension_regex, '', $image_url_webp ); // remove default extension $image_path_webp = self::image_path( $image_url_webp ); // check if WebP image exists if ( is_file( $image_path_webp ) ) { $image_url = $image_url_webp; } } } $conversion = implode( ', ', $image_urls ); return $conversion; } } /** * minify HTML * * @param string $page_contents contents of a page from the output buffer * @return string $minified_html|$page_contents minified page contents if applicable, unchanged otherwise */ private static function minify_html($page_contents) { // check if setting is enabled if ( ! A2_Optimized_Cache_Engine::$settings['minify_html'] ) { return $page_contents; } // HTML character limit if ( strlen( $page_contents ) > 700000) { return $page_contents; } // HTML tags to ignore hook $ignore_tags = (array) apply_filters( 'a2opt_cache_minify_html_ignore_tags', array( 'textarea', 'pre', 'code' ) ); // if setting selected exclude inline CSS and JavaScript if ( ! A2_Optimized_Cache_Engine::$settings['minify_inline_css_js'] ) { array_push( $ignore_tags, 'style', 'script' ); } // check if there are ignore tags if ( ! $ignore_tags ) { return $page_contents; } // stringify $ignore_tags_regex = implode( '|', $ignore_tags ); // remove HTML comments $minified_html = preg_replace( '##s', '', $page_contents ); // if setting selected remove CSS and JavaScript comments if ( A2_Optimized_Cache_Engine::$settings['minify_inline_css_js'] ) { $minified_html = preg_replace( '#/\*(?!!)[\s\S]*?\*/|(?:^[ \t]*)//.*$|((?;,{}[\]])//[^;\n]*$#m', '$1', $minified_html ); } // minify HTML $minified_html = preg_replace( '#(?>[^\S ]\s*|\s{2,})(?=[^<]*+(?:<(?!/?(?:' . $ignore_tags_regex . ')\b)[^<]*+)*+(?:<(?>' . $ignore_tags_regex . ')\b|\z))#ix', ' ', $minified_html ); // something went wrong if ( strlen( $minified_html ) <= 1 ) { return $page_contents; } return $minified_html; } /** * delete empty parent directory * * @param string $dir directory path */ private static function delete_parent_dir($dir) { $parent_dir = dirname( $dir ); $parent_dir_objects = self::get_dir_objects( $parent_dir ); if ( empty( $parent_dir_objects ) ) { // delete empty parent directory @rmdir( $parent_dir ); // add deleted parent directory to directories cleared list self::$dir_cleared[ $parent_dir ] = $parent_dir_objects; // delete parent directory if empty self::delete_parent_dir( $parent_dir ); } } /** * delete settings file * */ private static function delete_settings_file() { // get settings file $settings_file = self::get_settings_file(); // delete settings file @unlink( $settings_file ); // delete settings directory if empty @rmdir( self::$settings_dir ); } /** * delete asset (deprecated) * */ public static function delete_asset($url) { if ( empty( $url ) ) { wp_die( 'URL is empty.' ); } self::clear_dir( self::cache_file_dir_path( $url ) ); } }