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( '/