Redis Object Cache - Version 1.3.6

Version Description

  • Added support for Redis Sentinel
    • Added support for sharing
    • Switched to PHAR version of Predis
    • Improved diagnostics
    • Added WP_REDIS_SELECTIVE_FLUSH
    • Added $fail_gracefully parameter to WP_Object_Cache::__construct()
    • Always enforce WP_REDIS_MAXTTL
    • Pass $selective and $salt to redis_object_cache_flush action
    • Dont set WP_CACHE_KEY_SALT constant
Download this release

Release Info

Developer tillkruess
Plugin Icon 128x128 Redis Object Cache
Version 1.3.6
Comparing to
See all releases

Code changes from version 1.3.5 to 1.3.6

includes/admin-page.css CHANGED
@@ -1,36 +1,36 @@
1
 
2
  .settings_page_redis-cache .form-table th,
3
  .settings_page_redis-cache .form-table td {
4
- padding-top: 7px;
5
- padding-bottom: 7px;
6
  }
7
 
8
  .wrap .button.delete {
9
- background: #d54e21;
10
- border-color: #a83d1a;
11
- color: white;
12
- -webkit-box-shadow: inset 0 1px 0 #e68260, 0 1px 0 rgba(0, 0, 0, 0.15);
13
- box-shadow: inset 0 1px 0 #e68260, 0 1px 0 rgba(0, 0, 0, 0.15);
14
  }
15
 
16
  .wrap .button.delete:hover,
17
  .wrap .button.delete:focus {
18
- background: #c7491f;
19
- border-color: #923517;
20
- color: white;
21
  }
22
 
23
  .wrap .button.delete:focus {
24
- box-shadow: inset 0 1px 0 #e3704a,
25
- 0 0 0 1px #d54e21,
26
- 0 0 2px 1px rgba( 30, 140, 190, .8 );
27
  }
28
 
29
  .wrap .button.delete:active {
30
- background: #a83d1a;
31
- border-color: #923517;
32
- color: white;
33
- box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 ),
34
- 0 0 0 1px #d54e21,
35
- 0 0 2px 1px rgba( 30, 140, 190, .8 );
36
  }
1
 
2
  .settings_page_redis-cache .form-table th,
3
  .settings_page_redis-cache .form-table td {
4
+ padding-top: 7px;
5
+ padding-bottom: 7px;
6
  }
7
 
8
  .wrap .button.delete {
9
+ background: #d54e21;
10
+ border-color: #a83d1a;
11
+ color: white;
12
+ -webkit-box-shadow: inset 0 1px 0 #e68260, 0 1px 0 rgba(0, 0, 0, 0.15);
13
+ box-shadow: inset 0 1px 0 #e68260, 0 1px 0 rgba(0, 0, 0, 0.15);
14
  }
15
 
16
  .wrap .button.delete:hover,
17
  .wrap .button.delete:focus {
18
+ background: #c7491f;
19
+ border-color: #923517;
20
+ color: white;
21
  }
22
 
23
  .wrap .button.delete:focus {
24
+ box-shadow: inset 0 1px 0 #e3704a,
25
+ 0 0 0 1px #d54e21,
26
+ 0 0 2px 1px rgba( 30, 140, 190, .8 );
27
  }
28
 
29
  .wrap .button.delete:active {
30
+ background: #a83d1a;
31
+ border-color: #923517;
32
+ color: white;
33
+ box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 ),
34
+ 0 0 0 1px #d54e21,
35
+ 0 0 2px 1px rgba( 30, 140, 190, .8 );
36
  }
includes/admin-page.php CHANGED
@@ -3,68 +3,68 @@
3
 
4
  <div class="wrap">
5
 
6
- <h1><?php _e( 'Redis Object Cache', 'redis-cache' ); ?></h1>
7
 
8
- <h2 class="title"><?php _e( 'Overview', 'redis-cache' ); ?></h2>
9
 
10
- <table class="form-table">
11
 
12
- <tr>
13
- <th><?php _e( 'Status:', 'redis-cache' ); ?></th>
14
- <td><code><?php echo $this->get_status(); ?></code></td>
15
- </tr>
16
 
17
- <?php if ( ! is_null( $this->get_redis_client_name() ) ) : ?>
18
- <tr>
19
- <th><?php _e( 'Client:', 'redis-cache' ); ?></th>
20
- <td><code><?php echo esc_html( $this->get_redis_client_name() ); ?></code></td>
21
- </tr>
22
- <?php endif; ?>
23
 
24
- <?php if ( ! is_null( $this->get_redis_cachekey_prefix() ) && trim( $this->get_redis_cachekey_prefix() ) !== '' ) : ?>
25
- <tr>
26
- <th><?php _e( 'Key Prefix:', 'redis-cache' ); ?></th>
27
- <td><code><?php echo esc_html( $this->get_redis_cachekey_prefix() ); ?></code></td>
28
- </tr>
29
- <?php endif; ?>
30
 
31
- <?php if ( ! is_null( $this->get_redis_maxttl() ) ) : ?>
32
- <tr>
33
- <th><?php _e( 'Max. TTL:', 'redis-cache' ); ?></th>
34
- <td><code><?php echo esc_html( $this->get_redis_maxttl() ); ?></code></td>
35
- </tr>
36
- <?php endif; ?>
37
 
38
- </table>
39
 
40
- <p class="submit">
41
 
42
- <?php if ( $this->get_redis_status() ) : ?>
43
- <a href="<?php echo wp_nonce_url( network_admin_url( add_query_arg( 'action', 'flush-cache', $this->page ) ), 'flush-cache' ); ?>" class="button button-primary button-large"><?php _e( 'Flush Cache', 'redis-cache' ); ?></a> &nbsp;
44
- <?php endif; ?>
45
 
46
- <?php if ( ! $this->object_cache_dropin_exists() ) : ?>
47
- <a href="<?php echo wp_nonce_url( network_admin_url( add_query_arg( 'action', 'enable-cache', $this->page ) ), 'enable-cache' ); ?>" class="button button-primary button-large"><?php _e( 'Enable Object Cache', 'redis-cache' ); ?></a>
48
- <?php elseif ( $this->validate_object_cache_dropin() ) : ?>
49
- <a href="<?php echo wp_nonce_url( network_admin_url( add_query_arg( 'action', 'disable-cache', $this->page ) ), 'disable-cache' ); ?>" class="button button-secondary button-large delete"><?php _e( 'Disable Object Cache', 'redis-cache' ); ?></a>
50
- <?php endif; ?>
51
 
52
- </p>
53
 
54
- <h2 class="title"><?php _e( 'Servers', 'redis-cache' ); ?></h2>
55
 
56
- <?php $this->show_servers_list(); ?>
57
 
58
  <?php if ( isset( $_GET[ 'diagnostics' ] ) ) : ?>
59
 
60
- <h2 class="title"><?php _e( 'Diagnostics', 'redis-cache' ); ?></h2>
61
 
62
- <textarea class="large-text readonly" rows="20" readonly><?php include dirname( __FILE__ ) . '/diagnostics.php'; ?></textarea>
63
 
64
- <?php else : ?>
65
 
66
- <p><a href="<?php echo network_admin_url( add_query_arg( 'diagnostics', '1', $this->page ) ); ?>"><?php _e( 'Show Diagnostics', 'redis-cache' ); ?></a></p>
67
 
68
- <?php endif; ?>
69
 
70
  </div>
3
 
4
  <div class="wrap">
5
 
6
+ <h1><?php _e( 'Redis Object Cache', 'redis-cache' ); ?></h1>
7
 
8
+ <h2 class="title"><?php _e( 'Overview', 'redis-cache' ); ?></h2>
9
 
10
+ <table class="form-table">
11
 
12
+ <tr>
13
+ <th><?php _e( 'Status:', 'redis-cache' ); ?></th>
14
+ <td><code><?php echo $this->get_status(); ?></code></td>
15
+ </tr>
16
 
17
+ <?php if ( ! is_null( $this->get_redis_client_name() ) ) : ?>
18
+ <tr>
19
+ <th><?php _e( 'Client:', 'redis-cache' ); ?></th>
20
+ <td><code><?php echo esc_html( $this->get_redis_client_name() ); ?></code></td>
21
+ </tr>
22
+ <?php endif; ?>
23
 
24
+ <?php if ( ! is_null( $this->get_redis_cachekey_prefix() ) && trim( $this->get_redis_cachekey_prefix() ) !== '' ) : ?>
25
+ <tr>
26
+ <th><?php _e( 'Key Prefix:', 'redis-cache' ); ?></th>
27
+ <td><code><?php echo esc_html( $this->get_redis_cachekey_prefix() ); ?></code></td>
28
+ </tr>
29
+ <?php endif; ?>
30
 
31
+ <?php if ( ! is_null( $this->get_redis_maxttl() ) ) : ?>
32
+ <tr>
33
+ <th><?php _e( 'Max. TTL:', 'redis-cache' ); ?></th>
34
+ <td><code><?php echo esc_html( $this->get_redis_maxttl() ); ?></code></td>
35
+ </tr>
36
+ <?php endif; ?>
37
 
38
+ </table>
39
 
40
+ <p class="submit">
41
 
42
+ <?php if ( $this->get_redis_status() ) : ?>
43
+ <a href="<?php echo wp_nonce_url( network_admin_url( add_query_arg( 'action', 'flush-cache', $this->page ) ), 'flush-cache' ); ?>" class="button button-primary button-large"><?php _e( 'Flush Cache', 'redis-cache' ); ?></a> &nbsp;
44
+ <?php endif; ?>
45
 
46
+ <?php if ( ! $this->object_cache_dropin_exists() ) : ?>
47
+ <a href="<?php echo wp_nonce_url( network_admin_url( add_query_arg( 'action', 'enable-cache', $this->page ) ), 'enable-cache' ); ?>" class="button button-primary button-large"><?php _e( 'Enable Object Cache', 'redis-cache' ); ?></a>
48
+ <?php elseif ( $this->validate_object_cache_dropin() ) : ?>
49
+ <a href="<?php echo wp_nonce_url( network_admin_url( add_query_arg( 'action', 'disable-cache', $this->page ) ), 'disable-cache' ); ?>" class="button button-secondary button-large delete"><?php _e( 'Disable Object Cache', 'redis-cache' ); ?></a>
50
+ <?php endif; ?>
51
 
52
+ </p>
53
 
54
+ <h2 class="title"><?php _e( 'Servers', 'redis-cache' ); ?></h2>
55
 
56
+ <?php $this->show_servers_list(); ?>
57
 
58
  <?php if ( isset( $_GET[ 'diagnostics' ] ) ) : ?>
59
 
60
+ <h2 class="title"><?php _e( 'Diagnostics', 'redis-cache' ); ?></h2>
61
 
62
+ <textarea class="large-text readonly" rows="20" readonly><?php include dirname( __FILE__ ) . '/diagnostics.php'; ?></textarea>
63
 
64
+ <?php else : ?>
65
 
66
+ <p><a href="<?php echo network_admin_url( add_query_arg( 'diagnostics', '1', $this->page ) ); ?>"><?php _e( 'Show Diagnostics', 'redis-cache' ); ?></a></p>
67
 
68
+ <?php endif; ?>
69
 
70
  </div>
includes/diagnostics.php CHANGED
@@ -1,6 +1,26 @@
1
  <?php
2
 
3
- $info = array();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
  if ( defined( 'PHP_VERSION' ) ) {
6
  $info[ 'PHP Version' ] = PHP_VERSION;
@@ -10,13 +30,12 @@ if ( defined( 'HHVM_VERSION' ) ) {
10
  $info[ 'HHVM Version' ] = HHVM_VERSION;
11
  }
12
 
13
- $info[ __( 'Multisite', 'redis-cache' ) ] = is_multisite() ? __( 'Yes', 'redis-cache' ) : __( 'No', 'redis-cache' );
14
 
15
- $info[ __( 'Redis', 'redis-cache' ) ] = class_exists( 'Redis' ) ? phpversion( 'redis' ) : __( 'Not Found', 'redis-cache' );
16
- $info[ __( 'Predis', 'redis-cache' ) ] = class_exists( 'Predis\Client' ) ? Predis\Client::VERSION : __( 'Not Found', 'redis-cache' );
17
-
18
- $info[ __( 'Status', 'redis-cache' ) ] = $this->get_status();
19
- $info[ __( 'Client', 'redis-cache' ) ] = $this->get_redis_client_name();
20
 
21
  $constants = array(
22
  'WP_REDIS_DISABLED',
@@ -28,15 +47,17 @@ $constants = array(
28
  'WP_REDIS_DATABASE',
29
  'WP_REDIS_SERVERS',
30
  'WP_REDIS_CLUSTER',
 
 
31
  'WP_REDIS_MAXTTL',
32
- 'WP_REDIS_GLOBAL_GROUPS',
33
- 'WP_REDIS_IGNORED_GROUPS',
34
  'WP_CACHE_KEY_SALT',
 
 
35
  );
36
 
37
  foreach ( $constants as $constant ) {
38
  if ( defined( $constant ) ) {
39
- $info[$constant] = json_encode( constant( $constant ) );
40
  }
41
  }
42
 
@@ -44,16 +65,39 @@ if ( defined( 'WP_REDIS_PASSWORD' ) ) {
44
  $info[ 'WP_REDIS_PASSWORD' ] = json_encode( empty( WP_REDIS_PASSWORD ) ? null : str_repeat( '*', strlen( WP_REDIS_PASSWORD ) ) );
45
  }
46
 
47
- if ( $this->validate_object_cache_dropin() ) {
48
- $info[ __( 'Drop-in', 'redis-cache' ) ] = __( 'Valid', 'redis-cache' );
49
- $info[ __( 'Global Prefix', 'redis-cache' ) ] = json_encode( $GLOBALS[ 'wp_object_cache' ]->global_prefix );
50
- $info[ __( 'Blog Prefix', 'redis-cache' ) ] = json_encode( $GLOBALS[ 'wp_object_cache' ]->blog_prefix );
51
- $info[ __( 'Global Groups', 'redis-cache' ) ] = json_encode( $GLOBALS[ 'wp_object_cache' ]->global_groups );
52
- $info[ __( 'Ignored Groups', 'redis-cache' ) ] = json_encode( $GLOBALS[ 'wp_object_cache' ]->ignored_groups );
53
- } else {
54
- $info[ __( 'Drop-in', 'redis-cache' ) ] = __( 'Invalid', 'redis-cache' );
55
  }
56
 
57
- foreach ($info as $name => $value) {
58
  echo "{$name}: {$value}\r\n";
59
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <?php
2
 
3
+ global $wp_object_cache;
4
+
5
+ $info = $plugins = $dropins = array();
6
+ $dropin = $this->validate_object_cache_dropin();
7
+
8
+ $info[ 'Status' ] = $this->get_status();
9
+ $info[ 'Client' ] = $this->get_redis_client_name();
10
+
11
+ $info[ 'Drop-in' ] = $dropin ? 'Valid' : 'Invalid';
12
+
13
+ if ( $dropin ) {
14
+ try {
15
+ $cache = new WP_Object_Cache( false );
16
+ $info[ 'Ping' ] = $cache->redis_instance()->ping();
17
+ } catch ( Exception $exception ) {
18
+ $info[ 'Connection Exception' ] = sprintf( '%s (%s)', $exception->getMessage(), get_class( $exception ) );
19
+ }
20
+ }
21
+
22
+ $info[ 'Redis Extension' ] = class_exists( 'Redis' ) ? phpversion( 'redis' ) : 'Not Found';
23
+ $info[ 'Predis Client' ] = class_exists( 'Predis\Client' ) ? Predis\Client::VERSION : 'Not Found';
24
 
25
  if ( defined( 'PHP_VERSION' ) ) {
26
  $info[ 'PHP Version' ] = PHP_VERSION;
30
  $info[ 'HHVM Version' ] = HHVM_VERSION;
31
  }
32
 
33
+ $info[ 'Multisite' ] = is_multisite() ? 'Yes' : 'No';
34
 
35
+ if ( $dropin ) {
36
+ $info[ 'Global Prefix' ] = json_encode( $wp_object_cache->global_prefix );
37
+ $info[ 'Blog Prefix' ] = json_encode( $wp_object_cache->blog_prefix );
38
+ }
 
39
 
40
  $constants = array(
41
  'WP_REDIS_DISABLED',
47
  'WP_REDIS_DATABASE',
48
  'WP_REDIS_SERVERS',
49
  'WP_REDIS_CLUSTER',
50
+ 'WP_REDIS_SHARDS',
51
+ 'WP_REDIS_SENTINEL',
52
  'WP_REDIS_MAXTTL',
 
 
53
  'WP_CACHE_KEY_SALT',
54
+ 'WP_REDIS_GLOBAL_GROUPS',
55
+ 'WP_REDIS_IGNORED_GROUPS',
56
  );
57
 
58
  foreach ( $constants as $constant ) {
59
  if ( defined( $constant ) ) {
60
+ $info[ $constant ] = json_encode( constant( $constant ) );
61
  }
62
  }
63
 
65
  $info[ 'WP_REDIS_PASSWORD' ] = json_encode( empty( WP_REDIS_PASSWORD ) ? null : str_repeat( '*', strlen( WP_REDIS_PASSWORD ) ) );
66
  }
67
 
68
+ if ( $dropin ) {
69
+ $info[ 'Global Groups' ] = json_encode( $wp_object_cache->global_groups );
70
+ $info[ 'Ignored Groups' ] = json_encode( $wp_object_cache->ignored_groups );
 
 
 
 
 
71
  }
72
 
73
+ foreach ( $info as $name => $value ) {
74
  echo "{$name}: {$value}\r\n";
75
  }
76
+
77
+ foreach ( get_dropins() as $file => $details ) {
78
+ $dropins[ $file ] = sprintf(
79
+ ' - %s v%s by %s',
80
+ $details[ 'Name' ],
81
+ $details[ 'Version' ],
82
+ $details[ 'Author' ]
83
+ );
84
+ }
85
+
86
+ if ( ! empty( $dropins ) ) {
87
+ echo "Dropins: \r\n", implode( "\r\n", $dropins ), "\r\n";
88
+ }
89
+
90
+ foreach ( get_plugins() as $file => $details ) {
91
+ $plugins[] = sprintf(
92
+ ' - %s v%s by %s (%s%s)',
93
+ $details[ 'Name' ],
94
+ $details[ 'Version' ],
95
+ $details[ 'Author' ],
96
+ is_plugin_active( $file ) ? 'Active' : 'Inactive',
97
+ is_multisite() ? ( is_plugin_active_for_network( $file ) ? ' network-wide' : '' ) : ''
98
+ );
99
+ }
100
+
101
+ if ( ! empty( $plugins ) ) {
102
+ echo "Plugins: \r\n", implode( "\r\n", $plugins ), "\r\n";
103
+ }
includes/object-cache.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Redis Object Cache Drop-In
4
  Plugin URI: http://wordpress.org/plugins/redis-cache/
5
  Description: A persistent object cache backend powered by Redis. Supports Predis, PhpRedis, HHVM, replication, clustering and WP-CLI.
6
- Version: 1.3.5
7
  Author: Till Krüss
8
  Author URI: https://till.im/
9
  License: GPLv3
@@ -13,7 +13,7 @@ Based on Eric Mann's and Erick Hitter's Redis Object Cache:
13
  https://github.com/ericmann/Redis-Object-Cache
14
  */
15
 
16
- if ( ! defined( 'WP_REDIS_DISABLED' ) || ! WP_REDIS_DISABLED ) :
17
 
18
  /**
19
  * Adds a value to cache.
@@ -30,10 +30,11 @@ if ( ! defined( 'WP_REDIS_DISABLED' ) || ! WP_REDIS_DISABLED ) :
30
  *
31
  * @return bool Returns TRUE on success or FALSE on failure.
32
  */
33
- function wp_cache_add( $key, $value, $group = '', $expiration = 0 ) {
34
- global $wp_object_cache;
 
35
 
36
- return $wp_object_cache->add( $key, $value, $group, $expiration );
37
  }
38
 
39
  /**
@@ -46,8 +47,9 @@ function wp_cache_add( $key, $value, $group = '', $expiration = 0 ) {
46
  *
47
  * @return bool Always returns True
48
  */
49
- function wp_cache_close() {
50
- return true;
 
51
  }
52
 
53
  /**
@@ -61,10 +63,11 @@ function wp_cache_close() {
61
  *
62
  * @return int|bool Returns item's new value on success or FALSE on failure.
63
  */
64
- function wp_cache_decr( $key, $offset = 1, $group = '' ) {
65
- global $wp_object_cache;
 
66
 
67
- return $wp_object_cache->decrement( $key, $offset, $group );
68
  }
69
 
70
  /**
@@ -78,14 +81,16 @@ function wp_cache_decr( $key, $offset = 1, $group = '' ) {
78
  *
79
  * @return bool Returns TRUE on success or FALSE on failure.
80
  */
81
- function wp_cache_delete( $key, $group = '', $time = 0 ) {
82
- global $wp_object_cache;
 
83
 
84
- return $wp_object_cache->delete( $key, $group, $time );
85
  }
86
 
87
  /**
88
- * Invalidate all items in the cache.
 
89
  *
90
  * @param int $delay Number of seconds to wait before invalidating the items.
91
  *
@@ -93,10 +98,11 @@ function wp_cache_delete( $key, $group = '', $time = 0 ) {
93
  *
94
  * @return bool Returns TRUE on success or FALSE on failure.
95
  */
96
- function wp_cache_flush( $delay = 0 ) {
97
- global $wp_object_cache;
 
98
 
99
- return $wp_object_cache->flush( $delay );
100
  }
101
 
102
  /**
@@ -115,10 +121,11 @@ function wp_cache_flush( $delay = 0 ) {
115
  *
116
  * @return bool|mixed Cached object value.
117
  */
118
- function wp_cache_get( $key, $group = '', $force = false, &$found = null ) {
119
- global $wp_object_cache;
 
120
 
121
- return $wp_object_cache->get( $key, $group, $force, $found );
122
  }
123
 
124
  /**
@@ -136,10 +143,11 @@ function wp_cache_get( $key, $group = '', $force = false, &$found = null ) {
136
  *
137
  * @return bool|mixed Array of cached values, keys in the format $group:$key. Non-existent keys false
138
  */
139
- function wp_cache_get_multi( $groups ) {
140
- global $wp_object_cache;
 
141
 
142
- return $wp_object_cache->get_multi( $groups );
143
  }
144
 
145
  /**
@@ -153,10 +161,11 @@ function wp_cache_get_multi( $groups ) {
153
  *
154
  * @return int|bool Returns item's new value on success or FALSE on failure.
155
  */
156
- function wp_cache_incr( $key, $offset = 1, $group = '' ) {
157
- global $wp_object_cache;
 
158
 
159
- return $wp_object_cache->increment( $key, $offset, $group );
160
  }
161
 
162
  /**
@@ -166,12 +175,13 @@ function wp_cache_incr( $key, $offset = 1, $group = '' ) {
166
  *
167
  * @return void
168
  */
169
- function wp_cache_init() {
170
- global $wp_object_cache;
 
171
 
172
- if ( ! ( $wp_object_cache instanceof WP_Object_Cache ) ) {
173
- $wp_object_cache = new WP_Object_Cache;
174
- }
175
  }
176
 
177
  /**
@@ -189,10 +199,11 @@ function wp_cache_init() {
189
  *
190
  * @return bool Returns TRUE on success or FALSE on failure.
191
  */
192
- function wp_cache_replace( $key, $value, $group = '', $expiration = 0 ) {
193
- global $wp_object_cache;
 
194
 
195
- return $wp_object_cache->replace( $key, $value, $group, $expiration );
196
  }
197
 
198
  /**
@@ -209,10 +220,11 @@ function wp_cache_replace( $key, $value, $group = '', $expiration = 0 ) {
209
  *
210
  * @return bool Returns TRUE on success or FALSE on failure.
211
  */
212
- function wp_cache_set( $key, $value, $group = '', $expiration = 0 ) {
213
- global $wp_object_cache;
 
214
 
215
- return $wp_object_cache->set( $key, $value, $group, $expiration );
216
  }
217
 
218
  /**
@@ -226,10 +238,11 @@ function wp_cache_set( $key, $value, $group = '', $expiration = 0 ) {
226
  *
227
  * @return bool
228
  */
229
- function wp_cache_switch_to_blog( $_blog_id ) {
230
- global $wp_object_cache;
 
231
 
232
- return $wp_object_cache->switch_to_blog( $_blog_id );
233
  }
234
 
235
  /**
@@ -241,10 +254,11 @@ function wp_cache_switch_to_blog( $_blog_id ) {
241
  *
242
  * @return void
243
  */
244
- function wp_cache_add_global_groups( $groups ) {
245
- global $wp_object_cache;
 
246
 
247
- $wp_object_cache->add_global_groups( $groups );
248
  }
249
 
250
  /**
@@ -256,905 +270,955 @@ function wp_cache_add_global_groups( $groups ) {
256
  *
257
  * @return void
258
  */
259
- function wp_cache_add_non_persistent_groups( $groups ) {
260
- global $wp_object_cache;
 
261
 
262
- $wp_object_cache->add_non_persistent_groups( $groups );
263
  }
264
 
265
- class WP_Object_Cache {
266
-
267
- /**
268
- * The Redis client.
269
- *
270
- * @var mixed
271
- */
272
- private $redis;
273
-
274
- /**
275
- * Track if Redis is available
276
- *
277
- * @var bool
278
- */
279
- private $redis_connected = false;
280
-
281
- /**
282
- * Holds the non-Redis objects.
283
- *
284
- * @var array
285
- */
286
- public $cache = array();
287
-
288
- /**
289
- * Name of the used Redis client
290
- *
291
- * @var bool
292
- */
293
- public $redis_client = null;
294
-
295
- /**
296
- * List of global groups.
297
- *
298
- * @var array
299
- */
300
- public $global_groups = array(
301
- 'blog-details',
302
- 'blog-id-cache',
303
- 'blog-lookup',
304
- 'global-posts',
305
- 'networks',
306
- 'rss',
307
- 'sites',
308
- 'site-details',
309
- 'site-lookup',
310
- 'site-options',
311
- 'site-transient',
312
- 'users',
313
- 'useremail',
314
- 'userlogins',
315
- 'usermeta',
316
- 'user_meta',
317
- 'userslugs',
318
- );
319
-
320
- /**
321
- * List of groups not saved to Redis.
322
- *
323
- * @var array
324
- */
325
- public $ignored_groups = array( 'counts', 'plugins' );
326
-
327
- /**
328
- * Prefix used for global groups.
329
- *
330
- * @var string
331
- */
332
- public $global_prefix = '';
333
-
334
- /**
335
- * Prefix used for non-global groups.
336
- *
337
- * @var string
338
- */
339
- public $blog_prefix = '';
340
-
341
- /**
342
- * Track how many requests were found in cache
343
- *
344
- * @var int
345
- */
346
- public $cache_hits = 0;
347
-
348
- /**
349
- * Track how may requests were not cached
350
- *
351
- * @var int
352
- */
353
- public $cache_misses = 0;
354
-
355
- /**
356
- * Instantiate the Redis class.
357
- *
358
- * Instantiates the Redis class.
359
- *
360
- * @param null $persistent_id To create an instance that persists between requests, use persistent_id to specify a unique ID for the instance.
361
- */
362
- public function __construct() {
363
- global $blog_id, $table_prefix;
364
-
365
- $parameters = array(
366
- 'scheme' => 'tcp',
367
- 'host' => '127.0.0.1',
368
- 'port' => 6379
369
- );
370
-
371
- foreach ( array( 'scheme', 'host', 'port', 'path', 'password', 'database' ) as $setting ) {
372
- $constant = sprintf( 'WP_REDIS_%s', strtoupper( $setting ) );
373
- if ( defined( $constant ) ) {
374
- $parameters[ $setting ] = constant( $constant );
375
- }
376
- }
377
-
378
- if ( defined( 'WP_REDIS_GLOBAL_GROUPS' ) && is_array( WP_REDIS_GLOBAL_GROUPS ) ) {
379
- $this->global_groups = WP_REDIS_GLOBAL_GROUPS;
380
- }
381
-
382
- if ( defined( 'WP_REDIS_IGNORED_GROUPS' ) && is_array( WP_REDIS_IGNORED_GROUPS ) ) {
383
- $this->ignored_groups = WP_REDIS_IGNORED_GROUPS;
384
- }
385
-
386
- $client = defined( 'WP_REDIS_CLIENT' ) ? WP_REDIS_CLIENT : null;
387
-
388
- if ( class_exists( 'Redis' ) && strcasecmp( 'predis', $client ) !== 0 ) {
389
- $client = defined( 'HHVM_VERSION' ) ? 'hhvm' : 'pecl';
390
- } else {
391
- $client = 'predis';
392
- }
393
-
394
- try {
395
-
396
- if ( strcasecmp( 'hhvm', $client ) === 0 ) {
397
-
398
- $this->redis_client = sprintf( 'HHVM Extension (v%s)', HHVM_VERSION );
399
- $this->redis = new Redis();
400
-
401
- // Adjust host and port, if the scheme is `unix`
402
- if ( strcasecmp( 'unix', $parameters[ 'scheme' ] ) === 0 ) {
403
- $parameters[ 'host' ] = 'unix://' . $parameters[ 'path' ];
404
- $parameters[ 'port' ] = 0;
405
- }
406
-
407
- $this->redis->connect( $parameters[ 'host' ], $parameters[ 'port' ] );
408
- }
409
-
410
- if ( strcasecmp( 'pecl', $client ) === 0 ) {
411
-
412
- $this->redis_client = sprintf( 'PECL Extension (v%s)', phpversion( 'redis' ) );
413
- $this->redis = new Redis();
414
-
415
- if ( strcasecmp( 'unix', $parameters[ 'scheme' ] ) === 0 ) {
416
- $this->redis->connect( $parameters[ 'path' ] );
417
- } else {
418
- $this->redis->connect( $parameters[ 'host' ], $parameters[ 'port' ] );
419
- }
420
- }
421
-
422
- if ( strcasecmp( 'pecl', $client ) === 0 || strcasecmp( 'hhvm', $client ) === 0 ) {
423
- if ( isset( $parameters[ 'password' ] ) ) {
424
- $this->redis->auth( $parameters[ 'password' ] );
425
- }
426
-
427
- if ( isset( $parameters[ 'database' ] ) ) {
428
- $this->redis->select( $parameters[ 'database' ] );
429
- }
430
- }
431
-
432
- if ( strcasecmp( 'predis', $client ) === 0 ) {
433
-
434
- $this->redis_client = 'Predis';
435
-
436
- // Require PHP 5.4 or greater
437
- if ( version_compare( PHP_VERSION, '5.4.0', '<' ) ) {
438
- throw new Exception;
439
- }
440
-
441
- // Load bundled Predis library
442
- if ( ! class_exists( 'Predis\Client' ) ) {
443
- $plugin_dir = defined( 'WP_PLUGIN_DIR' ) ? WP_PLUGIN_DIR : WP_CONTENT_DIR . '/plugins';
444
- require_once $plugin_dir . '/redis-cache/includes/predis.php';
445
- Predis\Autoloader::register();
446
- }
447
-
448
- $options = array();
449
-
450
- if ( defined( 'WP_REDIS_CLUSTER' ) ) {
451
- $parameters = WP_REDIS_CLUSTER;
452
- $options[ 'cluster' ] = 'redis';
453
- }
454
-
455
- if ( defined( 'WP_REDIS_SERVERS' ) ) {
456
- $parameters = WP_REDIS_SERVERS;
457
- $options[ 'replication' ] = true;
458
- }
459
-
460
- if ( ( defined( 'WP_REDIS_SERVERS' ) || defined( 'WP_REDIS_CLUSTER' ) ) && defined( 'WP_REDIS_PASSWORD' ) ) {
461
- $options[ 'parameters' ][ 'password' ] = WP_REDIS_PASSWORD;
462
- }
463
-
464
- $this->redis = new Predis\Client( $parameters, $options );
465
- $this->redis->connect();
466
-
467
- $this->redis_client .= sprintf( ' (v%s)', Predis\Client::VERSION );
468
-
469
- }
470
-
471
- // Throws exception if Redis is unavailable
472
- $this->redis->ping();
473
-
474
- $this->redis_connected = true;
475
-
476
- } catch ( Exception $exception ) {
477
-
478
- // When Redis is unavailable, fall back to the internal back by forcing all groups to be "no redis" groups
479
- $this->ignored_groups = array_unique( array_merge( $this->ignored_groups, $this->global_groups ) );
480
-
481
- $this->redis_connected = false;
482
-
483
- }
484
-
485
- /**
486
- * This approach is borrowed from Sivel and Boren. Use the salt for easy cache invalidation and for
487
- * multi single WP installs on the same server.
488
- */
489
- if ( ! defined( 'WP_CACHE_KEY_SALT' ) ) {
490
- define( 'WP_CACHE_KEY_SALT', '' );
491
- }
492
-
493
- // Assign global and blog prefixes for use with keys
494
- if ( function_exists( 'is_multisite' ) ) {
495
- $this->global_prefix = ( is_multisite() || defined( 'CUSTOM_USER_TABLE' ) && defined( 'CUSTOM_USER_META_TABLE' ) ) ? '' : $table_prefix;
496
- $this->blog_prefix = ( is_multisite() ? $blog_id : $table_prefix );
497
- }
498
- }
499
-
500
- /**
501
- * Is Redis available?
502
- *
503
- * @return bool
504
- */
505
- public function redis_status() {
506
- return $this->redis_connected;
507
- }
508
-
509
- /**
510
- * Returns the Redis instance.
511
- *
512
- * @return mixed
513
- */
514
- public function redis_instance() {
515
- return $this->redis;
516
- }
517
-
518
- /**
519
- * Adds a value to cache.
520
- *
521
- * If the specified key already exists, the value is not stored and the function
522
- * returns false.
523
- *
524
- * @param string $key The key under which to store the value.
525
- * @param mixed $value The value to store.
526
- * @param string $group The group value appended to the $key.
527
- * @param int $expiration The expiration time, defaults to 0.
528
- * @return bool Returns TRUE on success or FALSE on failure.
529
- */
530
- public function add( $key, $value, $group = 'default', $expiration = 0 ) {
531
- return $this->add_or_replace( true, $key, $value, $group, $expiration );
532
- }
533
-
534
- /**
535
- * Replace a value in the cache.
536
- *
537
- * If the specified key doesn't exist, the value is not stored and the function
538
- * returns false.
539
- *
540
- * @param string $key The key under which to store the value.
541
- * @param mixed $value The value to store.
542
- * @param string $group The group value appended to the $key.
543
- * @param int $expiration The expiration time, defaults to 0.
544
- * @return bool Returns TRUE on success or FALSE on failure.
545
- */
546
- public function replace( $key, $value, $group = 'default', $expiration = 0 ) {
547
- return $this->add_or_replace( false, $key, $value, $group, $expiration );
548
- }
549
-
550
- /**
551
- * Add or replace a value in the cache.
552
- *
553
- * Add does not set the value if the key exists; replace does not replace if the value doesn't exist.
554
- *
555
- * @param bool $add True if should only add if value doesn't exist, false to only add when value already exists
556
- * @param string $key The key under which to store the value.
557
- * @param mixed $value The value to store.
558
- * @param string $group The group value appended to the $key.
559
- * @param int $expiration The expiration time, defaults to 0.
560
- * @return bool Returns TRUE on success or FALSE on failure.
561
- */
562
- protected function add_or_replace( $add, $key, $value, $group = 'default', $expiration = 0 ) {
563
- $result = true;
564
- $derived_key = $this->build_key( $key, $group );
565
-
566
- // save if group not excluded and redis is up
567
- if ( ! in_array( $group, $this->ignored_groups ) && $this->redis_status() ) {
568
- $exists = $this->redis->exists( $derived_key );
569
-
570
- if ( $add == $exists ) {
571
- return false;
572
- }
573
-
574
- $expiration = $this->validate_expiration( $expiration );
575
-
576
- if ( $expiration ) {
577
- $result = $this->parse_redis_response( $this->redis->setex( $derived_key, $expiration, $this->maybe_serialize( $value ) ) );
578
- } else {
579
- $result = $this->parse_redis_response( $this->redis->set( $derived_key, $this->maybe_serialize( $value ) ) );
580
- }
581
- }
582
-
583
- $exists = isset( $this->cache[ $derived_key ] );
584
-
585
- if ( $add == $exists ) {
586
- return false;
587
- }
588
-
589
- if ( $result ) {
590
- $this->add_to_internal_cache( $derived_key, $value );
591
- }
592
-
593
- return $result;
594
- }
595
-
596
- /**
597
- * Remove the item from the cache.
598
- *
599
- * @param string $key The key under which to store the value.
600
- * @param string $group The group value appended to the $key.
601
- * @return bool Returns TRUE on success or FALSE on failure.
602
- */
603
- public function delete( $key, $group = 'default' ) {
604
- $result = false;
605
- $derived_key = $this->build_key( $key, $group );
606
-
607
- if ( isset( $this->cache[ $derived_key ] ) ) {
608
- unset( $this->cache[ $derived_key ] );
609
- $result = true;
610
- }
611
-
612
- if ( $this->redis_status() && ! in_array( $group, $this->ignored_groups ) ) {
613
- $result = $this->parse_redis_response( $this->redis->del( $derived_key ) );
614
- }
615
-
616
- if ( function_exists( 'do_action' ) ) {
617
- do_action( 'redis_object_cache_delete', $key, $group );
618
- }
619
-
620
- return $result;
621
- }
622
-
623
- /**
624
- * Invalidate all items in the cache.
625
- *
626
- * @param int $delay Number of seconds to wait before invalidating the items.
627
- * @return bool Returns TRUE on success or FALSE on failure.
628
- */
629
- public function flush( $delay = 0 ) {
630
- $delay = abs( intval( $delay ) );
631
-
632
- if ( $delay ) {
633
- sleep( $delay );
634
- }
635
-
636
- $result = false;
637
- $this->cache = array();
638
-
639
- if ( $this->redis_status() ) {
640
- $result = $this->parse_redis_response( $this->redis->flushdb() );
641
-
642
- if ( function_exists( 'do_action' ) ) {
643
- do_action( 'redis_object_cache_flush', $result, $delay );
644
- }
645
- }
646
-
647
- return $result;
648
- }
649
-
650
- /**
651
- * Retrieve object from cache.
652
- *
653
- * Gets an object from cache based on $key and $group.
654
- *
655
- * @param string $key The key under which to store the value.
656
- * @param string $group The group value appended to the $key.
657
- * @param string $force Optional. Whether to force a refetch rather than relying on the local
658
- * cache. Default false.
659
- * @param bool &$found Optional. Whether the key was found in the cache. Disambiguates a return of
660
- * false, a storable value. Passed by reference. Default null.
661
- * @return bool|mixed Cached object value.
662
- */
663
- public function get( $key, $group = 'default', $force = false, &$found = null ) {
664
- $derived_key = $this->build_key( $key, $group );
665
-
666
- if ( isset( $this->cache[ $derived_key ] ) && ! $force ) {
667
- $found = true;
668
- $this->cache_hits++;
669
-
670
- return is_object( $this->cache[ $derived_key ] ) ? clone $this->cache[ $derived_key ] : $this->cache[ $derived_key ];
671
- } elseif ( in_array( $group, $this->ignored_groups ) || ! $this->redis_status() ) {
672
- $found = false;
673
- $this->cache_misses++;
674
-
675
- return false;
676
- }
677
-
678
- $result = $this->redis->get( $derived_key );
679
-
680
- if ( $result === null || $result === false ) {
681
- $found = false;
682
- $this->cache_misses++;
683
-
684
- return false;
685
- } else {
686
- $found = true;
687
- $this->cache_hits++;
688
- $value = $this->maybe_unserialize( $result );
689
- }
690
-
691
- $this->add_to_internal_cache( $derived_key, $value );
692
-
693
- $value = is_object( $value ) ? clone $value : $value;
694
-
695
- if ( function_exists( 'do_action' ) ) {
696
- do_action( 'redis_object_cache_get', $key, $value, $group, $force, $found );
697
- }
698
-
699
- if ( function_exists( 'apply_filters' ) && function_exists( 'has_filter' ) ) {
700
- if ( has_filter( 'redis_object_cache_get' ) ) {
701
- return apply_filters( 'redis_object_cache_get', $value, $key, $group, $force, $found );
702
- }
703
- }
704
-
705
- return $value;
706
- }
707
-
708
- /**
709
- * Retrieve multiple values from cache.
710
- *
711
- * Gets multiple values from cache, including across multiple groups
712
- *
713
- * Usage: array( 'group0' => array( 'key0', 'key1', 'key2', ), 'group1' => array( 'key0' ) )
714
- *
715
- * Mirrors the Memcached Object Cache plugin's argument and return-value formats
716
- *
717
- * @param array $groups Array of groups and keys to retrieve
718
- * @return bool|mixed Array of cached values, keys in the format $group:$key. Non-existent keys null.
719
- */
720
- public function get_multi( $groups ) {
721
- if ( empty( $groups ) || ! is_array( $groups ) ) {
722
- return false;
723
- }
724
-
725
- // Retrieve requested caches and reformat results to mimic Memcached Object Cache's output
726
- $cache = array();
727
-
728
- foreach ( $groups as $group => $keys ) {
729
- if ( in_array( $group, $this->ignored_groups ) || ! $this->redis_status() ) {
730
- foreach ( $keys as $key ) {
731
- $cache[ $this->build_key( $key, $group ) ] = $this->get( $key, $group );
732
- }
733
- } else {
734
- // Reformat arguments as expected by Redis
735
- $derived_keys = array();
736
-
737
- foreach ( $keys as $key ) {
738
- $derived_keys[] = $this->build_key( $key, $group );
739
- }
740
-
741
- // Retrieve from cache in a single request
742
- $group_cache = $this->redis->mget( $derived_keys );
743
-
744
- // Build an array of values looked up, keyed by the derived cache key
745
- $group_cache = array_combine( $derived_keys, $group_cache );
746
-
747
- // Restores cached data to its original data type
748
- $group_cache = array_map( array( $this, 'maybe_unserialize' ), $group_cache );
749
-
750
- // Redis returns null for values not found in cache, but expected return value is false in this instance
751
- $group_cache = array_map( array( $this, 'filter_redis_get_multi' ), $group_cache );
752
-
753
- $cache = array_merge( $cache, $group_cache );
754
- }
755
- }
756
-
757
- // Add to the internal cache the found values from Redis
758
- foreach ( $cache as $key => $value ) {
759
- if ( $value ) {
760
- $this->cache_hits++;
761
- $this->add_to_internal_cache( $key, $value );
762
- } else {
763
- $this->cache_misses++;
764
- }
765
- }
766
-
767
- return $cache;
768
- }
769
-
770
- /**
771
- * Sets a value in cache.
772
- *
773
- * The value is set whether or not this key already exists in Redis.
774
- *
775
- * @param string $key The key under which to store the value.
776
- * @param mixed $value The value to store.
777
- * @param string $group The group value appended to the $key.
778
- * @param int $expiration The expiration time, defaults to 0.
779
- * @return bool Returns TRUE on success or FALSE on failure.
780
- */
781
- public function set( $key, $value, $group = 'default', $expiration = 0 ) {
782
- $result = true;
783
- $derived_key = $this->build_key( $key, $group );
784
-
785
- // save if group not excluded from redis and redis is up
786
- if ( ! in_array( $group, $this->ignored_groups ) && $this->redis_status() ) {
787
- $expiration = $this->validate_expiration( $expiration );
788
-
789
- if ( $expiration ) {
790
- $result = $this->parse_redis_response( $this->redis->setex( $derived_key, $expiration, $this->maybe_serialize( $value ) ) );
791
- } else {
792
- $result = $this->parse_redis_response( $this->redis->set( $derived_key, $this->maybe_serialize( $value ) ) );
793
- }
794
- }
795
-
796
- // if the set was successful, or we didn't go to redis
797
- if ( $result ) {
798
- $this->add_to_internal_cache( $derived_key, $value );
799
- }
800
-
801
- if ( function_exists( 'do_action' ) ) {
802
- do_action( 'redis_object_cache_set', $key, $value, $group, $expiration );
803
- }
804
-
805
- return $result;
806
- }
807
-
808
- /**
809
- * Increment a Redis counter by the amount specified
810
- *
811
- * @param string $key
812
- * @param int $offset
813
- * @param string $group
814
- * @return int|bool
815
- */
816
- public function increment( $key, $offset = 1, $group = 'default' ) {
817
- $derived_key = $this->build_key( $key, $group );
818
- $offset = (int) $offset;
819
-
820
- // If group is a non-Redis group, save to internal cache, not Redis
821
- if ( in_array( $group, $this->ignored_groups ) || ! $this->redis_status() ) {
822
- $value = $this->get_from_internal_cache( $derived_key, $group );
823
- $value += $offset;
824
- $this->add_to_internal_cache( $derived_key, $value );
825
-
826
- return $value;
827
- }
828
-
829
- // Save to Redis
830
- $result = $this->parse_redis_response( $this->redis->incrBy( $derived_key, $offset ) );
831
-
832
- $this->add_to_internal_cache( $derived_key, (int) $this->redis->get( $derived_key ) );
833
-
834
- return $result;
835
- }
836
-
837
- /**
838
- * Alias of `increment()`.
839
- *
840
- * @param string $key
841
- * @param int $offset
842
- * @param string $group
843
- * @return bool
844
- */
845
- public function incr( $key, $offset = 1, $group = 'default' ) {
846
- return $this->increment( $key, $offset, $group );
847
- }
848
-
849
- /**
850
- * Decrement a Redis counter by the amount specified
851
- *
852
- * @param string $key
853
- * @param int $offset
854
- * @param string $group
855
- * @return int|bool
856
- */
857
- public function decrement( $key, $offset = 1, $group = 'default' ) {
858
- $derived_key = $this->build_key( $key, $group );
859
- $offset = (int) $offset;
860
-
861
- // If group is a non-Redis group, save to internal cache, not Redis
862
- if ( in_array( $group, $this->ignored_groups ) || ! $this->redis_status() ) {
863
- $value = $this->get_from_internal_cache( $derived_key, $group );
864
- $value -= $offset;
865
- $this->add_to_internal_cache( $derived_key, $value );
866
-
867
- return $value;
868
- }
869
-
870
- // Save to Redis
871
- $result = $this->parse_redis_response( $this->redis->decrBy( $derived_key, $offset ) );
872
-
873
- $this->add_to_internal_cache( $derived_key, (int) $this->redis->get( $derived_key ) );
874
-
875
- return $result;
876
- }
877
-
878
- /**
879
- * Render data about current cache requests
880
- *
881
- * @return string
882
- */
883
- public function stats() { ?>
884
-
885
- <p>
886
- <strong>Redis Status:</strong> <?php echo $this->redis_status() ? 'Connected' : 'Not Connected'; ?><br />
887
- <strong>Redis Client:</strong> <?php echo $this->redis_client; ?><br />
888
- <strong>Cache Hits:</strong> <?php echo $this->cache_hits; ?><br />
889
- <strong>Cache Misses:</strong> <?php echo $this->cache_misses; ?>
890
- </p>
891
-
892
- <ul>
893
- <?php foreach ( $this->cache as $group => $cache ) : ?>
894
- <li><?php printf( '%s - %sk', strip_tags( $group ), number_format( strlen( serialize( $cache ) ) / 1024, 2 ) ); ?></li>
895
- <?php endforeach; ?>
896
- </ul><?php
897
-
898
- }
899
-
900
- /**
901
- * Builds a key for the cached object using the prefix, group and key.
902
- *
903
- * @param string $key The key under which to store the value.
904
- * @param string $group The group value appended to the $key.
905
- *
906
- * @return string
907
- */
908
- public function build_key( $key, $group = 'default' ) {
909
- if ( empty( $group ) ) {
910
- $group = 'default';
911
- }
912
-
913
- if ( in_array( $group, $this->global_groups ) ) {
914
- $prefix = $this->global_prefix;
915
- } else {
916
- $prefix = $this->blog_prefix;
917
- }
918
-
919
- return WP_CACHE_KEY_SALT . "{$prefix}:{$group}:{$key}";
920
- }
921
-
922
- /**
923
- * Convert data types when using Redis MGET
924
- *
925
- * When requesting multiple keys, those not found in cache are assigned the value null upon return.
926
- * Expected value in this case is false, so we convert
927
- *
928
- * @param string $value Value to possibly convert
929
- * @return string Converted value
930
- */
931
- protected function filter_redis_get_multi( $value ) {
932
- if ( is_null( $value ) ) {
933
- $value = false;
934
- }
935
-
936
- return $value;
937
- }
938
-
939
- /**
940
- * Convert Redis responses into something meaningful
941
- *
942
- * @param mixed $response
943
- * @return mixed
944
- */
945
- protected function parse_redis_response( $response ) {
946
- if ( is_bool( $response ) ) {
947
- return $response;
948
- }
949
-
950
- if ( is_numeric( $response ) ) {
951
- return $response;
952
- }
953
-
954
- if ( is_object( $response ) && method_exists( $response, 'getPayload' ) ) {
955
- return $response->getPayload() === 'OK';
956
- }
957
-
958
- return false;
959
- }
960
-
961
- /**
962
- * Simple wrapper for saving object to the internal cache.
963
- *
964
- * @param string $derived_key Key to save value under.
965
- * @param mixed $value Object value.
966
- */
967
- public function add_to_internal_cache( $derived_key, $value ) {
968
- $this->cache[ $derived_key ] = $value;
969
- }
970
-
971
- /**
972
- * Get a value specifically from the internal, run-time cache, not Redis.
973
- *
974
- * @param int|string $key Key value.
975
- * @param int|string $group Group that the value belongs to.
976
- *
977
- * @return bool|mixed Value on success; false on failure.
978
- */
979
- public function get_from_internal_cache( $key, $group ) {
980
- $derived_key = $this->build_key( $key, $group );
981
-
982
- if ( isset( $this->cache[ $derived_key ] ) ) {
983
- return $this->cache[ $derived_key ];
984
- }
985
-
986
- return false;
987
- }
988
-
989
- /**
990
- * In multisite, switch blog prefix when switching blogs
991
- *
992
- * @param int $_blog_id
993
- * @return bool
994
- */
995
- public function switch_to_blog( $_blog_id ) {
996
- if ( ! function_exists( 'is_multisite' ) || ! is_multisite() ) {
997
- return false;
998
- }
999
-
1000
- $this->blog_prefix = $_blog_id;
1001
-
1002
- return true;
1003
- }
1004
-
1005
- /**
1006
- * Sets the list of global groups.
1007
- *
1008
- * @param array $groups List of groups that are global.
1009
- */
1010
- public function add_global_groups( $groups ) {
1011
- $groups = (array) $groups;
1012
-
1013
- if ( $this->redis_status() ) {
1014
- $this->global_groups = array_unique( array_merge( $this->global_groups, $groups ) );
1015
- } else {
1016
- $this->ignored_groups = array_unique( array_merge( $this->ignored_groups, $groups ) );
1017
- }
1018
- }
1019
-
1020
- /**
1021
- * Sets the list of groups not to be cached by Redis.
1022
- *
1023
- * @param array $groups List of groups that are to be ignored.
1024
- */
1025
- public function add_non_persistent_groups( $groups ) {
1026
- $groups = (array) $groups;
1027
-
1028
- $this->ignored_groups = array_unique( array_merge( $this->ignored_groups, $groups ) );
1029
- }
1030
-
1031
- /**
1032
- * Wrapper to validate the cache keys expiration value
1033
- *
1034
- * @param mixed $expiration Incomming expiration value (whatever it is)
1035
- */
1036
- protected function validate_expiration( $expiration ) {
1037
- $expiration = ( is_array( $expiration ) || is_object( $expiration ) ? 0 : abs( intval( $expiration ) ) );
1038
-
1039
- if ( $expiration === 0 && defined( 'WP_REDIS_MAXTTL' ) ) {
1040
- $expiration = intval( WP_REDIS_MAXTTL );
1041
- }
1042
-
1043
- return $expiration;
1044
- }
1045
-
1046
- /**
1047
- * Unserialize value only if it was serialized.
1048
- *
1049
- * @param string $original Maybe unserialized original, if is needed.
1050
- * @return mixed Unserialized data can be any type.
1051
- */
1052
- protected function maybe_unserialize( $original ) {
1053
- // don't attempt to unserialize data that wasn't serialized going in
1054
- if ( $this->is_serialized( $original ) ) {
1055
- return @unserialize( $original );
1056
- }
1057
-
1058
- return $original;
1059
- }
1060
-
1061
- /**
1062
- * Serialize data, if needed.
1063
- * @param string|array|object $data Data that might be serialized.
1064
- * @return mixed A scalar data
1065
- */
1066
- protected function maybe_serialize( $data ) {
1067
- if ( is_array( $data ) || is_object( $data ) ) {
1068
- return serialize( $data );
1069
- }
1070
-
1071
- if ( $this->is_serialized( $data, false ) ) {
1072
- return serialize( $data );
1073
- }
1074
-
1075
- return $data;
1076
- }
1077
-
1078
- /**
1079
- * Check value to find if it was serialized.
1080
- *
1081
- * If $data is not an string, then returned value will always be false.
1082
- * Serialized data is always a string.
1083
- *
1084
- * @param string $data Value to check to see if was serialized.
1085
- * @param bool $strict Optional. Whether to be strict about the end of the string. Default true.
1086
- * @return bool False if not serialized and true if it was.
1087
- */
1088
- protected function is_serialized( $data, $strict = true ) {
1089
- // if it isn't a string, it isn't serialized.
1090
- if ( ! is_string( $data ) ) {
1091
- return false;
1092
- }
1093
-
1094
- $data = trim( $data );
1095
-
1096
- if ( 'N;' == $data ) {
1097
- return true;
1098
- }
1099
-
1100
- if ( strlen( $data ) < 4 ) {
1101
- return false;
1102
- }
1103
-
1104
- if ( ':' !== $data[1] ) {
1105
- return false;
1106
- }
1107
-
1108
- if ( $strict ) {
1109
- $lastc = substr( $data, -1 );
1110
-
1111
- if ( ';' !== $lastc && '}' !== $lastc ) {
1112
- return false;
1113
- }
1114
- } else {
1115
- $semicolon = strpos( $data, ';' );
1116
- $brace = strpos( $data, '}' );
1117
-
1118
- // Either ; or } must exist.
1119
- if ( false === $semicolon && false === $brace ) {
1120
- return false;
1121
- }
1122
-
1123
- // But neither must be in the first X characters.
1124
- if ( false !== $semicolon && $semicolon < 3 ) {
1125
- return false;
1126
- }
1127
-
1128
- if ( false !== $brace && $brace < 4 ) {
1129
- return false;
1130
- }
1131
- }
1132
- $token = $data[0];
1133
-
1134
- switch ( $token ) {
1135
- case 's':
1136
- if ( $strict ) {
1137
- if ( '"' !== substr( $data, -2, 1 ) ) {
1138
- return false;
1139
- }
1140
- } elseif ( false === strpos( $data, '"' ) ) {
1141
- return false;
1142
- }
1143
- // or else fall through
1144
- case 'a':
1145
- case 'O':
1146
- return (bool) preg_match( "/^{$token}:[0-9]+:/s", $data );
1147
- case 'b':
1148
- case 'i':
1149
- case 'd':
1150
- $end = $strict ? '$' : '';
1151
-
1152
- return (bool) preg_match( "/^{$token}:[0-9.E-]+;$end/", $data );
1153
- }
1154
-
1155
- return false;
1156
- }
1157
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1158
  }
1159
 
1160
  endif;
3
  Plugin Name: Redis Object Cache Drop-In
4
  Plugin URI: http://wordpress.org/plugins/redis-cache/
5
  Description: A persistent object cache backend powered by Redis. Supports Predis, PhpRedis, HHVM, replication, clustering and WP-CLI.
6
+ Version: 1.3.6
7
  Author: Till Krüss
8
  Author URI: https://till.im/
9
  License: GPLv3
13
  https://github.com/ericmann/Redis-Object-Cache
14
  */
15
 
16
+ if (! defined('WP_REDIS_DISABLED') || ! WP_REDIS_DISABLED) :
17
 
18
  /**
19
  * Adds a value to cache.
30
  *
31
  * @return bool Returns TRUE on success or FALSE on failure.
32
  */
33
+ function wp_cache_add($key, $value, $group = '', $expiration = 0)
34
+ {
35
+ global $wp_object_cache;
36
 
37
+ return $wp_object_cache->add($key, $value, $group, $expiration);
38
  }
39
 
40
  /**
47
  *
48
  * @return bool Always returns True
49
  */
50
+ function wp_cache_close()
51
+ {
52
+ return true;
53
  }
54
 
55
  /**
63
  *
64
  * @return int|bool Returns item's new value on success or FALSE on failure.
65
  */
66
+ function wp_cache_decr($key, $offset = 1, $group = '')
67
+ {
68
+ global $wp_object_cache;
69
 
70
+ return $wp_object_cache->decrement($key, $offset, $group);
71
  }
72
 
73
  /**
81
  *
82
  * @return bool Returns TRUE on success or FALSE on failure.
83
  */
84
+ function wp_cache_delete($key, $group = '', $time = 0)
85
+ {
86
+ global $wp_object_cache;
87
 
88
+ return $wp_object_cache->delete($key, $group, $time);
89
  }
90
 
91
  /**
92
+ * Invalidate all items in the cache. If `WP_REDIS_SELECTIVE_FLUSH` is `true`,
93
+ * only keys prefixed with the `WP_CACHE_KEY_SALT` are flushed.
94
  *
95
  * @param int $delay Number of seconds to wait before invalidating the items.
96
  *
98
  *
99
  * @return bool Returns TRUE on success or FALSE on failure.
100
  */
101
+ function wp_cache_flush($delay = 0)
102
+ {
103
+ global $wp_object_cache;
104
 
105
+ return $wp_object_cache->flush($delay);
106
  }
107
 
108
  /**
121
  *
122
  * @return bool|mixed Cached object value.
123
  */
124
+ function wp_cache_get($key, $group = '', $force = false, &$found = null)
125
+ {
126
+ global $wp_object_cache;
127
 
128
+ return $wp_object_cache->get($key, $group, $force, $found);
129
  }
130
 
131
  /**
143
  *
144
  * @return bool|mixed Array of cached values, keys in the format $group:$key. Non-existent keys false
145
  */
146
+ function wp_cache_get_multi($groups)
147
+ {
148
+ global $wp_object_cache;
149
 
150
+ return $wp_object_cache->get_multi($groups);
151
  }
152
 
153
  /**
161
  *
162
  * @return int|bool Returns item's new value on success or FALSE on failure.
163
  */
164
+ function wp_cache_incr($key, $offset = 1, $group = '')
165
+ {
166
+ global $wp_object_cache;
167
 
168
+ return $wp_object_cache->increment($key, $offset, $group);
169
  }
170
 
171
  /**
175
  *
176
  * @return void
177
  */
178
+ function wp_cache_init()
179
+ {
180
+ global $wp_object_cache;
181
 
182
+ if (! ($wp_object_cache instanceof WP_Object_Cache)) {
183
+ $wp_object_cache = new WP_Object_Cache;
184
+ }
185
  }
186
 
187
  /**
199
  *
200
  * @return bool Returns TRUE on success or FALSE on failure.
201
  */
202
+ function wp_cache_replace($key, $value, $group = '', $expiration = 0)
203
+ {
204
+ global $wp_object_cache;
205
 
206
+ return $wp_object_cache->replace($key, $value, $group, $expiration);
207
  }
208
 
209
  /**
220
  *
221
  * @return bool Returns TRUE on success or FALSE on failure.
222
  */
223
+ function wp_cache_set($key, $value, $group = '', $expiration = 0)
224
+ {
225
+ global $wp_object_cache;
226
 
227
+ return $wp_object_cache->set($key, $value, $group, $expiration);
228
  }
229
 
230
  /**
238
  *
239
  * @return bool
240
  */
241
+ function wp_cache_switch_to_blog($_blog_id)
242
+ {
243
+ global $wp_object_cache;
244
 
245
+ return $wp_object_cache->switch_to_blog($_blog_id);
246
  }
247
 
248
  /**
254
  *
255
  * @return void
256
  */
257
+ function wp_cache_add_global_groups($groups)
258
+ {
259
+ global $wp_object_cache;
260
 
261
+ $wp_object_cache->add_global_groups($groups);
262
  }
263
 
264
  /**
270
  *
271
  * @return void
272
  */
273
+ function wp_cache_add_non_persistent_groups($groups)
274
+ {
275
+ global $wp_object_cache;
276
 
277
+ $wp_object_cache->add_non_persistent_groups($groups);
278
  }
279
 
280
+ class WP_Object_Cache
281
+ {
282
+
283
+ /**
284
+ * The Redis client.
285
+ *
286
+ * @var mixed
287
+ */
288
+ private $redis;
289
+
290
+ /**
291
+ * Track if Redis is available
292
+ *
293
+ * @var bool
294
+ */
295
+ private $redis_connected = false;
296
+
297
+ /**
298
+ * Holds the non-Redis objects.
299
+ *
300
+ * @var array
301
+ */
302
+ public $cache = array();
303
+
304
+ /**
305
+ * Name of the used Redis client
306
+ *
307
+ * @var bool
308
+ */
309
+ public $redis_client = null;
310
+
311
+ /**
312
+ * List of global groups.
313
+ *
314
+ * @var array
315
+ */
316
+ public $global_groups = array(
317
+ 'blog-details',
318
+ 'blog-id-cache',
319
+ 'blog-lookup',
320
+ 'global-posts',
321
+ 'networks',
322
+ 'rss',
323
+ 'sites',
324
+ 'site-details',
325
+ 'site-lookup',
326
+ 'site-options',
327
+ 'site-transient',
328
+ 'users',
329
+ 'useremail',
330
+ 'userlogins',
331
+ 'usermeta',
332
+ 'user_meta',
333
+ 'userslugs',
334
+ );
335
+
336
+ /**
337
+ * List of groups not saved to Redis.
338
+ *
339
+ * @var array
340
+ */
341
+ public $ignored_groups = array( 'counts', 'plugins' );
342
+
343
+ /**
344
+ * Prefix used for global groups.
345
+ *
346
+ * @var string
347
+ */
348
+ public $global_prefix = '';
349
+
350
+ /**
351
+ * Prefix used for non-global groups.
352
+ *
353
+ * @var string
354
+ */
355
+ public $blog_prefix = '';
356
+
357
+ /**
358
+ * Track how many requests were found in cache
359
+ *
360
+ * @var int
361
+ */
362
+ public $cache_hits = 0;
363
+
364
+ /**
365
+ * Track how may requests were not cached
366
+ *
367
+ * @var int
368
+ */
369
+ public $cache_misses = 0;
370
+
371
+ /**
372
+ * Instantiate the Redis class.
373
+ *
374
+ * @param bool $fail_gracefully
375
+ */
376
+ public function __construct($fail_gracefully = true)
377
+ {
378
+ global $blog_id, $table_prefix;
379
+
380
+ $parameters = array(
381
+ 'scheme' => 'tcp',
382
+ 'host' => '127.0.0.1',
383
+ 'port' => 6379
384
+ );
385
+
386
+ foreach (array( 'scheme', 'host', 'port', 'path', 'password', 'database' ) as $setting) {
387
+ $constant = sprintf('WP_REDIS_%s', strtoupper($setting));
388
+ if (defined($constant)) {
389
+ $parameters[ $setting ] = constant($constant);
390
+ }
391
+ }
392
+
393
+ if (defined('WP_REDIS_GLOBAL_GROUPS') && is_array(WP_REDIS_GLOBAL_GROUPS)) {
394
+ $this->global_groups = WP_REDIS_GLOBAL_GROUPS;
395
+ }
396
+
397
+ if (defined('WP_REDIS_IGNORED_GROUPS') && is_array(WP_REDIS_IGNORED_GROUPS)) {
398
+ $this->ignored_groups = WP_REDIS_IGNORED_GROUPS;
399
+ }
400
+
401
+ $client = defined('WP_REDIS_CLIENT') ? WP_REDIS_CLIENT : null;
402
+
403
+ if (class_exists('Redis') && strcasecmp('predis', $client) !== 0) {
404
+ $client = defined('HHVM_VERSION') ? 'hhvm' : 'pecl';
405
+ } else {
406
+ $client = 'predis';
407
+ }
408
+
409
+ try {
410
+ if (strcasecmp('hhvm', $client) === 0) {
411
+ $this->redis_client = sprintf('HHVM Extension (v%s)', HHVM_VERSION);
412
+ $this->redis = new Redis();
413
+
414
+ // Adjust host and port, if the scheme is `unix`
415
+ if (strcasecmp('unix', $parameters[ 'scheme' ]) === 0) {
416
+ $parameters[ 'host' ] = 'unix://' . $parameters[ 'path' ];
417
+ $parameters[ 'port' ] = 0;
418
+ }
419
+
420
+ $this->redis->connect($parameters[ 'host' ], $parameters[ 'port' ]);
421
+ }
422
+
423
+ if (strcasecmp('pecl', $client) === 0) {
424
+ $this->redis_client = sprintf('PhpRedis (v%s)', phpversion('redis'));
425
+
426
+ if (defined('WP_REDIS_SHARDS')) {
427
+ $this->redis = new RedisArray(array_values(WP_REDIS_CLUSTER));
428
+ } elseif (defined('WP_REDIS_CLUSTER')) {
429
+ $this->redis = new RedisCluster(null, array_values(WP_REDIS_CLUSTER));
430
+ } else {
431
+ $this->redis = new Redis();
432
+
433
+ if (strcasecmp('unix', $parameters[ 'scheme' ]) === 0) {
434
+ $this->redis->connect($parameters[ 'path' ]);
435
+ } else {
436
+ $this->redis->connect($parameters[ 'host' ], $parameters[ 'port' ]);
437
+ }
438
+ }
439
+ }
440
+
441
+ if (strcasecmp('pecl', $client) === 0 || strcasecmp('hhvm', $client) === 0) {
442
+ if (isset($parameters[ 'password' ])) {
443
+ $this->redis->auth($parameters[ 'password' ]);
444
+ }
445
+
446
+ if (isset($parameters[ 'database' ])) {
447
+ $this->redis->select($parameters[ 'database' ]);
448
+ }
449
+ }
450
+
451
+ if (strcasecmp('predis', $client) === 0) {
452
+ $this->redis_client = 'Predis';
453
+
454
+ // Require PHP 5.4 or greater
455
+ if (version_compare(PHP_VERSION, '5.4.0', '<')) {
456
+ throw new Exception;
457
+ }
458
+
459
+ // Load bundled Predis library
460
+ if (! class_exists('Predis\Client')) {
461
+ $plugin_dir = defined('WP_PLUGIN_DIR') ? WP_PLUGIN_DIR : WP_CONTENT_DIR . '/plugins';
462
+ require_once $plugin_dir . '/redis-cache/includes/predis.php';
463
+ Predis\Autoloader::register();
464
+ }
465
+
466
+ $options = array();
467
+
468
+ if (defined('WP_REDIS_SHARDS')) {
469
+ $parameters = WP_REDIS_SHARDS;
470
+ } elseif (defined('WP_REDIS_SENTINEL')) {
471
+ $parameters = WP_REDIS_SERVERS;
472
+ $options[ 'replication' ] = 'sentinel';
473
+ $options[ 'service' ] = WP_REDIS_SENTINEL;
474
+ } elseif (defined('WP_REDIS_SERVERS')) {
475
+ $parameters = WP_REDIS_SERVERS;
476
+ $options[ 'replication' ] = true;
477
+ } elseif (defined('WP_REDIS_CLUSTER')) {
478
+ $parameters = WP_REDIS_CLUSTER;
479
+ $options[ 'cluster' ] = 'redis';
480
+ }
481
+
482
+ foreach (array( 'WP_REDIS_SERVERS', 'WP_REDIS_SHARDS', 'WP_REDIS_CLUSTER' ) as $constant) {
483
+ if (defined('WP_REDIS_PASSWORD') && defined($constant)) {
484
+ $options[ 'parameters' ][ 'password' ] = WP_REDIS_PASSWORD;
485
+ }
486
+ }
487
+
488
+ $this->redis = new Predis\Client($parameters, $options);
489
+ $this->redis->connect();
490
+
491
+ $this->redis_client .= sprintf(' (v%s)', Predis\Client::VERSION);
492
+ }
493
+
494
+ // Throws exception if Redis is unavailable
495
+ $this->redis->ping();
496
+
497
+ $this->redis_connected = true;
498
+ } catch (Exception $exception) {
499
+
500
+ // When Redis is unavailable, fall back to the internal back by forcing all groups to be "no redis" groups
501
+ $this->ignored_groups = array_unique(array_merge($this->ignored_groups, $this->global_groups));
502
+
503
+ $this->redis_connected = false;
504
+
505
+ if (! $fail_gracefully) {
506
+ throw $exception;
507
+ }
508
+ }
509
+
510
+ // Assign global and blog prefixes for use with keys
511
+ if (function_exists('is_multisite')) {
512
+ $this->global_prefix = (is_multisite() || defined('CUSTOM_USER_TABLE') && defined('CUSTOM_USER_META_TABLE')) ? '' : $table_prefix;
513
+ $this->blog_prefix = (is_multisite() ? $blog_id : $table_prefix);
514
+ }
515
+ }
516
+
517
+ /**
518
+ * Is Redis available?
519
+ *
520
+ * @return bool
521
+ */
522
+ public function redis_status()
523
+ {
524
+ return $this->redis_connected;
525
+ }
526
+
527
+ /**
528
+ * Returns the Redis instance.
529
+ *
530
+ * @return mixed
531
+ */
532
+ public function redis_instance()
533
+ {
534
+ return $this->redis;
535
+ }
536
+
537
+ /**
538
+ * Adds a value to cache.
539
+ *
540
+ * If the specified key already exists, the value is not stored and the function
541
+ * returns false.
542
+ *
543
+ * @param string $key The key under which to store the value.
544
+ * @param mixed $value The value to store.
545
+ * @param string $group The group value appended to the $key.
546
+ * @param int $expiration The expiration time, defaults to 0.
547
+ * @return bool Returns TRUE on success or FALSE on failure.
548
+ */
549
+ public function add($key, $value, $group = 'default', $expiration = 0)
550
+ {
551
+ return $this->add_or_replace(true, $key, $value, $group, $expiration);
552
+ }
553
+
554
+ /**
555
+ * Replace a value in the cache.
556
+ *
557
+ * If the specified key doesn't exist, the value is not stored and the function
558
+ * returns false.
559
+ *
560
+ * @param string $key The key under which to store the value.
561
+ * @param mixed $value The value to store.
562
+ * @param string $group The group value appended to the $key.
563
+ * @param int $expiration The expiration time, defaults to 0.
564
+ * @return bool Returns TRUE on success or FALSE on failure.
565
+ */
566
+ public function replace($key, $value, $group = 'default', $expiration = 0)
567
+ {
568
+ return $this->add_or_replace(false, $key, $value, $group, $expiration);
569
+ }
570
+
571
+ /**
572
+ * Add or replace a value in the cache.
573
+ *
574
+ * Add does not set the value if the key exists; replace does not replace if the value doesn't exist.
575
+ *
576
+ * @param bool $add True if should only add if value doesn't exist, false to only add when value already exists
577
+ * @param string $key The key under which to store the value.
578
+ * @param mixed $value The value to store.
579
+ * @param string $group The group value appended to the $key.
580
+ * @param int $expiration The expiration time, defaults to 0.
581
+ * @return bool Returns TRUE on success or FALSE on failure.
582
+ */
583
+ protected function add_or_replace($add, $key, $value, $group = 'default', $expiration = 0)
584
+ {
585
+ $result = true;
586
+ $derived_key = $this->build_key($key, $group);
587
+
588
+ // save if group not excluded and redis is up
589
+ if (! in_array($group, $this->ignored_groups) && $this->redis_status()) {
590
+ $exists = $this->redis->exists($derived_key);
591
+
592
+ if ($add == $exists) {
593
+ return false;
594
+ }
595
+
596
+ $expiration = $this->validate_expiration($expiration);
597
+
598
+ if ($expiration) {
599
+ $result = $this->parse_redis_response($this->redis->setex($derived_key, $expiration, $this->maybe_serialize($value)));
600
+ } else {
601
+ $result = $this->parse_redis_response($this->redis->set($derived_key, $this->maybe_serialize($value)));
602
+ }
603
+ }
604
+
605
+ $exists = isset($this->cache[ $derived_key ]);
606
+
607
+ if ($add == $exists) {
608
+ return false;
609
+ }
610
+
611
+ if ($result) {
612
+ $this->add_to_internal_cache($derived_key, $value);
613
+ }
614
+
615
+ return $result;
616
+ }
617
+
618
+ /**
619
+ * Remove the item from the cache.
620
+ *
621
+ * @param string $key The key under which to store the value.
622
+ * @param string $group The group value appended to the $key.
623
+ * @return bool Returns TRUE on success or FALSE on failure.
624
+ */
625
+ public function delete($key, $group = 'default')
626
+ {
627
+ $result = false;
628
+ $derived_key = $this->build_key($key, $group);
629
+
630
+ if (isset($this->cache[ $derived_key ])) {
631
+ unset($this->cache[ $derived_key ]);
632
+ $result = true;
633
+ }
634
+
635
+ if ($this->redis_status() && ! in_array($group, $this->ignored_groups)) {
636
+ $result = $this->parse_redis_response($this->redis->del($derived_key));
637
+ }
638
+
639
+ if (function_exists('do_action')) {
640
+ do_action('redis_object_cache_delete', $key, $group);
641
+ }
642
+
643
+ return $result;
644
+ }
645
+
646
+ /**
647
+ * Invalidate all items in the cache. If `WP_REDIS_SELECTIVE_FLUSH` is `true`,
648
+ * only keys prefixed with the `WP_CACHE_KEY_SALT` are flushed.
649
+ *
650
+ * @param int $delay Number of seconds to wait before invalidating the items.
651
+ * @return bool Returns TRUE on success or FALSE on failure.
652
+ */
653
+ public function flush($delay = 0)
654
+ {
655
+ $delay = abs(intval($delay));
656
+
657
+ if ($delay) {
658
+ sleep($delay);
659
+ }
660
+
661
+ $result = false;
662
+ $this->cache = array();
663
+
664
+ if ($this->redis_status()) {
665
+ $salt = defined('WP_CACHE_KEY_SALT') ? trim(WP_CACHE_KEY_SALT) : null;
666
+ $selective = defined('WP_REDIS_SELECTIVE_FLUSH') ? WP_REDIS_SELECTIVE_FLUSH : null;
667
+
668
+ if ($salt && $selective) {
669
+ $script = "
670
+ local i = 0
671
+ for _,k in ipairs(redis.call('keys', '{$salt}*')) do
672
+ redis.call('del', k)
673
+ i = i + 1
674
+ end
675
+ return i
676
+ ";
677
+
678
+ $result = $this->parse_redis_response($this->redis->eval(
679
+ $script,
680
+ $this->redis instanceof Predis\Client ? 0 : []
681
+ ));
682
+ } else {
683
+ $result = $this->parse_redis_response($this->redis->flushdb());
684
+ }
685
+
686
+ if (function_exists('do_action')) {
687
+ do_action('redis_object_cache_flush', $result, $delay, $selective, $salt);
688
+ }
689
+ }
690
+
691
+ return $result;
692
+ }
693
+
694
+ /**
695
+ * Retrieve object from cache.
696
+ *
697
+ * Gets an object from cache based on $key and $group.
698
+ *
699
+ * @param string $key The key under which to store the value.
700
+ * @param string $group The group value appended to the $key.
701
+ * @param string $force Optional. Whether to force a refetch rather than relying on the local
702
+ * cache. Default false.
703
+ * @param bool &$found Optional. Whether the key was found in the cache. Disambiguates a return of
704
+ * false, a storable value. Passed by reference. Default null.
705
+ * @return bool|mixed Cached object value.
706
+ */
707
+ public function get($key, $group = 'default', $force = false, &$found = null)
708
+ {
709
+ $derived_key = $this->build_key($key, $group);
710
+
711
+ if (isset($this->cache[ $derived_key ]) && ! $force) {
712
+ $found = true;
713
+ $this->cache_hits++;
714
+
715
+ return is_object($this->cache[ $derived_key ]) ? clone $this->cache[ $derived_key ] : $this->cache[ $derived_key ];
716
+ } elseif (in_array($group, $this->ignored_groups) || ! $this->redis_status()) {
717
+ $found = false;
718
+ $this->cache_misses++;
719
+
720
+ return false;
721
+ }
722
+
723
+ $result = $this->redis->get($derived_key);
724
+
725
+ if ($result === null || $result === false) {
726
+ $found = false;
727
+ $this->cache_misses++;
728
+
729
+ return false;
730
+ } else {
731
+ $found = true;
732
+ $this->cache_hits++;
733
+ $value = $this->maybe_unserialize($result);
734
+ }
735
+
736
+ $this->add_to_internal_cache($derived_key, $value);
737
+
738
+ $value = is_object($value) ? clone $value : $value;
739
+
740
+ if (function_exists('do_action')) {
741
+ do_action('redis_object_cache_get', $key, $value, $group, $force, $found);
742
+ }
743
+
744
+ if (function_exists('apply_filters') && function_exists('has_filter')) {
745
+ if (has_filter('redis_object_cache_get')) {
746
+ return apply_filters('redis_object_cache_get', $value, $key, $group, $force, $found);
747
+ }
748
+ }
749
+
750
+ return $value;
751
+ }
752
+
753
+ /**
754
+ * Retrieve multiple values from cache.
755
+ *
756
+ * Gets multiple values from cache, including across multiple groups
757
+ *
758
+ * Usage: array( 'group0' => array( 'key0', 'key1', 'key2', ), 'group1' => array( 'key0' ) )
759
+ *
760
+ * Mirrors the Memcached Object Cache plugin's argument and return-value formats
761
+ *
762
+ * @param array $groups Array of groups and keys to retrieve
763
+ * @return bool|mixed Array of cached values, keys in the format $group:$key. Non-existent keys null.
764
+ */
765
+ public function get_multi($groups)
766
+ {
767
+ if (empty($groups) || ! is_array($groups)) {
768
+ return false;
769
+ }
770
+
771
+ // Retrieve requested caches and reformat results to mimic Memcached Object Cache's output
772
+ $cache = array();
773
+
774
+ foreach ($groups as $group => $keys) {
775
+ if (in_array($group, $this->ignored_groups) || ! $this->redis_status()) {
776
+ foreach ($keys as $key) {
777
+ $cache[ $this->build_key($key, $group) ] = $this->get($key, $group);
778
+ }
779
+ } else {
780
+ // Reformat arguments as expected by Redis
781
+ $derived_keys = array();
782
+
783
+ foreach ($keys as $key) {
784
+ $derived_keys[] = $this->build_key($key, $group);
785
+ }
786
+
787
+ // Retrieve from cache in a single request
788
+ $group_cache = $this->redis->mget($derived_keys);
789
+
790
+ // Build an array of values looked up, keyed by the derived cache key
791
+ $group_cache = array_combine($derived_keys, $group_cache);
792
+
793
+ // Restores cached data to its original data type
794
+ $group_cache = array_map(array( $this, 'maybe_unserialize' ), $group_cache);
795
+
796
+ // Redis returns null for values not found in cache, but expected return value is false in this instance
797
+ $group_cache = array_map(array( $this, 'filter_redis_get_multi' ), $group_cache);
798
+
799
+ $cache = array_merge($cache, $group_cache);
800
+ }
801
+ }
802
+
803
+ // Add to the internal cache the found values from Redis
804
+ foreach ($cache as $key => $value) {
805
+ if ($value) {
806
+ $this->cache_hits++;
807
+ $this->add_to_internal_cache($key, $value);
808
+ } else {
809
+ $this->cache_misses++;
810
+ }
811
+ }
812
+
813
+ return $cache;
814
+ }
815
+
816
+ /**
817
+ * Sets a value in cache.
818
+ *
819
+ * The value is set whether or not this key already exists in Redis.
820
+ *
821
+ * @param string $key The key under which to store the value.
822
+ * @param mixed $value The value to store.
823
+ * @param string $group The group value appended to the $key.
824
+ * @param int $expiration The expiration time, defaults to 0.
825
+ * @return bool Returns TRUE on success or FALSE on failure.
826
+ */
827
+ public function set($key, $value, $group = 'default', $expiration = 0)
828
+ {
829
+ $result = true;
830
+ $derived_key = $this->build_key($key, $group);
831
+
832
+ // save if group not excluded from redis and redis is up
833
+ if (! in_array($group, $this->ignored_groups) && $this->redis_status()) {
834
+ $expiration = $this->validate_expiration($expiration);
835
+
836
+ if ($expiration) {
837
+ $result = $this->parse_redis_response($this->redis->setex($derived_key, $expiration, $this->maybe_serialize($value)));
838
+ } else {
839
+ $result = $this->parse_redis_response($this->redis->set($derived_key, $this->maybe_serialize($value)));
840
+ }
841
+ }
842
+
843
+ // if the set was successful, or we didn't go to redis
844
+ if ($result) {
845
+ $this->add_to_internal_cache($derived_key, $value);
846
+ }
847
+
848
+ if (function_exists('do_action')) {
849
+ do_action('redis_object_cache_set', $key, $value, $group, $expiration);
850
+ }
851
+
852
+ return $result;
853
+ }
854
+
855
+ /**
856
+ * Increment a Redis counter by the amount specified
857
+ *
858
+ * @param string $key
859
+ * @param int $offset
860
+ * @param string $group
861
+ * @return int|bool
862
+ */
863
+ public function increment($key, $offset = 1, $group = 'default')
864
+ {
865
+ $derived_key = $this->build_key($key, $group);
866
+ $offset = (int) $offset;
867
+
868
+ // If group is a non-Redis group, save to internal cache, not Redis
869
+ if (in_array($group, $this->ignored_groups) || ! $this->redis_status()) {
870
+ $value = $this->get_from_internal_cache($derived_key, $group);
871
+ $value += $offset;
872
+ $this->add_to_internal_cache($derived_key, $value);
873
+
874
+ return $value;
875
+ }
876
+
877
+ // Save to Redis
878
+ $result = $this->parse_redis_response($this->redis->incrBy($derived_key, $offset));
879
+
880
+ $this->add_to_internal_cache($derived_key, (int) $this->redis->get($derived_key));
881
+
882
+ return $result;
883
+ }
884
+
885
+ /**
886
+ * Alias of `increment()`.
887
+ *
888
+ * @param string $key
889
+ * @param int $offset
890
+ * @param string $group
891
+ * @return bool
892
+ */
893
+ public function incr($key, $offset = 1, $group = 'default')
894
+ {
895
+ return $this->increment($key, $offset, $group);
896
+ }
897
+
898
+ /**
899
+ * Decrement a Redis counter by the amount specified
900
+ *
901
+ * @param string $key
902
+ * @param int $offset
903
+ * @param string $group
904
+ * @return int|bool
905
+ */
906
+ public function decrement($key, $offset = 1, $group = 'default')
907
+ {
908
+ $derived_key = $this->build_key($key, $group);
909
+ $offset = (int) $offset;
910
+
911
+ // If group is a non-Redis group, save to internal cache, not Redis
912
+ if (in_array($group, $this->ignored_groups) || ! $this->redis_status()) {
913
+ $value = $this->get_from_internal_cache($derived_key, $group);
914
+ $value -= $offset;
915
+ $this->add_to_internal_cache($derived_key, $value);
916
+
917
+ return $value;
918
+ }
919
+
920
+ // Save to Redis
921
+ $result = $this->parse_redis_response($this->redis->decrBy($derived_key, $offset));
922
+
923
+ $this->add_to_internal_cache($derived_key, (int) $this->redis->get($derived_key));
924
+
925
+ return $result;
926
+ }
927
+
928
+ /**
929
+ * Render data about current cache requests
930
+ *
931
+ * @return string
932
+ */
933
+ public function stats()
934
+ {
935
+ ?>
936
+
937
+ <p>
938
+ <strong>Redis Status:</strong> <?php echo $this->redis_status() ? 'Connected' : 'Not Connected'; ?><br />
939
+ <strong>Redis Client:</strong> <?php echo $this->redis_client; ?><br />
940
+ <strong>Cache Hits:</strong> <?php echo $this->cache_hits; ?><br />
941
+ <strong>Cache Misses:</strong> <?php echo $this->cache_misses; ?>
942
+ </p>
943
+
944
+ <ul>
945
+ <?php foreach ($this->cache as $group => $cache) : ?>
946
+ <li><?php printf('%s - %sk', strip_tags($group), number_format(strlen(serialize($cache)) / 1024, 2)); ?></li>
947
+ <?php endforeach; ?>
948
+ </ul><?php
949
+ }
950
+
951
+ /**
952
+ * Builds a key for the cached object using the prefix, group and key.
953
+ *
954
+ * @param string $key The key under which to store the value.
955
+ * @param string $group The group value appended to the $key.
956
+ *
957
+ * @return string
958
+ */
959
+ public function build_key($key, $group = 'default')
960
+ {
961
+ if (empty($group)) {
962
+ $group = 'default';
963
+ }
964
+
965
+ $salt = defined('WP_CACHE_KEY_SALT') ? trim(WP_CACHE_KEY_SALT) : '';
966
+ $prefix = in_array($group, $this->global_groups) ? $this->global_prefix : $this->blog_prefix;
967
+
968
+ return "{$salt}{$prefix}:{$group}:{$key}";
969
+ }
970
+
971
+ /**
972
+ * Convert data types when using Redis MGET
973
+ *
974
+ * When requesting multiple keys, those not found in cache are assigned the value null upon return.
975
+ * Expected value in this case is false, so we convert
976
+ *
977
+ * @param string $value Value to possibly convert
978
+ * @return string Converted value
979
+ */
980
+ protected function filter_redis_get_multi($value)
981
+ {
982
+ if (is_null($value)) {
983
+ $value = false;
984
+ }
985
+
986
+ return $value;
987
+ }
988
+
989
+ /**
990
+ * Convert Redis responses into something meaningful
991
+ *
992
+ * @param mixed $response
993
+ * @return mixed
994
+ */
995
+ protected function parse_redis_response($response)
996
+ {
997
+ if (is_bool($response)) {
998
+ return $response;
999
+ }
1000
+
1001
+ if (is_numeric($response)) {
1002
+ return $response;
1003
+ }
1004
+
1005
+ if (is_object($response) && method_exists($response, 'getPayload')) {
1006
+ return $response->getPayload() === 'OK';
1007
+ }
1008
+
1009
+ return false;
1010
+ }
1011
+
1012
+ /**
1013
+ * Simple wrapper for saving object to the internal cache.
1014
+ *
1015
+ * @param string $derived_key Key to save value under.
1016
+ * @param mixed $value Object value.
1017
+ */
1018
+ public function add_to_internal_cache($derived_key, $value)
1019
+ {
1020
+ $this->cache[ $derived_key ] = $value;
1021
+ }
1022
+
1023
+ /**
1024
+ * Get a value specifically from the internal, run-time cache, not Redis.
1025
+ *
1026
+ * @param int|string $key Key value.
1027
+ * @param int|string $group Group that the value belongs to.
1028
+ *
1029
+ * @return bool|mixed Value on success; false on failure.
1030
+ */
1031
+ public function get_from_internal_cache($key, $group)
1032
+ {
1033
+ $derived_key = $this->build_key($key, $group);
1034
+
1035
+ if (isset($this->cache[ $derived_key ])) {
1036
+ return $this->cache[ $derived_key ];
1037
+ }
1038
+
1039
+ return false;
1040
+ }
1041
+
1042
+ /**
1043
+ * In multisite, switch blog prefix when switching blogs
1044
+ *
1045
+ * @param int $_blog_id
1046
+ * @return bool
1047
+ */
1048
+ public function switch_to_blog($_blog_id)
1049
+ {
1050
+ if (! function_exists('is_multisite') || ! is_multisite()) {
1051
+ return false;
1052
+ }
1053
+
1054
+ $this->blog_prefix = $_blog_id;
1055
+
1056
+ return true;
1057
+ }
1058
+
1059
+ /**
1060
+ * Sets the list of global groups.
1061
+ *
1062
+ * @param array $groups List of groups that are global.
1063
+ */
1064
+ public function add_global_groups($groups)
1065
+ {
1066
+ $groups = (array) $groups;
1067
+
1068
+ if ($this->redis_status()) {
1069
+ $this->global_groups = array_unique(array_merge($this->global_groups, $groups));
1070
+ } else {
1071
+ $this->ignored_groups = array_unique(array_merge($this->ignored_groups, $groups));
1072
+ }
1073
+ }
1074
+
1075
+ /**
1076
+ * Sets the list of groups not to be cached by Redis.
1077
+ *
1078
+ * @param array $groups List of groups that are to be ignored.
1079
+ */
1080
+ public function add_non_persistent_groups($groups)
1081
+ {
1082
+ $groups = (array) $groups;
1083
+
1084
+ $this->ignored_groups = array_unique(array_merge($this->ignored_groups, $groups));
1085
+ }
1086
+
1087
+ /**
1088
+ * Wrapper to validate the cache keys expiration value
1089
+ *
1090
+ * @param mixed $expiration Incomming expiration value (whatever it is)
1091
+ */
1092
+ protected function validate_expiration($expiration)
1093
+ {
1094
+ $expiration = is_int($expiration) || ctype_digit($expiration) ? (int) $expiration : 0;
1095
+
1096
+ if (defined('WP_REDIS_MAXTTL')) {
1097
+ $max = (int) WP_REDIS_MAXTTL;
1098
+
1099
+ if ($expiration === 0 || $expiration > $max) {
1100
+ $expiration = $max;
1101
+ }
1102
+ }
1103
+
1104
+ return $expiration;
1105
+ }
1106
+
1107
+ /**
1108
+ * Unserialize value only if it was serialized.
1109
+ *
1110
+ * @param string $original Maybe unserialized original, if is needed.
1111
+ * @return mixed Unserialized data can be any type.
1112
+ */
1113
+ protected function maybe_unserialize($original)
1114
+ {
1115
+ // don't attempt to unserialize data that wasn't serialized going in
1116
+ if ($this->is_serialized($original)) {
1117
+ return @unserialize($original);
1118
+ }
1119
+
1120
+ return $original;
1121
+ }
1122
+
1123
+ /**
1124
+ * Serialize data, if needed.
1125
+ * @param string|array|object $data Data that might be serialized.
1126
+ * @return mixed A scalar data
1127
+ */
1128
+ protected function maybe_serialize($data)
1129
+ {
1130
+ if (is_array($data) || is_object($data)) {
1131
+ return serialize($data);
1132
+ }
1133
+
1134
+ if ($this->is_serialized($data, false)) {
1135
+ return serialize($data);
1136
+ }
1137
+
1138
+ return $data;
1139
+ }
1140
+
1141
+ /**
1142
+ * Check value to find if it was serialized.
1143
+ *
1144
+ * If $data is not an string, then returned value will always be false.
1145
+ * Serialized data is always a string.
1146
+ *
1147
+ * @param string $data Value to check to see if was serialized.
1148
+ * @param bool $strict Optional. Whether to be strict about the end of the string. Default true.
1149
+ * @return bool False if not serialized and true if it was.
1150
+ */
1151
+ protected function is_serialized($data, $strict = true)
1152
+ {
1153
+ // if it isn't a string, it isn't serialized.
1154
+ if (! is_string($data)) {
1155
+ return false;
1156
+ }
1157
+
1158
+ $data = trim($data);
1159
+
1160
+ if ('N;' == $data) {
1161
+ return true;
1162
+ }
1163
+
1164
+ if (strlen($data) < 4) {
1165
+ return false;
1166
+ }
1167
+
1168
+ if (':' !== $data[1]) {
1169
+ return false;
1170
+ }
1171
+
1172
+ if ($strict) {
1173
+ $lastc = substr($data, -1);
1174
+
1175
+ if (';' !== $lastc && '}' !== $lastc) {
1176
+ return false;
1177
+ }
1178
+ } else {
1179
+ $semicolon = strpos($data, ';');
1180
+ $brace = strpos($data, '}');
1181
+
1182
+ // Either ; or } must exist.
1183
+ if (false === $semicolon && false === $brace) {
1184
+ return false;
1185
+ }
1186
+
1187
+ // But neither must be in the first X characters.
1188
+ if (false !== $semicolon && $semicolon < 3) {
1189
+ return false;
1190
+ }
1191
+
1192
+ if (false !== $brace && $brace < 4) {
1193
+ return false;
1194
+ }
1195
+ }
1196
+ $token = $data[0];
1197
+
1198
+ switch ($token) {
1199
+ case 's':
1200
+ if ($strict) {
1201
+ if ('"' !== substr($data, -2, 1)) {
1202
+ return false;
1203
+ }
1204
+ } elseif (false === strpos($data, '"')) {
1205
+ return false;
1206
+ }
1207
+ // or else fall through
1208
+ // no break
1209
+ case 'a':
1210
+ case 'O':
1211
+ return (bool) preg_match("/^{$token}:[0-9]+:/s", $data);
1212
+ case 'b':
1213
+ case 'i':
1214
+ case 'd':
1215
+ $end = $strict ? '$' : '';
1216
+
1217
+ return (bool) preg_match("/^{$token}:[0-9.E-]+;$end/", $data);
1218
+ }
1219
+
1220
+ return false;
1221
+ }
1222
  }
1223
 
1224
  endif;
includes/predis.php CHANGED
Binary file
includes/servers-list.php CHANGED
@@ -98,8 +98,8 @@ class Servers_List extends WP_List_Table {
98
  $constant = sprintf( 'WP_REDIS_%s', strtoupper( $setting ) );
99
 
100
  if ( defined( $constant ) ) {
101
- $server[ $setting ] = constant( $constant );
102
- }
103
  }
104
 
105
  if ( defined( 'WP_REDIS_CLUSTER' ) ) {
98
  $constant = sprintf( 'WP_REDIS_%s', strtoupper( $setting ) );
99
 
100
  if ( defined( $constant ) ) {
101
+ $server[ $setting ] = constant( $constant );
102
+ }
103
  }
104
 
105
  if ( defined( 'WP_REDIS_CLUSTER' ) ) {
includes/wp-cli-commands.php CHANGED
@@ -4,146 +4,146 @@ WP_CLI::add_command( 'redis', 'RedisObjectCache_CLI_Commands' );
4
 
5
  class RedisObjectCache_CLI_Commands extends WP_CLI_Command {
6
 
7
- /**
8
- * Show the Redis object cache status and (when possible) client.
9
- *
10
- * ## EXAMPLES
11
- *
12
- * wp redis status
13
- *
14
- */
15
- public function status() {
16
 
17
- $plugin = $GLOBALS[ 'redisObjectCache' ];
18
- $status = $plugin->get_status();
19
- $client = $plugin->get_redis_client_name();
20
 
21
 
22
- switch ($status) {
23
- case __( 'Disabled', 'redis-cache' ):
24
- $status = WP_CLI::colorize( "%y{$status}%n" );
25
- break;
26
- case __( 'Connected', 'redis-cache' ):
27
- $status = WP_CLI::colorize( "%g{$status}%n" );
28
- break;
29
- case __( 'Not Connected', 'redis-cache' ):
30
- $status = WP_CLI::colorize( "%r{$status}%n" );
31
- break;
32
- }
33
 
34
- WP_CLI::line( 'Status: ' . $status );
35
-
36
- if ( ! is_null( $client ) ) {
37
- WP_CLI::line( 'Client: ' . $client );
38
- }
39
 
40
- }
41
 
42
- /**
43
- * Enables the Redis object cache.
44
- *
45
- * Default behavior is to create the object cache drop-in,
46
- * unless an unknown object cache drop-in is present.
47
- *
48
- * ## EXAMPLES
49
- *
50
- * wp redis enable
51
- *
52
- */
53
- public function enable() {
54
 
55
- global $wp_filesystem;
56
 
57
- $plugin = $GLOBALS[ 'redisObjectCache' ];
58
 
59
- if ( $plugin->object_cache_dropin_exists() ) {
60
 
61
- if ($plugin->validate_object_cache_dropin()) {
62
- WP_CLI::line( __( 'Redis object cache already enabled.', 'redis-cache' ) );
63
- } else {
64
- WP_CLI::error( __('An unknown object cache drop-in was found. To use Redis run: wp redis update-dropin.', 'redis-cache') );
65
- }
66
 
67
- } else {
68
 
69
- WP_Filesystem();
70
 
71
- if ($wp_filesystem->copy( WP_PLUGIN_DIR . '/redis-cache/includes/object-cache.php', WP_CONTENT_DIR . '/object-cache.php', true )) {
72
- WP_CLI::success( __( 'Object Cache enabled.', 'redis-cache' ) );
73
- } else {
74
- WP_CLI::error( __( 'Object Cache could not be enabled.', 'redis-cache' ) );
75
- }
76
 
77
- }
78
 
79
- }
80
 
81
- /**
82
- * Disables the Redis object cache.
83
- *
84
- * Default behavior is to delete the object cache drop-in,
85
- * unless an unknown object cache drop-in is present.
86
- *
87
- * ## EXAMPLES
88
- *
89
- * wp redis disable
90
- *
91
- */
92
- public function disable() {
93
 
94
- global $wp_filesystem;
95
 
96
- $plugin = $GLOBALS[ 'redisObjectCache' ];
97
 
98
- if ( ! $plugin->object_cache_dropin_exists() ) {
99
 
100
- WP_CLI::error( __( 'No object cache drop-in found.', 'redis-cache' ) );
101
 
102
- } else {
103
 
104
- if ( ! $plugin->validate_object_cache_dropin() ) {
105
 
106
- WP_CLI::error( __('An unknown object cache drop-in was found. To use Redis run: wp redis update-dropin.', 'redis-cache') );
107
 
108
- } else {
109
 
110
- WP_Filesystem();
111
 
112
- if ($wp_filesystem->delete( WP_CONTENT_DIR . '/object-cache.php' )) {
113
- WP_CLI::success( __( 'Object Cache disabled.', 'redis-cache' ) );
114
- } else {
115
- WP_CLI::error( __( 'Object Cache could not be disabled.', 'redis-cache' ) );
116
- }
117
 
118
- }
119
 
120
- }
121
 
122
- }
123
 
124
- /**
125
- * Updates the Redis object cache drop-in.
126
- *
127
- * Default behavior is to overwrite any existing object cache drop-in.
128
- *
129
- * ## EXAMPLES
130
- *
131
- * wp redis update-dropin
132
- *
133
- * @subcommand update-dropin
134
- */
135
- public function update_dropin() {
136
 
137
- global $wp_filesystem;
138
 
139
- WP_Filesystem();
140
 
141
- if ($wp_filesystem->copy( WP_PLUGIN_DIR . '/redis-cache/includes/object-cache.php', WP_CONTENT_DIR . '/object-cache.php', true )) {
142
- WP_CLI::success( __( 'Updated object cache drop-in and enabled Redis object cache.', 'redis-cache' ) );
143
- } else {
144
- WP_CLI::error( __( 'Object Cache drop-in could not be updated.', 'redis-cache' ) );
145
- }
146
 
147
- }
148
 
149
  }
4
 
5
  class RedisObjectCache_CLI_Commands extends WP_CLI_Command {
6
 
7
+ /**
8
+ * Show the Redis object cache status and (when possible) client.
9
+ *
10
+ * ## EXAMPLES
11
+ *
12
+ * wp redis status
13
+ *
14
+ */
15
+ public function status() {
16
 
17
+ $plugin = $GLOBALS[ 'redisObjectCache' ];
18
+ $status = $plugin->get_status();
19
+ $client = $plugin->get_redis_client_name();
20
 
21
 
22
+ switch ($status) {
23
+ case __( 'Disabled', 'redis-cache' ):
24
+ $status = WP_CLI::colorize( "%y{$status}%n" );
25
+ break;
26
+ case __( 'Connected', 'redis-cache' ):
27
+ $status = WP_CLI::colorize( "%g{$status}%n" );
28
+ break;
29
+ case __( 'Not Connected', 'redis-cache' ):
30
+ $status = WP_CLI::colorize( "%r{$status}%n" );
31
+ break;
32
+ }
33
 
34
+ WP_CLI::line( 'Status: ' . $status );
35
+
36
+ if ( ! is_null( $client ) ) {
37
+ WP_CLI::line( 'Client: ' . $client );
38
+ }
39
 
40
+ }
41
 
42
+ /**
43
+ * Enables the Redis object cache.
44
+ *
45
+ * Default behavior is to create the object cache drop-in,
46
+ * unless an unknown object cache drop-in is present.
47
+ *
48
+ * ## EXAMPLES
49
+ *
50
+ * wp redis enable
51
+ *
52
+ */
53
+ public function enable() {
54
 
55
+ global $wp_filesystem;
56
 
57
+ $plugin = $GLOBALS[ 'redisObjectCache' ];
58
 
59
+ if ( $plugin->object_cache_dropin_exists() ) {
60
 
61
+ if ($plugin->validate_object_cache_dropin()) {
62
+ WP_CLI::line( __( 'Redis object cache already enabled.', 'redis-cache' ) );
63
+ } else {
64
+ WP_CLI::error( __('An unknown object cache drop-in was found. To use Redis run: wp redis update-dropin.', 'redis-cache') );
65
+ }
66
 
67
+ } else {
68
 
69
+ WP_Filesystem();
70
 
71
+ if ($wp_filesystem->copy( WP_PLUGIN_DIR . '/redis-cache/includes/object-cache.php', WP_CONTENT_DIR . '/object-cache.php', true )) {
72
+ WP_CLI::success( __( 'Object cache enabled.', 'redis-cache' ) );
73
+ } else {
74
+ WP_CLI::error( __( 'Object cache could not be enabled.', 'redis-cache' ) );
75
+ }
76
 
77
+ }
78
 
79
+ }
80
 
81
+ /**
82
+ * Disables the Redis object cache.
83
+ *
84
+ * Default behavior is to delete the object cache drop-in,
85
+ * unless an unknown object cache drop-in is present.
86
+ *
87
+ * ## EXAMPLES
88
+ *
89
+ * wp redis disable
90
+ *
91
+ */
92
+ public function disable() {
93
 
94
+ global $wp_filesystem;
95
 
96
+ $plugin = $GLOBALS[ 'redisObjectCache' ];
97
 
98
+ if ( ! $plugin->object_cache_dropin_exists() ) {
99
 
100
+ WP_CLI::error( __( 'No object cache drop-in found.', 'redis-cache' ) );
101
 
102
+ } else {
103
 
104
+ if ( ! $plugin->validate_object_cache_dropin() ) {
105
 
106
+ WP_CLI::error( __('An unknown object cache drop-in was found. To use Redis run: wp redis update-dropin.', 'redis-cache') );
107
 
108
+ } else {
109
 
110
+ WP_Filesystem();
111
 
112
+ if ($wp_filesystem->delete( WP_CONTENT_DIR . '/object-cache.php' )) {
113
+ WP_CLI::success( __( 'Object cache disabled.', 'redis-cache' ) );
114
+ } else {
115
+ WP_CLI::error( __( 'Object cache could not be disabled.', 'redis-cache' ) );
116
+ }
117
 
118
+ }
119
 
120
+ }
121
 
122
+ }
123
 
124
+ /**
125
+ * Updates the Redis object cache drop-in.
126
+ *
127
+ * Default behavior is to overwrite any existing object cache drop-in.
128
+ *
129
+ * ## EXAMPLES
130
+ *
131
+ * wp redis update-dropin
132
+ *
133
+ * @subcommand update-dropin
134
+ */
135
+ public function update_dropin() {
136
 
137
+ global $wp_filesystem;
138
 
139
+ WP_Filesystem();
140
 
141
+ if ($wp_filesystem->copy( WP_PLUGIN_DIR . '/redis-cache/includes/object-cache.php', WP_CONTENT_DIR . '/object-cache.php', true )) {
142
+ WP_CLI::success( __( 'Updated object cache drop-in and enabled Redis object cache.', 'redis-cache' ) );
143
+ } else {
144
+ WP_CLI::error( __( 'Object cache drop-in could not be updated.', 'redis-cache' ) );
145
+ }
146
 
147
+ }
148
 
149
  }
readme.txt CHANGED
@@ -1,10 +1,10 @@
1
  === Redis Object Cache ===
2
  Contributors: tillkruess
3
  Donate link: https://www.paypal.me/tillkruss
4
- Tags: redis, predis, hhvm, pecl, caching, cache, object cache, wp object cache, server, performance, optimize, speed, load, replication, clustering
5
  Requires at least: 3.3
6
- Tested up to: 4.9
7
- Stable tag: 1.3.5
8
  License: GPLv3
9
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
10
 
@@ -26,7 +26,7 @@ For detailed installation instructions, please read the [standard installation p
26
 
27
  1. Make sure [Redis is installed and running](http://redis.io/topics/quickstart).
28
  2. Install and activate plugin.
29
- 3. Enable the object cache under _Settings -> Redis_.
30
  4. If necessary, adjust [connection parameters](http://wordpress.org/extend/plugins/redis-cache/other_notes/).
31
 
32
  If your server doesn't support the [WordPress Filesystem API](https://codex.wordpress.org/Filesystem_API), you have to manually copy the `object-cache.php` file from the `/plugins/redis-cache/includes/` directory to the `/wp-content/` directory.
@@ -73,7 +73,11 @@ To adjust the configuration, define any of the following constants in your `wp-c
73
 
74
  * `WP_CACHE_KEY_SALT` (default: _not set_)
75
 
76
- Set the prefix for all cache keys. Useful in setups where multiple installs share a common `wp-config.php` or `$table_prefix`, to guarantee uniqueness of cache keys.
 
 
 
 
77
 
78
  * `WP_REDIS_MAXTTL` (default: _not set_)
79
 
@@ -94,21 +98,38 @@ To adjust the configuration, define any of the following constants in your `wp-c
94
 
95
  == Replication & Clustering ==
96
 
97
- To use Replication and Clustering, make sure your server is running PHP7, your setup is using Predis to connect to Redis and you consulted the [Predis documentation](https://github.com/nrk/predis).
98
 
99
- For replication use the `WP_REDIS_SERVERS` constant and for clustering the `WP_REDIS_CLUSTER` constant. You can use a named array or an URI string to specify the parameters.
100
 
101
  For authentication use the `WP_REDIS_PASSWORD` constant.
102
 
103
- __Master-Slave Replication Example:__
 
 
 
 
 
 
 
104
 
 
 
105
  define( 'WP_REDIS_SERVERS', [
106
- 'tcp://127.0.0.1:6379?database=15&alias=master',
107
- 'tcp://127.0.0.2:6379?database=15&alias=slave-01',
 
108
  ] );
109
 
 
110
 
111
- __Clustering via Client-side Sharding Example:__
 
 
 
 
 
 
112
 
113
  define( 'WP_REDIS_CLUSTER', [
114
  'tcp://127.0.0.1:6379?database=15&alias=node-01',
@@ -150,6 +171,18 @@ The following commands are supported:
150
 
151
  == Changelog ==
152
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  = 1.3.5 =
154
 
155
  * Added basic diagnostics to admin interface
@@ -263,6 +296,10 @@ The following commands are supported:
263
 
264
  == Upgrade Notice ==
265
 
 
 
 
 
266
  = 1.3.5 =
267
 
268
  This update contains various changes, including performance improvements and better Batcache compatibility.
1
  === Redis Object Cache ===
2
  Contributors: tillkruess
3
  Donate link: https://www.paypal.me/tillkruss
4
+ Tags: redis, predis, phpredis, hhvm, pecl, caching, cache, object cache, performance, replication, clustering
5
  Requires at least: 3.3
6
+ Tested up to: 4.8
7
+ Stable tag: 1.3.6
8
  License: GPLv3
9
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
10
 
26
 
27
  1. Make sure [Redis is installed and running](http://redis.io/topics/quickstart).
28
  2. Install and activate plugin.
29
+ 3. Enable the object cache under _Settings -> Redis_, or in Multisite setups under _Network Admin -> Settings -> Redis_.
30
  4. If necessary, adjust [connection parameters](http://wordpress.org/extend/plugins/redis-cache/other_notes/).
31
 
32
  If your server doesn't support the [WordPress Filesystem API](https://codex.wordpress.org/Filesystem_API), you have to manually copy the `object-cache.php` file from the `/plugins/redis-cache/includes/` directory to the `/wp-content/` directory.
73
 
74
  * `WP_CACHE_KEY_SALT` (default: _not set_)
75
 
76
+ Set the prefix for all cache keys. Useful in setups where multiple installs share a common `wp-config.php` or `$table_prefix` to guarantee uniqueness of cache keys.
77
+
78
+ * `WP_REDIS_SELECTIVE_FLUSH` (default: _not set_)
79
+
80
+ If set to `true`, flushing the cache will only delete keys that are prefixed with `WP_CACHE_KEY_SALT` (instead of emptying the entire Redis database). The selective flush is an atomic `O(n)` operation.
81
 
82
  * `WP_REDIS_MAXTTL` (default: _not set_)
83
 
98
 
99
  == Replication & Clustering ==
100
 
101
+ To use Replication, Sharding or Clustering, make sure your server is running PHP7 or higher (HHVM is not supported) and you consulted the [Predis](https://github.com/nrk/predis) or [PhpRedis](https://github.com/phpredis/phpredis) documentation.
102
 
103
+ For replication use the `WP_REDIS_SERVERS` constant, for sharding the `WP_REDIS_SHARDS` constant and for clustering the `WP_REDIS_CLUSTER` constant.
104
 
105
  For authentication use the `WP_REDIS_PASSWORD` constant.
106
 
107
+ __Replication (Master-Slave):__
108
+
109
+ define( 'WP_REDIS_SERVERS', [
110
+ 'tcp://127.0.0.1:6379?database=5&alias=master',
111
+ 'tcp://127.0.0.2:6379?database=5&alias=slave-01',
112
+ ] );
113
+
114
+ __Replication (Redis Sentinel):__
115
 
116
+ define( 'WP_REDIS_CLIENT', 'predis' );
117
+ define( 'WP_REDIS_SENTINEL', 'mymaster' );
118
  define( 'WP_REDIS_SERVERS', [
119
+ 'tcp://127.0.0.1:5380',
120
+ 'tcp://127.0.0.2:5381',
121
+ 'tcp://127.0.0.3:5382',
122
  ] );
123
 
124
+ __Sharding:__
125
 
126
+ define( 'WP_REDIS_SHARDS', [
127
+ 'tcp://127.0.0.1:6379?database=10&alias=shard-01',
128
+ 'tcp://127.0.0.2:6379?database=10&alias=shard-02',
129
+ 'tcp://127.0.0.3:6379?database=10&alias=shard-03',
130
+ ] );
131
+
132
+ __Clustering (Redis 3.0+):__
133
 
134
  define( 'WP_REDIS_CLUSTER', [
135
  'tcp://127.0.0.1:6379?database=15&alias=node-01',
171
 
172
  == Changelog ==
173
 
174
+ = 1.3.6 =
175
+
176
+ * Added support for Redis Sentinel
177
+ * Added support for sharing
178
+ * Switched to PHAR version of Predis
179
+ * Improved diagnostics
180
+ * Added `WP_REDIS_SELECTIVE_FLUSH`
181
+ * Added `$fail_gracefully` parameter to `WP_Object_Cache::__construct()`
182
+ * Always enforce `WP_REDIS_MAXTTL`
183
+ * Pass `$selective` and `$salt` to `redis_object_cache_flush` action
184
+ * Don’t set `WP_CACHE_KEY_SALT` constant
185
+
186
  = 1.3.5 =
187
 
188
  * Added basic diagnostics to admin interface
296
 
297
  == Upgrade Notice ==
298
 
299
+ = 1.3.6 =
300
+
301
+ This update contains various improvements.
302
+
303
  = 1.3.5 =
304
 
305
  This update contains various changes, including performance improvements and better Batcache compatibility.
redis-cache.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Redis Object Cache
4
  Plugin URI: https://wordpress.org/plugins/redis-cache/
5
  Description: A persistent object cache backend powered by Redis. Supports Predis, PhpRedis, HHVM, replication, clustering and WP-CLI.
6
- Version: 1.3.5
7
  Text Domain: redis-cache
8
  Domain Path: /languages
9
  Author: Till Krüss
@@ -13,380 +13,380 @@ License URI: http://www.gnu.org/licenses/gpl-3.0.html
13
  */
14
 
15
  if ( ! defined( 'ABSPATH' ) ) {
16
- exit;
17
  }
18
 
19
  if ( defined( 'WP_CLI' ) && WP_CLI ) {
20
- require_once dirname( __FILE__ ) . '/includes/wp-cli-commands.php';
21
  }
22
 
23
  class RedisObjectCache {
24
 
25
- private $page;
26
- private $screen = 'settings_page_redis-cache';
27
- private $actions = array( 'enable-cache', 'disable-cache', 'flush-cache', 'update-dropin' );
28
 
29
- public function __construct() {
30
 
31
- load_plugin_textdomain( 'redis-cache', false, 'redis-cache/languages' );
32
 
33
- register_activation_hook( __FILE__, 'wp_cache_flush' );
34
 
35
- $this->page = is_multisite() ? 'settings.php?page=redis-cache' : 'options-general.php?page=redis-cache';
36
 
37
- add_action( 'deactivate_plugin', array( $this, 'on_deactivation' ) );
38
 
39
- add_action( is_multisite() ? 'network_admin_menu' : 'admin_menu', array( $this, 'add_admin_menu_page' ) );
40
- add_action( 'admin_notices', array( $this, 'show_admin_notices' ) );
41
- add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) );
42
- add_action( 'load-' . $this->screen, array( $this, 'do_admin_actions' ) );
43
- add_action( 'load-' . $this->screen, array( $this, 'add_admin_page_notices' ) );
44
 
45
- add_filter( sprintf(
46
- '%splugin_action_links_%s',
47
- is_multisite() ? 'network_admin_' : '',
48
- plugin_basename( __FILE__ )
49
- ), array( $this, 'add_plugin_actions_links' ) );
50
 
51
- }
52
 
53
- public function add_admin_menu_page() {
54
 
55
- // add sub-page to "Settings"
56
- add_submenu_page(
57
- is_multisite() ? 'settings.php' : 'options-general.php',
58
- __( 'Redis Object Cache', 'redis-cache'),
59
- __( 'Redis', 'redis-cache'),
60
- is_multisite() ? 'manage_network_options' : 'manage_options',
61
- 'redis-cache',
62
- array( $this, 'show_admin_page' )
63
- );
64
 
65
- }
66
 
67
- public function show_admin_page() {
68
 
69
- // request filesystem credentials?
70
- if ( isset( $_GET[ '_wpnonce' ], $_GET[ 'action' ] ) ) {
71
 
72
- $action = $_GET[ 'action' ];
73
 
74
- foreach ( $this->actions as $name ) {
75
 
76
- // verify nonce
77
- if ( $action === $name && wp_verify_nonce( $_GET[ '_wpnonce' ], $action ) ) {
78
 
79
- $url = wp_nonce_url( network_admin_url( add_query_arg( 'action', $action, $this->page ) ), $action );
80
 
81
- if ( $this->initialize_filesystem( $url ) === false ) {
82
- return; // request filesystem credentials
83
- }
84
 
85
- }
86
 
87
- }
88
 
89
- }
90
 
91
- // show admin page
92
- require_once plugin_dir_path( __FILE__ ) . '/includes/admin-page.php';
93
 
94
- }
95
 
96
- public function show_servers_list() {
97
 
98
- require_once plugin_dir_path( __FILE__ ) . '/includes/servers-list.php';
99
 
100
- $table = new Servers_List;
101
- $table->prepare_items();
102
- $table->display();
103
 
104
- }
105
 
106
- public function add_plugin_actions_links( $links ) {
107
 
108
- // add settings link to plugin actions
109
- return array_merge(
110
- array( sprintf( '<a href="%s">Settings</a>', network_admin_url( $this->page ) ) ),
111
- $links
112
- );
113
 
114
- }
115
 
116
- public function enqueue_admin_styles( $hook_suffix ) {
117
 
118
- if ( $hook_suffix === $this->screen ) {
119
- $plugin = get_plugin_data( __FILE__ );
120
- wp_enqueue_style( 'redis-cache', plugin_dir_url( __FILE__ ) . 'includes/admin-page.css', null, $plugin[ 'Version' ] );
121
- }
122
 
123
- }
124
 
125
- public function object_cache_dropin_exists() {
126
- return file_exists( WP_CONTENT_DIR . '/object-cache.php' );
127
- }
128
 
129
- public function validate_object_cache_dropin() {
130
 
131
- if ( ! $this->object_cache_dropin_exists() ) {
132
- return false;
133
- }
134
 
135
- $dropin = get_plugin_data( WP_CONTENT_DIR . '/object-cache.php' );
136
- $plugin = get_plugin_data( plugin_dir_path( __FILE__ ) . '/includes/object-cache.php' );
137
 
138
- if ( strcmp( $dropin[ 'PluginURI' ], $plugin[ 'PluginURI' ] ) !== 0 ) {
139
- return false;
140
- }
141
 
142
- return true;
143
 
144
- }
145
 
146
- public function get_status() {
147
 
148
- if ( ! $this->object_cache_dropin_exists() ) {
149
- return __( 'Disabled', 'redis-cache' );
150
- }
151
 
152
- if ( $this->validate_object_cache_dropin() ) {
153
- if ( $this->get_redis_status() ) {
154
- return __( 'Connected', 'redis-cache' );
155
- }
156
 
157
- if ( $this->get_redis_status() === false ) {
158
- return __( 'Not Connected', 'redis-cache' );
159
- }
160
- }
161
 
162
- return __( 'Unknown', 'redis-cache' );
163
 
164
- }
165
 
166
- public function get_redis_status() {
167
 
168
- global $wp_object_cache;
169
 
170
- if ( $this->validate_object_cache_dropin() ) {
171
- return $wp_object_cache->redis_status();
172
- }
173
 
174
- return;
175
 
176
- }
177
 
178
- public function get_redis_client_name() {
179
 
180
- global $wp_object_cache;
181
 
182
- if ( isset( $wp_object_cache->redis_client ) ) {
183
- return $wp_object_cache->redis_client;
184
- }
185
 
186
- if ( defined( 'WP_REDIS_CLIENT' ) ) {
187
- return WP_REDIS_CLIENT;
188
- }
189
 
190
- return null;
191
 
192
- }
193
 
194
- public function get_redis_cachekey_prefix() {
195
- return defined( 'WP_CACHE_KEY_SALT' ) ? WP_CACHE_KEY_SALT : null;
196
- }
197
 
198
- public function get_redis_maxttl() {
199
- return defined( 'WP_REDIS_MAXTTL' ) ? WP_REDIS_MAXTTL : null;
200
- }
201
 
202
- public function show_admin_notices() {
203
 
204
- // only show admin notices to users with the right capability
205
- if ( ! current_user_can( is_multisite() ? 'manage_network_options' : 'manage_options' ) ) {
206
- return;
207
- }
208
 
209
- if ( $this->object_cache_dropin_exists() ) {
210
 
211
- $url = wp_nonce_url( network_admin_url( add_query_arg( 'action', 'update-dropin', $this->page ) ), 'update-dropin' );
212
 
213
- if ( $this->validate_object_cache_dropin() ) {
214
 
215
- $dropin = get_plugin_data( WP_CONTENT_DIR . '/object-cache.php' );
216
- $plugin = get_plugin_data( plugin_dir_path( __FILE__ ) . '/includes/object-cache.php' );
217
 
218
- if ( version_compare( $dropin[ 'Version' ], $plugin[ 'Version' ], '<' ) ) {
219
- $message = sprintf( __( 'The Redis object cache drop-in is outdated. Please <a href="%s">update it now</a>.', 'redis-cache' ), $url );
220
- }
221
 
222
- } else {
223
 
224
- $message = sprintf( __( 'An unknown object cache drop-in was found. To use Redis, <a href="%s">please replace it now</a>.', 'redis-cache' ), $url );
225
 
226
- }
227
 
228
- if ( isset( $message ) ) {
229
- printf( '<div class="update-nag">%s</div>', $message );
230
- }
231
 
232
- }
233
 
234
- }
235
 
236
- public function add_admin_page_notices() {
237
 
238
- // show PHP version warning
239
- if ( version_compare( PHP_VERSION, '5.4.0', '<' ) ) {
240
- add_settings_error( '', 'redis-cache', __( 'This plugin requires PHP 5.4 or greater.', 'redis-cache' ) );
241
- }
242
 
243
- // show action success/failure messages
244
- if ( isset( $_GET[ 'message' ] ) ) {
245
 
246
- switch ( $_GET[ 'message' ] ) {
247
 
248
- case 'cache-enabled':
249
- $message = __( 'Object Cache enabled.', 'redis-cache' );
250
- break;
251
- case 'enable-cache-failed':
252
- $error = __( 'Object Cache could not be enabled.', 'redis-cache' );
253
- break;
254
- case 'cache-disabled':
255
- $message = __( 'Object Cache disabled.', 'redis-cache' );
256
- break;
257
- case 'disable-cache-failed':
258
- $error = __( 'Object Cache could not be disabled.', 'redis-cache' );
259
- break;
260
- case 'cache-flushed':
261
- $message = __( 'Object Cache flushed.', 'redis-cache' );
262
- break;
263
- case 'flush-cache-failed':
264
- $error = __( 'Object Cache could not be flushed.', 'redis-cache' );
265
- break;
266
- case 'dropin-updated':
267
- $message = __( 'Updated object cache drop-in and enabled Redis object cache.', 'redis-cache' );
268
- break;
269
- case 'update-dropin-failed':
270
- $error = __( 'Object cache drop-in could not be updated.', 'redis-cache' );
271
- break;
272
 
273
- }
274
 
275
- add_settings_error( '', 'redis-cache', isset( $message ) ? $message : $error, isset( $message ) ? 'updated' : 'error' );
276
 
277
- }
278
 
279
- }
280
 
281
- public function do_admin_actions() {
282
 
283
- global $wp_filesystem;
284
 
285
- if ( isset( $_GET[ '_wpnonce' ], $_GET[ 'action' ] ) ) {
286
 
287
- $action = $_GET[ 'action' ];
288
 
289
- // verify nonce
290
- foreach ( $this->actions as $name ) {
291
- if ( $action === $name && ! wp_verify_nonce( $_GET[ '_wpnonce' ], $action ) ) {
292
- return;
293
- }
294
- }
295
 
296
- if ( in_array( $action, $this->actions ) ) {
297
 
298
- $url = wp_nonce_url( network_admin_url( add_query_arg( 'action', $action, $this->page ) ), $action );
299
 
300
- if ( $action === 'flush-cache' ) {
301
- $message = wp_cache_flush() ? 'cache-flushed' : 'flush-cache-failed';
302
- }
303
 
304
- // do we have filesystem credentials?
305
- if ( $this->initialize_filesystem( $url, true ) ) {
306
 
307
- switch ( $action ) {
308
 
309
- case 'enable-cache':
310
- $result = $wp_filesystem->copy( plugin_dir_path( __FILE__ ) . '/includes/object-cache.php', WP_CONTENT_DIR . '/object-cache.php', true );
311
- do_action( 'redis_object_cache_enable', $result );
312
- $message = $result ? 'cache-enabled' : 'enable-cache-failed';
313
- break;
314
 
315
- case 'disable-cache':
316
- $result = $wp_filesystem->delete( WP_CONTENT_DIR . '/object-cache.php' );
317
- do_action( 'redis_object_cache_disable', $result );
318
- $message = $result ? 'cache-disabled' : 'disable-cache-failed';
319
- break;
320
 
321
- case 'update-dropin':
322
- $result = $wp_filesystem->copy( plugin_dir_path( __FILE__ ) . '/includes/object-cache.php', WP_CONTENT_DIR . '/object-cache.php', true );
323
- do_action( 'redis_object_cache_update_dropin', $result );
324
- $message = $result ? 'dropin-updated' : 'update-dropin-failed';
325
- break;
326
 
327
- }
328
 
329
- }
330
 
331
- // redirect if status `$message` was set
332
- if ( isset( $message ) ) {
333
- wp_safe_redirect( network_admin_url( add_query_arg( 'message', $message, $this->page ) ) );
334
- exit;
335
- }
336
 
337
- }
338
 
339
- }
340
 
341
- }
342
 
343
- public function initialize_filesystem( $url, $silent = false ) {
344
 
345
- if ( $silent ) {
346
- ob_start();
347
- }
348
 
349
- if ( ( $credentials = request_filesystem_credentials( $url ) ) === false ) {
350
 
351
- if ( $silent ) {
352
- ob_end_clean();
353
- }
354
 
355
- return false;
356
 
357
- }
358
 
359
- if ( ! WP_Filesystem( $credentials ) ) {
360
 
361
- request_filesystem_credentials( $url );
362
 
363
- if ( $silent ) {
364
- ob_end_clean();
365
- }
366
 
367
- return false;
368
 
369
- }
370
 
371
- return true;
372
 
373
- }
374
 
375
- public function on_deactivation( $plugin ) {
376
 
377
- global $wp_filesystem;
378
 
379
- if ( $plugin === plugin_basename( __FILE__ ) ) {
380
 
381
- wp_cache_flush();
382
 
383
- if ( $this->validate_object_cache_dropin() && $this->initialize_filesystem( '', true ) ) {
384
- $wp_filesystem->delete( WP_CONTENT_DIR . '/object-cache.php' );
385
- }
386
 
387
- }
388
 
389
- }
390
 
391
  }
392
 
3
  Plugin Name: Redis Object Cache
4
  Plugin URI: https://wordpress.org/plugins/redis-cache/
5
  Description: A persistent object cache backend powered by Redis. Supports Predis, PhpRedis, HHVM, replication, clustering and WP-CLI.
6
+ Version: 1.3.6
7
  Text Domain: redis-cache
8
  Domain Path: /languages
9
  Author: Till Krüss
13
  */
14
 
15
  if ( ! defined( 'ABSPATH' ) ) {
16
+ exit;
17
  }
18
 
19
  if ( defined( 'WP_CLI' ) && WP_CLI ) {
20
+ require_once dirname( __FILE__ ) . '/includes/wp-cli-commands.php';
21
  }
22
 
23
  class RedisObjectCache {
24
 
25
+ private $page;
26
+ private $screen = 'settings_page_redis-cache';
27
+ private $actions = array( 'enable-cache', 'disable-cache', 'flush-cache', 'update-dropin' );
28
 
29
+ public function __construct() {
30
 
31
+ load_plugin_textdomain( 'redis-cache', false, 'redis-cache/languages' );
32
 
33
+ register_activation_hook( __FILE__, 'wp_cache_flush' );
34
 
35
+ $this->page = is_multisite() ? 'settings.php?page=redis-cache' : 'options-general.php?page=redis-cache';
36
 
37
+ add_action( 'deactivate_plugin', array( $this, 'on_deactivation' ) );
38
 
39
+ add_action( is_multisite() ? 'network_admin_menu' : 'admin_menu', array( $this, 'add_admin_menu_page' ) );
40
+ add_action( 'admin_notices', array( $this, 'show_admin_notices' ) );
41
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) );
42
+ add_action( 'load-' . $this->screen, array( $this, 'do_admin_actions' ) );
43
+ add_action( 'load-' . $this->screen, array( $this, 'add_admin_page_notices' ) );
44
 
45
+ add_filter( sprintf(
46
+ '%splugin_action_links_%s',
47
+ is_multisite() ? 'network_admin_' : '',
48
+ plugin_basename( __FILE__ )
49
+ ), array( $this, 'add_plugin_actions_links' ) );
50
 
51
+ }
52
 
53
+ public function add_admin_menu_page() {
54
 
55
+ // add sub-page to "Settings"
56
+ add_submenu_page(
57
+ is_multisite() ? 'settings.php' : 'options-general.php',
58
+ __( 'Redis Object Cache', 'redis-cache'),
59
+ __( 'Redis', 'redis-cache'),
60
+ is_multisite() ? 'manage_network_options' : 'manage_options',
61
+ 'redis-cache',
62
+ array( $this, 'show_admin_page' )
63
+ );
64
 
65
+ }
66
 
67
+ public function show_admin_page() {
68
 
69
+ // request filesystem credentials?
70
+ if ( isset( $_GET[ '_wpnonce' ], $_GET[ 'action' ] ) ) {
71
 
72
+ $action = $_GET[ 'action' ];
73
 
74
+ foreach ( $this->actions as $name ) {
75
 
76
+ // verify nonce
77
+ if ( $action === $name && wp_verify_nonce( $_GET[ '_wpnonce' ], $action ) ) {
78
 
79
+ $url = wp_nonce_url( network_admin_url( add_query_arg( 'action', $action, $this->page ) ), $action );
80
 
81
+ if ( $this->initialize_filesystem( $url ) === false ) {
82
+ return; // request filesystem credentials
83
+ }
84
 
85
+ }
86
 
87
+ }
88
 
89
+ }
90
 
91
+ // show admin page
92
+ require_once plugin_dir_path( __FILE__ ) . '/includes/admin-page.php';
93
 
94
+ }
95
 
96
+ public function show_servers_list() {
97
 
98
+ require_once plugin_dir_path( __FILE__ ) . '/includes/servers-list.php';
99
 
100
+ $table = new Servers_List;
101
+ $table->prepare_items();
102
+ $table->display();
103
 
104
+ }
105
 
106
+ public function add_plugin_actions_links( $links ) {
107
 
108
+ // add settings link to plugin actions
109
+ return array_merge(
110
+ array( sprintf( '<a href="%s">Settings</a>', network_admin_url( $this->page ) ) ),
111
+ $links
112
+ );
113
 
114
+ }
115
 
116
+ public function enqueue_admin_styles( $hook_suffix ) {
117
 
118
+ if ( $hook_suffix === $this->screen ) {
119
+ $plugin = get_plugin_data( __FILE__ );
120
+ wp_enqueue_style( 'redis-cache', plugin_dir_url( __FILE__ ) . 'includes/admin-page.css', null, $plugin[ 'Version' ] );
121
+ }
122
 
123
+ }
124
 
125
+ public function object_cache_dropin_exists() {
126
+ return file_exists( WP_CONTENT_DIR . '/object-cache.php' );
127
+ }
128
 
129
+ public function validate_object_cache_dropin() {
130
 
131
+ if ( ! $this->object_cache_dropin_exists() ) {
132
+ return false;
133
+ }
134
 
135
+ $dropin = get_plugin_data( WP_CONTENT_DIR . '/object-cache.php' );
136
+ $plugin = get_plugin_data( plugin_dir_path( __FILE__ ) . '/includes/object-cache.php' );
137
 
138
+ if ( strcmp( $dropin[ 'PluginURI' ], $plugin[ 'PluginURI' ] ) !== 0 ) {
139
+ return false;
140
+ }
141
 
142
+ return true;
143
 
144
+ }
145
 
146
+ public function get_status() {
147
 
148
+ if ( ! $this->object_cache_dropin_exists() ) {
149
+ return __( 'Disabled', 'redis-cache' );
150
+ }
151
 
152
+ if ( $this->validate_object_cache_dropin() ) {
153
+ if ( $this->get_redis_status() ) {
154
+ return __( 'Connected', 'redis-cache' );
155
+ }
156
 
157
+ if ( $this->get_redis_status() === false ) {
158
+ return __( 'Not Connected', 'redis-cache' );
159
+ }
160
+ }
161
 
162
+ return __( 'Unknown', 'redis-cache' );
163
 
164
+ }
165
 
166
+ public function get_redis_status() {
167
 
168
+ global $wp_object_cache;
169
 
170
+ if ( $this->validate_object_cache_dropin() ) {
171
+ return $wp_object_cache->redis_status();
172
+ }
173
 
174
+ return;
175
 
176
+ }
177
 
178
+ public function get_redis_client_name() {
179
 
180
+ global $wp_object_cache;
181
 
182
+ if ( isset( $wp_object_cache->redis_client ) ) {
183
+ return $wp_object_cache->redis_client;
184
+ }
185
 
186
+ if ( defined( 'WP_REDIS_CLIENT' ) ) {
187
+ return WP_REDIS_CLIENT;
188
+ }
189
 
190
+ return null;
191
 
192
+ }
193
 
194
+ public function get_redis_cachekey_prefix() {
195
+ return defined( 'WP_CACHE_KEY_SALT' ) ? WP_CACHE_KEY_SALT : null;
196
+ }
197
 
198
+ public function get_redis_maxttl() {
199
+ return defined( 'WP_REDIS_MAXTTL' ) ? WP_REDIS_MAXTTL : null;
200
+ }
201
 
202
+ public function show_admin_notices() {
203
 
204
+ // only show admin notices to users with the right capability
205
+ if ( ! current_user_can( is_multisite() ? 'manage_network_options' : 'manage_options' ) ) {
206
+ return;
207
+ }
208
 
209
+ if ( $this->object_cache_dropin_exists() ) {
210
 
211
+ $url = wp_nonce_url( network_admin_url( add_query_arg( 'action', 'update-dropin', $this->page ) ), 'update-dropin' );
212
 
213
+ if ( $this->validate_object_cache_dropin() ) {
214
 
215
+ $dropin = get_plugin_data( WP_CONTENT_DIR . '/object-cache.php' );
216
+ $plugin = get_plugin_data( plugin_dir_path( __FILE__ ) . '/includes/object-cache.php' );
217
 
218
+ if ( version_compare( $dropin[ 'Version' ], $plugin[ 'Version' ], '<' ) ) {
219
+ $message = sprintf( __( 'The Redis object cache drop-in is outdated. Please <a href="%s">update it now</a>.', 'redis-cache' ), $url );
220
+ }
221
 
222
+ } else {
223
 
224
+ $message = sprintf( __( 'An unknown object cache drop-in was found. To use Redis, <a href="%s">please replace it now</a>.', 'redis-cache' ), $url );
225
 
226
+ }
227
 
228
+ if ( isset( $message ) ) {
229
+ printf( '<div class="update-nag">%s</div>', $message );
230
+ }
231
 
232
+ }
233
 
234
+ }
235
 
236
+ public function add_admin_page_notices() {
237
 
238
+ // show PHP version warning
239
+ if ( version_compare( PHP_VERSION, '5.4.0', '<' ) ) {
240
+ add_settings_error( '', 'redis-cache', __( 'This plugin requires PHP 5.4 or greater.', 'redis-cache' ) );
241
+ }
242
 
243
+ // show action success/failure messages
244
+ if ( isset( $_GET[ 'message' ] ) ) {
245
 
246
+ switch ( $_GET[ 'message' ] ) {
247
 
248
+ case 'cache-enabled':
249
+ $message = __( 'Object cache enabled.', 'redis-cache' );
250
+ break;
251
+ case 'enable-cache-failed':
252
+ $error = __( 'Object cache could not be enabled.', 'redis-cache' );
253
+ break;
254
+ case 'cache-disabled':
255
+ $message = __( 'Object cache disabled.', 'redis-cache' );
256
+ break;
257
+ case 'disable-cache-failed':
258
+ $error = __( 'Object cache could not be disabled.', 'redis-cache' );
259
+ break;
260
+ case 'cache-flushed':
261
+ $message = __( 'Object cache flushed.', 'redis-cache' );
262
+ break;
263
+ case 'flush-cache-failed':
264
+ $error = __( 'Object cache could not be flushed.', 'redis-cache' );
265
+ break;
266
+ case 'dropin-updated':
267
+ $message = __( 'Updated object cache drop-in and enabled Redis object cache.', 'redis-cache' );
268
+ break;
269
+ case 'update-dropin-failed':
270
+ $error = __( 'Object cache drop-in could not be updated.', 'redis-cache' );
271
+ break;
272
 
273
+ }
274
 
275
+ add_settings_error( '', 'redis-cache', isset( $message ) ? $message : $error, isset( $message ) ? 'updated' : 'error' );
276
 
277
+ }
278
 
279
+ }
280
 
281
+ public function do_admin_actions() {
282
 
283
+ global $wp_filesystem;
284
 
285
+ if ( isset( $_GET[ '_wpnonce' ], $_GET[ 'action' ] ) ) {
286
 
287
+ $action = $_GET[ 'action' ];
288
 
289
+ // verify nonce
290
+ foreach ( $this->actions as $name ) {
291
+ if ( $action === $name && ! wp_verify_nonce( $_GET[ '_wpnonce' ], $action ) ) {
292
+ return;
293
+ }
294
+ }
295
 
296
+ if ( in_array( $action, $this->actions ) ) {
297
 
298
+ $url = wp_nonce_url( network_admin_url( add_query_arg( 'action', $action, $this->page ) ), $action );
299
 
300
+ if ( $action === 'flush-cache' ) {
301
+ $message = wp_cache_flush() ? 'cache-flushed' : 'flush-cache-failed';
302
+ }
303
 
304
+ // do we have filesystem credentials?
305
+ if ( $this->initialize_filesystem( $url, true ) ) {
306
 
307
+ switch ( $action ) {
308
 
309
+ case 'enable-cache':
310
+ $result = $wp_filesystem->copy( plugin_dir_path( __FILE__ ) . '/includes/object-cache.php', WP_CONTENT_DIR . '/object-cache.php', true );
311
+ do_action( 'redis_object_cache_enable', $result );
312
+ $message = $result ? 'cache-enabled' : 'enable-cache-failed';
313
+ break;
314
 
315
+ case 'disable-cache':
316
+ $result = $wp_filesystem->delete( WP_CONTENT_DIR . '/object-cache.php' );
317
+ do_action( 'redis_object_cache_disable', $result );
318
+ $message = $result ? 'cache-disabled' : 'disable-cache-failed';
319
+ break;
320
 
321
+ case 'update-dropin':
322
+ $result = $wp_filesystem->copy( plugin_dir_path( __FILE__ ) . '/includes/object-cache.php', WP_CONTENT_DIR . '/object-cache.php', true );
323
+ do_action( 'redis_object_cache_update_dropin', $result );
324
+ $message = $result ? 'dropin-updated' : 'update-dropin-failed';
325
+ break;
326
 
327
+ }
328
 
329
+ }
330
 
331
+ // redirect if status `$message` was set
332
+ if ( isset( $message ) ) {
333
+ wp_safe_redirect( network_admin_url( add_query_arg( 'message', $message, $this->page ) ) );
334
+ exit;
335
+ }
336
 
337
+ }
338
 
339
+ }
340
 
341
+ }
342
 
343
+ public function initialize_filesystem( $url, $silent = false ) {
344
 
345
+ if ( $silent ) {
346
+ ob_start();
347
+ }
348
 
349
+ if ( ( $credentials = request_filesystem_credentials( $url ) ) === false ) {
350
 
351
+ if ( $silent ) {
352
+ ob_end_clean();
353
+ }
354
 
355
+ return false;
356
 
357
+ }
358
 
359
+ if ( ! WP_Filesystem( $credentials ) ) {
360
 
361
+ request_filesystem_credentials( $url );
362
 
363
+ if ( $silent ) {
364
+ ob_end_clean();
365
+ }
366
 
367
+ return false;
368
 
369
+ }
370
 
371
+ return true;
372
 
373
+ }
374
 
375
+ public function on_deactivation( $plugin ) {
376
 
377
+ global $wp_filesystem;
378
 
379
+ if ( $plugin === plugin_basename( __FILE__ ) ) {
380
 
381
+ wp_cache_flush();
382
 
383
+ if ( $this->validate_object_cache_dropin() && $this->initialize_filesystem( '', true ) ) {
384
+ $wp_filesystem->delete( WP_CONTENT_DIR . '/object-cache.php' );
385
+ }
386
 
387
+ }
388
 
389
+ }
390
 
391
  }
392
 
uninstall.php CHANGED
@@ -6,13 +6,16 @@ global $wp_filesystem;
6
 
7
  ob_start();
8
 
9
- if ( file_exists( WP_CONTENT_DIR . '/object-cache.php' ) && method_exists( $GLOBALS[ 'wp_object_cache' ], 'redis_status' ) ) {
 
 
 
10
 
11
- wp_cache_flush();
12
 
13
- if ( WP_Filesystem( request_filesystem_credentials( '' ) ) ) {
14
- $wp_filesystem->delete( WP_CONTENT_DIR . '/object-cache.php' );
15
- }
16
 
17
  }
18
 
6
 
7
  ob_start();
8
 
9
+ if (
10
+ file_exists( WP_CONTENT_DIR . '/object-cache.php' ) &&
11
+ method_exists( $GLOBALS[ 'wp_object_cache' ], 'redis_status' )
12
+ ) {
13
 
14
+ wp_cache_flush();
15
 
16
+ if ( WP_Filesystem( request_filesystem_credentials( '' ) ) ) {
17
+ $wp_filesystem->delete( WP_CONTENT_DIR . '/object-cache.php' );
18
+ }
19
 
20
  }
21