PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.0.4
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.0.4
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 1.2.0 All 28 releases
xspeed / includes / modules / Cache / CacheModule.php

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

245 lines 7.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Cache module.
4 *
5 * Owns the cache_expiry and excluded_urls settings. cache_enabled is
6 * deliberately NOT in this schema — flipping it triggers the
7 * advanced-cache.php drop-in install + WP_CACHE constant edit in
8 * wp-config.php, which is a sensitive single-purpose code path and lives
9 * in Cache::toggle() with its own dedicated /xspeed/v1/cache/toggle REST
10 * route. The dashboard's Cache page renders the special hero UI for it
11 * above this module's schema-driven settings panel.
12 *
13 * Tier: Free.
14 *
15 * @package XSpeed
16 */
17
18 declare(strict_types=1);
19
20 namespace XSpeed\Modules\Cache;
21
22 use XSpeed\Module;
23 use XSpeed\Settings_Manager;
24
25 final class CacheModule extends Module {
26
27 public const SLUG = 'cache';
28 public const TIER = self::TIER_FREE;
29 public const VERSION = '1.0.0';
30
31 public function ui_metadata(): array {
32 return array(
33 'label' => 'Cache',
34 'icon' => 'Database',
35 'description' => 'Page caching for non-logged-in visitors.',
36 );
37 }
38
39 public function settings_schema(): array {
40 return array(
41 'cache_expiry' => array(
42 'type' => 'int',
43 'default' => 24,
44 'min' => 1,
45 'max' => 720,
46 'label' => 'Cache Expiry (hours)',
47 'description' => 'How long cached pages live before regenerating. 1 to 720 hours (30 days).',
48 ),
49 'excluded_urls' => array(
50 'type' => 'list',
51 // Comprehensive LiteSpeed / WP Rocket-parity default URL
52 // exclusions (FBS-82181). Plain text = "contains", glob via
53 // * ? [ ], or a `~` prefix for raw regex (e.g. ~wp-.*\.php).
54 'default' => array(
55 '/wp-admin/',
56 '/wp-json/',
57 '/xmlrpc.php',
58 '~wp-.*\.php',
59 '/feed/',
60 'index.php',
61 '~sitemap(_index)?\.xml',
62 // Bare (no trailing slash) so "contains" matches both
63 // /cart and /cart/items — WooCommerce serves both forms.
64 '/cart',
65 '/checkout',
66 '/my-account',
67 'ao_noptirocket',
68 'ao_speedup_cachebuster',
69 'removed_item',
70 '/wc-api',
71 '/edd-api',
72 '/wp-login',
73 ),
74 'item_type' => 'string',
75 'label' => 'Excluded URLs',
76 'description' => 'One pattern per line. Plain text matches anywhere in the URL (e.g. /cart). Use glob for anchored matches (/cart/* matches /cart/items but not /foo/cart/bar; *.pdf matches PDFs). Prefix with ~ for a raw regex (e.g. ~wp-.*\.php).',
77 ),
78 'excluded_cookies' => array(
79 'type' => 'list',
80 // Cookies that signal a logged-in / transactional visitor
81 // whose response must not be served from a shared cache.
82 // `~` prefix = raw regex (e.g. ~wordpress_[a-f0-9]+). (FBS-82181)
83 'default' => array(
84 'comment_author',
85 '~wordpress_[a-f0-9]+',
86 'wp-postpass',
87 'wordpress_no_cache',
88 'wordpress_logged_in',
89 'edd_items_in_cart',
90 'woocommerce_items_in_cart',
91 'fct_cart_hash',
92 'comment_',
93 'woocommerce_',
94 'wordpress',
95 'xf_',
96 'edd_',
97 'jetpack',
98 'yith_wcwl_session_',
99 'yith_wrvp_',
100 'wpsc_',
101 'ecwid',
102 'ec_',
103 'bookly',
104 ),
105 'item_type' => 'string',
106 'label' => 'Excluded Cookies',
107 'description' => 'Skip cache for any visitor whose request carries a cookie whose NAME matches one of these patterns. Plain text = "contains"; glob (woocommerce_*) and ~regex (~wordpress_[a-f0-9]+) supported. One per line.',
108 ),
109 'bypass_user_agents' => array(
110 'type' => 'list',
111 'default' => array(),
112 'item_type' => 'string',
113 'label' => 'Bypass User Agents',
114 'description' => 'Substring match against the visitor User-Agent. Matched UAs bypass cache (useful for screenshot bots, internal previews, monitoring). Glob + ~regex supported. One per line.',
115 ),
116 'ignored_query_params' => array(
117 'type' => 'list',
118 // Analytics / ad / session query keys stripped before the
119 // cache key is computed, so /post?utm_source=x and /post
120 // share one entry. `~` prefix = raw regex. (FBS-82181)
121 'default' => array(
122 '__s',
123 '_ga',
124 '_ke',
125 '~[a-zA-Z0-9_-]+_sid',
126 'adgroupid',
127 'age-verified',
128 'ao_noptimize',
129 'campaignid',
130 'ck_subscriber_id',
131 'cn-reloaded',
132 'dclid',
133 'epik',
134 'fb_action_ids',
135 'fb_action_types',
136 'fb_source',
137 'fbclid',
138 'gclid',
139 'jobid',
140 'mc_cid',
141 'mc_eid',
142 'mkt_tok',
143 'msclkid',
144 'ref',
145 '~session_[a-zA-Z0-9_-]+_alive',
146 'sseid',
147 'sslid',
148 'usqp',
149 '~utm_[a-zA-Z0-9_-]+',
150 ),
151 'item_type' => 'string',
152 'label' => 'Ignored Query Parameters',
153 'description' => 'Query keys removed from the URL before computing the cache key, so /post?utm_source=x and /post share a cache entry. Defaults cover the common analytics + ad + session params. Glob + ~regex supported. One per line.',
154 ),
155 'mobile_separate' => array(
156 'type' => 'bool',
157 'default' => false,
158 'label' => 'Separate Mobile Cache',
159 'description' => 'Keep mobile and desktop responses in separate cache buckets. Turn on for AMP, mobile-specific themes (WPtouch / Jetpack mobile theme), or any setup that serves different HTML by device.',
160 ),
161 );
162 }
163
164 /**
165 * Seed per-module option from the legacy xspeed_options blob if we
166 * haven't done so yet. Idempotent — once xspeed_module_cache exists
167 * or the legacy keys are gone, this is a no-op. Runs on both boot
168 * and activate so installs on every code path are covered.
169 */
170 public function boot(): void {
171 $this->seed_from_legacy_if_needed();
172 }
173
174 public function activate(): void {
175 $this->seed_from_legacy_if_needed();
176 }
177
178 private function seed_from_legacy_if_needed(): void {
179 if ( null !== get_option( 'xspeed_module_cache', null ) ) {
180 return;
181 }
182 $legacy = get_option( 'xspeed_options', array() );
183 if ( ! is_array( $legacy ) ) {
184 return;
185 }
186 $seed = array( '_version' => self::VERSION );
187 $dirty = false;
188 if ( array_key_exists( 'cache_expiry', $legacy ) ) {
189 $seed['cache_expiry'] = max( 1, min( 720, (int) $legacy['cache_expiry'] ) );
190 unset( $legacy['cache_expiry'] );
191 $dirty = true;
192 }
193 if ( array_key_exists( 'excluded_urls', $legacy ) ) {
194 $seed['excluded_urls'] = is_array( $legacy['excluded_urls'] ) ? array_values( array_filter( $legacy['excluded_urls'], 'is_string' ) ) : array();
195 unset( $legacy['excluded_urls'] );
196 $dirty = true;
197 }
198 if ( $dirty ) {
199 update_option( 'xspeed_module_cache', $seed );
200 update_option( 'xspeed_options', $legacy );
201 }
202 }
203
204 public function cli_commands(): array {
205 return array(
206 array(
207 'name' => 'xspeed cache',
208 'callback' => array( $this, 'cli_handler' ),
209 'shortdesc' => 'Inspect Cache module settings (purge / toggle use the dedicated commands).',
210 'synopsis' => array(
211 array(
212 'type' => 'positional',
213 'name' => 'action',
214 'options' => array( 'status' ),
215 'optional' => true,
216 ),
217 ),
218 ),
219 );
220 }
221
222 public function cli_handler( array $args, array $assoc ): void {
223 $opts = Settings_Manager::get( self::SLUG );
224 \WP_CLI::log( 'cache_expiry ' . $opts['cache_expiry'] . 'h' );
225 \WP_CLI::log( 'excluded_urls ' . count( $opts['excluded_urls'] ) . ' entries' );
226 foreach ( $opts['excluded_urls'] as $u ) {
227 \WP_CLI::log( ' - ' . $u );
228 }
229 }
230
231 /**
232 * Static-rewrite directives for the unified nginx server-block
233 * snippet. Returns null when cache is disabled — there's no rewrite
234 * to install in that state. Delegates to \XSpeed\Cache::nginx_snippet()
235 * which already produces nginx-detection-gated output.
236 */
237 public function nginx_directives(): ?string {
238 $opts = get_option( 'xspeed_options', array() );
239 if ( empty( $opts['cache_enabled'] ) ) {
240 return null;
241 }
242 return \XSpeed\Cache::nginx_snippet();
243 }
244 }
245