DbSession.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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\db\Connection;
  10. use yii\db\Query;
  11. use yii\base\InvalidConfigException;
  12. /**
  13. * DbSession extends [[Session]] by using database as session data storage.
  14. *
  15. * By default, DbSession stores session data in a DB table named 'tbl_session'. This table
  16. * must be pre-created. The table name can be changed by setting [[sessionTable]].
  17. *
  18. * The following example shows how you can configure the application to use DbSession:
  19. * Add the following to your application config under `components`:
  20. *
  21. * ~~~
  22. * 'session' => [
  23. * 'class' => 'yii\web\DbSession',
  24. * // 'db' => 'mydb',
  25. * // 'sessionTable' => 'my_session',
  26. * ]
  27. * ~~~
  28. *
  29. * @property boolean $useCustomStorage Whether to use custom storage. This property is read-only.
  30. *
  31. * @author Qiang Xue <[email protected]>
  32. * @since 2.0
  33. */
  34. class DbSession extends Session
  35. {
  36. /**
  37. * @var Connection|string the DB connection object or the application component ID of the DB connection.
  38. * After the DbSession object is created, if you want to change this property, you should only assign it
  39. * with a DB connection object.
  40. */
  41. public $db = 'db';
  42. /**
  43. * @var string the name of the DB table that stores the session data.
  44. * The table should be pre-created as follows:
  45. *
  46. * ~~~
  47. * CREATE TABLE tbl_session
  48. * (
  49. * id CHAR(40) NOT NULL PRIMARY KEY,
  50. * expire INTEGER,
  51. * data BLOB
  52. * )
  53. * ~~~
  54. *
  55. * where 'BLOB' refers to the BLOB-type of your preferred DBMS. Below are the BLOB type
  56. * that can be used for some popular DBMS:
  57. *
  58. * - MySQL: LONGBLOB
  59. * - PostgreSQL: BYTEA
  60. * - MSSQL: BLOB
  61. *
  62. * When using DbSession in a production server, we recommend you create a DB index for the 'expire'
  63. * column in the session table to improve the performance.
  64. */
  65. public $sessionTable = '{{%session}}';
  66. /**
  67. * Initializes the DbSession component.
  68. * This method will initialize the [[db]] property to make sure it refers to a valid DB connection.
  69. * @throws InvalidConfigException if [[db]] is invalid.
  70. */
  71. public function init()
  72. {
  73. if (is_string($this->db)) {
  74. $this->db = Yii::$app->getComponent($this->db);
  75. }
  76. if (!$this->db instanceof Connection) {
  77. throw new InvalidConfigException("DbSession::db must be either a DB connection instance or the application component ID of a DB connection.");
  78. }
  79. parent::init();
  80. }
  81. /**
  82. * Returns a value indicating whether to use custom session storage.
  83. * This method overrides the parent implementation and always returns true.
  84. * @return boolean whether to use custom storage.
  85. */
  86. public function getUseCustomStorage()
  87. {
  88. return true;
  89. }
  90. /**
  91. * Updates the current session ID with a newly generated one .
  92. * Please refer to <http://php.net/session_regenerate_id> for more details.
  93. * @param boolean $deleteOldSession Whether to delete the old associated session file or not.
  94. */
  95. public function regenerateID($deleteOldSession = false)
  96. {
  97. $oldID = session_id();
  98. // if no session is started, there is nothing to regenerate
  99. if (empty($oldID)) {
  100. return;
  101. }
  102. parent::regenerateID(false);
  103. $newID = session_id();
  104. $query = new Query;
  105. $row = $query->from($this->sessionTable)
  106. ->where(['id' => $oldID])
  107. ->createCommand($this->db)
  108. ->queryOne();
  109. if ($row !== false) {
  110. if ($deleteOldSession) {
  111. $this->db->createCommand()
  112. ->update($this->sessionTable, ['id' => $newID], ['id' => $oldID])
  113. ->execute();
  114. } else {
  115. $row['id'] = $newID;
  116. $this->db->createCommand()
  117. ->insert($this->sessionTable, $row)
  118. ->execute();
  119. }
  120. } else {
  121. // shouldn't reach here normally
  122. $this->db->createCommand()
  123. ->insert($this->sessionTable, [
  124. 'id' => $newID,
  125. 'expire' => time() + $this->getTimeout(),
  126. ])->execute();
  127. }
  128. }
  129. /**
  130. * Session read handler.
  131. * Do not call this method directly.
  132. * @param string $id session ID
  133. * @return string the session data
  134. */
  135. public function readSession($id)
  136. {
  137. $query = new Query;
  138. $data = $query->select(['data'])
  139. ->from($this->sessionTable)
  140. ->where('[[expire]]>:expire AND [[id]]=:id', [':expire' => time(), ':id' => $id])
  141. ->createCommand($this->db)
  142. ->queryScalar();
  143. return $data === false ? '' : $data;
  144. }
  145. /**
  146. * Session write handler.
  147. * Do not call this method directly.
  148. * @param string $id session ID
  149. * @param string $data session data
  150. * @return boolean whether session write is successful
  151. */
  152. public function writeSession($id, $data)
  153. {
  154. // exception must be caught in session write handler
  155. // http://us.php.net/manual/en/function.session-set-save-handler.php
  156. try {
  157. $expire = time() + $this->getTimeout();
  158. $query = new Query;
  159. $exists = $query->select(['id'])
  160. ->from($this->sessionTable)
  161. ->where(['id' => $id])
  162. ->createCommand($this->db)
  163. ->queryScalar();
  164. if ($exists === false) {
  165. $this->db->createCommand()
  166. ->insert($this->sessionTable, [
  167. 'id' => $id,
  168. 'data' => $data,
  169. 'expire' => $expire,
  170. ])->execute();
  171. } else {
  172. $this->db->createCommand()
  173. ->update($this->sessionTable, ['data' => $data, 'expire' => $expire], ['id' => $id])
  174. ->execute();
  175. }
  176. } catch (\Exception $e) {
  177. if (YII_DEBUG) {
  178. echo $e->getMessage();
  179. }
  180. // it is too late to log an error message here
  181. return false;
  182. }
  183. return true;
  184. }
  185. /**
  186. * Session destroy handler.
  187. * Do not call this method directly.
  188. * @param string $id session ID
  189. * @return boolean whether session is destroyed successfully
  190. */
  191. public function destroySession($id)
  192. {
  193. $this->db->createCommand()
  194. ->delete($this->sessionTable, ['id' => $id])
  195. ->execute();
  196. return true;
  197. }
  198. /**
  199. * Session GC (garbage collection) handler.
  200. * Do not call this method directly.
  201. * @param integer $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up.
  202. * @return boolean whether session is GCed successfully
  203. */
  204. public function gcSession($maxLifetime)
  205. {
  206. $this->db->createCommand()
  207. ->delete($this->sessionTable, '[[expire]]<:expire', [':expire' => time()])
  208. ->execute();
  209. return true;
  210. }
  211. }