GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Session::has()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
ccs 0
cts 3
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @link https://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license https://www.yiiframework.com/license/
6
 */
7
8
namespace yii\web;
9
10
use Yii;
11
use yii\base\Component;
12
use yii\base\InvalidArgumentException;
13
use yii\base\InvalidConfigException;
14
15
/**
16
 * Session provides session data management and the related configurations.
17
 *
18
 * Session is a Web application component that can be accessed via `Yii::$app->session`.
19
 *
20
 * To start the session, call [[open()]]; To complete and send out session data, call [[close()]];
21
 * To destroy the session, call [[destroy()]].
22
 *
23
 * Session can be used like an array to set and get session data. For example,
24
 *
25
 * ```php
26
 * $session = new Session;
27
 * $session->open();
28
 * $value1 = $session['name1'];  // get session variable 'name1'
29
 * $value2 = $session['name2'];  // get session variable 'name2'
30
 * foreach ($session as $name => $value) // traverse all session variables
31
 * $session['name3'] = $value3;  // set session variable 'name3'
32
 * ```
33
 *
34
 * Session can be extended to support customized session storage.
35
 * To do so, override [[useCustomStorage]] so that it returns true, and
36
 * override these methods with the actual logic about using custom storage:
37
 * [[openSession()]], [[closeSession()]], [[readSession()]], [[writeSession()]],
38
 * [[destroySession()]] and [[gcSession()]].
39
 *
40
 * Session also supports a special type of session data, called *flash messages*.
41
 * A flash message is available only in the current request and the next request.
42
 * After that, it will be deleted automatically. Flash messages are particularly
43
 * useful for displaying confirmation messages. To use flash messages, simply
44
 * call methods such as [[setFlash()]], [[getFlash()]].
45
 *
46
 * For more details and usage information on Session, see the [guide article on sessions](guide:runtime-sessions-cookies).
47
 *
48
 * @property-read array $allFlashes Flash messages (key => message or key => [message1, message2]).
49
 * @property-read string $cacheLimiter Current cache limiter.
50
 * @property-read array $cookieParams The session cookie parameters.
51
 * @property-read int $count The number of session variables.
52
 * @property-write string $flash The key identifying the flash message. Note that flash messages and normal
53
 * session variables share the same name space. If you have a normal session variable using the same name, its
54
 * value will be overwritten by this method.
55
 * @property float $gCProbability The probability (percentage) that the GC (garbage collection) process is
56
 * started on every session initialization.
57
 * @property bool $hasSessionId Whether the current request has sent the session ID.
58
 * @property string $id The current session ID.
59
 * @property-read bool $isActive Whether the session has started.
60
 * @property string $name The current session name.
61
 * @property string $savePath The current session save path, defaults to '/tmp'.
62
 * @property int $timeout The number of seconds after which data will be seen as 'garbage' and cleaned up. The
63
 * default value is 1440 seconds (or the value of "session.gc_maxlifetime" set in php.ini).
64
 * @property bool|null $useCookies The value indicating whether cookies should be used to store session IDs.
65
 * @property-read bool $useCustomStorage Whether to use custom storage.
66
 * @property bool $useStrictMode Whether strict mode is enabled or not.
67
 * @property bool $useTransparentSessionID Whether transparent sid support is enabled or not, defaults to
68
 * false.
69
 *
70
 * @author Qiang Xue <[email protected]>
71
 * @since 2.0
72
 */
73
class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Countable
74
{
75
    /**
76
     * @var string|null Holds the original session module (before a custom handler is registered) so that it can be
77
     * restored when a Session component without custom handler is used after one that has.
78
     */
79
    protected static $_originalSessionModule = null;
80
    /**
81
     * Polyfill for ini directive session.use-strict-mode for PHP < 5.5.2.
82
     */
83
    private static $_useStrictModePolyfill = false;
84
    /**
85
     * @var string the name of the session variable that stores the flash message data.
86
     */
87
    public $flashParam = '__flash';
88
    /**
89
     * @var \SessionHandlerInterface|array an object implementing the SessionHandlerInterface or a configuration array. If set, will be used to provide persistency instead of build-in methods.
90
     */
91
    public $handler;
92
93
    /**
94
     * @var string|null Holds the session id in case useStrictMode is enabled and the session id needs to be regenerated
95
     */
96
    protected $_forceRegenerateId = null;
97
98
    /**
99
     * @var array parameter-value pairs to override default session cookie parameters that are used for session_set_cookie_params() function
100
     * Array may have the following possible keys: 'lifetime', 'path', 'domain', 'secure', 'httponly'
101
     * @see https://www.php.net/manual/en/function.session-set-cookie-params.php
102
     */
103
    private $_cookieParams = ['httponly' => true];
104
    /**
105
     * @var array|null is used for saving session between recreations due to session parameters update.
106
     */
107
    private $_frozenSessionData;
108
109
110
    /**
111
     * Initializes the application component.
112
     * This method is required by IApplicationComponent and is invoked by application.
113
     */
114 124
    public function init()
115
    {
116 124
        parent::init();
117 124
        register_shutdown_function([$this, 'close']);
118 124
        if ($this->getIsActive()) {
119 5
            Yii::warning('Session is already started', __METHOD__);
120 5
            $this->updateFlashCounters();
121
        }
122
    }
123
124
    /**
125
     * Returns a value indicating whether to use custom session storage.
126
     * This method should be overridden to return true by child classes that implement custom session storage.
127
     * To implement custom session storage, override these methods: [[openSession()]], [[closeSession()]],
128
     * [[readSession()]], [[writeSession()]], [[destroySession()]] and [[gcSession()]].
129
     * @return bool whether to use custom storage.
130
     */
131 68
    public function getUseCustomStorage()
132
    {
133 68
        return false;
134
    }
135
136
    /**
137
     * Starts the session.
138
     */
139 77
    public function open()
140
    {
141 77
        if ($this->getIsActive()) {
142 77
            return;
143
        }
144
145 76
        $this->registerSessionHandler();
146
147 76
        $this->setCookieParamsInternal();
148
149 76
        YII_DEBUG ? session_start() : @session_start();
150
151 76
        if ($this->getUseStrictMode() && $this->_forceRegenerateId) {
152 7
            $this->regenerateID();
153 7
            $this->_forceRegenerateId = null;
154
        }
155
156 76
        if ($this->getIsActive()) {
157 76
            Yii::info('Session started', __METHOD__);
158 76
            $this->updateFlashCounters();
159
        } else {
160
            $error = error_get_last();
161
            $message = isset($error['message']) ? $error['message'] : 'Failed to start session.';
162
            Yii::error($message, __METHOD__);
163
        }
164
    }
165
166
    /**
167
     * Registers session handler.
168
     * @throws \yii\base\InvalidConfigException
169
     */
170 76
    protected function registerSessionHandler()
171
    {
172 76
        $sessionModuleName = session_module_name();
173 76
        if (static::$_originalSessionModule === null) {
174 1
            static::$_originalSessionModule = $sessionModuleName;
175
        }
176
177 76
        if ($this->handler !== null) {
178
            if (!is_object($this->handler)) {
179
                $this->handler = Yii::createObject($this->handler);
180
            }
181
            if (!$this->handler instanceof \SessionHandlerInterface) {
182
                throw new InvalidConfigException('"' . get_class($this) . '::handler" must implement the SessionHandlerInterface.');
183
            }
184
            YII_DEBUG ? session_set_save_handler($this->handler, false) : @session_set_save_handler($this->handler, false);
185 76
        } elseif ($this->getUseCustomStorage()) {
186 8
            if (YII_DEBUG) {
187 8
                session_set_save_handler(
188 8
                    [$this, 'openSession'],
189 8
                    [$this, 'closeSession'],
190 8
                    [$this, 'readSession'],
191 8
                    [$this, 'writeSession'],
192 8
                    [$this, 'destroySession'],
193 8
                    [$this, 'gcSession']
194 8
                );
195
            } else {
196 8
                @session_set_save_handler(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for session_set_save_handler(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

196
                /** @scrutinizer ignore-unhandled */ @session_set_save_handler(

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
197 8
                    [$this, 'openSession'],
198 8
                    [$this, 'closeSession'],
199 8
                    [$this, 'readSession'],
200 8
                    [$this, 'writeSession'],
201 8
                    [$this, 'destroySession'],
202 8
                    [$this, 'gcSession']
203 8
                );
204
            }
205
        } elseif (
206 68
            $sessionModuleName !== static::$_originalSessionModule
207 68
            && static::$_originalSessionModule !== null
208 68
            && static::$_originalSessionModule !== 'user'
209
        ) {
210 1
            session_module_name(static::$_originalSessionModule);
211
        }
212
    }
213
214
    /**
215
     * Ends the current session and store session data.
216
     */
217 93
    public function close()
218
    {
219 93
        if ($this->getIsActive()) {
220 70
            YII_DEBUG ? session_write_close() : @session_write_close();
221
        }
222
223 93
        $this->_forceRegenerateId = null;
224
    }
225
226
    /**
227
     * Frees all session variables and destroys all data registered to a session.
228
     *
229
     * This method has no effect when session is not [[getIsActive()|active]].
230
     * Make sure to call [[open()]] before calling it.
231
     * @see open()
232
     * @see isActive
233
     */
234 7
    public function destroy()
235
    {
236 7
        if ($this->getIsActive()) {
237 7
            $sessionId = session_id();
238 7
            $this->close();
239 7
            $this->setId($sessionId);
240 7
            $this->open();
241 7
            session_unset();
242 7
            session_destroy();
243 7
            $this->setId($sessionId);
244
        }
245
    }
246
247
    /**
248
     * @return bool whether the session has started
249
     */
250 124
    public function getIsActive()
251
    {
252 124
        return session_status() === PHP_SESSION_ACTIVE;
253
    }
254
255
    private $_hasSessionId;
256
257
    /**
258
     * Returns a value indicating whether the current request has sent the session ID.
259
     * The default implementation will check cookie and $_GET using the session name.
260
     * If you send session ID via other ways, you may need to override this method
261
     * or call [[setHasSessionId()]] to explicitly set whether the session ID is sent.
262
     * @return bool whether the current request has sent the session ID.
263
     */
264 37
    public function getHasSessionId()
265
    {
266 37
        if ($this->_hasSessionId === null) {
267 37
            $name = $this->getName();
268 37
            $request = Yii::$app->getRequest();
269 37
            if (!empty($_COOKIE[$name]) && ini_get('session.use_cookies')) {
270
                $this->_hasSessionId = true;
271 37
            } elseif (!ini_get('session.use_only_cookies') && ini_get('session.use_trans_sid')) {
272
                $this->_hasSessionId = $request->get($name) != '';
0 ignored issues
show
Bug introduced by
The method get() does not exist on yii\console\Request. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

272
                $this->_hasSessionId = $request->/** @scrutinizer ignore-call */ get($name) != '';
Loading history...
273
            } else {
274 37
                $this->_hasSessionId = false;
275
            }
276
        }
277
278 37
        return $this->_hasSessionId;
279
    }
280
281
    /**
282
     * Sets the value indicating whether the current request has sent the session ID.
283
     * This method is provided so that you can override the default way of determining
284
     * whether the session ID is sent.
285
     * @param bool $value whether the current request has sent the session ID.
286
     */
287
    public function setHasSessionId($value)
288
    {
289
        $this->_hasSessionId = $value;
290
    }
291
292
    /**
293
     * Gets the session ID.
294
     * This is a wrapper for [PHP session_id()](https://www.php.net/manual/en/function.session-id.php).
295
     * @return string the current session ID
296
     */
297 21
    public function getId()
298
    {
299 21
        return session_id();
300
    }
301
302
    /**
303
     * Sets the session ID.
304
     * This is a wrapper for [PHP session_id()](https://www.php.net/manual/en/function.session-id.php).
305
     * @param string $value the session ID for the current session
306
     */
307 12
    public function setId($value)
308
    {
309 12
        session_id($value);
310
    }
311
312
    /**
313
     * Updates the current session ID with a newly generated one.
314
     *
315
     * Please refer to <https://www.php.net/session_regenerate_id> for more details.
316
     *
317
     * This method has no effect when session is not [[getIsActive()|active]].
318
     * Make sure to call [[open()]] before calling it.
319
     *
320
     * @param bool $deleteOldSession Whether to delete the old associated session file or not.
321
     * @see open()
322
     * @see isActive
323
     */
324 52
    public function regenerateID($deleteOldSession = false)
325
    {
326 52
        if ($this->getIsActive()) {
327
            // add @ to inhibit possible warning due to race condition
328
            // https://github.com/yiisoft/yii2/pull/1812
329 39
            if (YII_DEBUG && !headers_sent()) {
330 39
                session_regenerate_id($deleteOldSession);
331
            } else {
332
                @session_regenerate_id($deleteOldSession);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for session_regenerate_id(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

332
                /** @scrutinizer ignore-unhandled */ @session_regenerate_id($deleteOldSession);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
333
            }
334
        }
335
    }
336
337
    /**
338
     * Gets the name of the current session.
339
     * This is a wrapper for [PHP session_name()](https://www.php.net/manual/en/function.session-name.php).
340
     * @return string the current session name
341
     */
342 38
    public function getName()
343
    {
344 38
        return session_name();
345
    }
346
347
    /**
348
     * Sets the name for the current session.
349
     * This is a wrapper for [PHP session_name()](https://www.php.net/manual/en/function.session-name.php).
350
     * @param string $value the session name for the current session, must be an alphanumeric string.
351
     * It defaults to "PHPSESSID".
352
     */
353 1
    public function setName($value)
354
    {
355 1
        $this->freeze();
356 1
        session_name($value);
357 1
        $this->unfreeze();
358
    }
359
360
    /**
361
     * Gets the current session save path.
362
     * This is a wrapper for [PHP session_save_path()](https://www.php.net/manual/en/function.session-save-path.php).
363
     * @return string the current session save path, defaults to '/tmp'.
364
     */
365
    public function getSavePath()
366
    {
367
        return session_save_path();
368
    }
369
370
    /**
371
     * Sets the current session save path.
372
     * This is a wrapper for [PHP session_save_path()](https://www.php.net/manual/en/function.session-save-path.php).
373
     * @param string $value the current session save path. This can be either a directory name or a [path alias](guide:concept-aliases).
374
     * @throws InvalidArgumentException if the path is not a valid directory
375
     */
376
    public function setSavePath($value)
377
    {
378
        $path = Yii::getAlias($value);
379
        if (is_dir($path)) {
0 ignored issues
show
Bug introduced by
It seems like $path can also be of type false; however, parameter $filename of is_dir() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

379
        if (is_dir(/** @scrutinizer ignore-type */ $path)) {
Loading history...
380
            session_save_path($path);
0 ignored issues
show
Bug introduced by
It seems like $path can also be of type false; however, parameter $path of session_save_path() does only seem to accept null|string, maybe add an additional type check? ( Ignorable by Annotation )

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

380
            session_save_path(/** @scrutinizer ignore-type */ $path);
Loading history...
381
        } else {
382
            throw new InvalidArgumentException("Session save path is not a valid directory: $value");
383
        }
384
    }
385
386
    /**
387
     * @return array the session cookie parameters.
388
     * @see https://www.php.net/manual/en/function.session-get-cookie-params.php
389
     */
390 76
    public function getCookieParams()
391
    {
392 76
        return array_merge(session_get_cookie_params(), array_change_key_case($this->_cookieParams));
393
    }
394
395
    /**
396
     * Sets the session cookie parameters.
397
     * The cookie parameters passed to this method will be merged with the result
398
     * of `session_get_cookie_params()`.
399
     * @param array $value cookie parameters, valid keys include: `lifetime`, `path`, `domain`, `secure` and `httponly`.
400
     * Starting with Yii 2.0.21 `sameSite` is also supported. It requires PHP version 7.3.0 or higher.
401
     * For security, an exception will be thrown if `sameSite` is set while using an unsupported version of PHP.
402
     * To use this feature across different PHP versions check the version first. E.g.
403
     * ```php
404
     * [
405
     *     'sameSite' => PHP_VERSION_ID >= 70300 ? yii\web\Cookie::SAME_SITE_LAX : null,
406
     * ]
407
     * ```
408
     * See https://owasp.org/www-community/SameSite for more information about `sameSite`.
409
     *
410
     * @throws InvalidArgumentException if the parameters are incomplete.
411
     * @see https://www.php.net/manual/en/function.session-set-cookie-params.php
412
     */
413
    public function setCookieParams(array $value)
414
    {
415
        $this->_cookieParams = $value;
416
    }
417
418
    /**
419
     * Sets the session cookie parameters.
420
     * This method is called by [[open()]] when it is about to open the session.
421
     * @throws InvalidArgumentException if the parameters are incomplete.
422
     * @see https://www.php.net/manual/en/function.session-set-cookie-params.php
423
     */
424 76
    private function setCookieParamsInternal()
425
    {
426 76
        $data = $this->getCookieParams();
427 76
        if (isset($data['lifetime'], $data['path'], $data['domain'], $data['secure'], $data['httponly'])) {
428 76
            if (PHP_VERSION_ID >= 70300) {
429 76
                session_set_cookie_params($data);
430
            } else {
431
                if (!empty($data['samesite'])) {
432
                    $data['path'] .= '; samesite=' . $data['samesite'];
433
                }
434 76
                session_set_cookie_params($data['lifetime'], $data['path'], $data['domain'], $data['secure'], $data['httponly']);
435
            }
436
        } else {
437
            throw new InvalidArgumentException('Please make sure cookieParams contains these elements: lifetime, path, domain, secure and httponly.');
438
        }
439
    }
440
441
    /**
442
     * Returns the value indicating whether cookies should be used to store session IDs.
443
     * @return bool|null the value indicating whether cookies should be used to store session IDs.
444
     * @see setUseCookies()
445
     */
446 1
    public function getUseCookies()
447
    {
448 1
        if (ini_get('session.use_cookies') === '0') {
449 1
            return false;
450 1
        } elseif (ini_get('session.use_only_cookies') === '1') {
451 1
            return true;
452
        }
453
454
        return null;
455
    }
456
457
    /**
458
     * Sets the value indicating whether cookies should be used to store session IDs.
459
     *
460
     * Three states are possible:
461
     *
462
     * - true: cookies and only cookies will be used to store session IDs.
463
     * - false: cookies will not be used to store session IDs.
464
     * - null: if possible, cookies will be used to store session IDs; if not, other mechanisms will be used (e.g. GET parameter)
465
     *
466
     * @param bool|null $value the value indicating whether cookies should be used to store session IDs.
467
     */
468 4
    public function setUseCookies($value)
469
    {
470 4
        $this->freeze();
471 4
        if ($value === false) {
472 1
            ini_set('session.use_cookies', '0');
473 1
            ini_set('session.use_only_cookies', '0');
474 3
        } elseif ($value === true) {
0 ignored issues
show
introduced by
The condition $value === true is always true.
Loading history...
475 3
            ini_set('session.use_cookies', '1');
476 3
            ini_set('session.use_only_cookies', '1');
477
        } else {
478
            ini_set('session.use_cookies', '1');
479
            ini_set('session.use_only_cookies', '0');
480
        }
481 4
        $this->unfreeze();
482
    }
483
484
    /**
485
     * @return float the probability (percentage) that the GC (garbage collection) process is started on every session initialization.
486
     */
487 1
    public function getGCProbability()
488
    {
489 1
        return (float) (ini_get('session.gc_probability') / ini_get('session.gc_divisor') * 100);
490
    }
491
492
    /**
493
     * @param float $value the probability (percentage) that the GC (garbage collection) process is started on every session initialization.
494
     * @throws InvalidArgumentException if the value is not between 0 and 100.
495
     */
496 1
    public function setGCProbability($value)
497
    {
498 1
        $this->freeze();
499 1
        if ($value >= 0 && $value <= 100) {
500
            // percent * 21474837 / 2147483647 ≈ percent * 0.01
501 1
            ini_set('session.gc_probability', floor($value * 21474836.47));
502 1
            ini_set('session.gc_divisor', 2147483647);
503
        } else {
504
            throw new InvalidArgumentException('GCProbability must be a value between 0 and 100.');
505
        }
506 1
        $this->unfreeze();
507
    }
508
509
    /**
510
     * @return bool whether transparent sid support is enabled or not, defaults to false.
511
     */
512 1
    public function getUseTransparentSessionID()
513
    {
514 1
        return ini_get('session.use_trans_sid') == 1;
515
    }
516
517
    /**
518
     * @param bool $value whether transparent sid support is enabled or not.
519
     */
520 1
    public function setUseTransparentSessionID($value)
521
    {
522 1
        $this->freeze();
523 1
        ini_set('session.use_trans_sid', $value ? '1' : '0');
524 1
        $this->unfreeze();
525
    }
526
527
    /**
528
     * @return int the number of seconds after which data will be seen as 'garbage' and cleaned up.
529
     * The default value is 1440 seconds (or the value of "session.gc_maxlifetime" set in php.ini).
530
     */
531 28
    public function getTimeout()
532
    {
533 28
        return (int) ini_get('session.gc_maxlifetime');
534
    }
535
536
    /**
537
     * @param int $value the number of seconds after which data will be seen as 'garbage' and cleaned up
538
     */
539 4
    public function setTimeout($value)
540
    {
541 4
        $this->freeze();
542 4
        ini_set('session.gc_maxlifetime', $value);
543 4
        $this->unfreeze();
544
    }
545
546
    /**
547
     * @param bool $value Whether strict mode is enabled or not.
548
     * When `true` this setting prevents the session component to use an uninitialized session ID.
549
     * Note: Enabling `useStrictMode` on PHP < 5.5.2 is only supported with custom storage classes.
550
     * Warning! Although enabling strict mode is mandatory for secure sessions, the default value of 'session.use-strict-mode' is `0`.
551
     * @see https://www.php.net/manual/en/session.configuration.php#ini.session.use-strict-mode
552
     * @since 2.0.38
553
     */
554 10
    public function setUseStrictMode($value)
555
    {
556 10
        if (PHP_VERSION_ID < 50502) {
557
            if ($this->getUseCustomStorage() || !$value) {
558
                self::$_useStrictModePolyfill = $value;
559
            } else {
560
                throw new InvalidConfigException('Enabling `useStrictMode` on PHP < 5.5.2 is only supported with custom storage classes.');
561
            }
562
        } else {
563 10
            $this->freeze();
564 10
            ini_set('session.use_strict_mode', $value ? '1' : '0');
565 10
            $this->unfreeze();
566
        }
567
    }
568
569
    /**
570
     * @return bool Whether strict mode is enabled or not.
571
     * @see setUseStrictMode()
572
     * @since 2.0.38
573
     */
574 97
    public function getUseStrictMode()
575
    {
576 97
        if (PHP_VERSION_ID < 50502) {
577
            return self::$_useStrictModePolyfill;
578
        }
579
580 97
        return (bool)ini_get('session.use_strict_mode');
581
    }
582
583
    /**
584
     * Session open handler.
585
     * This method should be overridden if [[useCustomStorage]] returns true.
586
     * @internal Do not call this method directly.
587
     * @param string $savePath session save path
588
     * @param string $sessionName session name
589
     * @return bool whether session is opened successfully
590
     */
591 9
    public function openSession($savePath, $sessionName)
0 ignored issues
show
Unused Code introduced by
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

591
    public function openSession(/** @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...
Unused Code introduced by
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

591
    public function openSession($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...
592
    {
593 9
        return true;
594
    }
595
596
    /**
597
     * Session close handler.
598
     * This method should be overridden if [[useCustomStorage]] returns true.
599
     * @internal Do not call this method directly.
600
     * @return bool whether session is closed successfully
601
     */
602 8
    public function closeSession()
603
    {
604 8
        return true;
605
    }
606
607
    /**
608
     * Session read handler.
609
     * This method should be overridden if [[useCustomStorage]] returns true.
610
     * @internal Do not call this method directly.
611
     * @param string $id session ID
612
     * @return string the session data
613
     */
614
    public function readSession($id)
0 ignored issues
show
Unused Code introduced by
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

614
    public function readSession(/** @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...
615
    {
616
        return '';
617
    }
618
619
    /**
620
     * Session write handler.
621
     * This method should be overridden if [[useCustomStorage]] returns true.
622
     * @internal Do not call this method directly.
623
     * @param string $id session ID
624
     * @param string $data session data
625
     * @return bool whether session write is successful
626
     */
627
    public function writeSession($id, $data)
0 ignored issues
show
Unused Code introduced by
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

627
    public function writeSession(/** @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...
Unused Code introduced by
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

627
    public function writeSession($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...
628
    {
629
        return true;
630
    }
631
632
    /**
633
     * Session destroy handler.
634
     * This method should be overridden if [[useCustomStorage]] returns true.
635
     * @internal Do not call this method directly.
636
     * @param string $id session ID
637
     * @return bool whether session is destroyed successfully
638
     */
639 1
    public function destroySession($id)
0 ignored issues
show
Unused Code introduced by
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

639
    public function destroySession(/** @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...
640
    {
641 1
        return true;
642
    }
643
644
    /**
645
     * Session GC (garbage collection) handler.
646
     * This method should be overridden if [[useCustomStorage]] returns true.
647
     * @internal Do not call this method directly.
648
     * @param int $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up.
649
     * @return bool whether session is GCed successfully
650
     */
651
    public function gcSession($maxLifetime)
0 ignored issues
show
Unused Code introduced by
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

651
    public function gcSession(/** @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...
652
    {
653
        return true;
654
    }
655
656
    /**
657
     * Returns an iterator for traversing the session variables.
658
     * This method is required by the interface [[\IteratorAggregate]].
659
     * @return SessionIterator an iterator for traversing the session variables.
660
     */
661
    #[\ReturnTypeWillChange]
662
    public function getIterator()
663
    {
664
        $this->open();
665
        return new SessionIterator();
666
    }
667
668
    /**
669
     * Returns the number of items in the session.
670
     * @return int the number of session variables
671
     */
672
    public function getCount()
673
    {
674
        $this->open();
675
        return count($_SESSION);
676
    }
677
678
    /**
679
     * Returns the number of items in the session.
680
     * This method is required by [[\Countable]] interface.
681
     * @return int number of items in the session.
682
     */
683
    #[\ReturnTypeWillChange]
684
    public function count()
685
    {
686
        return $this->getCount();
687
    }
688
689
    /**
690
     * Returns the session variable value with the session variable name.
691
     * If the session variable does not exist, the `$defaultValue` will be returned.
692
     * @param string $key the session variable name
693
     * @param mixed $defaultValue the default value to be returned when the session variable does not exist.
694
     * @return mixed the session variable value, or $defaultValue if the session variable does not exist.
695
     */
696 77
    public function get($key, $defaultValue = null)
697
    {
698 77
        $this->open();
699 77
        return isset($_SESSION[$key]) ? $_SESSION[$key] : $defaultValue;
700
    }
701
702
    /**
703
     * Adds a session variable.
704
     * If the specified name already exists, the old value will be overwritten.
705
     * @param string $key session variable name
706
     * @param mixed $value session variable value
707
     */
708 60
    public function set($key, $value)
709
    {
710 60
        $this->open();
711 60
        $_SESSION[$key] = $value;
712
    }
713
714
    /**
715
     * Removes a session variable.
716
     * @param string $key the name of the session variable to be removed
717
     * @return mixed the removed value, null if no such session variable.
718
     */
719 45
    public function remove($key)
720
    {
721 45
        $this->open();
722 45
        if (isset($_SESSION[$key])) {
723 37
            $value = $_SESSION[$key];
724 37
            unset($_SESSION[$key]);
725
726 37
            return $value;
727
        }
728
729 45
        return null;
730
    }
731
732
    /**
733
     * Removes all session variables.
734
     */
735 22
    public function removeAll()
736
    {
737 22
        $this->open();
738 22
        foreach (array_keys($_SESSION) as $key) {
739 22
            unset($_SESSION[$key]);
740
        }
741
    }
742
743
    /**
744
     * @param mixed $key session variable name
745
     * @return bool whether there is the named session variable
746
     */
747
    public function has($key)
748
    {
749
        $this->open();
750
        return isset($_SESSION[$key]);
751
    }
752
753
    /**
754
     * Updates the counters for flash messages and removes outdated flash messages.
755
     * This method should only be called once in [[init()]].
756
     */
757 77
    protected function updateFlashCounters()
758
    {
759 77
        $counters = $this->get($this->flashParam, []);
760 77
        if (is_array($counters)) {
761 77
            foreach ($counters as $key => $count) {
762
                if ($count > 0) {
763
                    unset($counters[$key], $_SESSION[$key]);
764
                } elseif ($count == 0) {
765
                    $counters[$key]++;
766
                }
767
            }
768 77
            $_SESSION[$this->flashParam] = $counters;
769
        } else {
770
            // fix the unexpected problem that flashParam doesn't return an array
771
            unset($_SESSION[$this->flashParam]);
772
        }
773
    }
774
775
    /**
776
     * Returns a flash message.
777
     * @param string $key the key identifying the flash message
778
     * @param mixed $defaultValue value to be returned if the flash message does not exist.
779
     * @param bool $delete whether to delete this flash message right after this method is called.
780
     * If false, the flash message will be automatically deleted in the next request.
781
     * @return mixed the flash message or an array of messages if addFlash was used
782
     * @see setFlash()
783
     * @see addFlash()
784
     * @see hasFlash()
785
     * @see getAllFlashes()
786
     * @see removeFlash()
787
     */
788
    public function getFlash($key, $defaultValue = null, $delete = false)
789
    {
790
        $counters = $this->get($this->flashParam, []);
791
        if (isset($counters[$key])) {
792
            $value = $this->get($key, $defaultValue);
793
            if ($delete) {
794
                $this->removeFlash($key);
795
            } elseif ($counters[$key] < 0) {
796
                // mark for deletion in the next request
797
                $counters[$key] = 1;
798
                $_SESSION[$this->flashParam] = $counters;
799
            }
800
801
            return $value;
802
        }
803
804
        return $defaultValue;
805
    }
806
807
    /**
808
     * Returns all flash messages.
809
     *
810
     * You may use this method to display all the flash messages in a view file:
811
     *
812
     * ```php
813
     * <?php
814
     * foreach (Yii::$app->session->getAllFlashes() as $key => $message) {
815
     *     echo '<div class="alert alert-' . $key . '">' . $message . '</div>';
816
     * } ?>
817
     * ```
818
     *
819
     * With the above code you can use the [bootstrap alert][] classes such as `success`, `info`, `danger`
820
     * as the flash message key to influence the color of the div.
821
     *
822
     * Note that if you use [[addFlash()]], `$message` will be an array, and you will have to adjust the above code.
823
     *
824
     * [bootstrap alert]: https://getbootstrap.com/docs/3.4/components/#alerts
825
     *
826
     * @param bool $delete whether to delete the flash messages right after this method is called.
827
     * If false, the flash messages will be automatically deleted in the next request.
828
     * @return array flash messages (key => message or key => [message1, message2]).
829
     * @see setFlash()
830
     * @see addFlash()
831
     * @see getFlash()
832
     * @see hasFlash()
833
     * @see removeFlash()
834
     */
835
    public function getAllFlashes($delete = false)
836
    {
837
        $counters = $this->get($this->flashParam, []);
838
        $flashes = [];
839
        foreach (array_keys($counters) as $key) {
840
            if (array_key_exists($key, $_SESSION)) {
841
                $flashes[$key] = $_SESSION[$key];
842
                if ($delete) {
843
                    unset($counters[$key], $_SESSION[$key]);
844
                } elseif ($counters[$key] < 0) {
845
                    // mark for deletion in the next request
846
                    $counters[$key] = 1;
847
                }
848
            } else {
849
                unset($counters[$key]);
850
            }
851
        }
852
853
        $_SESSION[$this->flashParam] = $counters;
854
855
        return $flashes;
856
    }
857
858
    /**
859
     * Sets a flash message.
860
     * A flash message will be automatically deleted after it is accessed in a request and the deletion will happen
861
     * in the next request.
862
     * If there is already an existing flash message with the same key, it will be overwritten by the new one.
863
     * @param string $key the key identifying the flash message. Note that flash messages
864
     * and normal session variables share the same name space. If you have a normal
865
     * session variable using the same name, its value will be overwritten by this method.
866
     * @param mixed $value flash message
867
     * @param bool $removeAfterAccess whether the flash message should be automatically removed only if
868
     * it is accessed. If false, the flash message will be automatically removed after the next request,
869
     * regardless if it is accessed or not. If true (default value), the flash message will remain until after
870
     * it is accessed.
871
     * @see getFlash()
872
     * @see addFlash()
873
     * @see removeFlash()
874
     */
875
    public function setFlash($key, $value = true, $removeAfterAccess = true)
876
    {
877
        $counters = $this->get($this->flashParam, []);
878
        $counters[$key] = $removeAfterAccess ? -1 : 0;
879
        $_SESSION[$key] = $value;
880
        $_SESSION[$this->flashParam] = $counters;
881
    }
882
883
    /**
884
     * Adds a flash message.
885
     * If there are existing flash messages with the same key, the new one will be appended to the existing message array.
886
     * @param string $key the key identifying the flash message.
887
     * @param mixed $value flash message
888
     * @param bool $removeAfterAccess whether the flash message should be automatically removed only if
889
     * it is accessed. If false, the flash message will be automatically removed after the next request,
890
     * regardless if it is accessed or not. If true (default value), the flash message will remain until after
891
     * it is accessed.
892
     * @see getFlash()
893
     * @see setFlash()
894
     * @see removeFlash()
895
     */
896
    public function addFlash($key, $value = true, $removeAfterAccess = true)
897
    {
898
        $counters = $this->get($this->flashParam, []);
899
        $counters[$key] = $removeAfterAccess ? -1 : 0;
900
        $_SESSION[$this->flashParam] = $counters;
901
        if (empty($_SESSION[$key])) {
902
            $_SESSION[$key] = [$value];
903
        } elseif (is_array($_SESSION[$key])) {
904
            $_SESSION[$key][] = $value;
905
        } else {
906
            $_SESSION[$key] = [$_SESSION[$key], $value];
907
        }
908
    }
909
910
    /**
911
     * Removes a flash message.
912
     * @param string $key the key identifying the flash message. Note that flash messages
913
     * and normal session variables share the same name space.  If you have a normal
914
     * session variable using the same name, it will be removed by this method.
915
     * @return mixed the removed flash message. Null if the flash message does not exist.
916
     * @see getFlash()
917
     * @see setFlash()
918
     * @see addFlash()
919
     * @see removeAllFlashes()
920
     */
921
    public function removeFlash($key)
922
    {
923
        $counters = $this->get($this->flashParam, []);
924
        $value = isset($_SESSION[$key], $counters[$key]) ? $_SESSION[$key] : null;
925
        unset($counters[$key], $_SESSION[$key]);
926
        $_SESSION[$this->flashParam] = $counters;
927
928
        return $value;
929
    }
930
931
    /**
932
     * Removes all flash messages.
933
     * Note that flash messages and normal session variables share the same name space.
934
     * If you have a normal session variable using the same name, it will be removed
935
     * by this method.
936
     * @see getFlash()
937
     * @see setFlash()
938
     * @see addFlash()
939
     * @see removeFlash()
940
     */
941
    public function removeAllFlashes()
942
    {
943
        $counters = $this->get($this->flashParam, []);
944
        foreach (array_keys($counters) as $key) {
945
            unset($_SESSION[$key]);
946
        }
947
        unset($_SESSION[$this->flashParam]);
948
    }
949
950
    /**
951
     * Returns a value indicating whether there are flash messages associated with the specified key.
952
     * @param string $key key identifying the flash message type
953
     * @return bool whether any flash messages exist under specified key
954
     */
955
    public function hasFlash($key)
956
    {
957
        return $this->getFlash($key) !== null;
958
    }
959
960
    /**
961
     * This method is required by the interface [[\ArrayAccess]].
962
     * @param int|string $offset the offset to check on
963
     * @return bool
964
     */
965 3
    #[\ReturnTypeWillChange]
966
    public function offsetExists($offset)
967
    {
968 3
        $this->open();
969
970 3
        return isset($_SESSION[$offset]);
971
    }
972
973
    /**
974
     * This method is required by the interface [[\ArrayAccess]].
975
     * @param int|string $offset the offset to retrieve element.
976
     * @return mixed the element at the offset, null if no element is found at the offset
977
     */
978 3
    #[\ReturnTypeWillChange]
979
    public function offsetGet($offset)
980
    {
981 3
        $this->open();
982
983 3
        return isset($_SESSION[$offset]) ? $_SESSION[$offset] : null;
984
    }
985
986
    /**
987
     * This method is required by the interface [[\ArrayAccess]].
988
     * @param int|string $offset the offset to set element
989
     * @param mixed $item the element value
990
     */
991
    #[\ReturnTypeWillChange]
992
    public function offsetSet($offset, $item)
993
    {
994
        $this->open();
995
        $_SESSION[$offset] = $item;
996
    }
997
998
    /**
999
     * This method is required by the interface [[\ArrayAccess]].
1000
     * @param int|string $offset the offset to unset element
1001
     */
1002
    #[\ReturnTypeWillChange]
1003
    public function offsetUnset($offset)
1004
    {
1005
        $this->open();
1006
        unset($_SESSION[$offset]);
1007
    }
1008
1009
    /**
1010
     * If session is started it's not possible to edit session ini settings. In PHP7.2+ it throws exception.
1011
     * This function saves session data to temporary variable and stop session.
1012
     * @since 2.0.14
1013
     */
1014 19
    protected function freeze()
1015
    {
1016 19
        if ($this->getIsActive()) {
1017 4
            if (isset($_SESSION)) {
1018 4
                $this->_frozenSessionData = $_SESSION;
1019
            }
1020 4
            $this->close();
1021 4
            Yii::info('Session frozen', __METHOD__);
1022
        }
1023
    }
1024
1025
    /**
1026
     * Starts session and restores data from temporary variable
1027
     * @since 2.0.14
1028
     */
1029 19
    protected function unfreeze()
1030
    {
1031 19
        if (null !== $this->_frozenSessionData) {
1032 4
            YII_DEBUG ? session_start() : @session_start();
1033
1034 4
            if ($this->getIsActive()) {
1035 4
                Yii::info('Session unfrozen', __METHOD__);
1036
            } else {
1037
                $error = error_get_last();
1038
                $message = isset($error['message']) ? $error['message'] : 'Failed to unfreeze session.';
1039
                Yii::error($message, __METHOD__);
1040
            }
1041
1042 4
            $_SESSION = $this->_frozenSessionData;
1043 4
            $this->_frozenSessionData = null;
1044
        }
1045
    }
1046
1047
    /**
1048
     * Set cache limiter
1049
     *
1050
     * @param string $cacheLimiter
1051
     * @since 2.0.14
1052
     */
1053 1
    public function setCacheLimiter($cacheLimiter)
1054
    {
1055 1
        $this->freeze();
1056 1
        session_cache_limiter($cacheLimiter);
1057 1
        $this->unfreeze();
1058
    }
1059
1060
    /**
1061
     * Returns current cache limiter
1062
     *
1063
     * @return string current cache limiter
1064
     * @since 2.0.14
1065
     */
1066
    public function getCacheLimiter()
1067
    {
1068
        return session_cache_limiter();
1069
    }
1070
}
1071