PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.3.7
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.3.7
1.3.7 1.3.6 1.3.5 1.3.4 1.3.3 1.3.2 1.3.1 1.3.0 1.2.4 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 All 33 releases
← All changes | includes/advanced-cache.php +67 -3 1.3.1 → 1.3.7 View file →
@@ -1,8 +1,8 @@
1 1 <?php
2 2 /**
3 3 * XSPEED_DROPIN
4 - * XSPEED_DROPIN_VERSION: 8
4 + * XSPEED_DROPIN_VERSION: 10
5 5 * Drop-in cache loader. Serves cached HTML before WordPress fully boots.
6 6 *
7 7 * Bump XSPEED_DROPIN_VERSION whenever this file's serve logic changes so
8 8 * Cache::ensure_dropin_current() reinstalls it on existing sites (the
@@ -28,8 +28,15 @@
28 28 * v8: never serve an empty, stale, or short `.br` sibling — an uninflatable
29 29 * brotli stream renders as a blank page. THIS FILE IS A COPY made when
30 30 * caching was enabled, so without the bump an updated site keeps the old
31 31 * serve logic and never receives the fix (#286).
32 + * v9: carry the baked edge-header answer, so a hold set for a page reaches
33 + * the paths that run without PHP.
34 + * v10: keep bots, scanners, cached 404s and xSpeed's own requests (by UA
35 + * or the X-XSpeed-Self header) out of hits.log. Without
36 + * the bump an existing install keeps writing every crawler HIT into the
37 + * ratio while its misses are excluded, which reads MORE optimistic than
38 + * having no exclusion at all.
32 39 *
33 40 * IMPORTANT: This file is included by wp-settings.php BEFORE
34 41 * wp-includes/formatting.php and wp-includes/load.php are loaded, so NO
35 42 * WordPress functions (sanitize_text_field, wp_unslash, is_admin,
@@ -319,8 +326,42 @@
319 326 // rewrite sends "HIT (nginx)" for the fast 5-15ms path; same header,
320 327 // distinct value so you can tell which layer served the page.
321 328 header( 'X-XSpeed-Cache: HIT (php)' );
322 329
330 + // Edge/CDN headers decided by Cache::edge_headers_for(). No filter
331 + // can run here — plugins are not loaded — so Cache::install_dropin()
332 + // bakes the resolved pairs into the literal below and re-bakes them
333 + // on every cache settings save.
334 + //
335 + // An un-substituted placeholder means this file was copied straight
336 + // from a source checkout: it stays a string, is_array() rejects it,
337 + // and the HIT is served with no edge headers rather than a fatal.
338 + $xspeed_edge_headers = '@@XSPEED_EDGE_HEADERS@@';
339 +
340 + // A page whose answer differs from the site-wide one carries its own
341 + // pairs in the sidecar. It REPLACES the baked set rather than adding
342 + // to it: the two describe the same response, and merging would leave
343 + // the baked lifetime in place beside the hold meant to overrule it.
344 + if ( isset( $xspeed_meta['edge_headers'] ) && is_array( $xspeed_meta['edge_headers'] ) ) {
345 + $xspeed_edge_headers = $xspeed_meta['edge_headers'];
346 + }
347 +
348 + // The one setting this file reads for itself. Everything else about
349 + // the edge answer is baked, because re-deriving it here would mean
350 + // loading options before WordPress exists. `off` is the exception
351 + // because it is the emergency switch: when something is wrong in
352 + // production at three in the morning, waiting for a re-bake is not an
353 + // answer. Any other value is a pin, and a pin is already baked in.
354 + if ( defined( 'XSPEED_EDGE_PROVIDER' ) && 'off' === strtolower( (string) XSPEED_EDGE_PROVIDER ) ) {
355 + $xspeed_edge_headers = array();
356 + }
357 +
358 + if ( is_array( $xspeed_edge_headers ) ) {
359 + foreach ( $xspeed_edge_headers as $xspeed_edge_name => $xspeed_edge_value ) {
360 + header( $xspeed_edge_name . ': ' . $xspeed_edge_value );
361 + }
362 + }
363 +
323 364 // Record the HIT for the dashboard hit-ratio. The drop-in runs
324 365 // BEFORE WordPress loads, so it can't call Hit_Counter — instead
325 366 // it appends one line to the same hits.log the nginx static path
326 367 // uses, and Hit_Counter::collect_nginx_log_hits() drains + counts
@@ -338,10 +379,33 @@
338 379 $xspeed_hits_log = '@@XSPEED_HITS_LOG@@'; // replaced at install
339 380 if ( '@@' === substr( $xspeed_hits_log, 0, 2 ) ) {
340 381 $xspeed_hits_log = WP_CONTENT_DIR . '/uploads/xspeed/hits.log';
341 382 }
342 - // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- pre-WP drop-in; WP_Filesystem isn't loaded. One short line, append + lock; failures are non-fatal (the ratio just under-counts).
343 - @file_put_contents( $xspeed_hits_log, "hit\n", FILE_APPEND | LOCK_EX );
383 + // Don't count a bot, a scanner, a 404 or one of xSpeed's own
384 + // requests as a visitor hit. It has to be decided HERE: the log line
385 + // is just "hit" with no user agent, so Hit_Counter batch-counts these
386 + // lines blind and nothing downstream can reclassify one. The UA
387 + // pattern is baked in at install time from
388 + // Hit_Counter::excluded_ua_regex() (the drop-in runs before
389 + // WordPress, so it cannot ask). xSpeed's own requests also carry the
390 + // X-XSpeed-Self header (Self_Traffic::HEADER), which is what catches
391 + // a warmer renamed to a real browser's UA without dropping real
392 + // visitors on that browser. An empty UA counts as automated, like
393 + // is_bot_ua(''). A cached 404 is excluded on the PHP path too.
394 + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- pre-WP drop-in; only matched against a baked pattern, never echoed or stored.
395 + $xspeed_hit_ua = isset( $_SERVER['HTTP_USER_AGENT'] ) ? (string) $_SERVER['HTTP_USER_AGENT'] : '';
396 + $xspeed_hit_ex = '@@XSPEED_HIT_EXCLUDE_RE@@';
397 + $xspeed_self = '' === $xspeed_hit_ua
398 + || ! empty( $_SERVER['HTTP_X_XSPEED_SELF'] )
399 + || ( isset( $xspeed_meta['status'] ) && 404 === (int) $xspeed_meta['status'] );
400 + if ( ! $xspeed_self && '@@' !== substr( $xspeed_hit_ex, 0, 2 ) && '' !== $xspeed_hit_ex ) {
401 + // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- a pattern this file did not compose is not worth a warning on every hit.
402 + $xspeed_self = 1 === @preg_match( '#(' . $xspeed_hit_ex . ')#i', $xspeed_hit_ua );
403 + }
404 + if ( ! $xspeed_self ) {
405 + // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- pre-WP drop-in; WP_Filesystem isn't loaded. One short line, append + lock; failures are non-fatal (the ratio just under-counts).
406 + @file_put_contents( $xspeed_hits_log, "hit\n", FILE_APPEND | LOCK_EX );
407 + }
344 408
345 409 // Replay the cached response's status + content-type from .meta, so a
346 410 // cached 404 serves 404 (not a soft-404 200) and a cached feed serves
347 411 // application/rss+xml (not text/html). (FBS-82406, FBS-82407)