diff --git a/cache/stores/memcached/lib.php b/cache/stores/memcached/lib.php index 56df93771b9..ba7a6d55295 100644 --- a/cache/stores/memcached/lib.php +++ b/cache/stores/memcached/lib.php @@ -229,8 +229,56 @@ class cachestore_memcached extends cache_store implements cache_is_configurable $version = phpversion('memcached'); $this->candeletemulti = ($version && version_compare($version, self::REQUIRED_VERSION, '>=')); - // Test the connection to the main connection. - $this->isready = @$this->connection->set("ping", 'ping', 1); + $this->isready = $this->is_connection_ready(); + } + + /** + * Confirm whether the connection is ready and usable. + * + * @return boolean + */ + public function is_connection_ready() { + if (!@$this->connection->set("ping", 'ping', 1)) { + // Test the connection to the server. + return false; + } + + if ($this->isshared) { + // There is a bug in libmemcached which means that it is not possible to purge the cache in a shared cache + // configuration. + // This issue currently affects: + // - memcached 1.4.23+ with php-memcached <= 2.2.0 + // The following combinations are not affected: + // - memcached <= 1.4.22 with any version of php-memcached + // - any version of memcached with php-memcached >= 3.0.1 + + + // This check is cheapest as it does not involve connecting to the server at all. + $safecombination = false; + $extension = new ReflectionExtension('memcached'); + if ((version_compare($extension->getVersion(), '3.0.1') >= 0)) { + // This is php-memcached version >= 3.0.1 which is a safe combination. + $safecombination = true; + } + + if (!$safecombination && (version_compare($this->connection->getVersion(), '1.4.22') <= 0)) { + // This is memcached server version <= 1.4.22 which is a safe combination. + $safecombination = true; + } + + if (!$safecombination) { + // This is memcached 1.4.23+ and php-memcached < 3.0.1. + // The issue may have been resolved in a subsequent update to any of the three libraries. + // The only way to safely determine if the combination is safe is to call getAllKeys. + // A safe combination will return an array, whilst an affected combination will return false. + // This is the most expensive check. + if (!is_array($this->connection->getAllKeys())) { + return false; + } + } + } + + return true; } /** diff --git a/cache/stores/memcached/tests/memcached_test.php b/cache/stores/memcached/tests/memcached_test.php index bef5cbe2704..db41269cd74 100644 --- a/cache/stores/memcached/tests/memcached_test.php +++ b/cache/stores/memcached/tests/memcached_test.php @@ -288,6 +288,10 @@ class cachestore_memcached_test extends cachestore_tests { $definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_memcached', 'phpunit_test'); $cachestore = $this->create_test_cache_with_config($definition, array('isshared' => true)); + if (!$cachestore->is_connection_ready()) { + $this->markTestSkipped('Could not test cachestore_memcached. Connection is not ready.'); + } + $connection = new Memcached(crc32(__METHOD__)); $connection->addServers($this->get_servers(TEST_CACHESTORE_MEMCACHED_TESTSERVERS)); $connection->setOptions(array(