PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.4.2
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.4.2
1.4.2 1.4.1 1.4.0 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 All 36 releases
← All changes | includes/modules/ObjectCache/ObjectCacheModule.php +102 -32 1.3.1 → 1.4.2 View file →
@@ -19,8 +19,9 @@
19 19 defined( 'ABSPATH' ) || exit;
20 20
21 21 use XSpeed\Module;
22 22 use XSpeed\Object_Cache;
23 +use XSpeed\Object_Cache_Takeover;
23 24
24 25 final class ObjectCacheModule extends Module {
25 26
26 27 public const SLUG = 'object-cache';
@@ -30,9 +31,10 @@
30 31 public function ui_metadata(): array {
31 32 return array(
32 33 'label' => __( 'Object Cache', 'xspeed' ),
33 34 'icon' => 'Server',
34 - 'description' => __( 'Configure a persistent object cache (Redis / Memcached) and generate a paste-ready wp-config.php snippet.', 'xspeed' ),
35 + 'description' => __( 'Stores database query results in Redis or Memcached so pages build faster.', 'xspeed' ),
36 + 'group' => 'cache',
35 37 'custom_panel' => 'ObjectCachePanel',
36 38 );
37 39 }
38 40
@@ -57,17 +59,31 @@
57 59 'option_labels' => array(
58 60 'redis' => 'Redis',
59 61 'memcached' => 'Memcached',
60 62 ),
61 - 'label' => __( 'Backend', 'xspeed' ),
62 - 'description' => __( 'Which cache server you intend to use. Affects the generated wp-config snippet.', 'xspeed' ),
63 + 'label' => __( 'Cache server', 'xspeed' ),
64 + 'description' => __( 'The cache server your host provides. Ask your host if you are not sure.', 'xspeed' ),
63 65 ),
64 66 'redis_host' => array(
65 - 'type' => 'string',
66 - 'default' => '127.0.0.1',
67 - 'constants' => array( 'XSPEED_OC_HOST', 'WP_REDIS_HOST' ),
68 - 'label' => __( 'Redis Host', 'xspeed' ),
69 - 'description' => __( 'Hostname or IP of the Redis server. Use 127.0.0.1 for a local socket on the same machine as PHP.', 'xspeed' ),
67 + 'type' => 'string',
68 + 'default' => '127.0.0.1',
69 + // WP_REDIS_PATH is Redis Object Cache's name for a unix socket,
70 + // and it only means one when WP_REDIS_SCHEME says `unix` -- the
71 + // same pair that plugin reads. It ranks above WP_REDIS_HOST
72 + // because that plugin ignores the host once the scheme is unix,
73 + // and hosts that write the pair often leave a stale host
74 + // beside it. (#566)
75 + 'constants' => array( 'XSPEED_OC_HOST', 'WP_REDIS_PATH', 'WP_REDIS_HOST' ),
76 + 'constants_when' => array(
77 + 'WP_REDIS_PATH' => array(
78 + 'constant' => 'WP_REDIS_SCHEME',
79 + 'is' => 'unix',
80 + 'case_insensitive' => true,
81 + ),
82 + ),
83 + 'label' => __( 'Redis host', 'xspeed' ),
84 + 'description' => __( 'Address of the Redis server, or the path to its socket file if your host gave you one (for example /run/redis/redis.sock). Use 127.0.0.1 when Redis runs on the same server as your site.', 'xspeed' ),
85 + 'dependsOn' => array( 'field' => 'backend', 'value' => 'redis' ),
70 86 ),
71 87 'redis_port' => array(
72 88 'type' => 'int',
73 89 'default' => 6379,
@@ -73,10 +89,12 @@
73 89 'default' => 6379,
74 90 'min' => 1,
75 91 'max' => 65535,
76 92 'constants' => array( 'XSPEED_OC_PORT', 'WP_REDIS_PORT' ),
77 - 'label' => __( 'Redis Port', 'xspeed' ),
78 - 'description' => __( 'Default Redis port is 6379.', 'xspeed' ),
93 + 'label' => __( 'Redis port', 'xspeed' ),
94 + 'description' => __( 'The default Redis port is 6379. Not used when the host is a socket path.', 'xspeed' ),
95 + 'advanced' => true,
96 + 'dependsOn' => array( 'field' => 'backend', 'value' => 'redis' ),
79 97 ),
80 98 'redis_user' => array(
81 99 'type' => 'string',
82 100 'default' => '',
@@ -83,13 +101,16 @@
83 101 // WP_REDIS_PASSWORD trails the dedicated names because in its
84 102 // array form -- how xCloud and Cloudways hand out ACL
85 103 // credentials -- it carries the username too. It is pair-only:
86 104 // as a plain string it is a password, never a username.
87 - 'constants' => array( 'XSPEED_OC_USER', 'WP_REDIS_USER', 'WP_REDIS_PASSWORD' ),
105 + // WP_REDIS_USERNAME is Redis Object Cache's name for the same
106 + // thing, and that plugin lets it win over the array form. (#566)
107 + 'constants' => array( 'XSPEED_OC_USER', 'WP_REDIS_USERNAME', 'WP_REDIS_USER', 'WP_REDIS_PASSWORD' ),
88 108 'constants_pair_only' => array( 'WP_REDIS_PASSWORD' ),
89 109 'constant_pair' => 'user',
90 - 'label' => __( 'Redis User', 'xspeed' ),
91 - '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).', 'xspeed' ),
110 + 'label' => __( 'Redis username', 'xspeed' ),
111 + 'description' => __( 'Only needed if your host gave you a Redis username along with the password. Leave blank otherwise.', 'xspeed' ),
112 + 'dependsOn' => array( 'field' => 'backend', 'value' => 'redis' ),
92 113 ),
93 114 'redis_password' => array(
94 115 'type' => 'secret',
95 116 'default' => '',
@@ -94,10 +115,11 @@
94 115 'type' => 'secret',
95 116 'default' => '',
96 117 'constants' => array( 'XSPEED_OC_PASSWORD', 'WP_REDIS_PASSWORD' ),
97 118 'constant_pair' => 'password',
98 - 'label' => __( 'Redis Password', 'xspeed' ),
99 - 'description' => __( 'Leave blank if your Redis server runs without auth.', 'xspeed' ),
119 + 'label' => __( 'Redis password', 'xspeed' ),
120 + 'description' => __( 'Leave blank if your Redis server has no password.', 'xspeed' ),
121 + 'dependsOn' => array( 'field' => 'backend', 'value' => 'redis' ),
100 122 ),
101 123 'redis_database' => array(
102 124 'type' => 'int',
103 125 'default' => 0,
@@ -103,10 +125,12 @@
103 125 'default' => 0,
104 126 'min' => 0,
105 127 'max' => 15,
106 128 'constants' => array( 'XSPEED_OC_DATABASE', 'WP_REDIS_DATABASE' ),
107 - 'label' => __( 'Redis Database', 'xspeed' ),
108 - 'description' => __( 'Redis logical DB number (0-15). Use a dedicated DB per site if Redis is shared.', 'xspeed' ),
129 + 'label' => __( 'Redis database', 'xspeed' ),
130 + 'description' => __( 'Database number, 0 to 15. If several sites share one Redis server, give each site its own number.', 'xspeed' ),
131 + 'advanced' => true,
132 + 'dependsOn' => array( 'field' => 'backend', 'value' => 'redis' ),
109 133 ),
110 134 'memcached_host' => array(
111 135 'type' => 'string',
112 136 'default' => '127.0.0.1',
@@ -131,10 +155,11 @@
131 155 'var' => 'memcached_servers',
132 156 'reader' => array( '\\XSpeed\\Object_Cache', 'first_memcached_server' ),
133 157 'slot' => 0,
134 158 ),
135 - 'label' => __( 'Memcached Host', 'xspeed' ),
136 - 'description' => __( 'Used when Backend = Memcached.', 'xspeed' ),
159 + 'label' => __( 'Memcached host', 'xspeed' ),
160 + 'description' => __( 'Address of the Memcached server. Use 127.0.0.1 when it runs on the same server as your site.', 'xspeed' ),
161 + 'dependsOn' => array( 'field' => 'backend', 'value' => 'memcached' ),
137 162 ),
138 163 'memcached_port' => array(
139 164 'type' => 'int',
140 165 'default' => 11211,
@@ -153,10 +178,12 @@
153 178 'var' => 'memcached_servers',
154 179 'reader' => array( '\\XSpeed\\Object_Cache', 'first_memcached_server' ),
155 180 'slot' => 1,
156 181 ),
157 - 'label' => __( 'Memcached Port', 'xspeed' ),
158 - 'description' => __( 'Default Memcached port is 11211.', 'xspeed' ),
182 + 'label' => __( 'Memcached port', 'xspeed' ),
183 + 'description' => __( 'The default Memcached port is 11211.', 'xspeed' ),
184 + 'advanced' => true,
185 + 'dependsOn' => array( 'field' => 'backend', 'value' => 'memcached' ),
159 186 ),
160 187 'key_prefix' => array(
161 188 'type' => 'string',
162 189 'default' => '',
@@ -180,10 +207,12 @@
180 207 // unlock the field. With no salt of our own the field stays
181 208 // blank and editable; the drop-in still honours a defined
182 209 // WP_CACHE_KEY_SALT as its last-resort salt at runtime.
183 210 'constants' => array( 'XSPEED_OC_SALT', 'WP_REDIS_PREFIX' ),
184 - 'label' => __( 'Cache Key Prefix', 'xspeed' ),
185 - 'description' => __( 'Unique salt for this site\'s cache keys. Leave blank and xSpeed derives one automatically for this install, so sites sharing a Redis/Memcached server never collide. On ACL/namespaced Redis (e.g. xCloud), set this to the host\'s "Redis Object Cache Key" — otherwise cache writes are denied (NOPERM) and nothing persists.', 'xspeed' ),
211 + 'label' => __( 'Cache key prefix', 'xspeed' ),
212 + 'description' => __( 'Leave blank and xSpeed picks a unique prefix for this site. Some managed Redis hosts, such as xCloud, give you a key to paste here, or nothing gets saved.', 'xspeed' ),
213 + // Not advanced: on ACL hosts it is required, and the write-denied
214 + // notice (Object_Cache::write_denied_message) sends users here.
186 215 ),
187 216 'connection_timeout' => array(
188 217 'type' => 'int',
189 218 'default' => 1,
@@ -189,18 +218,20 @@
189 218 'default' => 1,
190 219 'min' => 0,
191 220 'max' => 60,
192 221 'constants' => array( 'XSPEED_OC_TIMEOUT', 'WP_REDIS_TIMEOUT' ),
193 - 'label' => __( 'Connection Timeout (seconds)', 'xspeed' ),
222 + 'label' => __( 'Connection timeout (seconds)', 'xspeed' ),
194 223 'unit' => 'seconds',
195 - 'description' => __( 'How long to wait for a connection. Keep low (1-2s) so a misconfigured cache never stalls the page.', 'xspeed' ),
224 + 'description' => __( 'How long to wait for the cache server. Keep it at 1 or 2 seconds so a broken cache server never holds up a page.', 'xspeed' ),
225 + 'advanced' => true,
196 226 ),
197 227 'persistent' => array(
198 228 'type' => 'bool',
199 229 'default' => true,
200 230 'constants' => array( 'XSPEED_OC_PERSISTENT', 'WP_REDIS_PERSISTENT' ),
201 - 'label' => __( 'Persistent Connections', 'xspeed' ),
202 - 'description' => __( 'Reuse the connection across PHP requests when supported. Generally a win unless the cache server complains about idle connections.', 'xspeed' ),
231 + 'label' => __( 'Persistent connections', 'xspeed' ),
232 + 'description' => __( 'Keep the connection open between page loads. Turn off only if your cache server reports too many idle connections.', 'xspeed' ),
233 + 'advanced' => true,
203 234 ),
204 235 );
205 236 }
206 237
@@ -644,9 +675,9 @@
644 675 * probe recorded on save knows; until this it had no reader outside
645 676 * WP-CLI, so the panel kept saying "Redis ready" over a cache that
646 677 * stored nothing. (#398)
647 678 */
648 - $detect = Object_Cache::detect();
679 + $detect = Object_Cache::detect( true );
649 680 $failure = self::live_write_failure();
650 681 if ( null !== $failure ) {
651 682 $detect['write_probe'] = $failure;
652 683 }
@@ -733,12 +764,22 @@
733 764
734 765 public function rest_enable( \WP_REST_Request $request ) {
735 766 // Persist any settings sent with the enable call first, then act on them.
736 767 $body = $request->get_json_params();
768 + // `takeover` is an action, not a setting: take it out before the body
769 + // is saved as the settings row. Sent in the body rather than the query
770 + // string because on plain permalinks the REST base already carries
771 + // `?rest_route=`. (#686)
772 + $takeover = is_array( $body ) && ! empty( $body['takeover'] )
773 + ? rest_sanitize_boolean( $body['takeover'] )
774 + : rest_sanitize_boolean( $request->get_param( 'takeover' ) );
775 + if ( is_array( $body ) ) {
776 + unset( $body['takeover'] );
777 + }
737 778 if ( is_array( $body ) && ! empty( $body ) ) {
738 779 \XSpeed\Settings_Manager::update( self::SLUG, $body );
739 780 }
740 - $result = Object_Cache::enable( $this->get_settings() );
781 + $result = Object_Cache::enable( $this->get_settings(), array( 'takeover' => $takeover ) );
741 782
742 783 if ( $result['ok'] && class_exists( '\\XSpeed\\Activity_Log' ) ) {
743 784 \XSpeed\Activity_Log::record(
744 785 'object_cache_enabled',
@@ -749,9 +790,11 @@
749 790 return rest_ensure_response( $result );
750 791 }
751 792
752 793 public function rest_disable( \WP_REST_Request $request ) {
753 - $result = Object_Cache::disable();
794 + $result = Object_Cache::disable(
795 + array( 'restore' => rest_sanitize_boolean( $request->get_param( 'restore' ) ) )
796 + );
754 797 if ( $result['ok'] && class_exists( '\\XSpeed\\Activity_Log' ) ) {
755 798 \XSpeed\Activity_Log::record(
756 799 'object_cache_disabled',
757 800 'Object cache disabled.',
@@ -784,8 +827,20 @@
784 827 'type' => 'positional',
785 828 'name' => 'value',
786 829 'optional' => true,
787 830 ),
831 + array(
832 + 'type' => 'flag',
833 + 'name' => 'takeover',
834 + 'description' => 'With enable: switch from the plugin that owns object-cache.php (backs it up, turns that plugin off, installs xSpeed, undoes everything on failure).',
835 + 'optional' => true,
836 + ),
837 + array(
838 + 'type' => 'flag',
839 + 'name' => 'restore',
840 + 'description' => 'With disable: put back the plugin xSpeed switched from.',
841 + 'optional' => true,
842 + ),
788 843 ),
789 844 ),
790 845 );
791 846 }
@@ -941,13 +996,28 @@
941 996 $t = Object_Cache::test_connection( $this->get_settings() );
942 997 $t['ok'] ? \WP_CLI::success( $t['message'] ) : \WP_CLI::error( $t['message'] );
943 998 return;
944 999 case 'enable':
945 - $r = Object_Cache::enable( $this->get_settings() );
1000 + $r = Object_Cache::enable(
1001 + $this->get_settings(),
1002 + array( 'takeover' => ! empty( $assoc['takeover'] ) )
1003 + );
1004 + if ( ! $r['ok'] && ! empty( $r['needs_takeover'] ) && Object_Cache_Takeover::STRATEGY_REFUSE !== ( $r['owner']['strategy'] ?? '' ) ) {
1005 + // This handler also answers MCP (through Cli_Bridge), where
1006 + // the switch is an argument, not a flag.
1007 + \WP_CLI::error( $r['message'] . ' To switch, run again with --takeover (MCP: takeover: true).' );
1008 + }
946 1009 $r['ok'] ? \WP_CLI::success( $r['message'] ) : \WP_CLI::error( $r['message'] );
947 1010 return;
948 1011 case 'disable':
949 - $r = Object_Cache::disable();
1012 + $r = Object_Cache::disable( array( 'restore' => ! empty( $assoc['restore'] ) ) );
1013 + // Disabling worked; a restore that could not complete is a
1014 + // warning, not a failed command.
1015 + if ( $r['ok'] && false === ( $r['restored'] ?? null ) ) {
1016 + \WP_CLI::warning( $r['message'] );
1017 + \WP_CLI::success( 'Object cache disabled.' );
1018 + return;
1019 + }
950 1020 $r['ok'] ? \WP_CLI::success( $r['message'] ) : \WP_CLI::error( $r['message'] );
951 1021 return;
952 1022 default:
953 1023 \WP_CLI::error( "Unknown action: $action" );