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/modules/BrowserCache/BrowserCacheModule.php +79 -17 1.0.1 → 1.3.7 View file →
@@ -13,9 +13,12 @@
13 13 declare(strict_types=1);
14 14
15 15 namespace XSpeed\Modules\BrowserCache;
16 16
17 +defined( 'ABSPATH' ) || exit;
18 +
17 19 use XSpeed\Browser_Cache;
20 +use XSpeed\Deep_Link;
18 21 use XSpeed\Module;
19 22 use XSpeed\Server;
20 23
21 24 final class BrowserCacheModule extends Module {
@@ -25,11 +28,12 @@
25 28 public const VERSION = '1.0.0';
26 29
27 30 public function ui_metadata(): array {
28 31 return array(
29 - 'label' => 'Browser Cache',
32 + 'label' => __( 'Browser Cache', 'xspeed' ),
30 33 'icon' => 'Clock',
31 - 'description' => 'Tell browsers (and intermediate CDNs) how long to cache static assets and HTML.',
34 + 'description' => __( 'Tells browsers and CDNs how long to keep your files and pages.', 'xspeed' ),
35 + 'group' => 'cache',
32 36 );
33 37 }
34 38
35 39 public function settings_schema(): array {
@@ -36,10 +40,10 @@
36 40 return array(
37 41 'enabled' => array(
38 42 'type' => 'bool',
39 43 'default' => false,
40 - 'label' => 'Enable browser cache headers',
41 - 'description' => 'On Apache/LiteSpeed this writes Cache-Control + Expires rules into .htaccess. On nginx it just stores the settings — you paste the snippet into your server block manually.',
44 + 'label' => __( 'Enable browser cache headers', 'xspeed' ),
45 + 'description' => __( 'On Apache and LiteSpeed, xSpeed adds the rules to .htaccess for you. On nginx, you copy the snippet into your server config.', 'xspeed' ),
42 46 ),
43 47 'asset_ttl' => array(
44 48 'type' => 'int',
45 49 'default' => Browser_Cache::DEFAULT_ASSET_TTL,
@@ -44,10 +48,12 @@
44 48 'type' => 'int',
45 49 'default' => Browser_Cache::DEFAULT_ASSET_TTL,
46 50 'min' => 0,
47 51 'max' => 31536000,
48 - 'label' => 'Static asset TTL (seconds)',
49 - 'description' => 'Cache lifetime for CSS, JS, fonts, images. Defaults to 1 year + immutable (the industry-standard "fingerprinted assets never change" pattern).',
52 + 'label' => __( 'File lifetime (seconds)', 'xspeed' ),
53 + 'unit' => 'seconds',
54 + 'description' => __( 'How long browsers keep CSS, JS, fonts and images. The default is 1 year, which suits files that change name when they change.', 'xspeed' ),
55 + 'dependsOn' => array( 'field' => 'enabled' ),
50 56 ),
51 57 'html_ttl' => array(
52 58 'type' => 'int',
53 59 'default' => Browser_Cache::DEFAULT_HTML_TTL,
@@ -52,10 +58,12 @@
52 58 'type' => 'int',
53 59 'default' => Browser_Cache::DEFAULT_HTML_TTL,
54 60 'min' => 0,
55 61 'max' => 31536000,
56 - 'label' => 'HTML TTL (seconds)',
57 - 'description' => 'Cache lifetime for the HTML document itself. Keep short (default 1h) so post edits roll out same-day.',
62 + 'label' => __( 'Page lifetime (seconds)', 'xspeed' ),
63 + 'unit' => 'seconds',
64 + 'description' => __( 'How long browsers keep the page itself. Keep it short (default 1 hour) so visitors see your edits the same day.', 'xspeed' ),
65 + 'dependsOn' => array( 'field' => 'enabled' ),
58 66 ),
59 67 );
60 68 }
61 69
@@ -68,8 +76,12 @@
68 76 if ( ! is_array( $new ) ) {
69 77 return;
70 78 }
71 79 Browser_Cache::apply( ! empty( $new['enabled'] ), $new );
80 + // Settings just changed — TTL values likely differ, so the
81 + // currently-cached "probe says headers active" answer is stale.
82 + // Clear the transient so the next dashboard load re-probes.
83 + delete_transient( 'xspeed_browser_cache_probe' );
72 84 }
73 85
74 86 public function on_settings_added( $name, $value ): void {
75 87 if ( ! is_array( $value ) ) {
@@ -75,21 +87,56 @@
75 87 if ( ! is_array( $value ) ) {
76 88 return;
77 89 }
78 90 Browser_Cache::apply( ! empty( $value['enabled'] ), $value );
91 + delete_transient( 'xspeed_browser_cache_probe' );
79 92 }
80 93
81 94 public function ui_notices(): array {
82 - if ( class_exists( '\\XSpeed\\Server' ) && ! Server::supports_htaccess() ) {
83 - return array(
84 - array(
85 - 'tone' => 'info',
86 - 'title' => 'Manual nginx config required',
87 - 'body' => "Your server doesn't support .htaccess. After saving, copy the nginx snippet below into your server block.\n\n" . Browser_Cache::nginx_snippet( $this->get_settings() ),
95 + if ( ! class_exists( '\\XSpeed\\Server' ) || Server::supports_htaccess() ) {
96 + return array();
97 + }
98 + $opts = $this->get_settings();
99 + if ( empty( $opts['enabled'] ) ) {
100 + return array();
101 + }
102 +
103 + // Probe the live response for caching headers on a known static
104 + // asset. Suppress the notice unless the probe PROVES nothing is
105 + // being sent:
106 + //
107 + // true → headers present, from our snippet or from the host's own
108 + // vhost — either way the feature's job is done (issue #329)
109 + // null → the loopback never completed, so we know nothing; a
110 + // broken probe is not evidence of a broken server and must
111 + // not raise a warning the operator cannot act on (issue #18)
112 + // false → proven absent, fall through and show the notice
113 + if ( false !== Browser_Cache::probe_headers_present() ) {
114 + return array();
115 + }
116 +
117 + // nginx hosts can't auto-write Cache-Control / Expires headers, and the
118 + // live probe just confirmed they are NOT being served — so the feature
119 + // reads "enabled" in the dashboard while doing nothing. That is a warning,
120 + // not a passive info note (a token that a user missed on this exact
121 + // account, issue #117): escalate the tone and say plainly that the config
122 + // is configured-but-not-live until the snippet is pasted + nginx reloaded.
123 + // The directives go into the unified server-block snippet on the Cache
124 + // panel — point users there instead of duplicating the snippet here.
125 + return array(
126 + array(
127 + 'tone' => 'warn',
128 + 'title' => __( 'Browser cache is enabled but not active on the server', 'xspeed' ),
129 + 'body' => __( 'Browser-cache headers need to live in your nginx config, and the live response shows they are not being sent yet — so this is on in settings but doing nothing. Your settings are included in the unified server-block snippet on the Cache panel: paste it once into your nginx vhost (or container nginx config) and reload nginx.', 'xspeed' ),
130 + // Lands on the snippet itself rather than on the Cache panel,
131 + // where it is one collapsed section among several (issue #49).
132 + 'action' => Deep_Link::action(
133 + __( 'Go to the snippet', 'xspeed' ),
134 + 'cache',
135 + 'nginx_snippet'
88 136 ),
89 - );
90 - }
91 - return array();
137 + ),
138 + );
92 139 }
93 140
94 141 public function deactivate(): void {
95 142 Browser_Cache::apply( false );
@@ -100,8 +147,9 @@
100 147 array(
101 148 'name' => 'xspeed browser-cache',
102 149 'callback' => array( $this, 'cli_handler' ),
103 150 'shortdesc' => 'Print the Apache or nginx browser-cache snippet.',
151 + 'ai_hint' => 'Get the server config snippet that sets browser cache-control headers for static assets. Use when PageSpeed reports "serve static assets with an efficient cache policy", or when the user needs the rules to paste into Apache/nginx.',
104 152 'synopsis' => array(
105 153 array(
106 154 'type' => 'positional',
107 155 'name' => 'flavor',
@@ -122,6 +170,20 @@
122 170 }
123 171 foreach ( Browser_Cache::apache_rules( $opts ) as $line ) {
124 172 \WP_CLI::log( $line );
125 173 }
174 + }
175 +
176 + /**
177 + * Cache-Control directives for the unified nginx server-block
178 + * snippet. nginx sends no Expires — `expires off;` pins the block so
179 + * only the explicit `add_header` speaks (#259). Null when the module is disabled — no
180 + * directives to install.
181 + */
182 + public function nginx_directives(): ?string {
183 + $opts = $this->get_settings();
184 + if ( empty( $opts['enabled'] ) ) {
185 + return null;
186 + }
187 + return Browser_Cache::nginx_snippet( $opts );
126 188 }
127 189 }