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 ( cd2d3a...6886d6 )
by Robert
09:38
created

Session::setCookieParamsInternal()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2.0116

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
ccs 6
cts 7
cp 0.8571
cc 2
eloc 6
nc 2
nop 0
crap 2.0116
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
 * @property array $allFlashes Flash messages (key => message or key => [message1, message2]). This property
47
 * is read-only.
48
 * @property array $cookieParams The session cookie parameters. This property is read-only.
49
 * @property integer $count The number of session variables. This property is read-only.
50
 * @property string $flash The key identifying the flash message. Note that flash messages and normal session
51
 * variables share the same name space. If you have a normal session variable using the same name, its value will
52
 * be overwritten by this method. This property is write-only.
53
 * @property float $gCProbability The probability (percentage) that the GC (garbage collection) process is
54
 * started on every session initialization, defaults to 1 meaning 1% chance.
55
 * @property boolean $hasSessionId Whether the current request has sent the session ID.
56
 * @property string $id The current session ID.
57
 * @property boolean $isActive Whether the session has started. This property is read-only.
58
 * @property SessionIterator $iterator An iterator for traversing the session variables. This property is
59
 * read-only.
60
 * @property string $name The current session name.
61
 * @property string $savePath The current session save path, defaults to '/tmp'.
62
 * @property integer $timeout The number of seconds after which data will be seen as 'garbage' and cleaned up.
63
 * The default value is 1440 seconds (or the value of "session.gc_maxlifetime" set in php.ini).
64
 * @property boolean|null $useCookies The value indicating whether cookies should be used to store session
65
 * IDs.
66
 * @property boolean $useCustomStorage Whether to use custom storage. This property is read-only.
67
 * @property boolean $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 the name of the session variable that stores the flash message data.
77
     */
78
    public $flashParam = '__flash';
79
    /**
80
     * @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.
81
     */
82
    public $handler;
83
84
    /**
85
     * @var array parameter-value pairs to override default session cookie parameters that are used for session_set_cookie_params() function
86
     * Array may have the following possible keys: 'lifetime', 'path', 'domain', 'secure', 'httponly'
87
     * @see http://www.php.net/manual/en/function.session-set-cookie-params.php
88
     */
89
    private $_cookieParams = ['httponly' => true];
90
91
92
    /**
93
     * Initializes the application component.
94
     * This method is required by IApplicationComponent and is invoked by application.
95
     */
96 32
    public function init()
97
    {
98 32
        parent::init();
99 32
        register_shutdown_function([$this, 'close']);
100 32
        if ($this->getIsActive()) {
101
            Yii::warning('Session is already started', __METHOD__);
102
            $this->updateFlashCounters();
103
        }
104 32
    }
105
106
    /**
107
     * Returns a value indicating whether to use custom session storage.
108
     * This method should be overridden to return true by child classes that implement custom session storage.
109
     * To implement custom session storage, override these methods: [[openSession()]], [[closeSession()]],
110
     * [[readSession()]], [[writeSession()]], [[destroySession()]] and [[gcSession()]].
111
     * @return boolean whether to use custom storage.
112
     */
113 19
    public function getUseCustomStorage()
114
    {
115 19
        return false;
116
    }
117
118
    /**
119
     * Starts the session.
120
     */
121 19
    public function open()
122
    {
123 19
        if ($this->getIsActive()) {
124 19
            return;
125
        }
126
127 19
        $this->registerSessionHandler();
128
129 19
        $this->setCookieParamsInternal();
130
131 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...
132
133 19
        if ($this->getIsActive()) {
134 19
            Yii::info('Session started', __METHOD__);
135 19
            $this->updateFlashCounters();
136 19
        } else {
137
            $error = error_get_last();
138
            $message = isset($error['message']) ? $error['message'] : 'Failed to start session.';
139
            Yii::error($message, __METHOD__);
140
        }
141 19
    }
142
143
    /**
144
     * Registers session handler.
145
     * @throws \yii\base\InvalidConfigException
146
     */
147 19
    protected function registerSessionHandler()
148
    {
149 19
        if ($this->handler !== null) {
150
            if (!is_object($this->handler)) {
151
                $this->handler = Yii::createObject($this->handler);
152
            }
153
            if (!$this->handler instanceof \SessionHandlerInterface) {
154
                throw new InvalidConfigException('"' . get_class($this) . '::handler" must implement the SessionHandlerInterface.');
155
            }
156
            YII_DEBUG ? session_set_save_handler($this->handler, false) : @session_set_save_handler($this->handler, false);
157 19
        } elseif ($this->getUseCustomStorage()) {
158
            if (YII_DEBUG) {
159
                session_set_save_handler(
160
                    [$this, 'openSession'],
161
                    [$this, 'closeSession'],
162
                    [$this, 'readSession'],
163
                    [$this, 'writeSession'],
164
                    [$this, 'destroySession'],
165
                    [$this, 'gcSession']
166
                );
167
            } else {
168
                @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...
169
                    [$this, 'openSession'],
170
                    [$this, 'closeSession'],
171
                    [$this, 'readSession'],
172
                    [$this, 'writeSession'],
173
                    [$this, 'destroySession'],
174
                    [$this, 'gcSession']
175
                );
176
            }
177
        }
178 19
    }
179
180
    /**
181
     * Ends the current session and store session data.
182
     */
183 27
    public function close()
184
    {
185 27
        if ($this->getIsActive()) {
186 19
            YII_DEBUG ? session_write_close() : @session_write_close();
187 19
        }
188 27
    }
189
190
    /**
191
     * Frees all session variables and destroys all data registered to a session.
192
     */
193 1
    public function destroy()
194
    {
195 1
        if ($this->getIsActive()) {
196 1
            $sessionId = session_id();
197 1
            $this->close();
198 1
            $this->setId($sessionId);
199 1
            $this->open();
200 1
            session_unset();
201 1
            session_destroy();
202 1
            $this->setId($sessionId);
203 1
        }
204 1
    }
205
206
    /**
207
     * @return boolean whether the session has started
208
     */
209 32
    public function getIsActive()
210
    {
211 32
        return session_status() === PHP_SESSION_ACTIVE;
212
    }
213
214
    private $_hasSessionId;
215
216
    /**
217
     * Returns a value indicating whether the current request has sent the session ID.
218
     * The default implementation will check cookie and $_GET using the session name.
219
     * If you send session ID via other ways, you may need to override this method
220
     * or call [[setHasSessionId()]] to explicitly set whether the session ID is sent.
221
     * @return boolean whether the current request has sent the session ID.
222
     */
223 9
    public function getHasSessionId()
224
    {
225 9
        if ($this->_hasSessionId === null) {
226 9
            $name = $this->getName();
227 9
            $request = Yii::$app->getRequest();
228 9
            if (!empty($_COOKIE[$name]) && ini_get('session.use_cookies')) {
229
                $this->_hasSessionId = true;
230 9
            } elseif (!ini_get('session.use_only_cookies') && ini_get('session.use_trans_sid')) {
231
                $this->_hasSessionId = $request->get($name) != '';
232
            } else {
233 9
                $this->_hasSessionId = false;
234
            }
235 9
        }
236
237 9
        return $this->_hasSessionId;
238
    }
239
240
    /**
241
     * Sets the value indicating whether the current request has sent the session ID.
242
     * This method is provided so that you can override the default way of determining
243
     * whether the session ID is sent.
244
     * @param boolean $value whether the current request has sent the session ID.
245
     */
246
    public function setHasSessionId($value)
247
    {
248
        $this->_hasSessionId = $value;
249
    }
250
251
    /**
252
     * Gets the session ID.
253
     * This is a wrapper for [PHP session_id()](http://php.net/manual/en/function.session-id.php).
254
     * @return string the current session ID
255
     */
256
    public function getId()
257
    {
258
        return session_id();
259
    }
260
261
    /**
262
     * Sets the session ID.
263
     * This is a wrapper for [PHP session_id()](http://php.net/manual/en/function.session-id.php).
264
     * @param string $value the session ID for the current session
265
     */
266 1
    public function setId($value)
267
    {
268 1
        session_id($value);
269 1
    }
270
271
    /**
272
     * Updates the current session ID with a newly generated one .
273
     * Please refer to <http://php.net/session_regenerate_id> for more details.
274
     * @param boolean $deleteOldSession Whether to delete the old associated session file or not.
275
     */
276 13
    public function regenerateID($deleteOldSession = false)
277
    {
278 13
        if ($this->getIsActive()) {
279
            // add @ to inhibit possible warning due to race condition
280
            // https://github.com/yiisoft/yii2/pull/1812
281 13
            if (YII_DEBUG && !headers_sent()) {
282
                session_regenerate_id($deleteOldSession);
283
            } else {
284 13
                @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...
285
            }
286 13
        }
287 13
    }
288
289
    /**
290
     * Gets the name of the current session.
291
     * This is a wrapper for [PHP session_name()](http://php.net/manual/en/function.session-name.php).
292
     * @return string the current session name
293
     */
294 9
    public function getName()
295
    {
296 9
        return session_name();
297
    }
298
299
    /**
300
     * Sets the name for the current session.
301
     * This is a wrapper for [PHP session_name()](http://php.net/manual/en/function.session-name.php).
302
     * @param string $value the session name for the current session, must be an alphanumeric string.
303
     * It defaults to "PHPSESSID".
304
     */
305
    public function setName($value)
306
    {
307
        session_name($value);
308
    }
309
310
    /**
311
     * Gets the current session save path.
312
     * This is a wrapper for [PHP session_save_path()](http://php.net/manual/en/function.session-save-path.php).
313
     * @return string the current session save path, defaults to '/tmp'.
314
     */
315
    public function getSavePath()
316
    {
317
        return session_save_path();
318
    }
319
320
    /**
321
     * Sets the current session save path.
322
     * This is a wrapper for [PHP session_save_path()](http://php.net/manual/en/function.session-save-path.php).
323
     * @param string $value the current session save path. This can be either a directory name or a path alias.
324
     * @throws InvalidParamException if the path is not a valid directory
325
     */
326
    public function setSavePath($value)
327
    {
328
        $path = Yii::getAlias($value);
329
        if (is_dir($path)) {
330
            session_save_path($path);
331
        } else {
332
            throw new InvalidParamException("Session save path is not a valid directory: $value");
333
        }
334
    }
335
336
    /**
337
     * @return array the session cookie parameters.
338
     * @see http://php.net/manual/en/function.session-get-cookie-params.php
339
     */
340 19
    public function getCookieParams()
341
    {
342 19
        return array_merge(session_get_cookie_params(), array_change_key_case($this->_cookieParams));
343
    }
344
345
    /**
346
     * Sets the session cookie parameters.
347
     * The cookie parameters passed to this method will be merged with the result
348
     * of `session_get_cookie_params()`.
349
     * @param array $value cookie parameters, valid keys include: `lifetime`, `path`, `domain`, `secure` and `httponly`.
350
     * @throws InvalidParamException if the parameters are incomplete.
351
     * @see http://us2.php.net/manual/en/function.session-set-cookie-params.php
352
     */
353
    public function setCookieParams(array $value)
354
    {
355
        $this->_cookieParams = $value;
356
    }
357
358
    /**
359
     * Sets the session cookie parameters.
360
     * This method is called by [[open()]] when it is about to open the session.
361
     * @throws InvalidParamException if the parameters are incomplete.
362
     * @see http://us2.php.net/manual/en/function.session-set-cookie-params.php
363
     */
364 19
    private function setCookieParamsInternal()
365
    {
366 19
        $data = $this->getCookieParams();
367 19
        if (isset($data['lifetime'], $data['path'], $data['domain'], $data['secure'], $data['httponly'])) {
368 19
            session_set_cookie_params($data['lifetime'], $data['path'], $data['domain'], $data['secure'], $data['httponly']);
369 19
        } else {
370
            throw new InvalidParamException('Please make sure cookieParams contains these elements: lifetime, path, domain, secure and httponly.');
371
        }
372 19
    }
373
374
    /**
375
     * Returns the value indicating whether cookies should be used to store session IDs.
376
     * @return boolean|null the value indicating whether cookies should be used to store session IDs.
377
     * @see setUseCookies()
378
     */
379
    public function getUseCookies()
380
    {
381
        if (ini_get('session.use_cookies') === '0') {
382
            return false;
383
        } elseif (ini_get('session.use_only_cookies') === '1') {
384
            return true;
385
        } else {
386
            return null;
387
        }
388
    }
389
390
    /**
391
     * Sets the value indicating whether cookies should be used to store session IDs.
392
     * Three states are possible:
393
     *
394
     * - true: cookies and only cookies will be used to store session IDs.
395
     * - false: cookies will not be used to store session IDs.
396
     * - null: if possible, cookies will be used to store session IDs; if not, other mechanisms will be used (e.g. GET parameter)
397
     *
398
     * @param boolean|null $value the value indicating whether cookies should be used to store session IDs.
399
     */
400
    public function setUseCookies($value)
401
    {
402
        if ($value === false) {
403
            ini_set('session.use_cookies', '0');
404
            ini_set('session.use_only_cookies', '0');
405
        } elseif ($value === true) {
406
            ini_set('session.use_cookies', '1');
407
            ini_set('session.use_only_cookies', '1');
408
        } else {
409
            ini_set('session.use_cookies', '1');
410
            ini_set('session.use_only_cookies', '0');
411
        }
412
    }
413
414
    /**
415
     * @return float the probability (percentage) that the GC (garbage collection) process is started on every session initialization, defaults to 1 meaning 1% chance.
416
     */
417
    public function getGCProbability()
418
    {
419
        return (float) (ini_get('session.gc_probability') / ini_get('session.gc_divisor') * 100);
420
    }
421
422
    /**
423
     * @param float $value the probability (percentage) that the GC (garbage collection) process is started on every session initialization.
424
     * @throws InvalidParamException if the value is not between 0 and 100.
425
     */
426
    public function setGCProbability($value)
427
    {
428
        if ($value >= 0 && $value <= 100) {
429
            // percent * 21474837 / 2147483647 ≈ percent * 0.01
430
            ini_set('session.gc_probability', floor($value * 21474836.47));
431
            ini_set('session.gc_divisor', 2147483647);
432
        } else {
433
            throw new InvalidParamException('GCProbability must be a value between 0 and 100.');
434
        }
435
    }
436
437
    /**
438
     * @return boolean whether transparent sid support is enabled or not, defaults to false.
439
     */
440
    public function getUseTransparentSessionID()
441
    {
442
        return ini_get('session.use_trans_sid') == 1;
443
    }
444
445
    /**
446
     * @param boolean $value whether transparent sid support is enabled or not.
447
     */
448
    public function setUseTransparentSessionID($value)
449
    {
450
        ini_set('session.use_trans_sid', $value ? '1' : '0');
451
    }
452
453
    /**
454
     * @return integer the number of seconds after which data will be seen as 'garbage' and cleaned up.
455
     * The default value is 1440 seconds (or the value of "session.gc_maxlifetime" set in php.ini).
456
     */
457 4
    public function getTimeout()
458
    {
459 4
        return (int) ini_get('session.gc_maxlifetime');
460
    }
461
462
    /**
463
     * @param integer $value the number of seconds after which data will be seen as 'garbage' and cleaned up
464
     */
465
    public function setTimeout($value)
466
    {
467
        ini_set('session.gc_maxlifetime', $value);
468
    }
469
470
    /**
471
     * Session open handler.
472
     * This method should be overridden if [[useCustomStorage]] returns true.
473
     * Do not call this method directly.
474
     * @param string $savePath session save path
475
     * @param string $sessionName session name
476
     * @return boolean whether session is opened successfully
477
     */
478
    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...
479
    {
480
        return true;
481
    }
482
483
    /**
484
     * Session close handler.
485
     * This method should be overridden if [[useCustomStorage]] returns true.
486
     * Do not call this method directly.
487
     * @return boolean whether session is closed successfully
488
     */
489
    public function closeSession()
490
    {
491
        return true;
492
    }
493
494
    /**
495
     * Session read handler.
496
     * This method should be overridden if [[useCustomStorage]] returns true.
497
     * Do not call this method directly.
498
     * @param string $id session ID
499
     * @return string the session data
500
     */
501
    public function readSession($id)
502
    {
503
        return '';
504
    }
505
506
    /**
507
     * Session write handler.
508
     * This method should be overridden if [[useCustomStorage]] returns true.
509
     * Do not call this method directly.
510
     * @param string $id session ID
511
     * @param string $data session data
512
     * @return boolean whether session write is successful
513
     */
514
    public function writeSession($id, $data)
515
    {
516
        return true;
517
    }
518
519
    /**
520
     * Session destroy handler.
521
     * This method should be overridden if [[useCustomStorage]] returns true.
522
     * Do not call this method directly.
523
     * @param string $id session ID
524
     * @return boolean whether session is destroyed successfully
525
     */
526
    public function destroySession($id)
527
    {
528
        return true;
529
    }
530
531
    /**
532
     * Session GC (garbage collection) handler.
533
     * This method should be overridden if [[useCustomStorage]] returns true.
534
     * Do not call this method directly.
535
     * @param integer $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up.
536
     * @return boolean whether session is GCed successfully
537
     */
538
    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...
539
    {
540
        return true;
541
    }
542
543
    /**
544
     * Returns an iterator for traversing the session variables.
545
     * This method is required by the interface [[\IteratorAggregate]].
546
     * @return SessionIterator an iterator for traversing the session variables.
547
     */
548
    public function getIterator()
549
    {
550
        $this->open();
551
        return new SessionIterator;
552
    }
553
554
    /**
555
     * Returns the number of items in the session.
556
     * @return integer the number of session variables
557
     */
558
    public function getCount()
559
    {
560
        $this->open();
561
        return count($_SESSION);
562
    }
563
564
    /**
565
     * Returns the number of items in the session.
566
     * This method is required by [[\Countable]] interface.
567
     * @return integer number of items in the session.
568
     */
569
    public function count()
570
    {
571
        return $this->getCount();
572
    }
573
574
    /**
575
     * Returns the session variable value with the session variable name.
576
     * If the session variable does not exist, the `$defaultValue` will be returned.
577
     * @param string $key the session variable name
578
     * @param mixed $defaultValue the default value to be returned when the session variable does not exist.
579
     * @return mixed the session variable value, or $defaultValue if the session variable does not exist.
580
     */
581 19
    public function get($key, $defaultValue = null)
582
    {
583 19
        $this->open();
584 19
        return isset($_SESSION[$key]) ? $_SESSION[$key] : $defaultValue;
585
    }
586
587
    /**
588
     * Adds a session variable.
589
     * If the specified name already exists, the old value will be overwritten.
590
     * @param string $key session variable name
591
     * @param mixed $value session variable value
592
     */
593 16
    public function set($key, $value)
594
    {
595 16
        $this->open();
596 16
        $_SESSION[$key] = $value;
597 16
    }
598
599
    /**
600
     * Removes a session variable.
601
     * @param string $key the name of the session variable to be removed
602
     * @return mixed the removed value, null if no such session variable.
603
     */
604 13
    public function remove($key)
605
    {
606 13
        $this->open();
607 13
        if (isset($_SESSION[$key])) {
608 13
            $value = $_SESSION[$key];
609 13
            unset($_SESSION[$key]);
610
611 13
            return $value;
612
        } else {
613 13
            return null;
614
        }
615
    }
616
617
    /**
618
     * Removes all session variables
619
     */
620 3
    public function removeAll()
621
    {
622 3
        $this->open();
623 3
        foreach (array_keys($_SESSION) as $key) {
624 3
            unset($_SESSION[$key]);
625 3
        }
626 3
    }
627
628
    /**
629
     * @param mixed $key session variable name
630
     * @return boolean whether there is the named session variable
631
     */
632
    public function has($key)
633
    {
634
        $this->open();
635
        return isset($_SESSION[$key]);
636
    }
637
638
    /**
639
     * Updates the counters for flash messages and removes outdated flash messages.
640
     * This method should only be called once in [[init()]].
641
     */
642 19
    protected function updateFlashCounters()
643
    {
644 19
        $counters = $this->get($this->flashParam, []);
645 19
        if (is_array($counters)) {
646 19
            foreach ($counters as $key => $count) {
647
                if ($count > 0) {
648
                    unset($counters[$key], $_SESSION[$key]);
649
                } elseif ($count == 0) {
650
                    $counters[$key]++;
651
                }
652 19
            }
653 19
            $_SESSION[$this->flashParam] = $counters;
654 19
        } else {
655
            // fix the unexpected problem that flashParam doesn't return an array
656
            unset($_SESSION[$this->flashParam]);
657
        }
658 19
    }
659
660
    /**
661
     * Returns a flash message.
662
     * @param string $key the key identifying the flash message
663
     * @param mixed $defaultValue value to be returned if the flash message does not exist.
664
     * @param boolean $delete whether to delete this flash message right after this method is called.
665
     * If false, the flash message will be automatically deleted in the next request.
666
     * @return mixed the flash message or an array of messages if addFlash was used
667
     * @see setFlash()
668
     * @see addFlash()
669
     * @see hasFlash()
670
     * @see getAllFlashes()
671
     * @see removeFlash()
672
     */
673
    public function getFlash($key, $defaultValue = null, $delete = false)
674
    {
675
        $counters = $this->get($this->flashParam, []);
676
        if (isset($counters[$key])) {
677
            $value = $this->get($key, $defaultValue);
678
            if ($delete) {
679
                $this->removeFlash($key);
680
            } elseif ($counters[$key] < 0) {
681
                // mark for deletion in the next request
682
                $counters[$key] = 1;
683
                $_SESSION[$this->flashParam] = $counters;
684
            }
685
686
            return $value;
687
        } else {
688
            return $defaultValue;
689
        }
690
    }
691
692
    /**
693
     * Returns all flash messages.
694
     *
695
     * You may use this method to display all the flash messages in a view file:
696
     *
697
     * ```php
698
     * <?php
699
     * foreach (Yii::$app->session->getAllFlashes() as $key => $message) {
700
     *     echo '<div class="alert alert-' . $key . '">' . $message . '</div>';
701
     * } ?>
702
     * ```
703
     *
704
     * With the above code you can use the [bootstrap alert][] classes such as `success`, `info`, `danger`
705
     * as the flash message key to influence the color of the div.
706
     *
707
     * Note that if you use [[addFlash()]], `$message` will be an array, and you will have to adjust the above code.
708
     *
709
     * [bootstrap alert]: http://getbootstrap.com/components/#alerts
710
     *
711
     * @param boolean $delete whether to delete the flash messages right after this method is called.
712
     * If false, the flash messages will be automatically deleted in the next request.
713
     * @return array flash messages (key => message or key => [message1, message2]).
714
     * @see setFlash()
715
     * @see addFlash()
716
     * @see getFlash()
717
     * @see hasFlash()
718
     * @see removeFlash()
719
     */
720
    public function getAllFlashes($delete = false)
721
    {
722
        $counters = $this->get($this->flashParam, []);
723
        $flashes = [];
724
        foreach (array_keys($counters) as $key) {
725
            if (array_key_exists($key, $_SESSION)) {
726
                $flashes[$key] = $_SESSION[$key];
727
                if ($delete) {
728
                    unset($counters[$key], $_SESSION[$key]);
729
                } elseif ($counters[$key] < 0) {
730
                    // mark for deletion in the next request
731
                    $counters[$key] = 1;
732
                }
733
            } else {
734
                unset($counters[$key]);
735
            }
736
        }
737
738
        $_SESSION[$this->flashParam] = $counters;
739
740
        return $flashes;
741
    }
742
743
    /**
744
     * Sets a flash message.
745
     * A flash message will be automatically deleted after it is accessed in a request and the deletion will happen
746
     * in the next request.
747
     * If there is already an existing flash message with the same key, it will be overwritten by the new one.
748
     * @param string $key the key identifying the flash message. Note that flash messages
749
     * and normal session variables share the same name space. If you have a normal
750
     * session variable using the same name, its value will be overwritten by this method.
751
     * @param mixed $value flash message
752
     * @param boolean $removeAfterAccess whether the flash message should be automatically removed only if
753
     * it is accessed. If false, the flash message will be automatically removed after the next request,
754
     * regardless if it is accessed or not. If true (default value), the flash message will remain until after
755
     * it is accessed.
756
     * @see getFlash()
757
     * @see addFlash()
758
     * @see removeFlash()
759
     */
760
    public function setFlash($key, $value = true, $removeAfterAccess = true)
761
    {
762
        $counters = $this->get($this->flashParam, []);
763
        $counters[$key] = $removeAfterAccess ? -1 : 0;
764
        $_SESSION[$key] = $value;
765
        $_SESSION[$this->flashParam] = $counters;
766
    }
767
768
    /**
769
     * Adds a flash message.
770
     * If there are existing flash messages with the same key, the new one will be appended to the existing message array.
771
     * @param string $key the key identifying the flash message.
772
     * @param mixed $value flash message
773
     * @param boolean $removeAfterAccess whether the flash message should be automatically removed only if
774
     * it is accessed. If false, the flash message will be automatically removed after the next request,
775
     * regardless if it is accessed or not. If true (default value), the flash message will remain until after
776
     * it is accessed.
777
     * @see getFlash()
778
     * @see setFlash()
779
     * @see removeFlash()
780
     */
781
    public function addFlash($key, $value = true, $removeAfterAccess = true)
782
    {
783
        $counters = $this->get($this->flashParam, []);
784
        $counters[$key] = $removeAfterAccess ? -1 : 0;
785
        $_SESSION[$this->flashParam] = $counters;
786
        if (empty($_SESSION[$key])) {
787
            $_SESSION[$key] = [$value];
788
        } else {
789
            if (is_array($_SESSION[$key])) {
790
                $_SESSION[$key][] = $value;
791
            } else {
792
                $_SESSION[$key] = [$_SESSION[$key], $value];
793
            }
794
        }
795
    }
796
797
    /**
798
     * Removes a flash message.
799
     * @param string $key the key identifying the flash message. Note that flash messages
800
     * and normal session variables share the same name space.  If you have a normal
801
     * session variable using the same name, it will be removed by this method.
802
     * @return mixed the removed flash message. Null if the flash message does not exist.
803
     * @see getFlash()
804
     * @see setFlash()
805
     * @see addFlash()
806
     * @see removeAllFlashes()
807
     */
808
    public function removeFlash($key)
809
    {
810
        $counters = $this->get($this->flashParam, []);
811
        $value = isset($_SESSION[$key], $counters[$key]) ? $_SESSION[$key] : null;
812
        unset($counters[$key], $_SESSION[$key]);
813
        $_SESSION[$this->flashParam] = $counters;
814
815
        return $value;
816
    }
817
818
    /**
819
     * Removes all flash messages.
820
     * Note that flash messages and normal session variables share the same name space.
821
     * If you have a normal session variable using the same name, it will be removed
822
     * by this method.
823
     * @see getFlash()
824
     * @see setFlash()
825
     * @see addFlash()
826
     * @see removeFlash()
827
     */
828
    public function removeAllFlashes()
829
    {
830
        $counters = $this->get($this->flashParam, []);
831
        foreach (array_keys($counters) as $key) {
832
            unset($_SESSION[$key]);
833
        }
834
        unset($_SESSION[$this->flashParam]);
835
    }
836
837
    /**
838
     * Returns a value indicating whether there are flash messages associated with the specified key.
839
     * @param string $key key identifying the flash message type
840
     * @return boolean whether any flash messages exist under specified key
841
     */
842
    public function hasFlash($key)
843
    {
844
        return $this->getFlash($key) !== null;
845
    }
846
847
    /**
848
     * This method is required by the interface [[\ArrayAccess]].
849
     * @param mixed $offset the offset to check on
850
     * @return boolean
851
     */
852
    public function offsetExists($offset)
853
    {
854
        $this->open();
855
856
        return isset($_SESSION[$offset]);
857
    }
858
859
    /**
860
     * This method is required by the interface [[\ArrayAccess]].
861
     * @param integer $offset the offset to retrieve element.
862
     * @return mixed the element at the offset, null if no element is found at the offset
863
     */
864
    public function offsetGet($offset)
865
    {
866
        $this->open();
867
868
        return isset($_SESSION[$offset]) ? $_SESSION[$offset] : null;
869
    }
870
871
    /**
872
     * This method is required by the interface [[\ArrayAccess]].
873
     * @param integer $offset the offset to set element
874
     * @param mixed $item the element value
875
     */
876
    public function offsetSet($offset, $item)
877
    {
878
        $this->open();
879
        $_SESSION[$offset] = $item;
880
    }
881
882
    /**
883
     * This method is required by the interface [[\ArrayAccess]].
884
     * @param mixed $offset the offset to unset element
885
     */
886
    public function offsetUnset($offset)
887
    {
888
        $this->open();
889
        unset($_SESSION[$offset]);
890
    }
891
}
892