http_response_code(), 'headers' => self::response_headers(), 'new_cookies_set' => self::response_cookie_names(), 'content_type' => self::response_content_type(), ); $classification = Argus_Cache_Eligibility::classify_response( $response_context ); if ( Argus_Cache_Eligibility::SAFE_TO_CACHE !== $classification['status'] || '' === $html ) { $log_status = '' === $html ? self::STATUS_BYPASS : ( Argus_Cache_Eligibility::FORCE_EXCLUDED === $classification['status'] ? self::STATUS_EXCLUDED : self::STATUS_BYPASS ); Argus_Cache_Log::record( $log_status, $url, '' === $html ? 'Empty response body.' : $classification['reason'], $response_ms ); self::release_lock( $lock ); return $html; } self::write_cache_entry( $paths, $html, $url ); Argus_Cache_Log::record( $status, $url, 'Stored.', $response_ms ); self::release_lock( $lock ); return $html; } protected static function write_cache_entry( array $paths, $html, $url ) { wp_mkdir_p( dirname( $paths['html'] ) ); file_put_contents( $paths['html'], $html ); // phpcs:ignore WordPress.WP.AlternativeFunctions $min_bytes = (int) Argus_Settings::get( 'cache_min_compress_bytes', 1024 ); if ( strlen( $html ) >= $min_bytes ) { if ( Argus_Settings::get( 'cache_gzip_enabled', true ) && function_exists( 'gzencode' ) ) { $level = (int) Argus_Settings::get( 'cache_gzip_level', 6 ); file_put_contents( $paths['gz'], gzencode( $html, max( 1, min( 9, $level ) ) ) ); // phpcs:ignore } else { @unlink( $paths['gz'] ); // phpcs:ignore } if ( Argus_Settings::get( 'cache_brotli_enabled', true ) && function_exists( 'brotli_compress' ) ) { $level = (int) Argus_Settings::get( 'cache_brotli_level', 5 ); file_put_contents( $paths['br'], brotli_compress( $html, max( 0, min( 11, $level ) ) ) ); // phpcs:ignore } else { @unlink( $paths['br'] ); // phpcs:ignore } } else { @unlink( $paths['gz'] ); // phpcs:ignore @unlink( $paths['br'] ); // phpcs:ignore } file_put_contents( // phpcs:ignore $paths['meta'], wp_json_encode( array( 'url' => $url, 'bytes' => strlen( $html ), 'created_at' => current_time( 'mysql', true ), ) ) ); } protected static function serve_hit( array $paths, $url, $status, $start ) { $encoding = self::negotiate_encoding(); $source = $paths['html']; $header_encoding = ''; if ( 'br' === $encoding && file_exists( $paths['br'] ) ) { $source = $paths['br']; $header_encoding = 'br'; } elseif ( 'gzip' === $encoding && file_exists( $paths['gz'] ) ) { $source = $paths['gz']; $header_encoding = 'gzip'; } header( 'X-Argus-Cache: ' . $status ); header( 'X-Argus-Cache-Age: ' . ( time() - filemtime( $paths['html'] ) ) ); header( 'Vary: Accept-Encoding' ); if ( $header_encoding ) { header( 'Content-Encoding: ' . $header_encoding ); } header( 'Content-Type: text/html; charset=UTF-8' ); readfile( $source ); // phpcs:ignore WordPress.WP.AlternativeFunctions Argus_Cache_Log::record( $status, $url, 'Served from cache.', (int) round( ( microtime( true ) - $start ) * 1000 ) ); exit; } protected static function negotiate_encoding() { $accept = strtolower( (string) ( $_SERVER['HTTP_ACCEPT_ENCODING'] ?? '' ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput if ( false !== strpos( $accept, 'br' ) ) { return 'br'; } if ( false !== strpos( $accept, 'gzip' ) ) { return 'gzip'; } return ''; } protected static function request_context() { $cookies = array(); foreach ( (array) $_COOKIE as $name => $value ) { // phpcs:ignore WordPress.Security.NonceVerification $cookies[ (string) $name ] = ''; } return array( 'method' => $_SERVER['REQUEST_METHOD'] ?? 'GET', // phpcs:ignore WordPress.Security.ValidatedSanitizedInput 'path' => (string) wp_parse_url( self::current_url(), PHP_URL_PATH ), 'query' => $_GET, // phpcs:ignore WordPress.Security.NonceVerification 'is_admin' => is_admin(), 'is_logged_in' => is_user_logged_in(), 'cookies' => $cookies, 'has_authorization_header' => ! empty( $_SERVER['HTTP_AUTHORIZATION'] ) || ! empty( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ), // phpcs:ignore 'is_ajax' => wp_doing_ajax(), 'is_rest' => defined( 'REST_REQUEST' ) && REST_REQUEST, 'is_cron' => defined( 'DOING_CRON' ) && DOING_CRON, ) + self::conditional_tag_excludes(); } protected static function conditional_tag_excludes() { if ( is_feed() || is_trackback() || is_preview() || is_search() || is_404() ) { return array( 'is_admin' => true ); } return array(); } protected static function response_headers() { $out = array(); foreach ( headers_list() as $header ) { $parts = explode( ':', $header, 2 ); if ( 2 !== count( $parts ) ) { continue; } $name = strtolower( trim( $parts[0] ) ); if ( isset( $out[ $name ] ) ) { $out[ $name ] .= ', ' . trim( $parts[1] ); } else { $out[ $name ] = trim( $parts[1] ); } } return $out; } protected static function response_cookie_names() { $names = array(); foreach ( headers_list() as $header ) { if ( 0 === stripos( $header, 'Set-Cookie:' ) && preg_match( '/^Set-Cookie:\s*([^=]+)=/i', $header, $m ) ) { $names[] = trim( $m[1] ); } } return $names; } protected static function response_content_type() { foreach ( headers_list() as $header ) { if ( 0 === stripos( $header, 'Content-Type:' ) ) { return trim( substr( $header, strlen( 'Content-Type:' ) ) ); } } return ''; } protected static function current_url() { $scheme = is_ssl() ? 'https' : 'http'; $host = (string) ( $_SERVER['HTTP_HOST'] ?? 'default' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput $uri = (string) ( $_SERVER['REQUEST_URI'] ?? '/' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput return $scheme . '://' . $host . $uri; } public static function cache_key( $url ) { $parts = wp_parse_url( $url ); $path = $parts['path'] ?? '/'; $query = array(); if ( ! empty( $parts['query'] ) ) { parse_str( $parts['query'], $query ); $query = array_diff_key( $query, array_flip( Argus_Cache_Eligibility::IGNORABLE_QUERY_PARAMS ) ); ksort( $query ); } $host = (string) ( $parts['host'] ?? 'default' ); return md5( $host . '|' . $path . '|' . http_build_query( $query ) ); } protected static function cache_dir() { $uploads = wp_get_upload_dir(); return trailingslashit( $uploads['basedir'] ) . 'argus-wpd-data/html-cache'; } public static function cache_paths( $key ) { $base = trailingslashit( self::cache_dir() ) . $key; return array( 'html' => $base . '.html', 'gz' => $base . '.html.gz', 'br' => $base . '.html.br', 'meta' => $base . '.meta.json', ); } protected static function lock_dir() { return trailingslashit( self::cache_dir() ) . 'locks'; } protected static function acquire_lock( $key ) { $dir = self::lock_dir(); if ( ! is_dir( $dir ) ) { wp_mkdir_p( $dir ); } $path = trailingslashit( $dir ) . $key . '.lock'; $fh = @fopen( $path, 'c' ); // phpcs:ignore WordPress.WP.AlternativeFunctions,WordPress.PHP.NoSilencedErrors if ( ! $fh ) { return false; } if ( ! flock( $fh, LOCK_EX | LOCK_NB ) ) { fclose( $fh ); // phpcs:ignore WordPress.WP.AlternativeFunctions return false; } return $fh; } protected static function release_lock( $fh ) { if ( $fh ) { flock( $fh, LOCK_UN ); fclose( $fh ); // phpcs:ignore WordPress.WP.AlternativeFunctions } } public static function purge_all() { $dir = self::cache_dir(); if ( ! is_dir( $dir ) ) { return 0; } $count = 0; foreach ( array( '*.html', '*.html.gz', '*.html.br', '*.meta.json' ) as $glob ) { foreach ( glob( trailingslashit( $dir ) . $glob ) ?: array() as $file ) { @unlink( $file ); // phpcs:ignore WordPress.PHP.NoSilencedErrors ++$count; } } Argus_Cache_Log::record( self::STATUS_PURGED, '*', 'Purge everything.' ); return $count; } public static function purge_url( $url ) { $key = self::cache_key( $url ); foreach ( self::cache_paths( $key ) as $path ) { @unlink( $path ); // phpcs:ignore WordPress.PHP.NoSilencedErrors } Argus_Cache_Log::record( self::STATUS_PURGED, $url, 'Purge single URL.' ); } public static function purge_pattern( $pattern ) { $dir = self::cache_dir(); if ( ! is_dir( $dir ) ) { return 0; } $regex = '/^' . str_replace( '\*', '.*', preg_quote( $pattern, '/' ) ) . '$/i'; $count = 0; foreach ( glob( trailingslashit( $dir ) . '*.meta.json' ) ?: array() as $meta_file ) { $meta = json_decode( (string) file_get_contents( $meta_file ), true ); // phpcs:ignore if ( ! is_array( $meta ) || empty( $meta['url'] ) || ! preg_match( $regex, $meta['url'] ) ) { continue; } $key = basename( $meta_file, '.meta.json' ); foreach ( self::cache_paths( $key ) as $path ) { @unlink( $path ); // phpcs:ignore WordPress.PHP.NoSilencedErrors } ++$count; } Argus_Cache_Log::record( self::STATUS_PURGED, $pattern, 'Purge by pattern (' . $count . ' entries).' ); return $count; } public static function purge_related_to_post( $post_id ) { if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) { return; } $permalink = get_permalink( $post_id ); if ( $permalink ) { self::purge_url( $permalink ); } self::purge_url( home_url( '/' ) ); $page_for_posts = (int) get_option( 'page_for_posts' ); if ( $page_for_posts ) { self::purge_url( get_permalink( $page_for_posts ) ); } } public static function purge_related_to_comment( $comment_id ) { $comment = get_comment( $comment_id ); if ( $comment && $comment->comment_post_ID ) { self::purge_related_to_post( (int) $comment->comment_post_ID ); } } public static function stats() { $dir = self::cache_dir(); if ( ! is_dir( $dir ) ) { return array( 'file_count' => 0, 'total_bytes' => 0 ); } $files = glob( trailingslashit( $dir ) . '*.html' ) ?: array(); $bytes = 0; foreach ( $files as $file ) { $bytes += filesize( $file ); foreach ( array( '.gz', '.br' ) as $ext ) { $variant = $file . $ext; if ( file_exists( $variant ) ) { $bytes += filesize( $variant ); } } } return array( 'file_count' => count( $files ), 'total_bytes' => $bytes ); } public static function compression_stats() { $dir = self::cache_dir(); $out = array( 'gzip' => array( 'files' => 0, 'avg_savings_pct' => 0.0, 'bytes_saved' => 0 ), 'brotli' => array( 'files' => 0, 'avg_savings_pct' => 0.0, 'bytes_saved' => 0 ), 'total_bytes_saved' => 0, ); if ( ! is_dir( $dir ) ) { return $out; } $files = glob( trailingslashit( $dir ) . '*.html' ) ?: array(); $pct_sums = array( 'gzip' => 0.0, 'brotli' => 0.0 ); foreach ( $files as $file ) { $plain_size = filesize( $file ); if ( ! $plain_size ) { continue; } foreach ( array( 'gzip' => '.gz', 'brotli' => '.br' ) as $format => $ext ) { $variant = $file . $ext; if ( ! file_exists( $variant ) ) { continue; } $variant_size = filesize( $variant ); if ( ! $variant_size || $variant_size >= $plain_size ) { continue; } $saved = $plain_size - $variant_size; ++$out[ $format ]['files']; $out[ $format ]['bytes_saved'] += $saved; $out['total_bytes_saved'] += $saved; $pct_sums[ $format ] += ( $saved / $plain_size ) * 100; } } foreach ( array( 'gzip', 'brotli' ) as $format ) { if ( $out[ $format ]['files'] > 0 ) { $out[ $format ]['avg_savings_pct'] = round( $pct_sums[ $format ] / $out[ $format ]['files'], 1 ); } } return $out; } public static function list_cached( $limit = 100 ) { $dir = self::cache_dir(); if ( ! is_dir( $dir ) ) { return array(); } $meta_files = glob( trailingslashit( $dir ) . '*.meta.json' ) ?: array(); usort( $meta_files, function ( $a, $b ) { return filemtime( $b ) <=> filemtime( $a ); } ); $out = array(); foreach ( array_slice( $meta_files, 0, $limit ) as $meta_file ) { $meta = json_decode( (string) file_get_contents( $meta_file ), true ); // phpcs:ignore if ( ! is_array( $meta ) ) { continue; } $key = basename( $meta_file, '.meta.json' ); $paths = self::cache_paths( $key ); $out[] = array( 'url' => $meta['url'] ?? '', 'bytes' => (int) ( $meta['bytes'] ?? 0 ), 'created_at' => $meta['created_at'] ?? '', 'age_secs' => file_exists( $paths['html'] ) ? ( time() - filemtime( $paths['html'] ) ) : null, 'has_gzip' => file_exists( $paths['gz'] ), 'has_brotli' => file_exists( $paths['br'] ), ); } return $out; } }