array( __CLASS__, 'sanitize_settings' ) ) ); add_action( 'update_option_' . self::OPTION_NAME, array( __CLASS__, 'handle_settings_update' ), 10, 2 ); } public static function sanitize_settings( $input ) { $clean = is_array( $input ) ? $input : array(); $existing = get_option( self::OPTION_NAME, array() ); if ( array_key_exists( 'enabled', $clean ) ) { $clean['enabled'] = ! empty( $clean['enabled'] ) ? 1 : 0; } else { $clean['enabled'] = ! empty( $existing['enabled'] ) ? 1 : 0; } $clean['clear_on_save'] = ! empty( $clean['clear_on_save'] ) ? 1 : 0; $clean['skip_woocommerce'] = ! empty( $clean['skip_woocommerce'] ) ? 1 : 0; $clean['exclude_urls'] = isset( $clean['exclude_urls'] ) ? sanitize_textarea_field( $clean['exclude_urls'] ) : ''; $clean['prewarm_auto'] = ! empty( $clean['prewarm_auto'] ) ? 1 : 0; $clean['prewarm_limit'] = isset( $clean['prewarm_limit'] ) ? max( 1, intval( $clean['prewarm_limit'] ) ) : 200; $clean['prewarm_delay'] = isset( $clean['prewarm_delay'] ) ? max( 0, intval( $clean['prewarm_delay'] ) ) : 200; $clean['mobile_cache'] = ! empty( $clean['mobile_cache'] ) ? 1 : 0; $clean['cache_ttl'] = isset( $clean['cache_ttl'] ) ? max( 60, intval( $clean['cache_ttl'] ) ) : 3600; $clean['browser_cache'] = ! empty( $clean['browser_cache'] ) ? 1 : 0; $server = isset( $clean['server_type'] ) ? sanitize_key( $clean['server_type'] ) : ''; $clean['server_type'] = $server !== '' ? $server : ( isset( $existing['server_type'] ) ? $existing['server_type'] : '' ); return $clean; } public static function render_page() { if ( ! current_user_can( 'manage_options' ) ) { return; } $settings = get_option( self::OPTION_NAME, array() ); $enabled = ! empty( $settings['enabled'] ); $clear_on_save = ! empty( $settings['clear_on_save'] ); $skip_woocommerce = ! empty( $settings['skip_woocommerce'] ); $exclude_urls = isset( $settings['exclude_urls'] ) ? $settings['exclude_urls'] : ''; $prewarm_auto = ! empty( $settings['prewarm_auto'] ); $prewarm_limit = isset( $settings['prewarm_limit'] ) ? (int) $settings['prewarm_limit'] : 200; $prewarm_delay = isset( $settings['prewarm_delay'] ) ? (int) $settings['prewarm_delay'] : 200; $mobile_cache = ! empty( $settings['mobile_cache'] ); $cache_ttl = isset( $settings['cache_ttl'] ) ? (int) $settings['cache_ttl'] : 3600; $browser_cache = ! array_key_exists( 'browser_cache', $settings ) || ! empty( $settings['browser_cache'] ); $server_type = isset( $settings['server_type'] ) ? $settings['server_type'] : ''; $detected = self::detect_server_type(); $server_type = $server_type !== '' ? $server_type : $detected; $log = get_option( self::LOG_OPTION, array() ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended - read-only param, sanitized inline. $status = isset( $_GET['ffascachemanager_cache_status'] ) ? sanitize_text_field( wp_unslash( $_GET['ffascachemanager_cache_status'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended - read-only param, sanitized inline. $prewarm_status = isset( $_GET['ffascachemanager_prewarm_status'] ) ? sanitize_text_field( wp_unslash( $_GET['ffascachemanager_prewarm_status'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended - read-only param, sanitized inline. $htaccess_status = isset( $_GET['ffascachemanager_cache_rules'] ) ? sanitize_text_field( wp_unslash( $_GET['ffascachemanager_cache_rules'] ) ) : ''; ?>

|

$ttl ) { wp_delete_file( $file ); return; } $contents = @file_get_contents( $file ); if ( $contents === false ) { return; } if ( ! headers_sent() ) { header( 'X-FFAS-Cache-Manager: HIT' ); header( 'Cache-Control: no-cache, must-revalidate' ); header( 'Vary: Accept-Encoding', false ); } // phpcs:ignore echo $contents; exit; } public static function start_cache_buffer() { if ( ! self::is_cacheable_request() ) { return; } ob_start( array( __CLASS__, 'cache_output' ) ); } public static function cache_output( $buffer ) { if ( stripos( $buffer, 'isDir() ) { // phpcs:ignore @rmdir( $item->getPathname() ); } else { wp_delete_file( $item->getPathname() ); } } // phpcs:ignore @rmdir( $dir ); return ! is_dir( $dir ); } private static function clear_cache_for_post( $post_id, $post ) { $urls = array(); $permalink = get_permalink( $post_id ); if ( $permalink ) { $urls[] = $permalink; } if ( $post instanceof WP_Post ) { if ( $post->post_type === 'post' ) { $urls[] = home_url( '/' ); $posts_page = intval( get_option( 'page_for_posts' ) ); if ( $posts_page ) { $posts_url = get_permalink( $posts_page ); if ( $posts_url ) { $urls[] = $posts_url; } } } elseif ( $post->post_type === 'page' ) { $front_id = intval( get_option( 'page_on_front' ) ); $posts_page = intval( get_option( 'page_for_posts' ) ); if ( $front_id && $front_id === $post_id ) { $urls[] = home_url( '/' ); } if ( $posts_page && $posts_page === $post_id ) { $posts_url = get_permalink( $posts_page ); if ( $posts_url ) { $urls[] = $posts_url; } } } elseif ( $post->post_type === 'product' ) { $shop_id = function_exists( 'wc_get_page_id' ) ? wc_get_page_id( 'shop' ) : 0; if ( $shop_id && $shop_id !== $post_id ) { $shop_url = get_permalink( $shop_id ); if ( $shop_url ) { $urls[] = $shop_url; } } } } $urls = array_values( array_unique( array_filter( $urls ) ) ); foreach ( $urls as $url ) { self::clear_cache_for_url( $url ); } } private static function clear_cache_for_url( $url ) { $path = wp_parse_url( $url, PHP_URL_PATH ); if ( $path === null ) { return; } $variants = array(); $path = $path === '' ? '/' : $path; $variants[] = $path; if ( $path !== '/' && substr( $path, -1 ) === '/' ) { $variants[] = rtrim( $path, '/' ); } elseif ( $path !== '/' ) { $variants[] = $path . '/'; } foreach ( $variants as $variant ) { $desktop = self::cache_file_path_for_uri( $variant, false ); if ( $desktop && file_exists( $desktop ) ) { wp_delete_file( $desktop ); } $mobile = self::cache_file_path_for_uri( $variant, true ); if ( $mobile && file_exists( $mobile ) ) { wp_delete_file( $mobile ); } } } private static function cache_file_path_for_uri( $uri, $force_mobile = null ) { $host = wp_parse_url( home_url(), PHP_URL_HOST ); if ( empty( $host ) ) { return ''; } $request_key = self::normalize_request_uri( $uri ); if ( '' === $request_key || false !== strpos( rawurldecode( $request_key ), "\0" ) ) { return ''; } $settings = get_option( self::OPTION_NAME, array() ); $is_mobile = $force_mobile; if ( $is_mobile === null ) { $is_mobile = ! empty( $settings['mobile_cache'] ) && function_exists( 'wp_is_mobile' ) && wp_is_mobile(); } $base = trailingslashit( self::cache_base_dir() ) . sanitize_file_name( $host ) . '/'; if ( $is_mobile ) { $base .= 'mobile/'; } return $base . hash( 'sha256', $request_key ) . '.html'; } private static function normalize_request_uri( $uri ) { $parts = wp_parse_url( $uri ); if ( empty( $parts ) ) { return $uri; } $path = isset( $parts['path'] ) ? $parts['path'] : '/'; $query = isset( $parts['query'] ) ? $parts['query'] : ''; if ( $query === '' ) { return $path; } parse_str( $query, $params ); $drop = array( 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'gclid', 'fbclid' ); foreach ( $drop as $key ) { if ( isset( $params[ $key ] ) ) { unset( $params[ $key ] ); } } if ( empty( $params ) ) { return $path; } return $path . '?' . http_build_query( $params ); } private static function parse_exclude_urls() { $settings = get_option( self::OPTION_NAME, array() ); $raw = isset( $settings['exclude_urls'] ) ? $settings['exclude_urls'] : ''; $lines = preg_split( '/\r\n|\r|\n/', (string) $raw ); $urls = array(); foreach ( $lines as $line ) { $line = trim( $line ); if ( $line === '' ) { continue; } $urls[] = $line; } return $urls; } private static function is_excluded_url( $request_uri ) { $excludes = self::parse_exclude_urls(); if ( empty( $excludes ) ) { return false; } $normalized = self::normalize_request_uri( $request_uri ); foreach ( $excludes as $line ) { $is_prefix = false; if ( substr( $line, -1 ) === '*' ) { $is_prefix = true; $line = rtrim( $line, '*' ); } $compare = ''; if ( preg_match( '#^https?://#i', $line ) ) { $compare = self::normalize_request_uri( wp_parse_url( $line, PHP_URL_PATH ) ?: '/' ); } else { $compare = self::normalize_request_uri( $line ); } if ( $compare === '' ) { continue; } if ( $is_prefix ) { if ( strpos( $normalized, $compare ) === 0 ) { return true; } } elseif ( $normalized === $compare ) { return true; } } return false; } private static function minify_html_safe( $content ) { $placeholders = array(); $index = 0; $content = preg_replace_callback( '/<(script|style)\b[^>]*>.*?<\/\1>/is', function ( $matches ) use ( &$placeholders, &$index ) { $token = '__FFASCACHEMANAGER_CACHE_BLOCK_' . $index . '__'; $placeholders[ $token ] = $matches[0]; $index++; return $token; }, $content ); $content = preg_replace( '//', '', $content ); $content = preg_replace( '/\s+/', ' ', $content ); $content = preg_replace( '/>\s+<', $content ); if ( ! empty( $placeholders ) ) { $content = str_replace( array_keys( $placeholders ), array_values( $placeholders ), $content ); } return trim( $content ); } private static function contains_form( $html ) { return (bool) preg_match( '/]*>/i', $html ); } private static function schedule_prewarm( $source ) { if ( wp_next_scheduled( 'ffascachemanager_cache_prewarm' ) ) { return; } update_option( self::PENDING_OPTION, array( 'source' => $source, 'time' => time(), ), false ); wp_schedule_single_event( time() + 30, 'ffascachemanager_cache_prewarm' ); } private static function run_prewarm( $source ) { $settings = get_option( self::OPTION_NAME, array() ); if ( empty( $settings['enabled'] ) ) { return; } if ( function_exists( 'set_time_limit' ) ) { @set_time_limit( 0 ); } if ( function_exists( 'ini_set' ) ) { @ini_set( 'max_execution_time', '0' ); } $limit = isset( $settings['prewarm_limit'] ) ? max( 1, intval( $settings['prewarm_limit'] ) ) : 200; $limit = min( 200, $limit ); $delay = isset( $settings['prewarm_delay'] ) ? max( 0, intval( $settings['prewarm_delay'] ) ) : 200; $mobile_enabled = ! empty( $settings['mobile_cache'] ); $mobile_headers = array( 'user-agent' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Mobile/15E148 Safari/604.1', ); $queue = get_option( self::PREWARM_QUEUE_OPTION, array() ); if ( empty( $queue ) || ! is_array( $queue ) ) { $queue = self::collect_prewarm_urls( $limit ); update_option( self::PREWARM_QUEUE_OPTION, $queue, false ); } if ( empty( $queue ) ) { return; } $attempted = 0; $succeeded = 0; $errors = array(); $batch = array_slice( $queue, 0, self::PREWARM_BATCH ); $remaining = array_slice( $queue, self::PREWARM_BATCH ); foreach ( $batch as $url ) { $path = wp_parse_url( $url, PHP_URL_PATH ); $path = $path ? $path : '/'; if ( self::is_excluded_url( $path ) ) { continue; } if ( ! empty( $settings['skip_woocommerce'] ) ) { if ( stripos( $path, 'checkout' ) !== false || stripos( $path, 'cart' ) !== false ) { continue; } } $attempted++; $response = wp_remote_get( $url, array( 'timeout' => 5, 'redirection' => 2 ) ); if ( is_wp_error( $response ) ) { /* translators: %s: text, %s: text. */ $errors[] = sprintf( '%s: %s', $url, $response->get_error_message() ); } else { $code = wp_remote_retrieve_response_code( $response ); if ( $code >= 200 && $code < 400 ) { $succeeded++; } else { /* translators: %s: text, %d: number. */ $errors[] = sprintf( '%s: HTTP %d', $url, $code ); } } if ( $mobile_enabled ) { $attempted++; $mobile_response = wp_remote_get( $url, array( 'timeout' => 5, 'redirection' => 2, 'headers' => $mobile_headers ) ); if ( is_wp_error( $mobile_response ) ) { /* translators: %s: text, %s: text. */ $errors[] = sprintf( '%s (mobile): %s', $url, $mobile_response->get_error_message() ); } else { $code = wp_remote_retrieve_response_code( $mobile_response ); if ( $code >= 200 && $code < 400 ) { $succeeded++; } else { /* translators: %s: text, %d: number. */ $errors[] = sprintf( '%s (mobile): HTTP %d', $url, $code ); } } } if ( $delay > 0 ) { usleep( $delay * 1000 ); } } update_option( self::LOG_OPTION, array( 'time' => time(), 'source' => $source, 'attempted' => $attempted, 'succeeded' => $succeeded, 'errors' => $errors, 'limit' => $limit, 'delay' => $delay, ), false ); if ( ! empty( $remaining ) ) { update_option( self::PREWARM_QUEUE_OPTION, $remaining, false ); wp_schedule_single_event( time() + 30, 'ffascachemanager_cache_prewarm' ); } else { delete_option( self::PREWARM_QUEUE_OPTION ); } } private static function collect_prewarm_urls( $limit ) { $urls = array( home_url( '/' ) ); $sitemap_path = FFASCACHEMANAGER_Pro::get_root_file_path( 'sitemap.xml' ); if ( file_exists( $sitemap_path ) ) { $xml = @file_get_contents( $sitemap_path ); if ( $xml !== false ) { libxml_use_internal_errors( true ); $doc = simplexml_load_string( $xml ); if ( $doc !== false ) { foreach ( $doc->url as $url_node ) { if ( isset( $url_node->loc ) ) { $loc = (string) $url_node->loc; if ( $loc !== '' ) { $urls[] = $loc; } } } } libxml_clear_errors(); } } $menu_urls = self::collect_menu_urls(); if ( ! empty( $menu_urls ) ) { $urls = array_merge( $urls, $menu_urls ); } $home_links = self::collect_homepage_links(); if ( ! empty( $home_links ) ) { $urls = array_merge( $urls, $home_links ); } $posts = get_posts( array( 'post_type' => array( 'page', 'post' ), 'post_status' => 'publish', 'posts_per_page' => $limit, 'fields' => 'ids', ) ); foreach ( $posts as $post_id ) { $permalink = get_permalink( $post_id ); if ( $permalink ) { $urls[] = $permalink; } } if ( class_exists( 'WooCommerce' ) ) { $products = get_posts( array( 'post_type' => 'product', 'post_status' => 'publish', 'posts_per_page' => $limit, 'fields' => 'ids', ) ); foreach ( $products as $product_id ) { $permalink = get_permalink( $product_id ); if ( $permalink ) { $urls[] = $permalink; } } } $urls = array_values( array_unique( array_filter( $urls ) ) ); if ( $limit <= 0 ) { $limit = 200; } return array_slice( $urls, 0, $limit ); } private static function collect_menu_urls() { if ( ! function_exists( 'get_nav_menu_locations' ) ) { return array(); } $locations = get_nav_menu_locations(); if ( empty( $locations ) ) { return array(); } $urls = array(); foreach ( $locations as $location ) { $items = wp_get_nav_menu_items( $location ); if ( empty( $items ) ) { continue; } foreach ( $items as $item ) { if ( empty( $item->url ) ) { continue; } if ( self::is_internal_url( $item->url ) ) { $urls[] = $item->url; } } } return $urls; } private static function collect_homepage_links() { if ( ! function_exists( 'wp_remote_get' ) ) { return array(); } $response = wp_remote_get( home_url( '/' ), array( 'timeout' => 8 ) ); if ( is_wp_error( $response ) ) { return array(); } $html = wp_remote_retrieve_body( $response ); if ( ! is_string( $html ) || $html === '' ) { return array(); } if ( ! class_exists( 'DOMDocument' ) ) { return array(); } libxml_use_internal_errors( true ); $dom = new DOMDocument(); $dom->loadHTML( '' . $html ); libxml_clear_errors(); $links = $dom->getElementsByTagName( 'a' ); if ( ! $links || $links->length === 0 ) { return array(); } $urls = array(); foreach ( $links as $link ) { $href = $link->getAttribute( 'href' ); if ( ! $href ) { continue; } if ( self::is_internal_url( $href ) ) { $urls[] = $href; } } return $urls; } private static function is_internal_url( $url ) { $home = wp_parse_url( home_url(), PHP_URL_HOST ); if ( ! $home ) { return false; } $parts = wp_parse_url( $url ); if ( empty( $parts['host'] ) ) { return true; } return strtolower( $parts['host'] ) === strtolower( $home ); } public static function handle_gc() { $settings = get_option( self::OPTION_NAME, array() ); if ( empty( $settings['enabled'] ) ) { return; } $ttl = isset( $settings['cache_ttl'] ) ? max( 60, intval( $settings['cache_ttl'] ) ) : 3600; self::garbage_collect( $ttl ); } private static function schedule_gc() { if ( wp_next_scheduled( self::GC_HOOK ) ) { return; } wp_schedule_event( time() + 300, 'hourly', self::GC_HOOK ); } private static function garbage_collect( $ttl ) { $dir = self::cache_base_dir(); if ( ! is_dir( $dir ) ) { return; } $now = time(); $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $dir, RecursiveDirectoryIterator::SKIP_DOTS ), RecursiveIteratorIterator::CHILD_FIRST ); foreach ( $iterator as $item ) { if ( $item->isFile() ) { $mtime = $item->getMTime(); if ( ( $now - $mtime ) > $ttl ) { wp_delete_file( $item->getPathname() ); } } elseif ( $item->isDir() ) { // phpcs:ignore @rmdir( $item->getPathname() ); } } } private static function cache_base_dir() { $cache_dir = trailingslashit( WP_CONTENT_DIR ) . 'cache/auditor'; return $cache_dir; } private static function cache_relative_path( $absolute ) { $abs = wp_normalize_path( $absolute ); $root = wp_normalize_path( ABSPATH ); if ( strpos( $abs, $root ) === 0 ) { return ltrim( substr( $abs, strlen( $root ) ), '/' ); } $content = wp_normalize_path( WP_CONTENT_DIR ); if ( strpos( $abs, $content ) === 0 ) { return ltrim( substr( $abs, strlen( $content ) ), '/' ); } return ltrim( $abs, '/' ); } private static function detect_server_type() { $software = isset( $_SERVER['SERVER_SOFTWARE'] ) ? strtolower( $_SERVER['SERVER_SOFTWARE'] ) : ''; if ( $software !== '' ) { if ( strpos( $software, 'nginx' ) !== false ) { return 'nginx'; } if ( strpos( $software, 'apache' ) !== false || strpos( $software, 'litespeed' ) !== false ) { return 'apache'; } } return 'unknown'; } private static function apache_rules_snippet() { $rules = array(); $rules[] = '# BEGIN ' . self::BROWSER_CACHE_MARKER; $rules[] = ''; $rules[] = 'ExpiresActive On'; $rules[] = 'ExpiresDefault "access plus 1 month"'; $rules[] = 'ExpiresByType text/css "access plus 1 year"'; $rules[] = 'ExpiresByType application/javascript "access plus 1 year"'; $rules[] = 'ExpiresByType text/javascript "access plus 1 year"'; $rules[] = 'ExpiresByType application/wasm "access plus 1 year"'; $rules[] = 'ExpiresByType application/manifest+json "access plus 1 month"'; $rules[] = 'ExpiresByType image/avif "access plus 1 year"'; $rules[] = 'ExpiresByType image/webp "access plus 1 year"'; $rules[] = 'ExpiresByType image/jpeg "access plus 1 year"'; $rules[] = 'ExpiresByType image/png "access plus 1 year"'; $rules[] = 'ExpiresByType image/gif "access plus 1 year"'; $rules[] = 'ExpiresByType image/svg+xml "access plus 1 year"'; $rules[] = 'ExpiresByType image/x-icon "access plus 1 year"'; $rules[] = 'ExpiresByType image/bmp "access plus 1 year"'; $rules[] = 'ExpiresByType image/tiff "access plus 1 year"'; $rules[] = 'ExpiresByType font/woff2 "access plus 1 year"'; $rules[] = 'ExpiresByType font/woff "access plus 1 year"'; $rules[] = 'ExpiresByType application/font-woff "access plus 1 year"'; $rules[] = 'ExpiresByType application/vnd.ms-fontobject "access plus 1 year"'; $rules[] = 'ExpiresByType font/ttf "access plus 1 year"'; $rules[] = 'ExpiresByType font/otf "access plus 1 year"'; $rules[] = 'ExpiresByType audio/mpeg "access plus 1 year"'; $rules[] = 'ExpiresByType audio/ogg "access plus 1 year"'; $rules[] = 'ExpiresByType audio/wav "access plus 1 year"'; $rules[] = 'ExpiresByType video/mp4 "access plus 1 year"'; $rules[] = 'ExpiresByType video/webm "access plus 1 year"'; $rules[] = 'ExpiresByType video/ogg "access plus 1 year"'; $rules[] = 'ExpiresByType application/pdf "access plus 1 month"'; $rules[] = 'ExpiresByType application/json "access plus 1 day"'; $rules[] = 'ExpiresByType application/xml "access plus 1 day"'; $rules[] = 'ExpiresByType text/xml "access plus 1 day"'; $rules[] = 'ExpiresByType application/rss+xml "access plus 1 hour"'; $rules[] = 'ExpiresByType application/atom+xml "access plus 1 hour"'; $rules[] = ''; $rules[] = ''; $rules[] = ''; $rules[] = 'Header set Cache-Control "public, max-age=31536000, immutable"'; $rules[] = ''; $rules[] = ''; $rules[] = 'Header set Cache-Control "public, max-age=86400, must-revalidate"'; $rules[] = ''; $rules[] = ''; $rules[] = '# END ' . self::BROWSER_CACHE_MARKER; return implode( "\n", $rules ); } private static function nginx_rules_snippet() { $lines = array(); $lines[] = 'location ~* \\.(?:avif|webp|jpe?g|png|gif|svg|ico|bmp|tiff?|css|js|mjs|wasm|woff2?|ttf|otf|eot|mp3|ogg|oga|wav|mp4|webm|ogv|webmanifest)$ {'; $lines[] = ' expires 1y;'; $lines[] = ' add_header Cache-Control "public, immutable";'; $lines[] = '}'; $lines[] = 'location ~* \\.(?:pdf|json|xml|rss|atom)$ {'; $lines[] = ' expires 1d;'; $lines[] = ' add_header Cache-Control "public, must-revalidate";'; $lines[] = '}'; return implode( "\n", $lines ); } private static function maybe_write_apache_rules() { $settings = get_option( self::OPTION_NAME, array() ); $server = isset( $settings['server_type'] ) ? $settings['server_type'] : ''; if ( $server === '' ) { $server = self::detect_server_type(); } if ( $server !== 'apache' ) { self::remove_apache_rules( true ); return true; } $htaccess = trailingslashit( ABSPATH ) . '.htaccess'; if ( file_exists( $htaccess ) ) { // phpcs:ignore if ( ! is_writable( $htaccess ) ) { return false; } $contents = file_get_contents( $htaccess ); if ( false === $contents ) { return false; } } else { // phpcs:ignore if ( ! is_writable( dirname( $htaccess ) ) ) { return false; } $contents = ''; } // Remove the old direct-delivery page-cache block. Cached HTML now goes // through WordPress so cookie, query-string, status-code, and TTL checks run. $contents = self::remove_marker_block( $contents, self::CACHE_MARKER ); $browser_cache = ! array_key_exists( 'browser_cache', $settings ) || ! empty( $settings['browser_cache'] ); if ( $browser_cache ) { $contents = self::replace_marker_block( $contents, self::apache_rules_snippet(), self::BROWSER_CACHE_MARKER ); } else { $contents = self::remove_marker_block( $contents, self::BROWSER_CACHE_MARKER ); } $written = file_put_contents( $htaccess, $contents ); return $written !== false; } private static function remove_apache_rules( $include_browser = false ) { $htaccess = trailingslashit( ABSPATH ) . '.htaccess'; // phpcs:ignore if ( ! file_exists( $htaccess ) || ! is_writable( $htaccess ) ) { return; } $contents = file_get_contents( $htaccess ); if ( $contents === false ) { return; } $contents = self::remove_marker_block( $contents, self::CACHE_MARKER ); if ( $include_browser ) { $contents = self::remove_marker_block( $contents, self::BROWSER_CACHE_MARKER ); } file_put_contents( $htaccess, trim( $contents ) . "\n" ); } private static function replace_marker_block( $contents, $block, $marker = '' ) { $marker = $marker ? $marker : self::CACHE_MARKER; $start = '# BEGIN ' . $marker; $end = '# END ' . $marker; $pattern = '/' . preg_quote( $start, '/' ) . '.*?' . preg_quote( $end, '/' ) . '\\s*/s'; if ( preg_match( $pattern, $contents ) ) { return preg_replace( $pattern, $block . "\n", $contents ); } return rtrim( $contents ) . "\n\n" . $block . "\n"; } private static function remove_marker_block( $contents, $marker ) { $start = '# BEGIN ' . $marker; $end = '# END ' . $marker; $pattern = '/' . preg_quote( $start, '/' ) . '.*?' . preg_quote( $end, '/' ) . '\\s*/s'; return preg_replace( $pattern, '', $contents ); } private static function is_mobile_cache_enabled() { $settings = get_option( self::OPTION_NAME, array() ); return ! empty( $settings['mobile_cache'] ); } }