← All changes
|
includes/modules/ObjectCache/ObjectCacheModule.php
+83
-28
1.3.6
→
1.4.0
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,18 @@ | ||
| 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 | 67 | 'type' => 'string', |
| 66 | 68 | 'default' => '127.0.0.1', |
| 67 | 69 | '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' ), | |
| 70 | + 'label' => __( 'Redis host', 'xspeed' ), | |
| 71 | + 'description' => __( 'Address of the Redis server. Use 127.0.0.1 when Redis runs on the same server as your site.', 'xspeed' ), | |
| 72 | + 'dependsOn' => array( 'field' => 'backend', 'value' => 'redis' ), | |
| 70 | 73 | ), |
| 71 | 74 | 'redis_port' => array( |
| 72 | 75 | 'type' => 'int', |
| 73 | 76 | 'default' => 6379, |
| @@ -73,10 +76,12 @@ | ||
| 73 | 76 | 'default' => 6379, |
| 74 | 77 | 'min' => 1, |
| 75 | 78 | 'max' => 65535, |
| 76 | 79 | 'constants' => array( 'XSPEED_OC_PORT', 'WP_REDIS_PORT' ), |
| 77 | - 'label' => __( 'Redis Port', 'xspeed' ), | |
| 78 | - 'description' => __( 'Default Redis port is 6379.', 'xspeed' ), | |
| 80 | + 'label' => __( 'Redis port', 'xspeed' ), | |
| 81 | + 'description' => __( 'The default Redis port is 6379.', 'xspeed' ), | |
| 82 | + 'advanced' => true, | |
| 83 | + 'dependsOn' => array( 'field' => 'backend', 'value' => 'redis' ), | |
| 79 | 84 | ), |
| 80 | 85 | 'redis_user' => array( |
| 81 | 86 | 'type' => 'string', |
| 82 | 87 | 'default' => '', |
| @@ -86,10 +91,11 @@ | ||
| 86 | 91 | // as a plain string it is a password, never a username. |
| 87 | 92 | 'constants' => array( 'XSPEED_OC_USER', 'WP_REDIS_USER', 'WP_REDIS_PASSWORD' ), |
| 88 | 93 | 'constants_pair_only' => array( 'WP_REDIS_PASSWORD' ), |
| 89 | 94 | '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' ), | |
| 95 | + 'label' => __( 'Redis username', 'xspeed' ), | |
| 96 | + 'description' => __( 'Only needed if your host gave you a Redis username along with the password. Leave blank otherwise.', 'xspeed' ), | |
| 97 | + 'dependsOn' => array( 'field' => 'backend', 'value' => 'redis' ), | |
| 92 | 98 | ), |
| 93 | 99 | 'redis_password' => array( |
| 94 | 100 | 'type' => 'secret', |
| 95 | 101 | 'default' => '', |
| @@ -94,10 +100,11 @@ | ||
| 94 | 100 | 'type' => 'secret', |
| 95 | 101 | 'default' => '', |
| 96 | 102 | 'constants' => array( 'XSPEED_OC_PASSWORD', 'WP_REDIS_PASSWORD' ), |
| 97 | 103 | 'constant_pair' => 'password', |
| 98 | - 'label' => __( 'Redis Password', 'xspeed' ), | |
| 99 | - 'description' => __( 'Leave blank if your Redis server runs without auth.', 'xspeed' ), | |
| 104 | + 'label' => __( 'Redis password', 'xspeed' ), | |
| 105 | + 'description' => __( 'Leave blank if your Redis server has no password.', 'xspeed' ), | |
| 106 | + 'dependsOn' => array( 'field' => 'backend', 'value' => 'redis' ), | |
| 100 | 107 | ), |
| 101 | 108 | 'redis_database' => array( |
| 102 | 109 | 'type' => 'int', |
| 103 | 110 | 'default' => 0, |
| @@ -103,10 +110,12 @@ | ||
| 103 | 110 | 'default' => 0, |
| 104 | 111 | 'min' => 0, |
| 105 | 112 | 'max' => 15, |
| 106 | 113 | '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' ), | |
| 114 | + 'label' => __( 'Redis database', 'xspeed' ), | |
| 115 | + 'description' => __( 'Database number, 0 to 15. If several sites share one Redis server, give each site its own number.', 'xspeed' ), | |
| 116 | + 'advanced' => true, | |
| 117 | + 'dependsOn' => array( 'field' => 'backend', 'value' => 'redis' ), | |
| 109 | 118 | ), |
| 110 | 119 | 'memcached_host' => array( |
| 111 | 120 | 'type' => 'string', |
| 112 | 121 | 'default' => '127.0.0.1', |
| @@ -131,10 +140,11 @@ | ||
| 131 | 140 | 'var' => 'memcached_servers', |
| 132 | 141 | 'reader' => array( '\\XSpeed\\Object_Cache', 'first_memcached_server' ), |
| 133 | 142 | 'slot' => 0, |
| 134 | 143 | ), |
| 135 | - 'label' => __( 'Memcached Host', 'xspeed' ), | |
| 136 | - 'description' => __( 'Used when Backend = Memcached.', 'xspeed' ), | |
| 144 | + 'label' => __( 'Memcached host', 'xspeed' ), | |
| 145 | + 'description' => __( 'Address of the Memcached server. Use 127.0.0.1 when it runs on the same server as your site.', 'xspeed' ), | |
| 146 | + 'dependsOn' => array( 'field' => 'backend', 'value' => 'memcached' ), | |
| 137 | 147 | ), |
| 138 | 148 | 'memcached_port' => array( |
| 139 | 149 | 'type' => 'int', |
| 140 | 150 | 'default' => 11211, |
| @@ -153,10 +163,12 @@ | ||
| 153 | 163 | 'var' => 'memcached_servers', |
| 154 | 164 | 'reader' => array( '\\XSpeed\\Object_Cache', 'first_memcached_server' ), |
| 155 | 165 | 'slot' => 1, |
| 156 | 166 | ), |
| 157 | - 'label' => __( 'Memcached Port', 'xspeed' ), | |
| 158 | - 'description' => __( 'Default Memcached port is 11211.', 'xspeed' ), | |
| 167 | + 'label' => __( 'Memcached port', 'xspeed' ), | |
| 168 | + 'description' => __( 'The default Memcached port is 11211.', 'xspeed' ), | |
| 169 | + 'advanced' => true, | |
| 170 | + 'dependsOn' => array( 'field' => 'backend', 'value' => 'memcached' ), | |
| 159 | 171 | ), |
| 160 | 172 | 'key_prefix' => array( |
| 161 | 173 | 'type' => 'string', |
| 162 | 174 | 'default' => '', |
| @@ -180,10 +192,12 @@ | ||
| 180 | 192 | // unlock the field. With no salt of our own the field stays |
| 181 | 193 | // blank and editable; the drop-in still honours a defined |
| 182 | 194 | // WP_CACHE_KEY_SALT as its last-resort salt at runtime. |
| 183 | 195 | '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' ), | |
| 196 | + 'label' => __( 'Cache key prefix', 'xspeed' ), | |
| 197 | + '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' ), | |
| 198 | + // Not advanced: on ACL hosts it is required, and the write-denied | |
| 199 | + // notice (Object_Cache::write_denied_message) sends users here. | |
| 186 | 200 | ), |
| 187 | 201 | 'connection_timeout' => array( |
| 188 | 202 | 'type' => 'int', |
| 189 | 203 | 'default' => 1, |
| @@ -189,18 +203,20 @@ | ||
| 189 | 203 | 'default' => 1, |
| 190 | 204 | 'min' => 0, |
| 191 | 205 | 'max' => 60, |
| 192 | 206 | 'constants' => array( 'XSPEED_OC_TIMEOUT', 'WP_REDIS_TIMEOUT' ), |
| 193 | - 'label' => __( 'Connection Timeout (seconds)', 'xspeed' ), | |
| 207 | + 'label' => __( 'Connection timeout (seconds)', 'xspeed' ), | |
| 194 | 208 | 'unit' => 'seconds', |
| 195 | - 'description' => __( 'How long to wait for a connection. Keep low (1-2s) so a misconfigured cache never stalls the page.', 'xspeed' ), | |
| 209 | + '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' ), | |
| 210 | + 'advanced' => true, | |
| 196 | 211 | ), |
| 197 | 212 | 'persistent' => array( |
| 198 | 213 | 'type' => 'bool', |
| 199 | 214 | 'default' => true, |
| 200 | 215 | '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' ), | |
| 216 | + 'label' => __( 'Persistent connections', 'xspeed' ), | |
| 217 | + 'description' => __( 'Keep the connection open between page loads. Turn off only if your cache server reports too many idle connections.', 'xspeed' ), | |
| 218 | + 'advanced' => true, | |
| 203 | 219 | ), |
| 204 | 220 | ); |
| 205 | 221 | } |
| 206 | 222 | |
| @@ -644,9 +660,9 @@ | ||
| 644 | 660 | * probe recorded on save knows; until this it had no reader outside |
| 645 | 661 | * WP-CLI, so the panel kept saying "Redis ready" over a cache that |
| 646 | 662 | * stored nothing. (#398) |
| 647 | 663 | */ |
| 648 | - $detect = Object_Cache::detect(); | |
| 664 | + $detect = Object_Cache::detect( true ); | |
| 649 | 665 | $failure = self::live_write_failure(); |
| 650 | 666 | if ( null !== $failure ) { |
| 651 | 667 | $detect['write_probe'] = $failure; |
| 652 | 668 | } |
| @@ -733,12 +749,22 @@ | ||
| 733 | 749 | |
| 734 | 750 | public function rest_enable( \WP_REST_Request $request ) { |
| 735 | 751 | // Persist any settings sent with the enable call first, then act on them. |
| 736 | 752 | $body = $request->get_json_params(); |
| 753 | + // `takeover` is an action, not a setting: take it out before the body | |
| 754 | + // is saved as the settings row. Sent in the body rather than the query | |
| 755 | + // string because on plain permalinks the REST base already carries | |
| 756 | + // `?rest_route=`. (#686) | |
| 757 | + $takeover = is_array( $body ) && ! empty( $body['takeover'] ) | |
| 758 | + ? rest_sanitize_boolean( $body['takeover'] ) | |
| 759 | + : rest_sanitize_boolean( $request->get_param( 'takeover' ) ); | |
| 760 | + if ( is_array( $body ) ) { | |
| 761 | + unset( $body['takeover'] ); | |
| 762 | + } | |
| 737 | 763 | if ( is_array( $body ) && ! empty( $body ) ) { |
| 738 | 764 | \XSpeed\Settings_Manager::update( self::SLUG, $body ); |
| 739 | 765 | } |
| 740 | - $result = Object_Cache::enable( $this->get_settings() ); | |
| 766 | + $result = Object_Cache::enable( $this->get_settings(), array( 'takeover' => $takeover ) ); | |
| 741 | 767 | |
| 742 | 768 | if ( $result['ok'] && class_exists( '\\XSpeed\\Activity_Log' ) ) { |
| 743 | 769 | \XSpeed\Activity_Log::record( |
| 744 | 770 | 'object_cache_enabled', |
| @@ -749,9 +775,11 @@ | ||
| 749 | 775 | return rest_ensure_response( $result ); |
| 750 | 776 | } |
| 751 | 777 | |
| 752 | 778 | public function rest_disable( \WP_REST_Request $request ) { |
| 753 | - $result = Object_Cache::disable(); | |
| 779 | + $result = Object_Cache::disable( | |
| 780 | + array( 'restore' => rest_sanitize_boolean( $request->get_param( 'restore' ) ) ) | |
| 781 | + ); | |
| 754 | 782 | if ( $result['ok'] && class_exists( '\\XSpeed\\Activity_Log' ) ) { |
| 755 | 783 | \XSpeed\Activity_Log::record( |
| 756 | 784 | 'object_cache_disabled', |
| 757 | 785 | 'Object cache disabled.', |
| @@ -784,8 +812,20 @@ | ||
| 784 | 812 | 'type' => 'positional', |
| 785 | 813 | 'name' => 'value', |
| 786 | 814 | 'optional' => true, |
| 787 | 815 | ), |
| 816 | + array( | |
| 817 | + 'type' => 'flag', | |
| 818 | + 'name' => 'takeover', | |
| 819 | + '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).', | |
| 820 | + 'optional' => true, | |
| 821 | + ), | |
| 822 | + array( | |
| 823 | + 'type' => 'flag', | |
| 824 | + 'name' => 'restore', | |
| 825 | + 'description' => 'With disable: put back the plugin xSpeed switched from.', | |
| 826 | + 'optional' => true, | |
| 827 | + ), | |
| 788 | 828 | ), |
| 789 | 829 | ), |
| 790 | 830 | ); |
| 791 | 831 | } |
| @@ -941,13 +981,28 @@ | ||
| 941 | 981 | $t = Object_Cache::test_connection( $this->get_settings() ); |
| 942 | 982 | $t['ok'] ? \WP_CLI::success( $t['message'] ) : \WP_CLI::error( $t['message'] ); |
| 943 | 983 | return; |
| 944 | 984 | case 'enable': |
| 945 | - $r = Object_Cache::enable( $this->get_settings() ); | |
| 985 | + $r = Object_Cache::enable( | |
| 986 | + $this->get_settings(), | |
| 987 | + array( 'takeover' => ! empty( $assoc['takeover'] ) ) | |
| 988 | + ); | |
| 989 | + if ( ! $r['ok'] && ! empty( $r['needs_takeover'] ) && Object_Cache_Takeover::STRATEGY_REFUSE !== ( $r['owner']['strategy'] ?? '' ) ) { | |
| 990 | + // This handler also answers MCP (through Cli_Bridge), where | |
| 991 | + // the switch is an argument, not a flag. | |
| 992 | + \WP_CLI::error( $r['message'] . ' To switch, run again with --takeover (MCP: takeover: true).' ); | |
| 993 | + } | |
| 946 | 994 | $r['ok'] ? \WP_CLI::success( $r['message'] ) : \WP_CLI::error( $r['message'] ); |
| 947 | 995 | return; |
| 948 | 996 | case 'disable': |
| 949 | - $r = Object_Cache::disable(); | |
| 997 | + $r = Object_Cache::disable( array( 'restore' => ! empty( $assoc['restore'] ) ) ); | |
| 998 | + // Disabling worked; a restore that could not complete is a | |
| 999 | + // warning, not a failed command. | |
| 1000 | + if ( $r['ok'] && false === ( $r['restored'] ?? null ) ) { | |
| 1001 | + \WP_CLI::warning( $r['message'] ); | |
| 1002 | + \WP_CLI::success( 'Object cache disabled.' ); | |
| 1003 | + return; | |
| 1004 | + } | |
| 950 | 1005 | $r['ok'] ? \WP_CLI::success( $r['message'] ) : \WP_CLI::error( $r['message'] ); |
| 951 | 1006 | return; |
| 952 | 1007 | default: |
| 953 | 1008 | \WP_CLI::error( "Unknown action: $action" ); |