2 declare(strict_types = 1);
18 use Psr\Log\LoggerAwareInterface;
19 use Psr\Log\LoggerAwareTrait;
75 $this->redis = new \Redis();
79 $this->applicationIdentifier =
'typo3_ses_'
81 . sha1(
$GLOBALS[
'TYPO3_CONF_VARS'][
'SYS'][
'encryptionKey']) .
'_';
92 if (!extension_loaded(
'redis')) {
93 throw new \RuntimeException(
94 'The PHP extension "redis" must be installed and loaded in order to use the redis session backend.',
99 if (isset($this->configuration[
'database'])) {
100 if (!is_int($this->configuration[
'database'])) {
101 throw new \InvalidArgumentException(
102 'The specified database number is of type "' . gettype($this->configuration[
'database']) .
103 '" but an integer is expected.',
108 if ($this->configuration[
'database'] < 0) {
109 throw new \InvalidArgumentException(
110 'The specified database "' . $this->configuration[
'database'] .
'" must be greater or equal than zero.',
117 public function hash(
string $sessionId): string
120 $key = sha1(
$GLOBALS[
'TYPO3_CONF_VARS'][
'SYS'][
'encryptionKey'] .
'core-session-backend');
121 return hash_hmac(
'sha256', $sessionId, $key);
131 public function get(
string $sessionId): array
135 $hashedSessionId = $this->
hash($sessionId);
137 if ($rawData !==
false) {
138 $decodedValue = json_decode($rawData,
true);
139 if (is_array($decodedValue)) {
140 return $decodedValue;
145 if ($rawData !==
false) {
146 $decodedValue = json_decode($rawData,
true);
147 if (is_array($decodedValue)) {
148 return $decodedValue;
151 throw new SessionNotFoundException(
'Session could not be fetched from redis', 1481885583);
161 public function remove(
string $sessionId):
bool
168 return $status || $statusLegacy;
181 public function set(
string $sessionId, array $sessionData): array
185 $hashedSessionId = $this->
hash($sessionId);
186 $sessionData[
'ses_id'] = $hashedSessionId;
187 $sessionData[
'ses_tstamp'] =
$GLOBALS[
'EXEC_TIME'] ?? time();
190 $jsonString = json_encode($sessionData);
191 $wasSet = is_string($jsonString) && $this->redis->set(
198 throw new SessionNotCreatedException(
'Session could not be written to Redis', 1481895647);
214 public function update(
string $sessionId, array $sessionData): array
216 $hashedSessionId = $this->
hash($sessionId);
218 $sessionData = array_merge($this->
get($sessionId), $sessionData);
219 }
catch (SessionNotFoundException $e) {
220 throw new SessionNotUpdatedException(
'Cannot update non-existing record', 1484389971, $e);
222 $sessionData[
'ses_id'] = $hashedSessionId;
223 $sessionData[
'ses_tstamp'] =
$GLOBALS[
'EXEC_TIME'] ?? time();
226 $jsonString = json_encode($sessionData);
227 $wasSet = is_string($jsonString) && $this->redis->set($key, $jsonString);
230 throw new SessionNotUpdatedException(
'Session could not be updated in Redis', 1481896383);
242 public function collectGarbage(
int $maximumLifetime,
int $maximumAnonymousLifetime = 0)
244 foreach ($this->
getAll() as $sessionRecord) {
245 if ($sessionRecord[
'ses_anonymous']) {
246 if ($maximumAnonymousLifetime > 0 && ($sessionRecord[
'ses_tstamp'] + $maximumAnonymousLifetime) <
$GLOBALS[
'EXEC_TIME']) {
250 if (($sessionRecord[
'ses_tstamp'] + $maximumLifetime) <
$GLOBALS[
'EXEC_TIME']) {
264 if ($this->connected) {
269 $this->connected = $this->redis->pconnect(
270 $this->configuration[
'hostname'] ??
'127.0.0.1',
271 $this->configuration[
'port'] ?? 6379,
275 }
catch (\RedisException $e) {
276 $this->logger->alert(
'Could not connect to redis server.', [
'exception' => $e]);
279 if (!$this->connected) {
280 throw new \RuntimeException(
281 'Could not connect to redis server at ' . $this->configuration[
'hostname'] .
':' . $this->configuration[
'port'],
286 if (isset($this->configuration[
'password'])
287 && $this->configuration[
'password'] !==
''
288 && !$this->redis->auth($this->configuration[
'password'])
290 throw new \RuntimeException(
291 'The given password was not accepted by the redis server.',
296 if (isset($this->configuration[
'database'])
297 && $this->configuration[
'database'] > 0
298 && !$this->redis->select($this->configuration[
'database'])
300 throw new \RuntimeException(
301 'The given database "' . $this->configuration[
'database'] .
'" could not be selected.',
312 public function getAll(): array
319 $this->redis->setOption(\Redis::OPT_SCAN, (
string)\Redis::SCAN_RETRY);
322 while (($keyChunk = $this->redis->scan($iterator, $pattern)) !==
false) {
323 foreach ($keyChunk as $key) {
328 $encodedSessions = $this->redis->mGet($keys);
329 if (!is_array($encodedSessions)) {
334 foreach ($encodedSessions as $session) {
335 if (is_string($session)) {
336 $decodedSession = json_decode($session,
true);
337 if ($decodedSession) {
338 $sessions[] = $decodedSession;
352 return $this->applicationIdentifier . $sessionId;