Redis Object Cache - Version 1.4.2

Version Description

  • Added graceful Redis failures and WP_REDIS_GRACEFUL constant
    • Improved cluster support
    • Added redis_cache_expiration filter
    • Renamed redis_object_cache_get filter to redis_object_cache_get_value
Download this release

Release Info

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

Code changes from version 1.4.1 to 1.4.2

includes/admin-page.php CHANGED
@@ -5,7 +5,13 @@
5
 
6
  <h1><?php _e( 'Redis Object Cache', 'redis-cache' ); ?></h1>
7
 
8
- <?php if ( defined( 'EAE_DISABLE_NOTICES' ) || ! is_plugin_active( 'email-address-encoder' ) && ! is_plugin_active( 'email-encoder-premium' ) && ! is_plugin_inactive( 'email-address-encoder' ) && ! is_plugin_inactive( 'email-encoder-premium' ) ) : ?>
 
 
 
 
 
 
9
  <div class="card">
10
  <h2 class="title">
11
  <?php _e( 'Are your email addresses protected?', 'redis-cache' ); ?>
5
 
6
  <h1><?php _e( 'Redis Object Cache', 'redis-cache' ); ?></h1>
7
 
8
+ <?php if (
9
+ defined( 'EAE_DISABLE_NOTICES' ) ||
10
+ ! is_plugin_active( 'email-address-encoder' ) &&
11
+ ! is_plugin_active( 'email-encoder-premium' ) &&
12
+ ! is_plugin_inactive( 'email-address-encoder' ) &&
13
+ ! is_plugin_inactive( 'email-encoder-premium' )
14
+ ) : ?>
15
  <div class="card">
16
  <h2 class="title">
17
  <?php _e( 'Are your email addresses protected?', 'redis-cache' ); ?>
includes/diagnostics.php CHANGED
@@ -13,7 +13,7 @@ $info[ 'Drop-in' ] = $dropin ? 'Valid' : 'Invalid';
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
  }
13
  if ( $dropin ) {
14
  try {
15
  $cache = new WP_Object_Cache( false );
16
+ $info[ 'Ping' ] = defined( 'WP_REDIS_CLUSTER' ) ? 'Not supported' : $cache->redis_instance()->ping();
17
  } catch ( Exception $exception ) {
18
  $info[ 'Connection Exception' ] = sprintf( '%s (%s)', $exception->getMessage(), get_class( $exception ) );
19
  }
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.4.1
7
  Author: Till Krüss
8
  Author URI: https://till.im/
9
  License: GPLv3
@@ -180,10 +180,15 @@ function wp_cache_init()
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
  /**
188
  * Replaces a value in cache.
189
  *
@@ -279,7 +284,6 @@ function wp_cache_add_non_persistent_groups($groups)
279
 
280
  class WP_Object_Cache
281
  {
282
-
283
  /**
284
  * The Redis client.
285
  *
@@ -294,6 +298,13 @@ class WP_Object_Cache
294
  */
295
  private $redis_connected = false;
296
 
 
 
 
 
 
 
 
297
  /**
298
  * Holds the non-Redis objects.
299
  *
@@ -338,7 +349,7 @@ class WP_Object_Cache
338
  *
339
  * @var array
340
  */
341
- public $ignored_groups = array( 'counts', 'plugins' );
342
 
343
  /**
344
  * Prefix used for global groups.
@@ -377,16 +388,19 @@ class WP_Object_Cache
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
 
@@ -412,12 +426,12 @@ class WP_Object_Cache
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) {
@@ -430,21 +444,21 @@ class WP_Object_Cache
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
 
@@ -453,7 +467,7 @@ class WP_Object_Cache
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
@@ -468,19 +482,19 @@ class WP_Object_Cache
468
  $parameters = WP_REDIS_SHARDS;
469
  } elseif (defined('WP_REDIS_SENTINEL')) {
470
  $parameters = WP_REDIS_SERVERS;
471
- $options[ 'replication' ] = 'sentinel';
472
- $options[ 'service' ] = WP_REDIS_SENTINEL;
473
  } elseif (defined('WP_REDIS_SERVERS')) {
474
  $parameters = WP_REDIS_SERVERS;
475
- $options[ 'replication' ] = true;
476
  } elseif (defined('WP_REDIS_CLUSTER')) {
477
  $parameters = WP_REDIS_CLUSTER;
478
- $options[ 'cluster' ] = 'redis';
479
  }
480
 
481
- foreach (array( 'WP_REDIS_SERVERS', 'WP_REDIS_SHARDS', 'WP_REDIS_CLUSTER' ) as $constant) {
482
  if (defined('WP_REDIS_PASSWORD') && defined($constant)) {
483
- $options[ 'parameters' ][ 'password' ] = WP_REDIS_PASSWORD;
484
  }
485
  }
486
 
@@ -490,20 +504,15 @@ class WP_Object_Cache
490
  $this->redis_client .= sprintf(' (v%s)', Predis\Client::VERSION);
491
  }
492
 
493
- // Throws exception if Redis is unavailable
494
- $this->redis->ping();
 
 
 
495
 
496
  $this->redis_connected = true;
497
  } catch (Exception $exception) {
498
-
499
- // When Redis is unavailable, fall back to the internal back by forcing all groups to be "no redis" groups
500
- $this->ignored_groups = array_unique(array_merge($this->ignored_groups, $this->global_groups));
501
-
502
- $this->redis_connected = false;
503
-
504
- if (! $fail_gracefully) {
505
- throw $exception;
506
- }
507
  }
508
 
509
  // Assign global and blog prefixes for use with keys
@@ -585,7 +594,7 @@ class WP_Object_Cache
585
  ? wp_suspend_cache_addition()
586
  : false;
587
 
588
- if ( $add && $cache_addition_suspended ) {
589
  return false;
590
  }
591
 
@@ -594,22 +603,28 @@ class WP_Object_Cache
594
 
595
  // save if group not excluded and redis is up
596
  if (! in_array($group, $this->ignored_groups) && $this->redis_status()) {
597
- $exists = $this->redis->exists($derived_key);
 
598
 
599
- if ($add == $exists) {
600
- return false;
601
- }
602
 
603
- $expiration = $this->validate_expiration($expiration);
604
 
605
- if ($expiration) {
606
- $result = $this->parse_redis_response($this->redis->setex($derived_key, $expiration, $this->maybe_serialize($value)));
607
- } else {
608
- $result = $this->parse_redis_response($this->redis->set($derived_key, $this->maybe_serialize($value)));
 
 
 
 
 
609
  }
610
  }
611
 
612
- $exists = isset($this->cache[ $derived_key ]);
613
 
614
  if ($add == $exists) {
615
  return false;
@@ -634,13 +649,19 @@ class WP_Object_Cache
634
  $result = false;
635
  $derived_key = $this->build_key($key, $group);
636
 
637
- if (isset($this->cache[ $derived_key ])) {
638
- unset($this->cache[ $derived_key ]);
639
  $result = true;
640
  }
641
 
642
  if ($this->redis_status() && ! in_array($group, $this->ignored_groups)) {
643
- $result = $this->parse_redis_response($this->redis->del($derived_key));
 
 
 
 
 
 
644
  }
645
 
646
  if (function_exists('do_action')) {
@@ -665,7 +686,7 @@ class WP_Object_Cache
665
  sleep($delay);
666
  }
667
 
668
- $result = false;
669
  $this->cache = array();
670
 
671
  if ($this->redis_status()) {
@@ -682,20 +703,71 @@ class WP_Object_Cache
682
  return i
683
  ";
684
 
685
- $result = $this->parse_redis_response($this->redis->eval(
686
- $script,
687
- $this->redis instanceof Predis\Client ? 0 : []
688
- ));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
689
  } else {
690
- $result = $this->parse_redis_response($this->redis->flushdb());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
691
  }
692
 
693
  if (function_exists('do_action')) {
694
- do_action('redis_object_cache_flush', $result, $delay, $selective, $salt);
695
  }
696
  }
697
 
698
- return $result;
 
 
 
 
 
 
 
 
 
 
699
  }
700
 
701
  /**
@@ -715,11 +787,11 @@ class WP_Object_Cache
715
  {
716
  $derived_key = $this->build_key($key, $group);
717
 
718
- if (isset($this->cache[ $derived_key ]) && ! $force) {
719
  $found = true;
720
  $this->cache_hits++;
721
 
722
- return is_object($this->cache[ $derived_key ]) ? clone $this->cache[ $derived_key ] : $this->cache[ $derived_key ];
723
  } elseif (in_array($group, $this->ignored_groups) || ! $this->redis_status()) {
724
  $found = false;
725
  $this->cache_misses++;
@@ -727,7 +799,13 @@ class WP_Object_Cache
727
  return false;
728
  }
729
 
730
- $result = $this->redis->get($derived_key);
 
 
 
 
 
 
731
 
732
  if ($result === null || $result === false) {
733
  $found = false;
@@ -749,8 +827,8 @@ class WP_Object_Cache
749
  }
750
 
751
  if (function_exists('apply_filters') && function_exists('has_filter')) {
752
- if (has_filter('redis_object_cache_get')) {
753
- return apply_filters('redis_object_cache_get', $value, $key, $group, $force, $found);
754
  }
755
  }
756
 
@@ -781,7 +859,7 @@ class WP_Object_Cache
781
  foreach ($groups as $group => $keys) {
782
  if (in_array($group, $this->ignored_groups) || ! $this->redis_status()) {
783
  foreach ($keys as $key) {
784
- $cache[ $this->build_key($key, $group) ] = $this->get($key, $group);
785
  }
786
  } else {
787
  // Reformat arguments as expected by Redis
@@ -792,16 +870,21 @@ class WP_Object_Cache
792
  }
793
 
794
  // Retrieve from cache in a single request
795
- $group_cache = $this->redis->mget($derived_keys);
 
 
 
 
 
796
 
797
  // Build an array of values looked up, keyed by the derived cache key
798
  $group_cache = array_combine($derived_keys, $group_cache);
799
 
800
  // Restores cached data to its original data type
801
- $group_cache = array_map(array( $this, 'maybe_unserialize' ), $group_cache);
802
 
803
  // Redis returns null for values not found in cache, but expected return value is false in this instance
804
- $group_cache = array_map(array( $this, 'filter_redis_get_multi' ), $group_cache);
805
 
806
  $cache = array_merge($cache, $group_cache);
807
  }
@@ -838,12 +921,18 @@ class WP_Object_Cache
838
 
839
  // save if group not excluded from redis and redis is up
840
  if (! in_array($group, $this->ignored_groups) && $this->redis_status()) {
841
- $expiration = $this->validate_expiration($expiration);
842
 
843
- if ($expiration) {
844
- $result = $this->parse_redis_response($this->redis->setex($derived_key, $expiration, $this->maybe_serialize($value)));
845
- } else {
846
- $result = $this->parse_redis_response($this->redis->set($derived_key, $this->maybe_serialize($value)));
 
 
 
 
 
 
847
  }
848
  }
849
 
@@ -882,9 +971,15 @@ class WP_Object_Cache
882
  }
883
 
884
  // Save to Redis
885
- $result = $this->parse_redis_response($this->redis->incrBy($derived_key, $offset));
 
 
 
 
 
886
 
887
- $this->add_to_internal_cache($derived_key, (int) $this->redis->get($derived_key));
 
888
 
889
  return $result;
890
  }
@@ -924,10 +1019,16 @@ class WP_Object_Cache
924
  return $value;
925
  }
926
 
927
- // Save to Redis
928
- $result = $this->parse_redis_response($this->redis->decrBy($derived_key, $offset));
 
929
 
930
- $this->add_to_internal_cache($derived_key, (int) $this->redis->get($derived_key));
 
 
 
 
 
931
 
932
  return $result;
933
  }
@@ -972,7 +1073,7 @@ class WP_Object_Cache
972
  $salt = defined('WP_CACHE_KEY_SALT') ? trim(WP_CACHE_KEY_SALT) : '';
973
  $prefix = in_array($group, $this->global_groups) ? $this->global_prefix : $this->blog_prefix;
974
 
975
- return "{$salt}{$prefix}:{$group}:{$key}";
976
  }
977
 
978
  /**
@@ -1024,7 +1125,7 @@ class WP_Object_Cache
1024
  */
1025
  public function add_to_internal_cache($derived_key, $value)
1026
  {
1027
- $this->cache[ $derived_key ] = $value;
1028
  }
1029
 
1030
  /**
@@ -1039,8 +1140,8 @@ class WP_Object_Cache
1039
  {
1040
  $derived_key = $this->build_key($key, $group);
1041
 
1042
- if (isset($this->cache[ $derived_key ])) {
1043
- return $this->cache[ $derived_key ];
1044
  }
1045
 
1046
  return false;
@@ -1119,7 +1220,7 @@ class WP_Object_Cache
1119
  */
1120
  protected function maybe_unserialize($original)
1121
  {
1122
- if (defined('WP_REDIS_IGBINARY') && WP_REDIS_IGBINARY) {
1123
  return igbinary_unserialize($original);
1124
  }
1125
 
@@ -1138,7 +1239,7 @@ class WP_Object_Cache
1138
  */
1139
  protected function maybe_serialize($data)
1140
  {
1141
- if (defined('WP_REDIS_IGBINARY') && WP_REDIS_IGBINARY) {
1142
  return igbinary_serialize($data);
1143
  }
1144
 
@@ -1234,6 +1335,24 @@ class WP_Object_Cache
1234
 
1235
  return false;
1236
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1237
  }
1238
 
1239
  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.4.2
7
  Author: Till Krüss
8
  Author URI: https://till.im/
9
  License: GPLv3
180
  global $wp_object_cache;
181
 
182
  if (! ($wp_object_cache instanceof WP_Object_Cache)) {
183
+ $fail_gracefully = ! defined('WP_REDIS_GRACEFUL') || WP_REDIS_GRACEFUL;
184
+
185
+ $wp_object_cache = new WP_Object_Cache($fail_gracefully);
186
  }
187
  }
188
 
189
+ // wp_cache_switch_to_blog
190
+ // wp_cache_switch_to_blog( get_current_blog_id() );
191
+
192
  /**
193
  * Replaces a value in cache.
194
  *
284
 
285
  class WP_Object_Cache
286
  {
 
287
  /**
288
  * The Redis client.
289
  *
298
  */
299
  private $redis_connected = false;
300
 
301
+ /**
302
+ * Check to fail gracefully or throw an exception.
303
+ *
304
+ * @var bool
305
+ */
306
+ private $fail_gracefully = true;
307
+
308
  /**
309
  * Holds the non-Redis objects.
310
  *
349
  *
350
  * @var array
351
  */
352
+ public $ignored_groups = array('counts', 'plugins');
353
 
354
  /**
355
  * Prefix used for global groups.
388
  {
389
  global $blog_id, $table_prefix;
390
 
391
+ $this->fail_gracefully = $fail_gracefully;
392
+
393
  $parameters = array(
394
  'scheme' => 'tcp',
395
  'host' => '127.0.0.1',
396
  'port' => 6379
397
  );
398
 
399
+ foreach (array('scheme', 'host', 'port', 'path', 'password', 'database') as $setting) {
400
  $constant = sprintf('WP_REDIS_%s', strtoupper($setting));
401
+
402
  if (defined($constant)) {
403
+ $parameters[$setting] = constant($constant);
404
  }
405
  }
406
 
426
  $this->redis = new Redis();
427
 
428
  // Adjust host and port, if the scheme is `unix`
429
+ if (strcasecmp('unix', $parameters['scheme']) === 0) {
430
+ $parameters['host'] = 'unix://' . $parameters['path'];
431
+ $parameters['port'] = 0;
432
  }
433
 
434
+ $this->redis->connect($parameters['host'], $parameters['port']);
435
  }
436
 
437
  if (strcasecmp('pecl', $client) === 0) {
444
  } else {
445
  $this->redis = new Redis();
446
 
447
+ if (strcasecmp('unix', $parameters['scheme']) === 0) {
448
+ $this->redis->connect($parameters['path']);
449
  } else {
450
+ $this->redis->connect($parameters['host'], $parameters['port']);
451
  }
452
  }
453
  }
454
 
455
  if (strcasecmp('pecl', $client) === 0 || strcasecmp('hhvm', $client) === 0) {
456
+ if (isset($parameters['password'])) {
457
+ $this->redis->auth($parameters['password']);
458
  }
459
 
460
+ if (isset($parameters['database'])) {
461
+ $this->redis->select($parameters['database']);
462
  }
463
  }
464
 
467
 
468
  // Require PHP 5.4 or greater
469
  if (version_compare(PHP_VERSION, '5.4.0', '<')) {
470
+ throw new Exception('Predis required PHP 5.4 or newer.');
471
  }
472
 
473
  // Load bundled Predis library
482
  $parameters = WP_REDIS_SHARDS;
483
  } elseif (defined('WP_REDIS_SENTINEL')) {
484
  $parameters = WP_REDIS_SERVERS;
485
+ $options['replication'] = 'sentinel';
486
+ $options['service'] = WP_REDIS_SENTINEL;
487
  } elseif (defined('WP_REDIS_SERVERS')) {
488
  $parameters = WP_REDIS_SERVERS;
489
+ $options['replication'] = true;
490
  } elseif (defined('WP_REDIS_CLUSTER')) {
491
  $parameters = WP_REDIS_CLUSTER;
492
+ $options['cluster'] = 'redis';
493
  }
494
 
495
+ foreach (array('WP_REDIS_SERVERS', 'WP_REDIS_SHARDS', 'WP_REDIS_CLUSTER') as $constant) {
496
  if (defined('WP_REDIS_PASSWORD') && defined($constant)) {
497
+ $options['parameters']['password'] = WP_REDIS_PASSWORD;
498
  }
499
  }
500
 
504
  $this->redis_client .= sprintf(' (v%s)', Predis\Client::VERSION);
505
  }
506
 
507
+ if (defined('WP_REDIS_CLUSTER')) {
508
+ $this->redis->ping(current(array_values(WP_REDIS_CLUSTER)));
509
+ } else {
510
+ $this->redis->ping();
511
+ }
512
 
513
  $this->redis_connected = true;
514
  } catch (Exception $exception) {
515
+ $this->handle_exception($exception);
 
 
 
 
 
 
 
 
516
  }
517
 
518
  // Assign global and blog prefixes for use with keys
594
  ? wp_suspend_cache_addition()
595
  : false;
596
 
597
+ if ($add && $cache_addition_suspended) {
598
  return false;
599
  }
600
 
603
 
604
  // save if group not excluded and redis is up
605
  if (! in_array($group, $this->ignored_groups) && $this->redis_status()) {
606
+ try {
607
+ $exists = $this->redis->exists($derived_key);
608
 
609
+ if ($add == $exists) {
610
+ return false;
611
+ }
612
 
613
+ $expiration = apply_filters('redis_cache_expiration', $this->validate_expiration($expiration), $key, $group);
614
 
615
+ if ($expiration) {
616
+ $result = $this->parse_redis_response($this->redis->setex($derived_key, $expiration, $this->maybe_serialize($value)));
617
+ } else {
618
+ $result = $this->parse_redis_response($this->redis->set($derived_key, $this->maybe_serialize($value)));
619
+ }
620
+ } catch (Exception $exception) {
621
+ $this->handle_exception($exception);
622
+
623
+ return false;
624
  }
625
  }
626
 
627
+ $exists = isset($this->cache[$derived_key]);
628
 
629
  if ($add == $exists) {
630
  return false;
649
  $result = false;
650
  $derived_key = $this->build_key($key, $group);
651
 
652
+ if (isset($this->cache[$derived_key])) {
653
+ unset($this->cache[$derived_key]);
654
  $result = true;
655
  }
656
 
657
  if ($this->redis_status() && ! in_array($group, $this->ignored_groups)) {
658
+ try {
659
+ $result = $this->parse_redis_response($this->redis->del($derived_key));
660
+ } catch (Exception $exception) {
661
+ $this->handle_exception($exception);
662
+
663
+ return false;
664
+ }
665
  }
666
 
667
  if (function_exists('do_action')) {
686
  sleep($delay);
687
  }
688
 
689
+ $results = [];
690
  $this->cache = array();
691
 
692
  if ($this->redis_status()) {
703
  return i
704
  ";
705
 
706
+ if (defined('WP_REDIS_CLUSTER')) {
707
+ try {
708
+ foreach ($this->redis->_masters() as $master) {
709
+ $redis = new Redis;
710
+ $redis->connect($master[0], $master[1]);
711
+ $results[] = $this->parse_redis_response($this->redis->eval($script));
712
+ unset($redis);
713
+ }
714
+ } catch (Exception $exception) {
715
+ $this->handle_exception($exception);
716
+
717
+ return false;
718
+ }
719
+ } else {
720
+ try {
721
+ $results[] = $this->parse_redis_response(
722
+ $this->redis->eval(
723
+ $script,
724
+ $this->redis instanceof Predis\Client ? 0 : []
725
+ )
726
+ );
727
+ } catch (Exception $exception) {
728
+ $this->handle_exception($exception);
729
+
730
+ return false;
731
+ }
732
+ }
733
  } else {
734
+ if (defined('WP_REDIS_CLUSTER')) {
735
+ try {
736
+ foreach ($this->redis->_masters() as $master) {
737
+ $results[] = $this->parse_redis_response($this->redis->flushdb($master));
738
+ }
739
+ } catch (Exception $exception) {
740
+ $this->handle_exception($exception);
741
+
742
+ return false;
743
+ }
744
+ } else {
745
+ try {
746
+ $results[] = $this->parse_redis_response($this->redis->flushdb());
747
+ } catch (Exception $exception) {
748
+ $this->handle_exception($exception);
749
+
750
+ return false;
751
+ }
752
+ }
753
  }
754
 
755
  if (function_exists('do_action')) {
756
+ do_action('redis_object_cache_flush', $results, $delay, $selective, $salt);
757
  }
758
  }
759
 
760
+ if (empty($results)) {
761
+ return false;
762
+ }
763
+
764
+ foreach ($results as $result) {
765
+ if (! $result) {
766
+ return false;
767
+ }
768
+ }
769
+
770
+ return true;
771
  }
772
 
773
  /**
787
  {
788
  $derived_key = $this->build_key($key, $group);
789
 
790
+ if (isset($this->cache[$derived_key]) && ! $force) {
791
  $found = true;
792
  $this->cache_hits++;
793
 
794
+ return is_object($this->cache[$derived_key]) ? clone $this->cache[$derived_key] : $this->cache[$derived_key];
795
  } elseif (in_array($group, $this->ignored_groups) || ! $this->redis_status()) {
796
  $found = false;
797
  $this->cache_misses++;
799
  return false;
800
  }
801
 
802
+ try {
803
+ $result = $this->redis->get($derived_key);
804
+ } catch (Exception $exception) {
805
+ $this->handle_exception($exception);
806
+
807
+ return false;
808
+ }
809
 
810
  if ($result === null || $result === false) {
811
  $found = false;
827
  }
828
 
829
  if (function_exists('apply_filters') && function_exists('has_filter')) {
830
+ if (has_filter('redis_object_cache_get_value')) {
831
+ return apply_filters('redis_object_cache_get_value', $value, $key, $group, $force, $found);
832
  }
833
  }
834
 
859
  foreach ($groups as $group => $keys) {
860
  if (in_array($group, $this->ignored_groups) || ! $this->redis_status()) {
861
  foreach ($keys as $key) {
862
+ $cache[$this->build_key($key, $group)] = $this->get($key, $group);
863
  }
864
  } else {
865
  // Reformat arguments as expected by Redis
870
  }
871
 
872
  // Retrieve from cache in a single request
873
+ try {
874
+ $group_cache = $this->redis->mget($derived_keys);
875
+ } catch (Exception $exception) {
876
+ $this->handle_exception($exception);
877
+ $group_cache = array_fill(0, count($derived_keys) - 1, false);
878
+ }
879
 
880
  // Build an array of values looked up, keyed by the derived cache key
881
  $group_cache = array_combine($derived_keys, $group_cache);
882
 
883
  // Restores cached data to its original data type
884
+ $group_cache = array_map(array($this, 'maybe_unserialize'), $group_cache);
885
 
886
  // Redis returns null for values not found in cache, but expected return value is false in this instance
887
+ $group_cache = array_map(array($this, 'filter_redis_get_multi'), $group_cache);
888
 
889
  $cache = array_merge($cache, $group_cache);
890
  }
921
 
922
  // save if group not excluded from redis and redis is up
923
  if (! in_array($group, $this->ignored_groups) && $this->redis_status()) {
924
+ $expiration = apply_filters('redis_cache_expiration', $this->validate_expiration($expiration), $key, $group);
925
 
926
+ try {
927
+ if ($expiration) {
928
+ $result = $this->parse_redis_response($this->redis->setex($derived_key, $expiration, $this->maybe_serialize($value)));
929
+ } else {
930
+ $result = $this->parse_redis_response($this->redis->set($derived_key, $this->maybe_serialize($value)));
931
+ }
932
+ } catch (Exception $exception) {
933
+ $this->handle_exception($exception);
934
+
935
+ return false;
936
  }
937
  }
938
 
971
  }
972
 
973
  // Save to Redis
974
+ try {
975
+ $result = $this->parse_redis_response($this->redis->incrBy($derived_key, $offset));
976
+
977
+ $this->add_to_internal_cache($derived_key, (int) $this->redis->get($derived_key));
978
+ } catch (Exception $exception) {
979
+ $this->handle_exception($exception);
980
 
981
+ return false;
982
+ }
983
 
984
  return $result;
985
  }
1019
  return $value;
1020
  }
1021
 
1022
+ try {
1023
+ // Save to Redis
1024
+ $result = $this->parse_redis_response($this->redis->decrBy($derived_key, $offset));
1025
 
1026
+ $this->add_to_internal_cache($derived_key, (int) $this->redis->get($derived_key));
1027
+ } catch (Exception $exception) {
1028
+ $this->handle_exception($exception);
1029
+
1030
+ return false;
1031
+ }
1032
 
1033
  return $result;
1034
  }
1073
  $salt = defined('WP_CACHE_KEY_SALT') ? trim(WP_CACHE_KEY_SALT) : '';
1074
  $prefix = in_array($group, $this->global_groups) ? $this->global_prefix : $this->blog_prefix;
1075
 
1076
+ return "{{$salt}{$prefix}}:{$group}:{$key}";
1077
  }
1078
 
1079
  /**
1125
  */
1126
  public function add_to_internal_cache($derived_key, $value)
1127
  {
1128
+ $this->cache[$derived_key] = $value;
1129
  }
1130
 
1131
  /**
1140
  {
1141
  $derived_key = $this->build_key($key, $group);
1142
 
1143
+ if (isset($this->cache[$derived_key])) {
1144
+ return $this->cache[$derived_key];
1145
  }
1146
 
1147
  return false;
1220
  */
1221
  protected function maybe_unserialize($original)
1222
  {
1223
+ if (defined('WP_REDIS_IGBINARY') && WP_REDIS_IGBINARY && function_exists('igbinary_unserialize')) {
1224
  return igbinary_unserialize($original);
1225
  }
1226
 
1239
  */
1240
  protected function maybe_serialize($data)
1241
  {
1242
+ if (defined('WP_REDIS_IGBINARY') && WP_REDIS_IGBINARY && function_exists('igbinary_serialize')) {
1243
  return igbinary_serialize($data);
1244
  }
1245
 
1335
 
1336
  return false;
1337
  }
1338
+
1339
+ /**
1340
+ * Handle the redis failure gracefully or throw an exception.
1341
+ *
1342
+ * @param \Exception $exception Exception thrown.
1343
+ */
1344
+ protected function handle_exception($exception) {
1345
+ $this->redis_connected = false;
1346
+
1347
+ // When Redis is unavailable, fall back to the internal cache by forcing all groups to be "no redis" groups
1348
+ $this->ignored_groups = array_unique(array_merge($this->ignored_groups, $this->global_groups));
1349
+
1350
+ if (! $this->fail_gracefully) {
1351
+ throw $exception;
1352
+ }
1353
+
1354
+ error_log($exception);
1355
+ }
1356
  }
1357
 
1358
  endif;
includes/servers-list.php CHANGED
@@ -52,7 +52,11 @@ class Servers_List extends WP_List_Table {
52
 
53
  $this->items = $this->get_servers();
54
 
55
- $this->_column_headers = array($this->get_columns(), $this->get_hidden_columns(), array());
 
 
 
 
56
 
57
  }
58
 
@@ -93,7 +97,7 @@ class Servers_List extends WP_List_Table {
93
  'scheme' => 'tcp',
94
  );
95
 
96
- foreach ( [ 'scheme', 'host', 'port', 'path', 'password', 'database' ] as $setting ) {
97
  $constant = sprintf( 'WP_REDIS_%s', strtoupper( $setting ) );
98
 
99
  if ( defined( $constant ) ) {
@@ -117,9 +121,9 @@ class Servers_List extends WP_List_Table {
117
  $servers = array( $server );
118
  }
119
 
120
- return array_map(function($parameters) {
121
- return is_string($parameters) ? Predis\Connection\Parameters::parse($parameters) : $parameters;
122
- }, $servers);
123
 
124
  }
125
 
52
 
53
  $this->items = $this->get_servers();
54
 
55
+ $this->_column_headers = array(
56
+ $this->get_columns(),
57
+ $this->get_hidden_columns(),
58
+ array()
59
+ );
60
 
61
  }
62
 
97
  'scheme' => 'tcp',
98
  );
99
 
100
+ foreach ( array( 'scheme', 'host', 'port', 'path', 'password', 'database' ) as $setting ) {
101
  $constant = sprintf( 'WP_REDIS_%s', strtoupper( $setting ) );
102
 
103
  if ( defined( $constant ) ) {
121
  $servers = array( $server );
122
  }
123
 
124
+ return array_map( function ( $parameters ) {
125
+ return is_string( $parameters ) ? Predis\Connection\Parameters::parse( $parameters ) : $parameters;
126
+ }, $servers );
127
 
128
  }
129
 
includes/wp-cli-commands.php CHANGED
@@ -19,7 +19,7 @@ class RedisObjectCache_CLI_Commands extends WP_CLI_Command {
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;
@@ -58,7 +58,7 @@ class RedisObjectCache_CLI_Commands extends WP_CLI_Command {
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') );
@@ -68,7 +68,7 @@ class RedisObjectCache_CLI_Commands extends WP_CLI_Command {
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' ) );
@@ -103,13 +103,13 @@ class RedisObjectCache_CLI_Commands extends WP_CLI_Command {
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' ) );
@@ -138,7 +138,7 @@ class RedisObjectCache_CLI_Commands extends WP_CLI_Command {
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' ) );
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;
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') );
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' ) );
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' ) );
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' ) );
readme.txt CHANGED
@@ -3,8 +3,8 @@ 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: 5.0
7
- Stable tag: 1.4.1
8
  License: GPLv3
9
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
10
 
@@ -95,6 +95,10 @@ To adjust the configuration, define any of the following constants in your `wp-c
95
 
96
  Set to `true` to disable the object cache at runtime.
97
 
 
 
 
 
98
  * `WP_REDIS_IGBINARY` (default: _not set_)
99
 
100
  Set to `true` to enable the [igbinary](https://github.com/igbinary/igbinary) serializer.
@@ -175,6 +179,13 @@ The following commands are supported:
175
 
176
  == Changelog ==
177
 
 
 
 
 
 
 
 
178
  = 1.4.1 =
179
 
180
  * Fixed potential fatal error related to `wp_suspend_cache_addition()`
@@ -323,6 +334,10 @@ The following commands are supported:
323
 
324
  == Upgrade Notice ==
325
 
 
 
 
 
326
  = 1.4.0 =
327
 
328
  This update adds support for igbinary and `wp_suspend_cache_addition()`.
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: 5.2
7
+ Stable tag: 1.4.2
8
  License: GPLv3
9
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
10
 
95
 
96
  Set to `true` to disable the object cache at runtime.
97
 
98
+ * `WP_REDIS_GRACEFUL` (default: _not set_)
99
+
100
+ Set to `false` to disable graceful failures and throw exceptions.
101
+
102
  * `WP_REDIS_IGBINARY` (default: _not set_)
103
 
104
  Set to `true` to enable the [igbinary](https://github.com/igbinary/igbinary) serializer.
179
 
180
  == Changelog ==
181
 
182
+ = 1.4.2 =
183
+
184
+ * Added graceful Redis failures and `WP_REDIS_GRACEFUL` constant
185
+ * Improved cluster support
186
+ * Added `redis_cache_expiration` filter
187
+ * Renamed `redis_object_cache_get` filter to `redis_object_cache_get_value`
188
+
189
  = 1.4.1 =
190
 
191
  * Fixed potential fatal error related to `wp_suspend_cache_addition()`
334
 
335
  == Upgrade Notice ==
336
 
337
+ = 1.4.2 =
338
+
339
+ This update renames the `redis_object_cache_get` filter to avoid conflicts. Update your code if necessary.
340
+
341
  = 1.4.0 =
342
 
343
  This update adds support for igbinary and `wp_suspend_cache_addition()`.
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.4.1
7
  Text Domain: redis-cache
8
  Domain Path: /languages
9
  Author: Till Krüss
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.4.2
7
  Text Domain: redis-cache
8
  Domain Path: /languages
9
  Author: Till Krüss