PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.1.2
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.1.2
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 1.1.5 1.1.6 1.1.7 1.1.8 All 29 releases
xspeed / includes / modules / BrowserCache / BrowserCacheModule.php

BrowserCacheModule.php in xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN 1.1.2, at includes/modules/BrowserCache/BrowserCacheModule.php

175 lines 5.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Browser cache headers module — writes Cache-Control + Expires
4 * directives to .htaccess (Apache/LiteSpeed) or surfaces an nginx
5 * snippet for manual paste on other servers.
6 *
7 * Tier: Free per FEATURES.md (LiteSpeed parity — Browser Cache
8 * settings are all in LS free).
9 *
10 * @package XSpeed
11 */
12
13 declare(strict_types=1);
14
15 namespace XSpeed\Modules\BrowserCache;
16
17 defined( 'ABSPATH' ) || exit;
18
19 use XSpeed\Browser_Cache;
20 use XSpeed\Deep_Link;
21 use XSpeed\Module;
22 use XSpeed\Server;
23
24 final class BrowserCacheModule extends Module {
25
26 public const SLUG = 'browser-cache';
27 public const TIER = self::TIER_FREE;
28 public const VERSION = '1.0.0';
29
30 public function ui_metadata(): array {
31 return array(
32 'label' => 'Browser Cache',
33 'icon' => 'Clock',
34 'description' => 'Tell browsers (and intermediate CDNs) how long to cache static assets and HTML.',
35 );
36 }
37
38 public function settings_schema(): array {
39 return array(
40 'enabled' => array(
41 'type' => 'bool',
42 'default' => false,
43 'label' => 'Enable browser cache headers',
44 '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.',
45 ),
46 'asset_ttl' => array(
47 'type' => 'int',
48 'default' => Browser_Cache::DEFAULT_ASSET_TTL,
49 'min' => 0,
50 'max' => 31536000,
51 'label' => 'Static asset TTL (seconds)',
52 'description' => 'Cache lifetime for CSS, JS, fonts, images. Defaults to 1 year + immutable (the industry-standard "fingerprinted assets never change" pattern).',
53 'dependsOn' => array( 'field' => 'enabled' ),
54 ),
55 'html_ttl' => array(
56 'type' => 'int',
57 'default' => Browser_Cache::DEFAULT_HTML_TTL,
58 'min' => 0,
59 'max' => 31536000,
60 'label' => 'HTML TTL (seconds)',
61 'description' => 'Cache lifetime for the HTML document itself. Keep short (default 1h) so post edits roll out same-day.',
62 'dependsOn' => array( 'field' => 'enabled' ),
63 ),
64 );
65 }
66
67 public function boot(): void {
68 add_action( 'update_option_xspeed_module_browser-cache', array( $this, 'on_settings_change' ), 10, 2 );
69 add_action( 'add_option_xspeed_module_browser-cache', array( $this, 'on_settings_added' ), 10, 2 );
70 }
71
72 public function on_settings_change( $old, $new ): void {
73 if ( ! is_array( $new ) ) {
74 return;
75 }
76 Browser_Cache::apply( ! empty( $new['enabled'] ), $new );
77 // Settings just changed — TTL values likely differ, so the
78 // currently-cached "probe says headers active" answer is stale.
79 // Clear the transient so the next dashboard load re-probes.
80 delete_transient( 'xspeed_browser_cache_probe' );
81 }
82
83 public function on_settings_added( $name, $value ): void {
84 if ( ! is_array( $value ) ) {
85 return;
86 }
87 Browser_Cache::apply( ! empty( $value['enabled'] ), $value );
88 delete_transient( 'xspeed_browser_cache_probe' );
89 }
90
91 public function ui_notices(): array {
92 if ( ! class_exists( '\\XSpeed\\Server' ) || Server::supports_htaccess() ) {
93 return array();
94 }
95 $opts = $this->get_settings();
96 if ( empty( $opts['enabled'] ) ) {
97 return array();
98 }
99
100 // Probe whether the snippet has actually been pasted + reloaded.
101 // If the live HEAD shows Cache-Control: immutable on a known
102 // static asset, the user is done — suppress the notice. Avoids
103 // the false-alarm "do something" prompt we used to show forever.
104 if ( Browser_Cache::probe_headers_present() ) {
105 return array();
106 }
107
108 // nginx hosts can't auto-write Cache-Control / Expires headers.
109 // The directives go into the unified server-block snippet on
110 // the Cache panel — point users there instead of duplicating the
111 // snippet here. Topology-aware wording lives in NginxServerBlock.
112 return array(
113 array(
114 'tone' => 'info',
115 'title' => __( 'nginx server config required', 'xspeed' ),
116 'body' => __( 'Browser-cache headers need to live in your nginx config. Your updated settings are included in the unified server-block snippet on the Cache panel — copy + paste it once into your nginx vhost (or container nginx config), and reload nginx.', 'xspeed' ),
117 // Lands on the snippet itself rather than on the Cache panel,
118 // where it is one collapsed section among several (issue #49).
119 'action' => Deep_Link::action(
120 __( 'Go to the snippet', 'xspeed' ),
121 'cache',
122 'nginx_snippet'
123 ),
124 ),
125 );
126 }
127
128 public function deactivate(): void {
129 Browser_Cache::apply( false );
130 }
131
132 public function cli_commands(): array {
133 return array(
134 array(
135 'name' => 'xspeed browser-cache',
136 'callback' => array( $this, 'cli_handler' ),
137 'shortdesc' => 'Print the Apache or nginx browser-cache snippet.',
138 'synopsis' => array(
139 array(
140 'type' => 'positional',
141 'name' => 'flavor',
142 'options' => array( 'apache', 'nginx' ),
143 'optional' => true,
144 ),
145 ),
146 ),
147 );
148 }
149
150 public function cli_handler( array $args, array $assoc ): void {
151 $flavor = $args[0] ?? 'apache';
152 $opts = $this->get_settings();
153 if ( 'nginx' === $flavor ) {
154 \WP_CLI::log( Browser_Cache::nginx_snippet( $opts ) );
155 return;
156 }
157 foreach ( Browser_Cache::apache_rules( $opts ) as $line ) {
158 \WP_CLI::log( $line );
159 }
160 }
161
162 /**
163 * Cache-Control / Expires directives for the unified nginx
164 * server-block snippet. Null when the module is disabled — no
165 * directives to install.
166 */
167 public function nginx_directives(): ?string {
168 $opts = $this->get_settings();
169 if ( empty( $opts['enabled'] ) ) {
170 return null;
171 }
172 return Browser_Cache::nginx_snippet( $opts );
173 }
174 }
175