hasAnonymousSessions = (bool)($configuration['has_anonymous'] ?? false); $this->configuration = $configuration; } /** * Checks if the configuration is valid * * @throws \InvalidArgumentException * @internal To be used only by SessionManager */ public function validateConfiguration(): bool { if (empty($this->configuration['table'])) { throw new \InvalidArgumentException( 'The session backend "' . static::class . '" needs a "table" configuration.', 1442996707 ); } return true; } public function hash(string $sessionId): string { return GeneralUtility::makeInstance(HashService::class) ->hmac($sessionId, 'core-session-backend', HashAlgo::SHA3_256); } /** * Read session data * * @return array Returns the session data * @throws SessionNotFoundException */ public function get(string $sessionId): array { $query = $this->getQueryBuilder(); $query->select('*') ->from($this->configuration['table']) ->where($query->expr()->eq('ses_id', $query->createNamedParameter($this->hash($sessionId)))); $result = $query->executeQuery()->fetchAssociative(); if (!is_array($result)) { throw new SessionNotFoundException( 'The session with identifier ' . $sessionId . ' was not found ', 1481885483 ); } return $result; } /** * Delete a session record * * @return bool true if the session was deleted, false it session could not be found */ public function remove(string $sessionId): bool { $query = $this->getQueryBuilder(); $query->delete($this->configuration['table']) ->where( $query->expr()->or( $query->expr()->eq('ses_id', $query->createNamedParameter($this->hash($sessionId))), $query->expr()->eq('ses_id', $query->createNamedParameter($sessionId)) ) ); return (bool)$query->executeStatement(); } /** * Write session data. This method prevents overriding existing session data. * ses_id will always be set to $sessionId and overwritten if existing in $sessionData * This method updates ses_tstamp automatically * * @return array The newly created session record. * @throws SessionNotCreatedException */ public function set(string $sessionId, array $sessionData): array { $sessionId = $this->hash($sessionId); $sessionData['ses_id'] = $sessionId; $sessionData['ses_tstamp'] = $GLOBALS['EXEC_TIME'] ?? time(); try { $this->getConnection()->insert( $this->configuration['table'], $sessionData, ['ses_data' => Connection::PARAM_LOB] ); } catch (DBALException $e) { throw new SessionNotCreatedException( 'Session could not be written to database: ' . $e->getMessage(), 1481895005, $e ); } return $sessionData; } /** * Updates the session data. * ses_id will always be set to $sessionId and overwritten if existing in $sessionData * This method updates ses_tstamp automatically * * @param array $sessionData The session data to update. Data may be partial. * @return array $sessionData The newly updated session record. * @throws SessionNotUpdatedException */ public function update(string $sessionId, array $sessionData): array { $hashedSessionId = $this->hash($sessionId); $sessionData['ses_id'] = $hashedSessionId; $sessionData['ses_tstamp'] = $GLOBALS['EXEC_TIME'] ?? time(); try { // allow 0 records to be affected, happens when no columns where changed $this->getConnection()->update( $this->configuration['table'], $sessionData, ['ses_id' => $hashedSessionId], ['ses_data' => Connection::PARAM_LOB] ); } catch (DBALException $e) { throw new SessionNotUpdatedException( 'Session with id ' . $sessionId . ' could not be updated: ' . $e->getMessage(), 1481889220, $e ); } return $sessionData; } /** * Garbage Collection * * @param int $maximumLifetime maximum lifetime of authenticated user sessions, in seconds. * @param int $maximumAnonymousLifetime maximum lifetime of non-authenticated user sessions, in seconds. If set to 0, non-authenticated sessions are ignored. */ public function collectGarbage(int $maximumLifetime, int $maximumAnonymousLifetime = 0) { $query = $this->getQueryBuilder(); $query->delete($this->configuration['table']) ->where($query->expr()->lt('ses_tstamp', (int)($GLOBALS['EXEC_TIME'] - (int)$maximumLifetime))) ->andWhere($this->hasAnonymousSessions ? $query->expr()->neq('ses_userid', 0) : ' 1 = 1'); $query->executeStatement(); if ($maximumAnonymousLifetime > 0 && $this->hasAnonymousSessions) { $query = $this->getQueryBuilder(); $query->delete($this->configuration['table']) ->where($query->expr()->lt('ses_tstamp', (int)($GLOBALS['EXEC_TIME'] - (int)$maximumAnonymousLifetime))) ->andWhere($query->expr()->eq('ses_userid', 0)); $query->executeStatement(); } } /** * List all sessions * * @return array Return a list of all user sessions. The list may be empty */ public function getAll(): array { $query = $this->getQueryBuilder(); $query->select('*')->from($this->configuration['table']); return $query->executeQuery()->fetchAllAssociative(); } protected function getQueryBuilder(): QueryBuilder { return $this->getConnection()->createQueryBuilder(); } protected function getConnection(): Connection { return $this->connectionPool->getConnectionForTable($this->configuration['table']); } }