Issues (1474)

framework/Web/THttpSession.php (15 issues)

1
<?php
2
3
/**
4
 * THttpSession class
5
 *
6
 * @author Qiang Xue <[email protected]>
7
 * @link https://github.com/pradosoft/prado
8
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
9
 */
10
11
namespace Prado\Web;
12
13
use Prado\Exceptions\TInvalidDataValueException;
14
use Prado\Exceptions\TInvalidOperationException;
15
use Prado\TPropertyValue;
16
use Prado\Prado;
17
18
/**
19
 * THttpSession class
20
 *
21
 * THttpSession provides session-level data management and the related configurations.
22
 * To start the session, call {@see open}; to complete and send out session data, call {@see close};
23
 * to destroy the session, call {@see destroy}. If AutoStart is true, then the session
24
 * will be started once the session module is loaded and initialized.
25
 *
26
 * To access data stored in session, use THttpSession like an associative array. For example,
27
 * ```php
28
 *   $session=new THttpSession;
29
 *   $session->open();
30
 *   $value1=$session['name1'];  // get session variable 'name1'
31
 *   $value2=$session['name2'];  // get session variable 'name2'
32
 *   foreach($session as $name=>$value) // traverse all session variables
33
 *   $session['name3']=$value3;  // set session variable 'name3'
34
 * ```
35
 *
36
 * The following configurations are available for session:
37
 * {@see setAutoStart AutoStart}, {@see setCookieMode CookieMode},
38
 * {@see setSavePath SavePath},
39
 * {@see setUseCustomStorage UseCustomStorage}, {@see setGCProbability GCProbability},
40
 * {@see setTimeout Timeout}.
41
 * See the corresponding setter and getter documentation for more information.
42
 * Note, these properties must be set before the session is started.
43
 *
44
 * THttpSession can be inherited with customized session storage method.
45
 * Override {@see _open}, {@see _close}, {@see _read}, {@see _write}, {@see _destroy} and {@see _gc}
46
 * and set {@see setUseCustomStorage UseCustomStorage} to true.
47
 * Then, the session data will be stored using the above methods.
48
 *
49
 * By default, THttpSession is registered with {@see \Prado\TApplication} as the
50
 * request module. It can be accessed via {@see \Prado\TApplication::getSession()}.
51
 *
52
 * THttpSession may be configured in application configuration file as follows,
53
 * ```xml
54
 * <module id="session" class="THttpSession" SessionName="SSID" SavePath="/tmp"
55
 *         CookieMode="Allow" UseCustomStorage="false" AutoStart="true" GCProbability="1"
56
 *         UseTransparentSessionID="true" TimeOut="3600" />
57
 * ```
58
 * where {@see getSessionName SessionName}, {@see getSavePath SavePath},
59
 * {@see getCookieMode CookieMode}, {@see getUseCustomStorage
60
 * UseCustomStorage}, {@see getAutoStart AutoStart}, {@see getGCProbability
61
 * GCProbability}, {@see getUseTransparentSessionID UseTransparentSessionID}
62
 * and {@see getTimeout TimeOut} are configurable properties of THttpSession.
63
 *
64
 * To avoid the possibility of identity theft through some variants of XSS attacks,
65
 * THttpSessionshould always be configured to enforce HttpOnly setting on session cookie.
66
 * The HttpOnly setting is disabled by default. To enable it, configure the THttpSession
67
 * module as follows,
68
 * ```xml
69
 * <module id="session" class="THttpSession" Cookie.HttpOnly="true" >
70
 * ```
71
 *
72
 * @author Qiang Xue <[email protected]>
73
 * @since 3.0
74
 */
75
class THttpSession extends \Prado\TApplicationComponent implements \IteratorAggregate, \ArrayAccess, \Countable, \Prado\IModule
76
{
77
	/**
78
	 * @var bool whether this module has been initialized
79
	 */
80
	private $_initialized = false;
81
	/**
82
	 * @var bool whether the session has started
83
	 */
84
	private $_started = false;
85
	/**
86
	 * @var bool whether the session should be started when the module is initialized
87
	 */
88
	private $_autoStart = false;
89
	/**
90
	 * @var THttpCookie cookie to be used to store session ID and other data
91
	 */
92
	private $_cookie;
93
	/**
94
	 * @var string module id
95
	 */
96
	private $_id;
97
	/**
98
	 * @var bool
99
	 */
100
	private $_customStorage = false;
101
102
	/**
103
	 * @return string id of this module
104
	 */
105
	public function getID()
106
	{
107
		return $this->_id;
108
	}
109
110
	/**
111
	 * @param string $value id of this module
112
	 */
113
	public function setID($value)
114
	{
115
		$this->_id = $value;
116
	}
117
118
	/**
119
	 * Initializes the module.
120
	 * This method is required by IModule.
121
	 * If AutoStart is true, the session will be started.
122
	 * @param \Prado\Xml\TXmlElement $config module configuration
123
	 */
124
	public function init($config)
125 10
	{
126
		if ($this->_autoStart) {
127 10
			$this->open();
128
		}
129
		$this->_initialized = true;
130 10
		$this->getApplication()->setSession($this);
131 10
		register_shutdown_function([$this, "close"]);
132 10
	}
133 10
134
	/**
135
	 * Starts the session if it has not started yet.
136
	 */
137
	public function open()
138
	{
139
		if (!$this->_started) {
140
			if ($this->_customStorage) {
141
				$handler = new THttpSessionHandler($this);
142
				session_set_save_handler($handler);
143
			}
144
			if ($this->_cookie !== null) {
145
				session_set_cookie_params($this->_cookie->getPhpOptions('lifetime'));
146
			}
147
			if (ini_get('session.auto_start') !== '1') {
148
				session_start();
149
			}
150
			$this->_started = true;
151
		}
152
	}
153
154
	/**
155
	 * Ends the current session and store session data.
156
	 */
157
	public function close()
158
	{
159
		if ($this->_started) {
160
			session_write_close();
161
			$this->_started = false;
162
		}
163
	}
164
165
	/**
166
	 * Destroys all data registered to a session.
167
	 */
168 1
	public function destroy()
169
	{
170 1
		if ($this->_started) {
171
			session_destroy();
172
			$this->_started = false;
173
		}
174 1
	}
175
176
	/**
177
	 * Update the current session id with a newly generated one
178
	 *
179
	 * @param bool $deleteOld Whether to delete the old associated session or not.
180
	 * @return string old session id
181
	 * @link http://php.net/manual/en/function.session-regenerate-id.php
182
	 */
183
	public function regenerate($deleteOld = false)
184
	{
185
		$old = $this->getSessionID();
186
		session_regenerate_id($deleteOld);
187
		return $old;
188
	}
189
190
	/**
191
	 * @return bool whether the session has started
192
	 */
193 1
	public function getIsStarted()
194
	{
195 1
		return $this->_started;
196
	}
197
198
	/**
199
	 * @return string the current session ID
200
	 */
201
	public function getSessionID()
202
	{
203
		return session_id();
204
	}
205
206
	/**
207
	 * @param string $value the session ID for the current session
208
	 * @throws TInvalidOperationException if session is started already
209
	 */
210
	public function setSessionID($value)
211
	{
212
		if ($this->_started) {
213
			throw new TInvalidOperationException('httpsession_sessionid_unchangeable');
214
		} else {
215
			session_id($value);
216
		}
217
	}
218
219
	/**
220
	 * @return string the current session name
221
	 */
222
	public function getSessionName()
223
	{
224
		return session_name();
225
	}
226
227
	/**
228
	 * @param string $value the session name for the current session, must be an alphanumeric string, defaults to PHPSESSID
229
	 * @throws TInvalidOperationException if session is started already
230
	 */
231
	public function setSessionName($value)
232
	{
233
		if ($this->_started) {
234
			throw new TInvalidOperationException('httpsession_sessionname_unchangeable');
235
		} elseif (ctype_alnum($value)) {
236
			session_name($value);
237
		} else {
238
			throw new TInvalidDataValueException('httpsession_sessionname_invalid', $value);
239
		}
240
	}
241
242
	/**
243
	 * @return string the current session save path, defaults to '/tmp'.
244
	 */
245
	public function getSavePath()
246
	{
247
		return session_save_path();
248
	}
249
250
	/**
251
	 * @param string $value the current session save path
252
	 * @throws TInvalidOperationException if session is started already
253
	 */
254
	public function setSavePath($value)
255
	{
256
		if ($this->_started) {
257
			throw new TInvalidOperationException('httpsession_savepath_unchangeable');
258
		} elseif (is_dir($value)) {
259
			session_save_path(realpath($value));
260
		} elseif (null !== ($ns = Prado::getPathOfNamespace($value)) && is_dir($ns)) {
261
			session_save_path(realpath($ns));
262
		} else {
263
			throw new TInvalidDataValueException('httpsession_savepath_invalid', $value);
264
		}
265
	}
266
267
	/**
268
	 * @return bool whether to use user-specified handlers to store session data. Defaults to false.
269
	 */
270
	public function getUseCustomStorage()
271
	{
272
		return $this->_customStorage;
273
	}
274
275
	/**
276
	 * @param bool $value whether to use user-specified handlers to store session data.
277
	 * If true, make sure the methods {@see _open}, {@see _close}, {@see _read},
278
	 * {@see _write}, {@see _destroy}, and {@see _gc} are overridden in child
279
	 * class, because they will be used as the callback handlers.
280
	 */
281 10
	public function setUseCustomStorage($value)
282
	{
283 10
		$this->_customStorage = TPropertyValue::ensureBoolean($value);
284 10
	}
285
286
	/**
287
	 * @return THttpCookie cookie that will be used to store session ID
288
	 */
289
	public function getCookie()
290
	{
291
		if ($this->_cookie === null) {
292
			$this->_cookie = new THttpCookie($this->getSessionName(), $this->getSessionID());
293
		}
294
		return $this->_cookie;
295
	}
296
297
	/**
298
	 * @return THttpSessionCookieMode how to use cookie to store session ID. Defaults to THttpSessionCookieMode::Allow.
299
	 */
300 3
	public function getCookieMode()
301
	{
302 3
		if (ini_get('session.use_cookies') === '0') {
303 1
			return THttpSessionCookieMode::None;
0 ignored issues
show
Bug Best Practice introduced by
The expression return Prado\Web\THttpSessionCookieMode::None returns the type string which is incompatible with the documented return type Prado\Web\THttpSessionCookieMode.
Loading history...
Deprecated Code introduced by
The constant Prado\Web\THttpSessionCookieMode::None has been deprecated: 4.3.1 Since PHP 8.4 disabling session.use_only_cookies INI setting is deprecated; use THttpSessionCookieMode::Only instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

303
			return /** @scrutinizer ignore-deprecated */ THttpSessionCookieMode::None;

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
304 2
		} elseif (ini_get('session.use_only_cookies') === '0') {
305 1
			return THttpSessionCookieMode::Allow;
0 ignored issues
show
Bug Best Practice introduced by
The expression return Prado\Web\THttpSessionCookieMode::Allow returns the type string which is incompatible with the documented return type Prado\Web\THttpSessionCookieMode.
Loading history...
Deprecated Code introduced by
The constant Prado\Web\THttpSessionCookieMode::Allow has been deprecated: 4.3.1 Since PHP 8.4 disabling session.use_only_cookies INI setting is deprecated; use THttpSessionCookieMode::Only instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

305
			return /** @scrutinizer ignore-deprecated */ THttpSessionCookieMode::Allow;

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
306
		} else {
307 1
			return THttpSessionCookieMode::Only;
0 ignored issues
show
Bug Best Practice introduced by
The expression return Prado\Web\THttpSessionCookieMode::Only returns the type string which is incompatible with the documented return type Prado\Web\THttpSessionCookieMode.
Loading history...
308
		}
309
	}
310
311
	/**
312
	 * @param THttpSessionCookieMode $value how to use cookie to store session ID
313
	 * @throws TInvalidOperationException if session is started already
314
	 * @deprecated 4.3.1 Since PHP 8.4 disabling session.use_only_cookies
315 3
	 * INI setting is deprecated; Only THttpSessionCookieMode::Only is supported.
316
	 */
317 3
	public function setCookieMode($value)
318
	{
319
		if ($this->_started) {
320 3
			throw new TInvalidOperationException('httpsession_cookiemode_unchangeable');
321 3
		} else {
322 1
			$value = TPropertyValue::ensureEnum($value, THttpSessionCookieMode::class);
323 1
			if ($value === THttpSessionCookieMode::None) {
0 ignored issues
show
Deprecated Code introduced by
The constant Prado\Web\THttpSessionCookieMode::None has been deprecated: 4.3.1 Since PHP 8.4 disabling session.use_only_cookies INI setting is deprecated; use THttpSessionCookieMode::Only instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

323
			if ($value === /** @scrutinizer ignore-deprecated */ THttpSessionCookieMode::None) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
324 2
				ini_set('session.use_cookies', '0');
325 1
				ini_set('session.use_only_cookies', '0');
326 1
			} elseif ($value === THttpSessionCookieMode::Allow) {
0 ignored issues
show
Deprecated Code introduced by
The constant Prado\Web\THttpSessionCookieMode::Allow has been deprecated: 4.3.1 Since PHP 8.4 disabling session.use_only_cookies INI setting is deprecated; use THttpSessionCookieMode::Only instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

326
			} elseif ($value === /** @scrutinizer ignore-deprecated */ THttpSessionCookieMode::Allow) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
327
				ini_set('session.use_cookies', '1');
328 1
				ini_set('session.use_only_cookies', '0');
329 1
			} else {
330 1
				ini_set('session.use_cookies', '1');
331
				ini_set('session.use_only_cookies', '1');
332
				ini_set('session.use_trans_sid', 0);
333 3
			}
334
		}
335
	}
336
337
	/**
338
	 * @return bool whether the session should be automatically started when the session module is initialized, defaults to false.
339
	 */
340
	public function getAutoStart()
341
	{
342
		return $this->_autoStart;
343
	}
344
345
	/**
346
	 * @param bool $value whether the session should be automatically started when the session module is initialized, defaults to false.
347
	 * @throws TInvalidOperationException if session is started already
348
	 */
349
	public function setAutoStart($value)
350
	{
351
		if ($this->_initialized) {
352
			throw new TInvalidOperationException('httpsession_autostart_unchangeable');
353
		} else {
354
			$this->_autoStart = TPropertyValue::ensureBoolean($value);
355
		}
356
	}
357
358
	/**
359
	 * @return int the probability (percentage) that the gc (garbage collection) process is started on every session initialization, defaults to 1 meaning 1% chance.
360
	 */
361
	public function getGCProbability()
362
	{
363
		return TPropertyValue::ensureInteger(ini_get('session.gc_probability'));
364
	}
365
366
	/**
367
	 * @param int $value the probability (percentage) that the gc (garbage collection) process is started on every session initialization.
368
	 * @throws TInvalidOperationException if session is started already
369
	 * @throws TInvalidDataValueException if the value is beyond [0,100].
370
	 */
371
	public function setGCProbability($value)
372
	{
373
		if ($this->_started) {
374
			throw new TInvalidOperationException('httpsession_gcprobability_unchangeable');
375
		} else {
376
			$value = TPropertyValue::ensureInteger($value);
377
			if ($value >= 0 && $value <= 100) {
378
				ini_set('session.gc_probability', $value);
379
				ini_set('session.gc_divisor', '100');
380
			} else {
381
				throw new TInvalidDataValueException('httpsession_gcprobability_invalid', $value);
382
			}
383
		}
384
	}
385
386
	/**
387
	 * @return bool whether transparent sid support is enabled or not, defaults to false.
388
	 */
389
	public function getUseTransparentSessionID()
390
	{
391
		return ini_get('session.use_trans_sid') === '1';
392
	}
393
394
	/**
395
	 * Ensure that {@see setCookieMode CookieMode} is not set to "None" before enabling
396
	 * the use of transparent session ids. Refer to the main documentation of the class
397
	 * THttpSession class for a configuration example.
398
	 *
399
	 * @param bool $value whether transparent sid support is enabled or not.
400
	 */
401
	public function setUseTransparentSessionID($value)
402
	{
403
		if ($this->_started) {
404
			throw new TInvalidOperationException('httpsession_transid_unchangeable');
405
		} else {
406
			$value = TPropertyValue::ensureBoolean($value);
407
			if ($value && $this->getCookieMode() == THttpSessionCookieMode::Only) {
0 ignored issues
show
The condition $this->getCookieMode() =...SessionCookieMode::Only is always false.
Loading history...
408
				throw new TInvalidOperationException('httpsession_transid_cookieonly');
409
			}
410
			ini_set('session.use_trans_sid', $value ? '1' : '0');
411
		}
412
	}
413
414
	/**
415
	 * @return int the number of seconds after which data will be seen as 'garbage' and cleaned up, defaults to 1440 seconds.
416
	 */
417
	public function getTimeout()
418
	{
419
		return TPropertyValue::ensureInteger(ini_get('session.gc_maxlifetime'));
420
	}
421
422
	/**
423
	 * @param int $value the number of seconds after which data will be seen as 'garbage' and cleaned up
424
	 * @throws TInvalidOperationException if session is started already
425
	 */
426
	public function setTimeout($value)
427
	{
428
		if ($this->_started) {
429
			throw new TInvalidOperationException('httpsession_maxlifetime_unchangeable');
430
		} else {
431
			ini_set('session.gc_maxlifetime', $value);
432
		}
433
	}
434
435
	/**
436
	 * Session open handler.
437
	 * This method should be overridden if {@see setUseCustomStorage UseCustomStorage} is set true.
438
	 * @param string $savePath session save path
439
	 * @param string $sessionName session name
440
	 * @return bool whether session is opened successfully
441
	 */
442
	public function _open($savePath, $sessionName)
0 ignored issues
show
The parameter $savePath is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

442
	public function _open(/** @scrutinizer ignore-unused */ $savePath, $sessionName)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $sessionName is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

442
	public function _open($savePath, /** @scrutinizer ignore-unused */ $sessionName)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
443
	{
444
		return true;
445
	}
446
447
	/**
448
	 * Session close handler.
449
	 * This method should be overridden if {@see setUseCustomStorage UseCustomStorage} is set true.
450
	 * @return bool whether session is closed successfully
451
	 */
452
	public function _close()
453
	{
454
		return true;
455
	}
456
457
	/**
458
	 * Session read handler.
459
	 * This method should be overridden if {@see setUseCustomStorage UseCustomStorage} is set true.
460
	 * @param string $id session ID
461
	 * @return string the session data
462
	 */
463
	public function _read($id)
0 ignored issues
show
The parameter $id is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

463
	public function _read(/** @scrutinizer ignore-unused */ $id)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
464
	{
465
		return '';
466
	}
467
468
	/**
469
	 * Session write handler.
470
	 * This method should be overridden if {@see setUseCustomStorage UseCustomStorage} is set true.
471
	 * @param string $id session ID
472
	 * @param string $data session data
473
	 * @return bool whether session write is successful
474
	 */
475
	public function _write($id, $data)
0 ignored issues
show
The parameter $data is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

475
	public function _write($id, /** @scrutinizer ignore-unused */ $data)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $id is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

475
	public function _write(/** @scrutinizer ignore-unused */ $id, $data)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
476
	{
477
		return true;
478
	}
479
480
	/**
481
	 * Session destroy handler.
482
	 * This method should be overridden if {@see setUseCustomStorage UseCustomStorage} is set true.
483
	 * @param string $id session ID
484
	 * @return bool whether session is destroyed successfully
485
	 */
486
	public function _destroy($id)
0 ignored issues
show
The parameter $id is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

486
	public function _destroy(/** @scrutinizer ignore-unused */ $id)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
487
	{
488
		return true;
489
	}
490
491
	/**
492
	 * Session GC (garbage collection) handler.
493
	 * This method should be overridden if {@see setUseCustomStorage UseCustomStorage} is set true.
494
	 * @param int $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up.
495
	 * @return bool whether session is GCed successfully
496
	 */
497
	public function _gc($maxLifetime)
0 ignored issues
show
The parameter $maxLifetime is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

497
	public function _gc(/** @scrutinizer ignore-unused */ $maxLifetime)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
498
	{
499
		return true;
500
	}
501
502
	//------ The following methods enable THttpSession to be TMap-like -----
503
504
	/**
505
	 * Returns an iterator for traversing the session variables.
506
	 * This method is required by the interface \IteratorAggregate.
507
	 * @return TSessionIterator an iterator for traversing the session variables.
508
	 */
509
	#[\ReturnTypeWillChange]
510
	public function getIterator()
511
	{
512
		return new TSessionIterator();
513
	}
514
515
	/**
516
	 * @return int the number of session variables
517
	 */
518
	public function getCount()
519
	{
520
		return count($_SESSION);
521
	}
522
523
	/**
524
	 * Returns the number of items in the session.
525
	 * This method is required by \Countable interface.
526
	 * @return int number of items in the session.
527
	 */
528
	public function count(): int
529
	{
530
		return $this->getCount();
531
	}
532
533
	/**
534
	 * @return array the list of session variable names
535
	 */
536
	public function getKeys()
537
	{
538
		return array_keys($_SESSION);
539
	}
540
541
	/**
542
	 * Returns the session variable value with the session variable name.
543
	 * This method is exactly the same as {@see offsetGet}.
544
	 * @param mixed $key the session variable name
545
	 * @return mixed the session variable value, null if no such variable exists
546
	 */
547
	public function itemAt($key)
548
	{
549
		return $_SESSION[$key] ?? null;
550
	}
551
552
	/**
553
	 * Adds a session variable.
554
	 * Note, if the specified name already exists, the old value will be removed first.
555 1
	 * @param mixed $key session variable name
556
	 * @param mixed $value session variable value
557 1
	 */
558 1
	public function add($key, $value)
559
	{
560
		$_SESSION[$key] = $value;
561
	}
562
563
	/**
564
	 * Removes a session variable.
565 1
	 * @param mixed $key the name of the session variable to be removed
566
	 * @return mixed the removed value, null if no such session variable.
567 1
	 */
568 1
	public function remove($key)
569 1
	{
570 1
		if (isset($_SESSION[$key])) {
571
			$value = $_SESSION[$key];
572
			unset($_SESSION[$key]);
573
			return $value;
574
		} else {
575
			return null;
576
		}
577
	}
578
579
	/**
580
	 * Removes all session variables
581
	 */
582
	public function clear()
583
	{
584
		foreach (array_keys($_SESSION) as $key) {
585
			unset($_SESSION[$key]);
586
		}
587
	}
588
589
	/**
590
	 * @param mixed $key session variable name
591
	 * @return bool whether there is the named session variable
592
	 */
593
	public function contains($key)
594
	{
595
		return isset($_SESSION[$key]);
596
	}
597
598
	/**
599
	 * @return array the list of all session variables in array
600
	 */
601
	public function toArray()
602
	{
603
		return $_SESSION;
604
	}
605
606
	/**
607
	 * This method is required by the interface \ArrayAccess.
608
	 * @param mixed $offset the offset to check on
609
	 * @return bool
610
	 */
611
	public function offsetExists($offset): bool
612
	{
613
		return isset($_SESSION[$offset]);
614
	}
615
616
	/**
617
	 * This method is required by the interface \ArrayAccess.
618 4
	 * @param int $offset the offset to retrieve element.
619
	 * @return mixed the element at the offset, null if no element is found at the offset
620 4
	 */
621
	#[\ReturnTypeWillChange]
622
	public function offsetGet($offset)
623
	{
624
		return $_SESSION[$offset] ?? null;
625
	}
626
627
	/**
628 2
	 * This method is required by the interface \ArrayAccess.
629
	 * @param int $offset the offset to set element
630 2
	 * @param mixed $item the element value
631 2
	 */
632
	public function offsetSet($offset, $item): void
633
	{
634
		$_SESSION[$offset] = $item;
635
	}
636
637
	/**
638
	 * This method is required by the interface \ArrayAccess.
639
	 * @param mixed $offset the offset to unset element
640
	 */
641
	public function offsetUnset($offset): void
642
	{
643
		unset($_SESSION[$offset]);
644
	}
645
}
646