-
-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathSaveHandler.php
More file actions
318 lines (295 loc) · 8.49 KB
/
Copy pathSaveHandler.php
File metadata and controls
318 lines (295 loc) · 8.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
<?php declare(strict_types=1);
/*
* This file is part of Aplus Framework Session Library.
*
* (c) Natan Felles <natanfelles@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Framework\Session;
use Framework\Log\Logger;
use Framework\Log\LogLevel;
use SensitiveParameter;
/**
* Class SaveHandler.
*
* @see https://www.php.net/manual/en/class.sessionhandler.php
* @see https://gist.github.com/mindplay-dk/623bdd50c1b4c0553cd3
* @see https://www.cloudways.com/blog/setup-redis-as-session-handler-php/#sessionlifecycle
*
* @package session
*/
abstract class SaveHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface
{
/**
* The configurations used by the save handler.
*
* @var array<string,mixed>
*/
protected array $config;
/**
* The current data fingerprint.
*
* @var string
*/
protected string $fingerprint;
/**
* The lock id or false if is not locked.
*
* @var false|string
*/
protected false | string $lockId = false;
/**
* Tells if the session exists (if was read).
*
* @var bool
*/
protected bool $sessionExists = false;
/**
* The current session ID.
*
* @var string|null
*/
protected ?string $sessionId;
/**
* The Logger instance or null if it was not set.
*
* @var Logger|null
*/
protected ?Logger $logger;
/**
* SessionSaveHandler constructor.
*
* @param array<string,mixed> $config
* @param Logger|null $logger
*/
public function __construct(
#[SensitiveParameter]
array $config = [],
?Logger $logger = null
) {
$this->prepareConfig($config);
$this->logger = $logger;
}
/**
* Prepare configurations to be used by the save handler.
*
* @param array<string,mixed> $config Custom configs
*
* @codeCoverageIgnore
*/
protected function prepareConfig(#[SensitiveParameter] array $config) : void
{
$this->config = $config;
}
/**
* @return array<string,mixed>
*/
public function getConfig() : array
{
return $this->config;
}
/**
* Log a message if the Logger is set.
*
* @param string $message The message to log
* @param LogLevel $level The log level
*/
protected function log(string $message, LogLevel $level = LogLevel::ERROR) : void
{
$this->logger?->log($level, $message);
}
/**
* Set the data fingerprint.
*
* @param string $data The data to set the new fingerprint
*/
protected function setFingerprint(string $data) : void
{
$this->fingerprint = $this->makeFingerprint($data);
}
/**
* Make the fingerprint value.
*
* @param string $data The data to get the fingerprint
*
* @return string The fingerprint hash
*/
private function makeFingerprint(string $data) : string
{
return \hash('xxh3', $data);
}
/**
* Tells if the data has the same current fingerprint.
*
* @param string $data The data to compare
*
* @return bool True if the fingerprints are the same, otherwise false
*/
protected function hasSameFingerprint(string $data) : bool
{
return $this->fingerprint === $this->makeFingerprint($data);
}
/**
* Get the maxlifetime (TTL) used by cache handlers or locking.
*
* NOTE: It will use the `maxlifetime` config or the ini value of
* `session.gc_maxlifetime` as fallback.
*
* @return int The maximum lifetime of a session in seconds
*/
protected function getMaxlifetime() : int
{
return (int) ($this->config['maxlifetime'] ?? \ini_get('session.gc_maxlifetime'));
}
/**
* Get the remote IP address.
*
* @return string
*/
protected function getIP() : string
{
return $_SERVER['REMOTE_ADDR'] ?? '';
}
/**
* Get the HTTP User-Agent.
*
* @return string
*/
protected function getUA() : string
{
return $_SERVER['HTTP_USER_AGENT'] ?? '';
}
protected function getKeySuffix() : string
{
$suffix = '';
if ($this->config['match_ip']) {
$suffix .= ':' . $this->getIP();
}
if ($this->config['match_ua']) {
$suffix .= ':' . $this->getUA();
}
if ($suffix) {
$suffix = \hash('xxh3', $suffix);
}
return $suffix;
}
/**
* Validate session id.
*
* @param string $id The session id
*
* @see https://www.php.net/manual/en/sessionupdatetimestamphandlerinterface.validateid.php
*
* @return bool Returns TRUE if the id is valid, otherwise FALSE
*/
public function validateId($id) : bool
{
$bits = \ini_get('session.sid_bits_per_character') ?: 5;
$length = \ini_get('session.sid_length') ?: 40;
$bitsRegex = [
4 => '[0-9a-f]',
5 => '[0-9a-v]',
6 => '[0-9a-zA-Z,-]',
];
return isset($bitsRegex[$bits])
&& \preg_match('#\A' . $bitsRegex[$bits] . '{' . $length . '}\z#', $id);
}
/**
* Initialize the session.
*
* @param string $path The path where to store/retrieve the session
* @param string $name The session name
*
* @see https://www.php.net/manual/en/sessionhandlerinterface.open.php
*
* @return bool Returns TRUE on success, FALSE on failure
*/
abstract public function open($path, $name) : bool;
/**
* Read session data.
*
* @param string $id The session id to read data for
*
* @see https://www.php.net/manual/en/sessionhandlerinterface.read.php
*
* @return string Returns an encoded string of the read data.
* If nothing was read, it returns an empty string
*/
abstract public function read($id) : string;
/**
* Write session data.
*
* @param string $id The session id
* @param string $data The encoded session data. This data is the result
* of the PHP internally encoding the $_SESSION superglobal to a serialized
* string and passing it as this parameter.
*
* NOTE: Sessions can use an alternative serialization method
*
* @see https://www.php.net/manual/en/sessionhandlerinterface.write.php
*
* @return bool Returns TRUE on success, FALSE on failure
*/
abstract public function write($id, $data) : bool;
/**
* Update the timestamp of a session.
*
* @param string $id The session id
* @param string $data The encoded session data. This data is the result
* of the PHP internally encoding the $_SESSION superglobal to a serialized
* string and passing it as this parameter.
*
* NOTE: Sessions can use an alternative serialization method
*
* @see https://www.php.net/manual/en/sessionupdatetimestamphandlerinterface.updatetimestamp.php
*
* @return bool Returns TRUE on success, FALSE on failure
*/
abstract public function updateTimestamp($id, $data) : bool;
/**
* Close the session.
*
* @see https://www.php.net/manual/en/sessionhandlerinterface.close.php
*
* @return bool Returns TRUE on success, FALSE on failure
*/
abstract public function close() : bool;
/**
* Destroy a session.
*
* @param string $id The session ID being destroyed
*
* @see https://www.php.net/manual/en/sessionhandlerinterface.destroy.php
*
* @return bool Returns TRUE on success, FALSE on failure
*/
abstract public function destroy($id) : bool;
/**
* Cleanup old sessions.
*
* @param int $max_lifetime Sessions that have not updated for
* the last $maxLifetime seconds will be removed
*
* @see https://www.php.net/manual/en/sessionhandlerinterface.gc.php
*
* @return false|int Returns the number of deleted session data for success,
* false for failure
*/
abstract public function gc($max_lifetime) : false | int;
/**
* Acquire a lock for a session id.
*
* @param string $id The session id
*
* @return bool Returns TRUE on success, FALSE on failure
*/
abstract protected function lock(string $id) : bool;
/**
* Unlock the current session lock id.
*
* @return bool Returns TRUE on success, FALSE on failure
*/
abstract protected function unlock() : bool;
}