3
declare(strict_types=1);
6
* This file is part of CodeIgniter 4 framework.
8
* (c) CodeIgniter Foundation <admin@codeigniter.com>
10
* For the full copyright and license information, please view
11
* the LICENSE file that was distributed with this source code.
14
namespace CodeIgniter\Session\Handlers;
16
use CodeIgniter\Database\BaseBuilder;
17
use CodeIgniter\Database\BaseConnection;
18
use CodeIgniter\Session\Exceptions\SessionException;
20
use Config\Session as SessionConfig;
21
use ReturnTypeWillChange;
24
* Base database session handler
26
* Do not use this class. Use database specific handler class.
28
class DatabaseHandler extends BaseHandler
31
* The database group to use for storage.
38
* The name of the table to store session info.
45
* The DB Connection instance.
63
protected $rowExists = false;
66
* ID prefix for multiple session cookies
68
protected string $idPrefix;
71
* @throws SessionException
73
public function __construct(SessionConfig $config, string $ipAddress)
75
parent::__construct($config, $ipAddress);
77
// Store Session configurations
78
$this->DBGroup = $config->DBGroup ?? config(Database::class)->defaultGroup;
79
// Add sessionCookieName for multiple session cookies.
80
$this->idPrefix = $config->cookieName . ':';
82
$this->table = $this->savePath;
83
if (empty($this->table)) {
84
throw SessionException::forMissingDatabaseTable();
87
$this->db = Database::connect($this->DBGroup);
88
$this->platform = $this->db->getPlatform();
92
* Re-initialize existing session, or creates a new one.
94
* @param string $path The path where to store/retrieve the session
95
* @param string $name The session name
97
public function open($path, $name): bool
99
if (empty($this->db->connID)) {
100
$this->db->initialize();
107
* Reads the session data from the session storage, and returns the results.
109
* @param string $id The session ID
111
* @return false|string Returns an encoded string of the read data.
112
* If nothing was read, it must return false.
114
#[ReturnTypeWillChange]
115
public function read($id)
117
if ($this->lockSession($id) === false) {
118
$this->fingerprint = md5('');
123
if (! isset($this->sessionID)) {
124
$this->sessionID = $id;
127
$builder = $this->db->table($this->table)->where('id', $this->idPrefix . $id);
129
if ($this->matchIP) {
130
$builder = $builder->where('ip_address', $this->ipAddress);
133
$this->setSelect($builder);
135
$result = $builder->get()->getRow();
137
if ($result === null) {
138
// PHP7 will reuse the same SessionHandler object after
139
// ID regeneration, so we need to explicitly set this to
140
// FALSE instead of relying on the default ...
141
$this->rowExists = false;
142
$this->fingerprint = md5('');
147
$result = is_bool($result) ? '' : $this->decodeData($result->data);
149
$this->fingerprint = md5($result);
150
$this->rowExists = true;
158
protected function setSelect(BaseBuilder $builder)
160
$builder->select('data');
164
* Decodes column data
166
* @param string $data
168
* @return false|string
170
protected function decodeData($data)
176
* Writes the session data to the session storage.
178
* @param string $id The session ID
179
* @param string $data The encoded session data
181
public function write($id, $data): bool
183
if ($this->lock === false) {
184
return $this->fail();
187
if ($this->sessionID !== $id) {
188
$this->rowExists = false;
189
$this->sessionID = $id;
192
if ($this->rowExists === false) {
194
'id' => $this->idPrefix . $id,
195
'ip_address' => $this->ipAddress,
196
'data' => $this->prepareData($data),
199
if (! $this->db->table($this->table)->set('timestamp', 'now()', false)->insert($insertData)) {
200
return $this->fail();
203
$this->fingerprint = md5($data);
204
$this->rowExists = true;
209
$builder = $this->db->table($this->table)->where('id', $this->idPrefix . $id);
211
if ($this->matchIP) {
212
$builder = $builder->where('ip_address', $this->ipAddress);
217
if ($this->fingerprint !== md5($data)) {
218
$updateData['data'] = $this->prepareData($data);
221
if (! $builder->set('timestamp', 'now()', false)->update($updateData)) {
222
return $this->fail();
225
$this->fingerprint = md5($data);
231
* Prepare data to insert/update
233
protected function prepareData(string $data): string
239
* Closes the current session.
241
public function close(): bool
243
return ($this->lock && ! $this->releaseLock()) ? $this->fail() : true;
249
* @param string $id The session ID being destroyed
251
public function destroy($id): bool
254
$builder = $this->db->table($this->table)->where('id', $this->idPrefix . $id);
256
if ($this->matchIP) {
257
$builder = $builder->where('ip_address', $this->ipAddress);
260
if (! $builder->delete()) {
261
return $this->fail();
265
if ($this->close()) {
266
$this->destroyCookie();
271
return $this->fail();
275
* Cleans up expired sessions.
277
* @param int $max_lifetime Sessions that have not updated
278
* for the last max_lifetime seconds will be removed.
280
* @return false|int Returns the number of deleted sessions on success, or false on failure.
282
#[ReturnTypeWillChange]
283
public function gc($max_lifetime)
286
$interval = implode($separator, ['', "{$max_lifetime} second", '']);
288
return $this->db->table($this->table)->where(
290
"now() - INTERVAL {$interval}",
292
)->delete() ? 1 : $this->fail();
296
* Releases the lock, if any.
298
protected function releaseLock(): bool
304
// Unsupported DB? Let the parent handle the simple version.
305
return parent::releaseLock();