PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.0.7
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.0.7
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 / ObjectCache / ObjectCacheModule.php

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

287 lines 9.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Object Cache module — status, settings, wp-config snippet, flush.
4 *
5 * Tier: Free per FEATURES.md "Object Cache" §1-11 (LiteSpeed parity).
6 *
7 * We don't ship our own object-cache.php drop-in in this Free release
8 * (see Object_Cache class docblock for rationale). The settings here
9 * are surfaced via the wp-config snippet; advanced consumers (Pro,
10 * external drop-ins like Redis Object Cache) can read them too.
11 *
12 * @package XSpeed
13 */
14
15 declare(strict_types=1);
16
17 namespace XSpeed\Modules\ObjectCache;
18
19 defined( 'ABSPATH' ) || exit;
20
21 use XSpeed\Module;
22 use XSpeed\Object_Cache;
23
24 final class ObjectCacheModule extends Module {
25
26 public const SLUG = 'object-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' => 'Object Cache',
33 'icon' => 'Server',
34 'description' => 'Configure a persistent object cache (Redis / Memcached) and generate a paste-ready wp-config.php snippet.',
35 'custom_panel' => 'ObjectCachePanel',
36 );
37 }
38
39 public function settings_schema(): array {
40 return array(
41 'backend' => array(
42 'type' => 'enum',
43 'default' => 'redis',
44 'options' => array( 'redis', 'memcached' ),
45 'option_labels' => array(
46 'redis' => 'Redis',
47 'memcached' => 'Memcached',
48 ),
49 'label' => 'Backend',
50 'description' => 'Which cache server you intend to use. Affects the generated wp-config snippet.',
51 ),
52 'redis_host' => array(
53 'type' => 'string',
54 'default' => '127.0.0.1',
55 'label' => 'Redis Host',
56 'description' => 'Hostname or IP of the Redis server. Use 127.0.0.1 for a local socket on the same machine as PHP.',
57 ),
58 'redis_port' => array(
59 'type' => 'int',
60 'default' => 6379,
61 'min' => 1,
62 'max' => 65535,
63 'label' => 'Redis Port',
64 'description' => 'Default Redis port is 6379.',
65 ),
66 'redis_user' => array(
67 'type' => 'string',
68 'default' => '',
69 'label' => 'Redis User',
70 'description' => 'Optional. Set this only if your host provisioned a dedicated Redis ACL user (Redis 6+) — e.g. some managed hosts issue a Redis User alongside the password. Leave blank to authenticate as the default user (legacy password-only Redis).',
71 ),
72 'redis_password' => array(
73 'type' => 'string',
74 'default' => '',
75 'label' => 'Redis Password',
76 'description' => 'Leave blank if your Redis server runs without auth.',
77 ),
78 'redis_database' => array(
79 'type' => 'int',
80 'default' => 0,
81 'min' => 0,
82 'max' => 15,
83 'label' => 'Redis Database',
84 'description' => 'Redis logical DB number (0-15). Use a dedicated DB per site if Redis is shared.',
85 ),
86 'memcached_host' => array(
87 'type' => 'string',
88 'default' => '127.0.0.1',
89 'label' => 'Memcached Host',
90 'description' => 'Used when Backend = Memcached.',
91 ),
92 'memcached_port' => array(
93 'type' => 'int',
94 'default' => 11211,
95 'min' => 1,
96 'max' => 65535,
97 'label' => 'Memcached Port',
98 'description' => 'Default Memcached port is 11211.',
99 ),
100 'key_prefix' => array(
101 'type' => 'string',
102 'default' => '',
103 'label' => 'Cache Key Prefix',
104 'description' => 'Unique salt for this site\'s cache keys. Critical when multiple WP sites share one Redis/Memcached server. On ACL/namespaced Redis (e.g. xCloud), this MUST match the host\'s "Redis Object Cache Key" — otherwise cache writes are denied (NOPERM) and nothing persists.',
105 ),
106 'connection_timeout' => array(
107 'type' => 'int',
108 'default' => 1,
109 'min' => 0,
110 'max' => 60,
111 'label' => 'Connection Timeout (seconds)',
112 'description' => 'How long to wait for a connection. Keep low (1-2s) so a misconfigured cache never stalls the page.',
113 ),
114 'persistent' => array(
115 'type' => 'bool',
116 'default' => true,
117 'label' => 'Persistent Connections',
118 'description' => 'Reuse the connection across PHP requests when supported. Generally a win unless the cache server complains about idle connections.',
119 ),
120 );
121 }
122
123 public function rest_routes(): array {
124 $default = parent::rest_routes();
125 return array_merge(
126 $default,
127 array(
128 array(
129 'path' => '/detect',
130 'methods' => 'GET',
131 'callback' => array( $this, 'rest_detect' ),
132 ),
133 array(
134 'path' => '/flush',
135 'methods' => 'POST',
136 'callback' => array( $this, 'rest_flush' ),
137 ),
138 array(
139 'path' => '/snippet',
140 'methods' => 'GET',
141 'callback' => array( $this, 'rest_snippet' ),
142 ),
143 array(
144 'path' => '/test-connection',
145 'methods' => 'POST',
146 'callback' => array( $this, 'rest_test_connection' ),
147 ),
148 array(
149 'path' => '/enable',
150 'methods' => 'POST',
151 'callback' => array( $this, 'rest_enable' ),
152 ),
153 array(
154 'path' => '/disable',
155 'methods' => 'POST',
156 'callback' => array( $this, 'rest_disable' ),
157 ),
158 )
159 );
160 }
161
162 public function rest_detect( \WP_REST_Request $request ) {
163 return rest_ensure_response( Object_Cache::detect() );
164 }
165
166 public function rest_flush( \WP_REST_Request $request ) {
167 $ok = Object_Cache::flush();
168 if ( $ok && class_exists( '\\XSpeed\\Activity_Log' ) ) {
169 \XSpeed\Activity_Log::record(
170 'object_cache_flushed',
171 'Object cache flushed.',
172 \XSpeed\Activity_Log::INFO
173 );
174 }
175 return rest_ensure_response( array( 'ok' => $ok ) );
176 }
177
178 public function rest_snippet( \WP_REST_Request $request ) {
179 return rest_ensure_response(
180 array( 'snippet' => Object_Cache::render_config_snippet( $this->get_settings() ) )
181 );
182 }
183
184 /**
185 * Merge any settings sent in the request body over the saved settings, so
186 * the UI can "Test connection" with unsaved values. Only known keys pass.
187 */
188 private function settings_with_overrides( \WP_REST_Request $request ): array {
189 $settings = $this->get_settings();
190 $body = $request->get_json_params();
191 if ( is_array( $body ) ) {
192 foreach ( $settings as $key => $value ) {
193 if ( array_key_exists( $key, $body ) ) {
194 $settings[ $key ] = $body[ $key ];
195 }
196 }
197 }
198 return $settings;
199 }
200
201 public function rest_test_connection( \WP_REST_Request $request ) {
202 return rest_ensure_response( Object_Cache::test_connection( $this->settings_with_overrides( $request ) ) );
203 }
204
205 public function rest_enable( \WP_REST_Request $request ) {
206 // Persist any settings sent with the enable call first, then act on them.
207 $body = $request->get_json_params();
208 if ( is_array( $body ) && ! empty( $body ) ) {
209 \XSpeed\Settings_Manager::update( self::SLUG, $body );
210 }
211 $result = Object_Cache::enable( $this->get_settings() );
212
213 if ( $result['ok'] && class_exists( '\\XSpeed\\Activity_Log' ) ) {
214 \XSpeed\Activity_Log::record(
215 'object_cache_enabled',
216 'Object cache enabled (' . ( $result['test']['backend'] ?? '' ) . ').',
217 \XSpeed\Activity_Log::INFO
218 );
219 }
220 return rest_ensure_response( $result );
221 }
222
223 public function rest_disable( \WP_REST_Request $request ) {
224 $result = Object_Cache::disable();
225 if ( $result['ok'] && class_exists( '\\XSpeed\\Activity_Log' ) ) {
226 \XSpeed\Activity_Log::record(
227 'object_cache_disabled',
228 'Object cache disabled.',
229 \XSpeed\Activity_Log::INFO
230 );
231 }
232 return rest_ensure_response( $result );
233 }
234
235 public function cli_commands(): array {
236 return array(
237 array(
238 'name' => 'xspeed objcache',
239 'callback' => array( $this, 'cli_handler' ),
240 'shortdesc' => 'Show object cache status, flush, or print the wp-config snippet.',
241 'synopsis' => array(
242 array(
243 'type' => 'positional',
244 'name' => 'action',
245 'options' => array( 'status', 'flush', 'snippet', 'enable', 'disable', 'test' ),
246 'optional' => true,
247 ),
248 ),
249 ),
250 );
251 }
252
253 public function cli_handler( array $args, array $assoc ): void {
254 $action = $args[0] ?? 'status';
255 switch ( $action ) {
256 case 'status':
257 $d = Object_Cache::detect();
258 \WP_CLI::log( 'drop-in installed: ' . ( $d['drop_in_installed'] ? 'yes' : 'no' ) );
259 \WP_CLI::log( 'label: ' . $d['drop_in_label'] );
260 \WP_CLI::log( 'backend: ' . $d['backend'] );
261 \WP_CLI::log( 'ext object cache: ' . ( $d['wp_cache_active'] ? 'yes' : 'no' ) );
262 return;
263 case 'flush':
264 $ok = Object_Cache::flush();
265 $ok ? \WP_CLI::success( 'Flushed.' ) : \WP_CLI::error( 'Flush failed.' );
266 return;
267 case 'snippet':
268 \WP_CLI::log( Object_Cache::render_config_snippet( $this->get_settings() ) );
269 return;
270 case 'test':
271 $t = Object_Cache::test_connection( $this->get_settings() );
272 $t['ok'] ? \WP_CLI::success( $t['message'] ) : \WP_CLI::error( $t['message'] );
273 return;
274 case 'enable':
275 $r = Object_Cache::enable( $this->get_settings() );
276 $r['ok'] ? \WP_CLI::success( $r['message'] ) : \WP_CLI::error( $r['message'] );
277 return;
278 case 'disable':
279 $r = Object_Cache::disable();
280 $r['ok'] ? \WP_CLI::success( $r['message'] ) : \WP_CLI::error( $r['message'] );
281 return;
282 default:
283 \WP_CLI::error( "Unknown action: $action" );
284 }
285 }
286 }
287