ping()) { switch (NN_CACHE_TYPE) { case self::TYPE_REDIS: case self::TYPE_MEMCACHED: return $this->server->set($key, $data, $expiration); case self::TYPE_APC: return apc_add($key, $data, $expiration); } } return false; } /** * Attempt to retrieve a value from the cache server, if not set it. * * * @param $key * @return bool|mixed|string * @throws \Blacklight\libraries\CacheException */ public function get($key) { if ($this->ping()) { $data = ''; switch (NN_CACHE_TYPE) { case self::TYPE_REDIS: case self::TYPE_MEMCACHED: $data = $this->server->get($key); break; case self::TYPE_APC: $data = apc_fetch($key); break; } return $data; } return false; } /** * Delete data tied to a key on the cache server. * * * @param $key * @return bool|string[] * @throws \Blacklight\libraries\CacheException */ public function delete($key) { if ($this->ping()) { switch (NN_CACHE_TYPE) { case self::TYPE_REDIS: case self::TYPE_MEMCACHED: return (bool) $this->server->delete($key); case self::TYPE_APC: return apc_delete($key); } } return false; } /** * Flush all data from the cache server? * * * @throws \Blacklight\libraries\CacheException */ public function flush() { if ($this->ping()) { switch (NN_CACHE_TYPE) { case self::TYPE_REDIS: $this->server->flushAll(); break; case self::TYPE_MEMCACHED: $this->server->flush(); break; case self::TYPE_APC: apc_clear_cache('user'); apc_clear_cache(); break; } } } /** * Create a SHA1 hash from a string which can be used to store/retrieve data. * * @param string $string * * @return string SHA1 hash of the input string. */ public function createKey($string) { return sha1($string); } /** * Get cache server statistics. * * * @return array|bool * @throws \Blacklight\libraries\CacheException */ public function serverStatistics() { if ($this->ping()) { switch (NN_CACHE_TYPE) { case self::TYPE_REDIS: return $this->server->info(); case self::TYPE_MEMCACHED: return $this->server->getStats(); case self::TYPE_APC: return apc_cache_info(); } } return []; } /** * Verify the user's cache settings, try to connect to the cache server. * * @throws \Blacklight\libraries\CacheException */ public function __construct() { if (! \defined('NN_CACHE_HOSTS')) { throw new CacheException( 'The NN_CACHE_HOSTS is not defined! Define it in settings.php' ); } if (! \defined('NN_CACHE_TIMEOUT')) { throw new CacheException( 'The NN_CACHE_TIMEOUT is not defined! Define it in settings.php, it is the time in seconds to time out from your cache server.' ); } $this->socketFile = false; if (\defined('NN_CACHE_SOCKET_FILE') && NN_CACHE_SOCKET_FILE != '') { $this->socketFile = true; } $serializer = false; if (\defined('NN_CACHE_SERIALIZER')) { $serializer = true; } switch (NN_CACHE_TYPE) { case self::TYPE_REDIS: if (! \extension_loaded('redis')) { throw new CacheException('The redis extension is not loaded!'); } $this->server = new \Redis(); $this->connect(); if ($serializer) { $this->server->setOption(\Redis::OPT_SERIALIZER, $this->verifySerializer()); } break; case self::TYPE_MEMCACHED: if (! \extension_loaded('memcached')) { throw new CacheException('The memcached extension is not loaded!'); } $this->server = new \Memcached(); if ($serializer) { $this->server->setOption(\Memcached::OPT_SERIALIZER, $this->verifySerializer()); } $this->server->setOption(\Memcached::OPT_COMPRESSION, (defined('NN_CACHE_COMPRESSION') ? NN_CACHE_COMPRESSION : false)); $this->connect(); break; case self::TYPE_APC: // Faster than checking if apcu or apc is loaded. if (! \function_exists('apc_add')) { throw new CacheException('The APCu extension is not loaded or enabled!'); } $this->connect(); break; case self::TYPE_DISABLED: default: break; } } /** * Destroy the connections. */ public function __destruct() { switch (NN_CACHE_TYPE) { case self::TYPE_REDIS: $this->server->close(); break; case self::TYPE_MEMCACHED: $this->server->quit(); break; } } /** * Connect to the cache server(s). * * @throws CacheException */ private function connect() { $this->connected = false; switch (NN_CACHE_TYPE) { case self::TYPE_REDIS: if ($this->socketFile === false) { $servers = unserialize(NN_CACHE_HOSTS); foreach ($servers as $server) { if ($this->server->connect($server['host'], $server['port'], (float) NN_CACHE_TIMEOUT) === false) { throw new CacheException('Error connecting to the Redis server!'); } else { $this->connected = true; } } } else { if ($this->server->connect(NN_CACHE_SOCKET_FILE) === false) { throw new CacheException('Error connecting to the Redis server!'); } else { $this->connected = true; } } break; case self::TYPE_MEMCACHED: $params = ($this->socketFile === false ? unserialize(NN_CACHE_HOSTS) : [[NN_CACHE_SOCKET_FILE, 'port' => 0]]); if ($this->server->addServers($params) === false) { throw new CacheException('Error connecting to the Memcached server!'); } else { $this->connected = true; } break; case self::TYPE_APC: $this->connected = true; break; } } /** * Check if we are still connected to the cache server, reconnect if not. * * * @return bool * @throws \Blacklight\libraries\CacheException */ private function ping() { if (! $this->connected) { return false; } switch (NN_CACHE_TYPE) { case self::TYPE_REDIS: try { return (bool) $this->server->ping(); } catch (\RedisException $error) { // nothing to see here, move along } break; case self::TYPE_MEMCACHED: $versions = $this->server->getVersion(); if ($versions) { foreach ($versions as $version) { if ($version != '255.255.255') { return true; } } } break; case self::TYPE_APC: return true; default: return false; } $this->connect(); return $this->connected; } /** * Verify the user selected serializer, return the memcached or redis appropriate serializer option. * * @return int * @throws CacheException */ private function verifySerializer() { switch (NN_CACHE_SERIALIZER) { case self::SERIALIZER_IGBINARY: if (! \extension_loaded('igbinary')) { throw new CacheException('Error: The igbinary extension is not loaded!'); } switch (NN_CACHE_TYPE) { case self::TYPE_REDIS: // If this is not defined, it means phpredis was not compiled with --enable-redis-igbinary if (! \defined('\Redis::SERIALIZER_IGBINARY')) { throw new CacheException('Error: phpredis was not compiled with igbinary support!'); } return \Redis::SERIALIZER_IGBINARY; case self::TYPE_MEMCACHED: if (\Memcached::HAVE_IGBINARY > 0) { return \Memcached::SERIALIZER_IGBINARY; } throw new CacheException('Error: You have not compiled Memcached with igbinary support!'); case self::TYPE_APC: // Ignore - set by apc.serializer setting. default: return null; } // no break case self::SERIALIZER_NONE: // Only redis supports this. if (NN_CACHE_TYPE !== self::TYPE_REDIS) { throw new CacheException('Error: Disabled serialization is only available on Redis!'); } return \Redis::SERIALIZER_NONE; case self::SERIALIZER_PHP: default: switch (NN_CACHE_TYPE) { case self::TYPE_REDIS: return \Redis::SERIALIZER_PHP; case self::TYPE_MEMCACHED: return \Memcached::SERIALIZER_PHP; default: return null; } } } }