You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

867 satır
30KB

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