142 lines
4.5KB

  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. /**
  9. * MultiFieldSession is the base class for session storage implementations with multi-field data storage support.
  10. *
  11. * With multi-field data storage, session data can be split between several fields in the storage record.
  12. * Using such a storage allows saving particular session data into separated field, which then can be used
  13. * to manipulate sessions in the way plain PHP does not allow.
  14. *
  15. * For example the ID of the authenticated user can be saved as separated column in the MySQL 'session' table,
  16. * which allows to query all active sessions for a particular user or terminate them at will.
  17. *
  18. * Customizing of the session writing is performed via [[writeCallback]], reading via [[readCallback]].
  19. *
  20. * While extending this class you should use [[composeFields()]] method - while writing the session data into the storage and
  21. * [[extractData()]] - while reading session data from the storage.
  22. *
  23. * @property boolean $useCustomStorage Whether to use custom storage. This property is read-only.
  24. *
  25. * @author Paul Klimov <klimov.paul@gmail.com>
  26. * @since 2.0.6
  27. */
  28. abstract class MultiFieldSession extends Session
  29. {
  30. /**
  31. * @var callable a callback that will be called during session data reading.
  32. * The signature of the callback should be as follows:
  33. *
  34. * ```
  35. * function ($fields)
  36. * ```
  37. *
  38. * where `$fields` is the storage field set for read session and `$session` is this session instance.
  39. * If callback returns an array, it will be merged into the session data.
  40. *
  41. * For example:
  42. *
  43. * ```php
  44. * function ($fields) {
  45. * return [
  46. * 'expireDate' => Yii::$app->formatter->asDate($fields['expire']),
  47. * ];
  48. * }
  49. * ```
  50. */
  51. public $readCallback;
  52. /**
  53. * @var callable a callback that will be called during session data writing.
  54. * The signature of the callback should be as follows:
  55. *
  56. * ```
  57. * function ($session)
  58. * ```
  59. *
  60. * where `$session` is this session instance, this variable can be used to retrieve session data.
  61. * Callback should return the actual fields set, which should be saved into the session storage.
  62. *
  63. * For example:
  64. *
  65. * ```php
  66. * function ($session) {
  67. * return [
  68. * 'user_id' => Yii::$app->user->id,
  69. * 'ip' => $_SERVER['REMOTE_ADDR'],
  70. * 'is_trusted' => $session->get('is_trusted', false),
  71. * ];
  72. * }
  73. * ```
  74. */
  75. public $writeCallback;
  76. /**
  77. * Returns a value indicating whether to use custom session storage.
  78. * This method overrides the parent implementation and always returns true.
  79. * @return boolean whether to use custom storage.
  80. */
  81. public function getUseCustomStorage()
  82. {
  83. return true;
  84. }
  85. /**
  86. * Composes storage field set for session writing.
  87. * @param string $id session id
  88. * @param string $data session data
  89. * @return array storage fields
  90. */
  91. protected function composeFields($id, $data)
  92. {
  93. $fields = [
  94. 'data' => $data,
  95. ];
  96. if ($this->writeCallback !== null) {
  97. $fields = array_merge(
  98. $fields,
  99. call_user_func($this->writeCallback, $this)
  100. );
  101. if (!is_string($fields['data'])) {
  102. $_SESSION = $fields['data'];
  103. $fields['data'] = session_encode();
  104. }
  105. }
  106. // ensure 'id' and 'expire' are never affected by [[writeCallback]]
  107. $fields = array_merge($fields, [
  108. 'id' => $id,
  109. 'expire' => time() + $this->getTimeout(),
  110. ]);
  111. return $fields;
  112. }
  113. /**
  114. * Extracts session data from storage field set.
  115. * @param array $fields storage fields.
  116. * @return string session data.
  117. */
  118. protected function extractData($fields)
  119. {
  120. if ($this->readCallback !== null) {
  121. if (!isset($fields['data'])) {
  122. $fields['data'] = '';
  123. }
  124. $extraData = call_user_func($this->readCallback, $fields);
  125. if (!empty($extraData)) {
  126. session_decode($fields['data']);
  127. $_SESSION = array_merge((array)$_SESSION, (array)$extraData);
  128. return session_encode();
  129. }
  130. return $fields['data'];
  131. } else {
  132. return isset($fields['data']) ? $fields['data'] : '';
  133. }
  134. }
  135. }