PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.0.3
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.0.3
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 / Cache / CacheModule.php

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

167 lines 5.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 'default' => array(),
52 'item_type' => 'string',
53 'label' => 'Excluded URLs',
54 'description' => 'One path per line. Plain text matches anywhere in the URL (e.g. /cart). Use glob syntax for anchored matches: /cart/* matches /cart/items but not /foo/cart/bar; *.pdf matches PDFs.',
55 ),
56 'excluded_cookies' => array(
57 'type' => 'list',
58 'default' => array(),
59 'item_type' => 'string',
60 'label' => 'Excluded Cookies',
61 'description' => 'Skip cache for any visitor whose request carries a cookie whose NAME matches one of these patterns. Glob supported (comment_author_*, woocommerce_*). One per line.',
62 ),
63 'bypass_user_agents' => array(
64 'type' => 'list',
65 'default' => array(),
66 'item_type' => 'string',
67 'label' => 'Bypass User Agents',
68 'description' => 'Substring match against the visitor User-Agent. Matched UAs bypass cache (useful for screenshot bots, internal previews, monitoring). One per line.',
69 ),
70 'ignored_query_params' => array(
71 'type' => 'list',
72 'default' => array( 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid' ),
73 'item_type' => 'string',
74 'label' => 'Ignored Query Parameters',
75 '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 params. One per line.',
76 ),
77 'mobile_separate' => array(
78 'type' => 'bool',
79 'default' => false,
80 'label' => 'Separate Mobile Cache',
81 '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.',
82 ),
83 );
84 }
85
86 /**
87 * Seed per-module option from the legacy xspeed_options blob if we
88 * haven't done so yet. Idempotent — once xspeed_module_cache exists
89 * or the legacy keys are gone, this is a no-op. Runs on both boot
90 * and activate so installs on every code path are covered.
91 */
92 public function boot(): void {
93 $this->seed_from_legacy_if_needed();
94 }
95
96 public function activate(): void {
97 $this->seed_from_legacy_if_needed();
98 }
99
100 private function seed_from_legacy_if_needed(): void {
101 if ( null !== get_option( 'xspeed_module_cache', null ) ) {
102 return;
103 }
104 $legacy = get_option( 'xspeed_options', array() );
105 if ( ! is_array( $legacy ) ) {
106 return;
107 }
108 $seed = array( '_version' => self::VERSION );
109 $dirty = false;
110 if ( array_key_exists( 'cache_expiry', $legacy ) ) {
111 $seed['cache_expiry'] = max( 1, min( 720, (int) $legacy['cache_expiry'] ) );
112 unset( $legacy['cache_expiry'] );
113 $dirty = true;
114 }
115 if ( array_key_exists( 'excluded_urls', $legacy ) ) {
116 $seed['excluded_urls'] = is_array( $legacy['excluded_urls'] ) ? array_values( array_filter( $legacy['excluded_urls'], 'is_string' ) ) : array();
117 unset( $legacy['excluded_urls'] );
118 $dirty = true;
119 }
120 if ( $dirty ) {
121 update_option( 'xspeed_module_cache', $seed );
122 update_option( 'xspeed_options', $legacy );
123 }
124 }
125
126 public function cli_commands(): array {
127 return array(
128 array(
129 'name' => 'xspeed cache',
130 'callback' => array( $this, 'cli_handler' ),
131 'shortdesc' => 'Inspect Cache module settings (purge / toggle use the dedicated commands).',
132 'synopsis' => array(
133 array(
134 'type' => 'positional',
135 'name' => 'action',
136 'options' => array( 'status' ),
137 'optional' => true,
138 ),
139 ),
140 ),
141 );
142 }
143
144 public function cli_handler( array $args, array $assoc ): void {
145 $opts = Settings_Manager::get( self::SLUG );
146 \WP_CLI::log( 'cache_expiry ' . $opts['cache_expiry'] . 'h' );
147 \WP_CLI::log( 'excluded_urls ' . count( $opts['excluded_urls'] ) . ' entries' );
148 foreach ( $opts['excluded_urls'] as $u ) {
149 \WP_CLI::log( ' - ' . $u );
150 }
151 }
152
153 /**
154 * Static-rewrite directives for the unified nginx server-block
155 * snippet. Returns null when cache is disabled — there's no rewrite
156 * to install in that state. Delegates to \XSpeed\Cache::nginx_snippet()
157 * which already produces nginx-detection-gated output.
158 */
159 public function nginx_directives(): ?string {
160 $opts = get_option( 'xspeed_options', array() );
161 if ( empty( $opts['cache_enabled'] ) ) {
162 return null;
163 }
164 return \XSpeed\Cache::nginx_snippet();
165 }
166 }
167