* @copyright Copyright (c) 2022, Robert Strutts. * @license https://mit-license.org/ */ namespace tts\services\sessions; class redis_sessions implements \SessionHandlerInterface { public static int $zlib_compression_level = 4; public static bool $use_compression = true; public $ttl = 1800; // 30 minutes default protected $db; protected $prefix; private $enc; public function __construct($enc, array $options) { $exists = \main_tts\registry::get('di')->exists('session_redis_servers'); if ($exists) { $db = \main_tts\registry::get('di')->get_service('session_redis_servers'); } if (! is_object($db)) { throw new \Exception("Session service type not set"); } $this->db = $options['db'] ?? $db; if (isset($options['prefix'])) { $prefix = $options['prefix']; } else { $prefix = 'SESS:'; } $this->prefix = $prefix; $this->enc = $enc; if (isset($options['compression_level'])) { self::$zlib_compression_level = $options['compression_level']; } if (isset($options['use_compression'])) { self::$use_compression = $options['use_compression']; } } private function encrypt(string & $data): void { if ($this->enc === false) { return; } $data = $this->enc->encrypt($data); } private function decrypt(string & $data): void { if ($this->enc === false) { return; } try { $data = $this->enc->decrypt($data); } catch (\Exception $e) { $data = false; // Maybe it has no data to decode } } private function compress(string & $data): void { if (self::$use_compression === false) { return; } $data = gzdeflate($data, self::$zlib_compression_level); if ($data === false) { throw new \Exception('Failed to compress session data'); } } private function decompress(string & $data): void { if (self::$use_compression === false) { return; } $ret = gzinflate($data); if ($ret !== false) { $data = $ret; } } public function open($save_path, $session_name): bool{ // No action necessary because connection is injected // in constructor and arguments are not applicable. } public function close(): bool { $this->db = null; unset($this->db); } public function read($id): false|string { $id = $this->prefix . $id; $data = $this->db->get($id); $this->db->expire($id, $this->ttl); if ($data === null) { return ""; } $data = base64_decode($data); if ($data === false) { return ""; } $this->decrypt($data); if ($data === false) { return ""; } $this->decompress($data); return ($data !== false) ? $data : ''; } public function write($id, $data): bool { $this->compress($data); $this->encrypt($data); $data = base64_encode($data); $id = $this->prefix . $id; $this->db->set($id, $data); $this->db->expire($id, $this->ttl); } public function destroy($id): bool { $this->db->del($this->prefix . $id); } public function gc($max_lifetime): int|false { // no action necessary because using EXPIRE } }