diff options
Diffstat (limited to 'lib/classes/cache/RedisCache.php')
| -rw-r--r-- | lib/classes/cache/RedisCache.php | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/lib/classes/cache/RedisCache.php b/lib/classes/cache/RedisCache.php new file mode 100644 index 0000000..a78f751 --- /dev/null +++ b/lib/classes/cache/RedisCache.php @@ -0,0 +1,198 @@ +<?php + +namespace Studip\Cache; + +use BadMethodCallException; +use Config; +use DateTime; +use Exception; +use Psr\Cache\CacheItemInterface; +use Redis; +use RedisException; + +/** + * Cache implementation using redis. + * + * @author Jan-Hendrik Willms <tleilax+studip@gmail.com> + * @license GPL2 or any later version + * @package studip + * @subpackage cache + * @since Stud.IP 5.0 + */ +class RedisCache extends Cache +{ + use KeyTrait; + + private $redis; + + /** + * @return string A translateable display name for this cache class. + */ + public static function getDisplayName(): string + { + return _('Redis'); + } + + /** + * Construct a cache instance. + * + * @param string $hostname Hostname of redis server + * @param int $port Port of redis server + * @param string $auth Optional auth token/password + * + * @throws RedisException + */ + public function __construct($hostname, $port, string $auth = '') + { + if (!extension_loaded('redis')) { + throw new Exception('Redis extension missing.'); + } + + $this->redis = new Redis(); + $status = $this->redis->connect($hostname, $port, 1); + + if (!$status) { + throw new Exception('Could not add cache.'); + } + + if ($auth !== '') { + $this->redis->auth($auth); + } + } + + /** + * Returns the instance of the redis server connection. + * + * @return Redis instance + */ + public function getRedis() + { + return $this->redis; + } + + /** + * Expire item from the cache. + * + * Example: + * + * # expires foo + * $cache->expire('foo'); + * + * @param string $arg a single key. + */ + public function expire($arg) + { + $key = $this->getCacheKey($arg); + $this->redis->unlink($key); + } + + /** + * Expire all items from the cache. + */ + public function flush() + { + $pattern = $this->getCacheKey('*'); + foreach ($this->redis->keys($pattern) as $key) { + $this->redis->unlink($key); + } + } + + /** + * @param string $method Method to call + * @param array $args Arguments to pass + * @return false|mixed + */ + public function __call($method, $args) + { + if (is_callable([$this->redis, $method])) { + return call_user_func_array([$this->redis, $method], $args); + } + throw new BadMethodCallException("Method {$method} does not exist"); + } + + /** + * Return statistics. + * + * @StudipCache::getStats() + * + * @return array|array[] + */ + public function getStats(): array + { + $stats = $this->redis->info(); + $stats['size'] = count($this->redis->keys($this->getCacheKey('*'))); + return ["{$this->redis->getHost()}:{$this->redis->getPort()}" => $stats]; + } + + /* + * Return the Vue component name and props that handle configuration. + * + * @see StudipCache::getConfig() + * + * @return array + */ + public static function getConfig(): array + { + $currentCache = Config::get()->SYSTEMCACHE; + + // Set default config for this cache + $currentConfig = [ + 'hostname' => '', + 'port' => null + ]; + + // If this cache is set as system cache, use config from global settings. + if ($currentCache['type'] == __CLASS__) { + $currentConfig = $currentCache['config']; + $currentConfig['port'] = $currentConfig['port'] ? (int) $currentConfig['port'] : null; + } + + return [ + 'component' => 'RedisCacheConfig', + 'props' => $currentConfig + ]; + } + + /** + * @inheritDoc + */ + public function getItem(string $key): CacheItemInterface + { + $item = new Item($key); + $real_key = $this->getCacheKey($key); + $result = $this->redis->get($real_key); + if ($result === null) { + return $item; + } + $item->setHit(); + $item->set(unserialize($result)); + $expiration = new DateTime(); + $expiration->setTimestamp($this->redis->expiretime($real_key)); + $item->expiresAt($expiration); + return $item; + } + + /** + * @inheritDoc + */ + public function hasItem(string $key): bool + { + $real_key = $this->getCacheKey($key); + return $this->redis->get($real_key) !== null; + } + + /** + * @inheritDoc + */ + public function save(CacheItemInterface $item): bool + { + $expiration = $this->getExpiration($item); + if ($expiration < 1) { + // The item would expire immediately. + return false; + } + + $real_key = $this->getCacheKey($item->getKey()); + return $this->redis->setEx($real_key, $expiration, serialize($item->get())); + } +} |
