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, ); } }