set('redis', function($c) { * $settings = $c->get('settings'); * $config = $c->get('config'); * return new RedisCache( * $config['hostname'], * 6379, * $settings['redis_password'] * ); * }); * * Raw \Redis Access: * * During migration, legacy code may need raw \Redis: * $rawRedis = $redisCache->getConnection(); * $rawRedis->multi(); * $rawRedis->zAdd('LIVE', $timestamp, $uuid); * $rawRedis->exec(); * * @see CacheInterface * @see RedisManager::ensureConnected() * * @package XC_VM_Core_Cache * @author Divarion_D * @copyright 2025-2026 Vateron Media * @link https://github.com/Vateron-Media/XC_VM * @license AGPL-3.0 https://www.gnu.org/licenses/agpl-3.0.html */ class RedisCache implements CacheInterface { /** @var \Redis|null phpredis connection */ protected $redis = null; /** @var string \Redis host */ protected $host; /** @var int \Redis port */ protected $port; /** @var string|null \Redis password */ protected $password; /** @var bool Whether connection is established */ protected $connected = false; /** @var string Key prefix to avoid collisions */ protected $prefix = ''; /** * @param string $host \Redis host * @param int $port \Redis port * @param string|null $password \Redis AUTH password * @param string $prefix Optional key prefix */ public function __construct(string $host = '127.0.0.1', int $port = 6379, ?string $password = null, string $prefix = '') { $this->host = $host; $this->port = $port; $this->password = $password; $this->prefix = $prefix; } /** * Establish \Redis connection (lazy — called on first operation) * * @return bool */ public function connect() { if ($this->connected && $this->redis !== null) { return true; } try { $this->redis = new \Redis(); $this->redis->connect($this->host, $this->port); if ($this->password) { $this->redis->auth($this->password); } // Use igbinary serializer if available for consistency with FileCache if (defined('Redis::SERIALIZER_IGBINARY')) { $this->redis->setOption(\Redis::OPT_SERIALIZER, \Redis::SERIALIZER_IGBINARY); } $this->connected = true; return true; } catch (\Exception $e) { $this->redis = null; $this->connected = false; return false; } } /** * {@inheritdoc} */ public function get($key, $maxAge = null) { if (!$this->ensureConnected()) { return false; } $prefixedKey = $this->prefix . $key; $data = $this->redis->get($prefixedKey); if ($data === false) { return false; } // maxAge is handled by \Redis TTL, not by us // But if caller wants to check age, we can't — \Redis doesn't store creation time // For file-based TTL compat, we ignore maxAge here (\Redis uses its own TTL) return $data; } /** * {@inheritdoc} */ public function set($key, $data, $ttl = 0) { if (!$this->ensureConnected()) { return false; } $prefixedKey = $this->prefix . $key; if ($ttl > 0) { return $this->redis->setex($prefixedKey, $ttl, $data); } return $this->redis->set($prefixedKey, $data); } /** * {@inheritdoc} */ public function delete($key) { if (!$this->ensureConnected()) { return false; } $prefixedKey = $this->prefix . $key; $this->redis->del($prefixedKey); return true; } /** * {@inheritdoc} */ public function has($key, $maxAge = null) { if (!$this->ensureConnected()) { return false; } $prefixedKey = $this->prefix . $key; return (bool) $this->redis->exists($prefixedKey); } /** * {@inheritdoc} * * WARNING: Flushes the ENTIRE \Redis database. Use with caution. */ public function flush() { if (!$this->ensureConnected()) { return false; } return $this->redis->flushDB(); } // ─────────────────────────────────────────────────────────── // Raw \Redis Access (for migration period) // ─────────────────────────────────────────────────────────── /** * Get the raw phpredis connection * * Allows legacy code to use \Redis-specific operations (sorted sets, * pipelines, pub/sub, etc.) that don't fit the CacheInterface. * * @return \Redis|null */ public function getConnection() { $this->ensureConnected(); return $this->redis; } /** * Check if connection is alive * * @return bool */ public function isConnected() { if (!$this->connected || !$this->redis) { return false; } try { return $this->redis->ping() !== false; } catch (\Exception $e) { $this->connected = false; return false; } } /** * Close connection */ public function disconnect() { if ($this->redis) { try { $this->redis->close(); } catch (\Exception $e) { // ignore } } $this->redis = null; $this->connected = false; } /** * Ensure connection is active, reconnect if needed * * @return bool */ protected function ensureConnected() { if ($this->connected && $this->redis !== null) { return true; } return $this->connect(); } /** * Disconnect from \Redis when the instance is destroyed. */ public function __destruct() { $this->disconnect(); } }