# xspeed/1.0.4/includes/class-cache-benchmark.php

xSpeed Cache: AI-Powered Performance Hub with MCP, Caching &amp; CDN, version 1.0.4. 152 lines.

- Page: https://pluginprobe.com/plugins/xspeed/1.0.4/code/includes/class-cache-benchmark.php
- Raw: https://pluginprobe.com/plugins/xspeed/1.0.4/raw/includes/class-cache-benchmark.php
- Modified: 2026-06-14T07:01:26+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/xspeed/1.0.4/code/includes/class-cache-benchmark.php#L10-L20`.

```php
<?php
/**
 * Cache_Benchmark — fetches home_url() twice (with + without cache) and
 * returns side-by-side TTFB / total-time / transfer-bytes timings.
 *
 * Powers the "before vs after cache" widget on the wizard's Done step
 * and the Cache panel. Synthetic — measures local HTTP only, no real
 * RUM. Good enough for a directional "cache helps by X%" number; the
 * RUM module is what you want for real percentiles.
 *
 * Mechanics:
 *  - "Without cache": HTTP GET home_url() with header
 *    `X-XSpeed-Bypass: 1`. The advanced-cache drop-in honors this
 *    header and short-circuits, so WordPress fully renders.
 *  - "With cache": HTTP GET home_url() with no special header. The
 *    drop-in serves the cached file (cache HIT) when available.
 *  - First "with-cache" call may MISS if no entry yet — we run a
 *    warm-up hit first so the timed pair is HIT vs render.
 *
 * Each measurement records:
 *   ttfb_ms  — curl_getinfo CURLINFO_STARTTRANSFER_TIME (or our own
 *              "time before body read" fallback).
 *   time_ms  — total response time.
 *   bytes    — Content-Length or strlen(body).
 *   status   — HTTP code.
 *
 * @package XSpeed
 */

declare(strict_types=1);

namespace XSpeed;

defined( 'ABSPATH' ) || exit;

final class Cache_Benchmark {

	/**
	 * @return array{
	 *   url:string,
	 *   without_cache:array{ttfb_ms:float,time_ms:float,bytes:int,status:int},
	 *   with_cache:array{ttfb_ms:float,time_ms:float,bytes:int,status:int,was_hit:bool},
	 *   savings_pct:?float,
	 *   savings_ms:?float,
	 *   cache_enabled:bool,
	 * }
	 */
	public static function run( ?string $url = null ): array {
		if ( null === $url ) {
			$url = home_url( '/' );
		}
		// Cache enablement lives on the legacy Settings option
		// (`cache_enabled` boolean). Cache::is_enabled() doesn't
		// exist — read through Settings::get() instead.
		$cache_enabled = false;
		if ( class_exists( '\\XSpeed\\Settings' ) ) {
			$opts          = Settings::get();
			$cache_enabled = ! empty( $opts['cache_enabled'] );
		}

		// Warm up so the "with cache" timing reflects a HIT, not the
		// initial generation cost.
		if ( $cache_enabled ) {
			self::measure( $url, false );
		}

		$without = self::measure( $url, true );  // bypass
		$with    = self::measure( $url, false ); // normal — should HIT

		$savings_ms  = null;
		$savings_pct = null;
		if ( $cache_enabled && $without['time_ms'] > 0 && $with['time_ms'] > 0 ) {
			$diff        = $without['time_ms'] - $with['time_ms'];
			$savings_ms  = max( 0.0, round( $diff, 1 ) );
			$savings_pct = round( ( $diff / $without['time_ms'] ) * 100, 1 );
			if ( $savings_pct < 0 ) {
				$savings_pct = 0.0;
			}
		}

		return array(
			'url'           => $url,
			'without_cache' => $without,
			'with_cache'    => $with + array( 'was_hit' => $cache_enabled ),
			'savings_pct'   => $savings_pct,
			'savings_ms'    => $savings_ms,
			'cache_enabled' => $cache_enabled,
		);
	}

	/**
	 * Single timed request. wp_remote_get's `args` don't expose curl-
	 * level timing on every transport, so we wrap the call ourselves
	 * with microtime — close enough for a directional comparison.
	 *
	 * @return array{ttfb_ms:float,time_ms:float,bytes:int,status:int}
	 */
	private static function measure( string $url, bool $bypass ): array {
		$headers = array(
			'User-Agent' => 'xSpeed Benchmark/1.0',
		);
		if ( $bypass ) {
			// The X-XSpeed-Bypass header only short-circuits the PHP
			// drop-in. When nginx static-rewrite is firing it would
			// still serve the cached file directly, so the "without
			// cache" measurement would look identical to "with cache"
			// (the bug visible on the dashboard's benchmark widget).
			// Append a cache-buster query string so the nginx
			// snippet's `if ($args)` check bails too, forcing the
			// request all the way through to PHP.
			$headers['X-XSpeed-Bypass'] = '1';
			$bust_url = $url . ( false === strpos( $url, '?' ) ? '?' : '&' )
				. 'xspeed_bypass=' . wp_generate_password( 12, false, false );
			$url = $bust_url;
		}
		$start = microtime( true );
		$res   = wp_remote_get(
			$url,
			array(
				'timeout' => 10,
				'headers' => $headers,
				// Disable WP's internal caching layer — every call MUST hit the network.
				'reject_unsafe_urls' => false,
				'sslverify' => false, // self-signed sandboxes
			)
		);
		$elapsed = ( microtime( true ) - $start ) * 1000.0;

		if ( is_wp_error( $res ) ) {
			return array(
				'ttfb_ms' => 0.0,
				'time_ms' => round( $elapsed, 1 ),
				'bytes'   => 0,
				'status'  => 0,
			);
		}
		$status = (int) wp_remote_retrieve_response_code( $res );
		$body   = wp_remote_retrieve_body( $res );
		$bytes  = is_string( $body ) ? strlen( $body ) : 0;

		// wp_remote_get doesn't surface TTFB separately on the default
		// transport. We surface total time as both fields and let the
		// widget pick a sensible display.
		return array(
			'ttfb_ms' => round( $elapsed, 1 ),
			'time_ms' => round( $elapsed, 1 ),
			'bytes'   => $bytes,
			'status'  => $status,
		);
	}
}

```
