← All changes
|
includes/modules/ObjectCache/ObjectCacheModule.php
+102
-32
1.3.2
→
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" ); |