should_cache() ) { // Serve cache as early as possible on init, fallback to template_redirect. add_action( 'init', array( $this, 'maybe_serve_cache' ), 1 ); add_action( 'template_redirect', array( $this, 'maybe_serve_cache' ), 0 ); // WordPress 6.9+ standardized template enhancement output buffer support. add_filter( 'wp_should_output_buffer_template_for_enhancement', '__return_true' ); add_filter( 'wp_template_enhancement_output_buffer', array( $this, 'filter_template_enhancement_buffer' ), 20, 2 ); add_action( 'template_redirect', array( $this, 'start_buffer' ), 1 ); } } /** * Check if the current request should be cached. * * @return bool */ private function should_cache() { if ( is_admin() ) { return false; } if ( defined( 'WP_CLI' ) && WP_CLI ) { return false; } if ( wpsco_get_setting( 'emergency_disable', false ) ) { return false; } return true; } /** * Try to serve a cached page. */ public function maybe_serve_cache() { if ( headers_sent() ) { return; } // Instant force-purge action for administrators via URL (?wpsco_purge=1 or ?wpsco_purge_all=1 or ?purge=1). // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Administrative cache purge parameter check. if ( isset( $_GET['wpsco_purge'] ) || isset( $_GET['wpsco_purge_all'] ) || isset( $_GET['purge'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Administrative cache purge parameter check. if ( function_exists( 'current_user_can' ) && current_user_can( 'manage_options' ) && isset( $_GET['_wpnonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'wpsco_purge_cache' ) ) { $purger = new WPSCO_Cache_Purger(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified above. if ( isset( $_GET['wpsco_purge_all'] ) ) { $purger->purge_everything(); } else { $cache_file = $this->get_cache_file_path(); wp_delete_file( $cache_file ); wp_delete_file( $cache_file . '.gz' ); } header( 'X-WP-Speed-Cache: PURGED' ); header( 'Cache-Control: no-cache, no-store, must-revalidate, max-age=0' ); header( 'Pragma: no-cache' ); return; } } if ( wpsco_is_cache_excluded() ) { header( 'X-WP-Speed-Cache: BYPASS' ); header( 'Cache-Control: no-cache, no-store, must-revalidate, max-age=0' ); header( 'Pragma: no-cache' ); return; } $cache_file = $this->get_cache_file_path(); if ( ! file_exists( $cache_file ) ) { return; } $ttl = (int) wpsco_get_setting( 'page_cache_ttl', 3600 ); $file_time = filemtime( $cache_file ); if ( ( time() - $file_time ) > $ttl ) { wp_delete_file( $cache_file ); wp_delete_file( $cache_file . '.gz' ); return; } $gz_file = $cache_file . '.gz'; $http_accept_encoding = isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_ACCEPT_ENCODING'] ) ) : ''; $accept_gzip = ! empty( $http_accept_encoding ) && stripos( $http_accept_encoding, 'gzip' ) !== false; $remaining = max( 0, min( 120, $ttl - ( time() - $file_time ) ) ); // Send cache headers. header( 'X-WP-Speed-Cache: HIT' ); header( 'X-WP-Speed-Cache-Age: ' . ( time() - $file_time ) ); header( 'Cache-Control: public, max-age=' . $remaining . ', must-revalidate' ); header( 'Vary: Accept-Encoding, User-Agent' ); header( 'Content-Type: text/html; charset=UTF-8' ); // Serve compressed if client supports gzip. if ( $accept_gzip ) { header( 'Content-Encoding: gzip' ); if ( file_exists( $gz_file ) ) { $gz_content = wpsco_read_file( $gz_file ); if ( false !== $gz_content ) { echo $gz_content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Pre-compressed gzipped HTML cache output. exit; } } $content = wpsco_read_file( $cache_file ); if ( false !== $content ) { echo gzencode( $content, 6 ); // phpcs:ignore exit; } } $content = wpsco_read_file( $cache_file ); if ( false !== $content ) { echo $content; // phpcs:ignore exit; } } /** * Start output buffering. */ public function start_buffer() { if ( wpsco_is_cache_excluded() ) { return; } // If WordPress 6.9+ core template enhancement output buffer is active, let core manage it. if ( function_exists( 'has_filter' ) && has_filter( 'wp_template_enhancement_output_buffer' ) && did_action( 'wp_finalized_template_enhancement_output_buffer' ) ) { return; } $this->buffer_active = true; ob_start( array( $this, 'save_cache' ) ); add_action( 'shutdown', array( $this, 'end_buffer' ), 0 ); } /** * Explicitly close and flush the output buffer at shutdown to prevent buffer-stack misalignment. */ public function end_buffer() { if ( $this->buffer_active && ob_get_level() > 0 ) { $this->buffer_active = false; ob_end_flush(); } } /** * WordPress 6.9+ template enhancement output buffer callback. * * @param string $buffer Rendered HTML output. * @param string $original Original HTML before core enhancements. * @return string */ public function filter_template_enhancement_buffer( $buffer, $original = '' ) { $this->buffer_active = true; return $this->save_cache( $buffer ); } /** * Save the output buffer to cache. * * @param string $buffer Page output. * @return string */ public function save_cache( $buffer ) { if ( ! $this->buffer_active || empty( $buffer ) ) { return $buffer; } if ( http_response_code() !== 200 ) { return $buffer; } if ( wpsco_is_woocommerce_nocache_page() || wpsco_is_cache_excluded() ) { return $buffer; } // Add speed cache stamp. $timestamp = gmdate( 'Y-m-d H:i:s' ); $buffer .= "\n"; $cache_file = $this->get_cache_file_path(); // Save raw HTML via safe WordPress Filesystem API. wpsco_write_file( $cache_file, $buffer ); // Save pre-compressed GZIP copy for instant delivery. if ( function_exists( 'gzencode' ) ) { wpsco_write_file( $cache_file . '.gz', gzencode( $buffer, 6 ) ); } if ( ! headers_sent() ) { header( 'X-WP-Speed-Cache: MISS' ); } return $buffer; } /** * Get the cache file path for the current request. * * @return string */ private function get_cache_file_path() { $request = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '/'; $parsed = wp_parse_url( $request ); $path = isset( $parsed['path'] ) ? $parsed['path'] : '/'; $path = wpsco_sanitize_cache_path( $path ); if ( empty( $path ) ) { $path = 'index'; } $host = isset( $_SERVER['HTTP_HOST'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : 'default'; $host = preg_replace( '/[^a-zA-Z0-9\.\-]/', '', $host ); $suffix = ''; if ( wp_is_mobile() ) { $suffix = '-mobile'; } return wpsco_get_cache_dir( 'html/' . $host . '/' . $path . $suffix . '.html' ); } }