diff --git a/object-cache.php b/object-cache.php index 3dfc032..7fb261e 100644 --- a/object-cache.php +++ b/object-cache.php @@ -714,7 +714,21 @@ protected function _connect_redis() { $port = ! empty( $redis_server['port'] ) ? $redis_server['port'] : 6379; $this->redis->connect( $redis_server['host'], $port, 1, NULL, 100 ); # 1s timeout, 100ms delay between reconnections if ( ! empty( $redis_server['auth'] ) ) { - $this->redis->auth( $redis_server['auth'] ); + try { + $this->redis->auth( $redis_server['auth'] ); + // PhpRedis throws an Exception when it fails a server call. + // To prevent WordPress from fataling, we catch the Exception. + } catch ( RedisException $e ) { + try { + $this->last_triggered_error = 'WP Redis: ' . $e->getMessage(); + // Be friendly to developers debugging production servers by triggering an error + trigger_error( $this->last_triggered_error, E_USER_WARNING ); + } catch( PHPUnit_Framework_Error_Warning $e ) { + // PHPUnit throws an Exception when `trigger_error()` is called. + // To ensure our tests (which expect Exceptions to be caught) continue to run, + // we catch the PHPUnit exception and inspect the RedisException message + } + } } $this->is_redis_connected = $this->redis->isConnected(); if ( ! $this->is_redis_connected ) { @@ -740,15 +754,20 @@ protected function _call_redis( $method ) { try { $retval = call_user_func_array( array( $this->redis, $method ), $arguments ); return $retval; + // PhpRedis throws an Exception when it fails a server call. + // To prevent WordPress from fataling, we catch the Exception. } catch( RedisException $e ) { $retry_exception_messages = array( 'socket error on read socket', 'Connection closed', 'Redis server went away' ); $retry_exception_messages = apply_filters( 'wp_redis_retry_exception_messages', $retry_exception_messages ); if ( in_array( $e->getMessage(), $retry_exception_messages ) ) { try { $this->last_triggered_error = 'WP Redis: ' . $e->getMessage(); + // Be friendly to developers debugging production servers by triggering an error trigger_error( $this->last_triggered_error, E_USER_WARNING ); } catch( PHPUnit_Framework_Error_Warning $e ) { - // We'll inspect this in the test + // PHPUnit throws an Exception when `trigger_error()` is called. + // To ensure our tests (which expect Exceptions to be caught) continue to run, + // we catch the PHPUnit exception and inspect the RedisException message } // Attempt to refresh the connection if it was successfully established once // $this->is_redis_connected will be set inside _connect_redis() diff --git a/tests/test-cache.php b/tests/test-cache.php index dae7e47..97c0d12 100644 --- a/tests/test-cache.php +++ b/tests/test-cache.php @@ -87,6 +87,22 @@ public function test_redis_reload_force_cache_flush() { $this->assertEquals( "SELECT option_value FROM {$wpdb->options} WHERE option_name='wp_redis_do_redis_failback_flush'", $wpdb->last_query ); } + public function test_redis_bad_authentication() { + global $redis_server; + if ( ! class_exists( 'Redis' ) ) { + $this->markTestSkipped( 'PHPRedis extension not available.' ); + } + $redis_server['host'] = '127.0.0.1'; + $redis_server['port'] = 9999; + $redis_server['auth'] = 'foobar'; + $cache = new WP_Object_Cache; + $this->assertEquals( 'WP Redis: Redis server went away', $cache->last_triggered_error ); + $this->assertFalse( $cache->is_redis_connected ); + // Fails back to the internal object cache + $cache->set( 'foo', 'bar' ); + $this->assertEquals( 'bar', $cache->get( 'foo' ) ); + } + function test_miss() { $this->assertEquals(NULL, $this->cache->get(rand_str())); }