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.
Completed
Push — master ( ecd2dc...aafa57 )
by Robert
18:49
created

Session   D

Complexity

Total Complexity 104

Size/Duplication

Total Lines 819
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 5

Test Coverage

Coverage 38.22%

Importance

Changes 0
Metric Value
wmc 104
lcom 2
cbo 5
dl 0
loc 819
ccs 112
cts 293
cp 0.3822
rs 4.4444
c 0
b 0
f 0

53 Methods

Rating   Name   Duplication   Size   Complexity  
A getUseCookies() 0 10 3
A setUseCookies() 0 13 3
A getGCProbability() 0 4 1
A setGCProbability() 0 10 3
A getUseTransparentSessionID() 0 4 1
A setUseTransparentSessionID() 0 4 2
A getTimeout() 0 4 1
A setTimeout() 0 4 1
A readSession() 0 4 1
A writeSession() 0 4 1
A destroySession() 0 4 1
A gcSession() 0 4 1
A getIterator() 0 5 1
A getCount() 0 5 1
A count() 0 4 1
A get() 0 5 2
A set() 0 5 1
A remove() 0 12 2
A removeAll() 0 7 2
A has() 0 5 1
B updateFlashCounters() 0 17 5
A getFlash() 0 18 4
B getAllFlashes() 0 22 5
A setFlash() 0 7 2
A addFlash() 0 15 4
A removeFlash() 0 9 2
A removeAllFlashes() 0 8 2
A hasFlash() 0 4 1
A offsetExists() 0 6 1
A offsetGet() 0 6 2
A offsetSet() 0 5 1
A offsetUnset() 0 5 1
A getUseCustomStorage() 0 4 1
A open() 0 21 4
A close() 0 6 3
A destroy() 0 12 2
A getIsActive() 0 4 1
B getHasSessionId() 0 16 6
A setHasSessionId() 0 4 1
A setId() 0 4 1
A regenerateID() 0 12 4
A getName() 0 4 1
A setName() 0 4 1
A getSavePath() 0 4 1
A setSavePath() 0 9 2
A getCookieParams() 0 4 1
A setCookieParams() 0 4 1
A setCookieParamsInternal() 0 9 2
A init() 0 9 2
C registerSessionHandler() 0 32 7
A getId() 0 4 1
A openSession() 0 4 1
A closeSession() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Session often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Session, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\web;
9
10
use Yii;
11
use yii\base\Component;
12
use yii\base\InvalidConfigException;
13
use yii\base\InvalidParamException;
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 array $allFlashes Flash messages (key => message or key => [message1, message2]). This property
49
 * is read-only.
50
 * @property array $cookieParams The session cookie parameters. This property is read-only.
51
 * @property int $count The number of session variables. This property is read-only.
52
 * @property string $flash The key identifying the flash message. Note that flash messages and normal session
53
 * variables share the same name space. If you have a normal session variable using the same name, its value will
54
 * be overwritten by this method. This property is write-only.
55
 * @property float $gCProbability The probability (percentage) that the GC (garbage collection) process is
56
 * started on every session initialization, defaults to 1 meaning 1% chance.
57
 * @property bool $hasSessionId Whether the current request has sent the session ID.
58
 * @property string $id The current session ID.
59
 * @property bool $isActive Whether the session has started. This property is read-only.
60
 * @property SessionIterator $iterator An iterator for traversing the session variables. This property is
61
 * read-only.
62
 * @property string $name The current session name.
63
 * @property string $savePath The current session save path, defaults to '/tmp'.
64
 * @property int $timeout The number of seconds after which data will be seen as 'garbage' and cleaned up. The
65
 * default value is 1440 seconds (or the value of "session.gc_maxlifetime" set in php.ini).
66
 * @property bool|null $useCookies The value indicating whether cookies should be used to store session IDs.
67
 * @property bool $useCustomStorage Whether to use custom storage. This property is read-only.
68
 * @property bool $useTransparentSessionID Whether transparent sid support is enabled or not, defaults to
69
 * false.
70
 *
71
 * @author Qiang Xue <[email protected]>
72
 * @since 2.0
73
 */
74
class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Countable
75
{
76
    /**
77
     * @var string the name of the session variable that stores the flash message data.
78
     */
79
    public $flashParam = '__flash';
80
    /**
81
     * @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.
82
     */
83
    public $handler;
84
85
    /**
86
     * @var array parameter-value pairs to override default session cookie parameters that are used for session_set_cookie_params() function
87
     * Array may have the following possible keys: 'lifetime', 'path', 'domain', 'secure', 'httponly'
88
     * @see http://www.php.net/manual/en/function.session-set-cookie-params.php
89
     */
90
    private $_cookieParams = ['httponly' => true];
91
92
93
    /**
94
     * Initializes the application component.
95
     * This method is required by IApplicationComponent and is invoked by application.
96
     */
97 33
    public function init()
98
    {
99 33
        parent::init();
100 33
        register_shutdown_function([$this, 'close']);
101 33
        if ($this->getIsActive()) {
102 4
            Yii::warning('Session is already started', __METHOD__);
103 4
            $this->updateFlashCounters();
104 4
        }
105 33
    }
106
107
    /**
108
     * Returns a value indicating whether to use custom session storage.
109
     * This method should be overridden to return true by child classes that implement custom session storage.
110
     * To implement custom session storage, override these methods: [[openSession()]], [[closeSession()]],
111
     * [[readSession()]], [[writeSession()]], [[destroySession()]] and [[gcSession()]].
112
     * @return bool whether to use custom storage.
113
     */
114 18
    public function getUseCustomStorage()
115
    {
116 18
        return false;
117
    }
118
119
    /**
120
     * Starts the session.
121
     */
122 23
    public function open()
123
    {
124 23
        if ($this->getIsActive()) {
125 23
            return;
126
        }
127
128 19
        $this->registerSessionHandler();
129
130 19
        $this->setCookieParamsInternal();
131
132 19
        @session_start();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
133
134 19
        if ($this->getIsActive()) {
135 19
            Yii::info('Session started', __METHOD__);
136 19
            $this->updateFlashCounters();
137 19
        } else {
138
            $error = error_get_last();
139
            $message = isset($error['message']) ? $error['message'] : 'Failed to start session.';
140
            Yii::error($message, __METHOD__);
141
        }
142 19
    }
143
144
    /**
145
     * Registers session handler.
146
     * @throws \yii\base\InvalidConfigException
147
     */
148 19
    protected function registerSessionHandler()
149
    {
150 19
        if ($this->handler !== null) {
151
            if (!is_object($this->handler)) {
152
                $this->handler = Yii::createObject($this->handler);
153
            }
154
            if (!$this->handler instanceof \SessionHandlerInterface) {
155
                throw new InvalidConfigException('"' . get_class($this) . '::handler" must implement the SessionHandlerInterface.');
156
            }
157
            YII_DEBUG ? session_set_save_handler($this->handler, false) : @session_set_save_handler($this->handler, false);
158 19
        } elseif ($this->getUseCustomStorage()) {
159 1
            if (YII_DEBUG) {
160 1
                session_set_save_handler(
161 1
                    [$this, 'openSession'],
162 1
                    [$this, 'closeSession'],
163 1
                    [$this, 'readSession'],
164 1
                    [$this, 'writeSession'],
165 1
                    [$this, 'destroySession'],
166 1
                    [$this, 'gcSession']
167 1
                );
168 1
            } else {
169
                @session_set_save_handler(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
170
                    [$this, 'openSession'],
171
                    [$this, 'closeSession'],
172
                    [$this, 'readSession'],
173
                    [$this, 'writeSession'],
174
                    [$this, 'destroySession'],
175
                    [$this, 'gcSession']
176
                );
177
            }
178 1
        }
179 19
    }
180
181
    /**
182
     * Ends the current session and store session data.
183
     */
184 27
    public function close()
185
    {
186 27
        if ($this->getIsActive()) {
187 19
            YII_DEBUG ? session_write_close() : @session_write_close();
188 19
        }
189 27
    }
190
191
    /**
192
     * Frees all session variables and destroys all data registered to a session.
193
     */
194 1
    public function destroy()
195
    {
196 1
        if ($this->getIsActive()) {
197 1
            $sessionId = session_id();
198 1
            $this->close();
199 1
            $this->setId($sessionId);
200 1
            $this->open();
201 1
            session_unset();
202 1
            session_destroy();
203 1
            $this->setId($sessionId);
204 1
        }
205 1
    }
206
207
    /**
208
     * @return bool whether the session has started
209
     */
210 33
    public function getIsActive()
211
    {
212 33
        return session_status() === PHP_SESSION_ACTIVE;
213
    }
214
215
    private $_hasSessionId;
216
217
    /**
218
     * Returns a value indicating whether the current request has sent the session ID.
219
     * The default implementation will check cookie and $_GET using the session name.
220
     * If you send session ID via other ways, you may need to override this method
221
     * or call [[setHasSessionId()]] to explicitly set whether the session ID is sent.
222
     * @return bool whether the current request has sent the session ID.
223
     */
224 9
    public function getHasSessionId()
225
    {
226 9
        if ($this->_hasSessionId === null) {
227 9
            $name = $this->getName();
228 9
            $request = Yii::$app->getRequest();
229 9
            if (!empty($_COOKIE[$name]) && ini_get('session.use_cookies')) {
230
                $this->_hasSessionId = true;
231 9
            } elseif (!ini_get('session.use_only_cookies') && ini_get('session.use_trans_sid')) {
232
                $this->_hasSessionId = $request->get($name) != '';
233
            } else {
234 9
                $this->_hasSessionId = false;
235
            }
236 9
        }
237
238 9
        return $this->_hasSessionId;
239
    }
240
241
    /**
242
     * Sets the value indicating whether the current request has sent the session ID.
243
     * This method is provided so that you can override the default way of determining
244
     * whether the session ID is sent.
245
     * @param bool $value whether the current request has sent the session ID.
246
     */
247
    public function setHasSessionId($value)
248
    {
249
        $this->_hasSessionId = $value;
250
    }
251
252
    /**
253
     * Gets the session ID.
254
     * This is a wrapper for [PHP session_id()](http://php.net/manual/en/function.session-id.php).
255
     * @return string the current session ID
256
     */
257 1
    public function getId()
258
    {
259 1
        return session_id();
260
    }
261
262
    /**
263
     * Sets the session ID.
264
     * This is a wrapper for [PHP session_id()](http://php.net/manual/en/function.session-id.php).
265
     * @param string $value the session ID for the current session
266
     */
267 1
    public function setId($value)
268
    {
269 1
        session_id($value);
270 1
    }
271
272
    /**
273
     * Updates the current session ID with a newly generated one .
274
     * Please refer to <http://php.net/session_regenerate_id> for more details.
275
     * @param bool $deleteOldSession Whether to delete the old associated session file or not.
276
     */
277
    public function regenerateID($deleteOldSession = false)
278
    {
279
        if ($this->getIsActive()) {
280
            // add @ to inhibit possible warning due to race condition
281
            // https://github.com/yiisoft/yii2/pull/1812
282
            if (YII_DEBUG && !headers_sent()) {
283
                session_regenerate_id($deleteOldSession);
284
            } else {
285
                @session_regenerate_id($deleteOldSession);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
286
            }
287
        }
288
    }
289
290
    /**
291
     * Gets the name of the current session.
292
     * This is a wrapper for [PHP session_name()](http://php.net/manual/en/function.session-name.php).
293
     * @return string the current session name
294
     */
295 9
    public function getName()
296
    {
297 9
        return session_name();
298
    }
299
300
    /**
301
     * Sets the name for the current session.
302
     * This is a wrapper for [PHP session_name()](http://php.net/manual/en/function.session-name.php).
303
     * @param string $value the session name for the current session, must be an alphanumeric string.
304
     * It defaults to "PHPSESSID".
305
     */
306
    public function setName($value)
307
    {
308
        session_name($value);
309
    }
310
311
    /**
312
     * Gets the current session save path.
313
     * This is a wrapper for [PHP session_save_path()](http://php.net/manual/en/function.session-save-path.php).
314
     * @return string the current session save path, defaults to '/tmp'.
315
     */
316
    public function getSavePath()
317
    {
318
        return session_save_path();
319
    }
320
321
    /**
322
     * Sets the current session save path.
323
     * This is a wrapper for [PHP session_save_path()](http://php.net/manual/en/function.session-save-path.php).
324
     * @param string $value the current session save path. This can be either a directory name or a path alias.
325
     * @throws InvalidParamException if the path is not a valid directory
326
     */
327
    public function setSavePath($value)
328
    {
329
        $path = Yii::getAlias($value);
330
        if (is_dir($path)) {
331
            session_save_path($path);
332
        } else {
333
            throw new InvalidParamException("Session save path is not a valid directory: $value");
334
        }
335
    }
336
337
    /**
338
     * @return array the session cookie parameters.
339
     * @see http://php.net/manual/en/function.session-get-cookie-params.php
340
     */
341 19
    public function getCookieParams()
342
    {
343 19
        return array_merge(session_get_cookie_params(), array_change_key_case($this->_cookieParams));
344
    }
345
346
    /**
347
     * Sets the session cookie parameters.
348
     * The cookie parameters passed to this method will be merged with the result
349
     * of `session_get_cookie_params()`.
350
     * @param array $value cookie parameters, valid keys include: `lifetime`, `path`, `domain`, `secure` and `httponly`.
351
     * @throws InvalidParamException if the parameters are incomplete.
352
     * @see http://us2.php.net/manual/en/function.session-set-cookie-params.php
353
     */
354
    public function setCookieParams(array $value)
355
    {
356
        $this->_cookieParams = $value;
357
    }
358
359
    /**
360
     * Sets the session cookie parameters.
361
     * This method is called by [[open()]] when it is about to open the session.
362
     * @throws InvalidParamException if the parameters are incomplete.
363
     * @see http://us2.php.net/manual/en/function.session-set-cookie-params.php
364
     */
365 19
    private function setCookieParamsInternal()
366
    {
367 19
        $data = $this->getCookieParams();
368 19
        if (isset($data['lifetime'], $data['path'], $data['domain'], $data['secure'], $data['httponly'])) {
369 19
            session_set_cookie_params($data['lifetime'], $data['path'], $data['domain'], $data['secure'], $data['httponly']);
370 19
        } else {
371
            throw new InvalidParamException('Please make sure cookieParams contains these elements: lifetime, path, domain, secure and httponly.');
372
        }
373 19
    }
374
375
    /**
376
     * Returns the value indicating whether cookies should be used to store session IDs.
377
     * @return bool|null the value indicating whether cookies should be used to store session IDs.
378
     * @see setUseCookies()
379
     */
380
    public function getUseCookies()
381
    {
382
        if (ini_get('session.use_cookies') === '0') {
383
            return false;
384
        } elseif (ini_get('session.use_only_cookies') === '1') {
385
            return true;
386
        } else {
387
            return null;
388
        }
389
    }
390
391
    /**
392
     * Sets the value indicating whether cookies should be used to store session IDs.
393
     * Three states are possible:
394
     *
395
     * - true: cookies and only cookies will be used to store session IDs.
396
     * - false: cookies will not be used to store session IDs.
397
     * - null: if possible, cookies will be used to store session IDs; if not, other mechanisms will be used (e.g. GET parameter)
398
     *
399
     * @param bool|null $value the value indicating whether cookies should be used to store session IDs.
400
     */
401
    public function setUseCookies($value)
402
    {
403
        if ($value === false) {
404
            ini_set('session.use_cookies', '0');
405
            ini_set('session.use_only_cookies', '0');
406
        } elseif ($value === true) {
407
            ini_set('session.use_cookies', '1');
408
            ini_set('session.use_only_cookies', '1');
409
        } else {
410
            ini_set('session.use_cookies', '1');
411
            ini_set('session.use_only_cookies', '0');
412
        }
413
    }
414
415
    /**
416
     * @return float the probability (percentage) that the GC (garbage collection) process is started on every session initialization, defaults to 1 meaning 1% chance.
417
     */
418
    public function getGCProbability()
419
    {
420
        return (float) (ini_get('session.gc_probability') / ini_get('session.gc_divisor') * 100);
421
    }
422
423
    /**
424
     * @param float $value the probability (percentage) that the GC (garbage collection) process is started on every session initialization.
425
     * @throws InvalidParamException if the value is not between 0 and 100.
426
     */
427
    public function setGCProbability($value)
428
    {
429
        if ($value >= 0 && $value <= 100) {
430
            // percent * 21474837 / 2147483647 ≈ percent * 0.01
431
            ini_set('session.gc_probability', floor($value * 21474836.47));
432
            ini_set('session.gc_divisor', 2147483647);
433
        } else {
434
            throw new InvalidParamException('GCProbability must be a value between 0 and 100.');
435
        }
436
    }
437
438
    /**
439
     * @return bool whether transparent sid support is enabled or not, defaults to false.
440
     */
441
    public function getUseTransparentSessionID()
442
    {
443
        return ini_get('session.use_trans_sid') == 1;
444
    }
445
446
    /**
447
     * @param bool $value whether transparent sid support is enabled or not.
448
     */
449
    public function setUseTransparentSessionID($value)
450
    {
451
        ini_set('session.use_trans_sid', $value ? '1' : '0');
452
    }
453
454
    /**
455
     * @return int the number of seconds after which data will be seen as 'garbage' and cleaned up.
456
     * The default value is 1440 seconds (or the value of "session.gc_maxlifetime" set in php.ini).
457
     */
458 11
    public function getTimeout()
459
    {
460 11
        return (int) ini_get('session.gc_maxlifetime');
461
    }
462
463
    /**
464
     * @param int $value the number of seconds after which data will be seen as 'garbage' and cleaned up
465
     */
466
    public function setTimeout($value)
467
    {
468
        ini_set('session.gc_maxlifetime', $value);
469
    }
470
471
    /**
472
     * Session open handler.
473
     * This method should be overridden if [[useCustomStorage]] returns true.
474
     * @internal Do not call this method directly.
475
     * @param string $savePath session save path
476
     * @param string $sessionName session name
477
     * @return bool whether session is opened successfully
478
     */
479 7
    public function openSession($savePath, $sessionName)
0 ignored issues
show
Unused Code introduced by
The parameter $savePath is not used and could be removed.

This check looks from 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.

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

Loading history...
480
    {
481 7
        return true;
482
    }
483
484
    /**
485
     * Session close handler.
486
     * This method should be overridden if [[useCustomStorage]] returns true.
487
     * @internal Do not call this method directly.
488
     * @return bool whether session is closed successfully
489
     */
490 7
    public function closeSession()
491
    {
492 7
        return true;
493
    }
494
495
    /**
496
     * Session read handler.
497
     * This method should be overridden if [[useCustomStorage]] returns true.
498
     * @internal Do not call this method directly.
499
     * @param string $id session ID
500
     * @return string the session data
501
     */
502
    public function readSession($id)
503
    {
504
        return '';
505
    }
506
507
    /**
508
     * Session write handler.
509
     * This method should be overridden if [[useCustomStorage]] returns true.
510
     * @internal Do not call this method directly.
511
     * @param string $id session ID
512
     * @param string $data session data
513
     * @return bool whether session write is successful
514
     */
515
    public function writeSession($id, $data)
516
    {
517
        return true;
518
    }
519
520
    /**
521
     * Session destroy handler.
522
     * This method should be overridden if [[useCustomStorage]] returns true.
523
     * @internal Do not call this method directly.
524
     * @param string $id session ID
525
     * @return bool whether session is destroyed successfully
526
     */
527
    public function destroySession($id)
528
    {
529
        return true;
530
    }
531
532
    /**
533
     * Session GC (garbage collection) handler.
534
     * This method should be overridden if [[useCustomStorage]] returns true.
535
     * @internal Do not call this method directly.
536
     * @param int $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up.
537
     * @return bool whether session is GCed successfully
538
     */
539
    public function gcSession($maxLifetime)
0 ignored issues
show
Unused Code introduced by
The parameter $maxLifetime is not used and could be removed.

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

Loading history...
540
    {
541
        return true;
542
    }
543
544
    /**
545
     * Returns an iterator for traversing the session variables.
546
     * This method is required by the interface [[\IteratorAggregate]].
547
     * @return SessionIterator an iterator for traversing the session variables.
548
     */
549
    public function getIterator()
550
    {
551
        $this->open();
552
        return new SessionIterator;
553
    }
554
555
    /**
556
     * Returns the number of items in the session.
557
     * @return int the number of session variables
558
     */
559
    public function getCount()
560
    {
561
        $this->open();
562
        return count($_SESSION);
563
    }
564
565
    /**
566
     * Returns the number of items in the session.
567
     * This method is required by [[\Countable]] interface.
568
     * @return int number of items in the session.
569
     */
570
    public function count()
571
    {
572
        return $this->getCount();
573
    }
574
575
    /**
576
     * Returns the session variable value with the session variable name.
577
     * If the session variable does not exist, the `$defaultValue` will be returned.
578
     * @param string $key the session variable name
579
     * @param mixed $defaultValue the default value to be returned when the session variable does not exist.
580
     * @return mixed the session variable value, or $defaultValue if the session variable does not exist.
581
     */
582 23
    public function get($key, $defaultValue = null)
583
    {
584 23
        $this->open();
585 23
        return isset($_SESSION[$key]) ? $_SESSION[$key] : $defaultValue;
586
    }
587
588
    /**
589
     * Adds a session variable.
590
     * If the specified name already exists, the old value will be overwritten.
591
     * @param string $key session variable name
592
     * @param mixed $value session variable value
593
     */
594 18
    public function set($key, $value)
595
    {
596 18
        $this->open();
597 18
        $_SESSION[$key] = $value;
598 18
    }
599
600
    /**
601
     * Removes a session variable.
602
     * @param string $key the name of the session variable to be removed
603
     * @return mixed the removed value, null if no such session variable.
604
     */
605 13
    public function remove($key)
606
    {
607 13
        $this->open();
608 13
        if (isset($_SESSION[$key])) {
609 13
            $value = $_SESSION[$key];
610 13
            unset($_SESSION[$key]);
611
612 13
            return $value;
613
        } else {
614 13
            return null;
615
        }
616
    }
617
618
    /**
619
     * Removes all session variables
620
     */
621 3
    public function removeAll()
622
    {
623 3
        $this->open();
624 3
        foreach (array_keys($_SESSION) as $key) {
625 3
            unset($_SESSION[$key]);
626 3
        }
627 3
    }
628
629
    /**
630
     * @param mixed $key session variable name
631
     * @return bool whether there is the named session variable
632
     */
633
    public function has($key)
634
    {
635
        $this->open();
636
        return isset($_SESSION[$key]);
637
    }
638
639
    /**
640
     * Updates the counters for flash messages and removes outdated flash messages.
641
     * This method should only be called once in [[init()]].
642
     */
643 23
    protected function updateFlashCounters()
644
    {
645 23
        $counters = $this->get($this->flashParam, []);
646 23
        if (is_array($counters)) {
647 23
            foreach ($counters as $key => $count) {
648
                if ($count > 0) {
649
                    unset($counters[$key], $_SESSION[$key]);
650
                } elseif ($count == 0) {
651
                    $counters[$key]++;
652
                }
653 23
            }
654 23
            $_SESSION[$this->flashParam] = $counters;
655 23
        } else {
656
            // fix the unexpected problem that flashParam doesn't return an array
657
            unset($_SESSION[$this->flashParam]);
658
        }
659 23
    }
660
661
    /**
662
     * Returns a flash message.
663
     * @param string $key the key identifying the flash message
664
     * @param mixed $defaultValue value to be returned if the flash message does not exist.
665
     * @param bool $delete whether to delete this flash message right after this method is called.
666
     * If false, the flash message will be automatically deleted in the next request.
667
     * @return mixed the flash message or an array of messages if addFlash was used
668
     * @see setFlash()
669
     * @see addFlash()
670
     * @see hasFlash()
671
     * @see getAllFlashes()
672
     * @see removeFlash()
673
     */
674
    public function getFlash($key, $defaultValue = null, $delete = false)
675
    {
676
        $counters = $this->get($this->flashParam, []);
677
        if (isset($counters[$key])) {
678
            $value = $this->get($key, $defaultValue);
679
            if ($delete) {
680
                $this->removeFlash($key);
681
            } elseif ($counters[$key] < 0) {
682
                // mark for deletion in the next request
683
                $counters[$key] = 1;
684
                $_SESSION[$this->flashParam] = $counters;
685
            }
686
687
            return $value;
688
        } else {
689
            return $defaultValue;
690
        }
691
    }
692
693
    /**
694
     * Returns all flash messages.
695
     *
696
     * You may use this method to display all the flash messages in a view file:
697
     *
698
     * ```php
699
     * <?php
700
     * foreach (Yii::$app->session->getAllFlashes() as $key => $message) {
701
     *     echo '<div class="alert alert-' . $key . '">' . $message . '</div>';
702
     * } ?>
703
     * ```
704
     *
705
     * With the above code you can use the [bootstrap alert][] classes such as `success`, `info`, `danger`
706
     * as the flash message key to influence the color of the div.
707
     *
708
     * Note that if you use [[addFlash()]], `$message` will be an array, and you will have to adjust the above code.
709
     *
710
     * [bootstrap alert]: http://getbootstrap.com/components/#alerts
711
     *
712
     * @param bool $delete whether to delete the flash messages right after this method is called.
713
     * If false, the flash messages will be automatically deleted in the next request.
714
     * @return array flash messages (key => message or key => [message1, message2]).
715
     * @see setFlash()
716
     * @see addFlash()
717
     * @see getFlash()
718
     * @see hasFlash()
719
     * @see removeFlash()
720
     */
721
    public function getAllFlashes($delete = false)
722
    {
723
        $counters = $this->get($this->flashParam, []);
724
        $flashes = [];
725
        foreach (array_keys($counters) as $key) {
726
            if (array_key_exists($key, $_SESSION)) {
727
                $flashes[$key] = $_SESSION[$key];
728
                if ($delete) {
729
                    unset($counters[$key], $_SESSION[$key]);
730
                } elseif ($counters[$key] < 0) {
731
                    // mark for deletion in the next request
732
                    $counters[$key] = 1;
733
                }
734
            } else {
735
                unset($counters[$key]);
736
            }
737
        }
738
739
        $_SESSION[$this->flashParam] = $counters;
740
741
        return $flashes;
742
    }
743
744
    /**
745
     * Sets a flash message.
746
     * A flash message will be automatically deleted after it is accessed in a request and the deletion will happen
747
     * in the next request.
748
     * If there is already an existing flash message with the same key, it will be overwritten by the new one.
749
     * @param string $key the key identifying the flash message. Note that flash messages
750
     * and normal session variables share the same name space. If you have a normal
751
     * session variable using the same name, its value will be overwritten by this method.
752
     * @param mixed $value flash message
753
     * @param bool $removeAfterAccess whether the flash message should be automatically removed only if
754
     * it is accessed. If false, the flash message will be automatically removed after the next request,
755
     * regardless if it is accessed or not. If true (default value), the flash message will remain until after
756
     * it is accessed.
757
     * @see getFlash()
758
     * @see addFlash()
759
     * @see removeFlash()
760
     */
761
    public function setFlash($key, $value = true, $removeAfterAccess = true)
762
    {
763
        $counters = $this->get($this->flashParam, []);
764
        $counters[$key] = $removeAfterAccess ? -1 : 0;
765
        $_SESSION[$key] = $value;
766
        $_SESSION[$this->flashParam] = $counters;
767
    }
768
769
    /**
770
     * Adds a flash message.
771
     * If there are existing flash messages with the same key, the new one will be appended to the existing message array.
772
     * @param string $key the key identifying the flash message.
773
     * @param mixed $value flash message
774
     * @param bool $removeAfterAccess whether the flash message should be automatically removed only if
775
     * it is accessed. If false, the flash message will be automatically removed after the next request,
776
     * regardless if it is accessed or not. If true (default value), the flash message will remain until after
777
     * it is accessed.
778
     * @see getFlash()
779
     * @see setFlash()
780
     * @see removeFlash()
781
     */
782
    public function addFlash($key, $value = true, $removeAfterAccess = true)
783
    {
784
        $counters = $this->get($this->flashParam, []);
785
        $counters[$key] = $removeAfterAccess ? -1 : 0;
786
        $_SESSION[$this->flashParam] = $counters;
787
        if (empty($_SESSION[$key])) {
788
            $_SESSION[$key] = [$value];
789
        } else {
790
            if (is_array($_SESSION[$key])) {
791
                $_SESSION[$key][] = $value;
792
            } else {
793
                $_SESSION[$key] = [$_SESSION[$key], $value];
794
            }
795
        }
796
    }
797
798
    /**
799
     * Removes a flash message.
800
     * @param string $key the key identifying the flash message. Note that flash messages
801
     * and normal session variables share the same name space.  If you have a normal
802
     * session variable using the same name, it will be removed by this method.
803
     * @return mixed the removed flash message. Null if the flash message does not exist.
804
     * @see getFlash()
805
     * @see setFlash()
806
     * @see addFlash()
807
     * @see removeAllFlashes()
808
     */
809
    public function removeFlash($key)
810
    {
811
        $counters = $this->get($this->flashParam, []);
812
        $value = isset($_SESSION[$key], $counters[$key]) ? $_SESSION[$key] : null;
813
        unset($counters[$key], $_SESSION[$key]);
814
        $_SESSION[$this->flashParam] = $counters;
815
816
        return $value;
817
    }
818
819
    /**
820
     * Removes all flash messages.
821
     * Note that flash messages and normal session variables share the same name space.
822
     * If you have a normal session variable using the same name, it will be removed
823
     * by this method.
824
     * @see getFlash()
825
     * @see setFlash()
826
     * @see addFlash()
827
     * @see removeFlash()
828
     */
829
    public function removeAllFlashes()
830
    {
831
        $counters = $this->get($this->flashParam, []);
832
        foreach (array_keys($counters) as $key) {
833
            unset($_SESSION[$key]);
834
        }
835
        unset($_SESSION[$this->flashParam]);
836
    }
837
838
    /**
839
     * Returns a value indicating whether there are flash messages associated with the specified key.
840
     * @param string $key key identifying the flash message type
841
     * @return bool whether any flash messages exist under specified key
842
     */
843
    public function hasFlash($key)
844
    {
845
        return $this->getFlash($key) !== null;
846
    }
847
848
    /**
849
     * This method is required by the interface [[\ArrayAccess]].
850
     * @param mixed $offset the offset to check on
851
     * @return bool
852
     */
853
    public function offsetExists($offset)
854
    {
855
        $this->open();
856
857
        return isset($_SESSION[$offset]);
858
    }
859
860
    /**
861
     * This method is required by the interface [[\ArrayAccess]].
862
     * @param int $offset the offset to retrieve element.
863
     * @return mixed the element at the offset, null if no element is found at the offset
864
     */
865
    public function offsetGet($offset)
866
    {
867
        $this->open();
868
869
        return isset($_SESSION[$offset]) ? $_SESSION[$offset] : null;
870
    }
871
872
    /**
873
     * This method is required by the interface [[\ArrayAccess]].
874
     * @param int $offset the offset to set element
875
     * @param mixed $item the element value
876
     */
877
    public function offsetSet($offset, $item)
878
    {
879
        $this->open();
880
        $_SESSION[$offset] = $item;
881
    }
882
883
    /**
884
     * This method is required by the interface [[\ArrayAccess]].
885
     * @param mixed $offset the offset to unset element
886
     */
887
    public function offsetUnset($offset)
888
    {
889
        $this->open();
890
        unset($_SESSION[$offset]);
891
    }
892
}
893