CDbHttpSession.php 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. <?php
  2. /**
  3. * CDbHttpSession class
  4. *
  5. * @author Qiang Xue <qiang.xue@gmail.com>
  6. * @link http://www.yiiframework.com/
  7. * @copyright 2008-2013 Yii Software LLC
  8. * @license http://www.yiiframework.com/license/
  9. */
  10. /**
  11. * CDbHttpSession extends {@link CHttpSession} by using database as session data storage.
  12. *
  13. * CDbHttpSession stores session data in a DB table named 'YiiSession'. The table name
  14. * can be changed by setting {@link sessionTableName}. If the table does not exist,
  15. * it will be automatically created if {@link autoCreateSessionTable} is set true.
  16. *
  17. * The following is the table structure:
  18. *
  19. * <pre>
  20. * CREATE TABLE YiiSession
  21. * (
  22. * id CHAR(32) PRIMARY KEY,
  23. * expire INTEGER,
  24. * data BLOB
  25. * )
  26. * </pre>
  27. * Where 'BLOB' refers to the BLOB-type of your preffered database.
  28. *
  29. * Note that if your session IDs are more than 32 characters (can be changed via
  30. * session.hash_bits_per_character or session.hash_function) you should modify
  31. * SQL schema accordingly.
  32. *
  33. * CDbHttpSession relies on {@link http://www.php.net/manual/en/ref.pdo.php PDO} to access database.
  34. *
  35. * By default, it will use an SQLite3 database named 'session-YiiVersion.db' under the application runtime directory.
  36. * You can also specify {@link connectionID} so that it makes use of a DB application component to access database.
  37. *
  38. * When using CDbHttpSession in a production server, we recommend you pre-create the session DB table
  39. * and set {@link autoCreateSessionTable} to be false. This will greatly improve the performance.
  40. * You may also create a DB index for the 'expire' column in the session table to further improve the performance.
  41. *
  42. * @property boolean $useCustomStorage Whether to use custom storage.
  43. *
  44. * @author Qiang Xue <qiang.xue@gmail.com>
  45. * @package system.web
  46. * @since 1.0
  47. */
  48. class CDbHttpSession extends CHttpSession
  49. {
  50. /**
  51. * @var string the ID of a {@link CDbConnection} application component. If not set, a SQLite database
  52. * will be automatically created and used. The SQLite database file is
  53. * is <code>protected/runtime/session-YiiVersion.db</code>.
  54. */
  55. public $connectionID;
  56. /**
  57. * @var string the name of the DB table to store session content.
  58. * Note, if {@link autoCreateSessionTable} is false and you want to create the DB table manually by yourself,
  59. * you need to make sure the DB table is of the following structure:
  60. * <pre>
  61. * (id CHAR(32) PRIMARY KEY, expire INTEGER, data BLOB)
  62. * </pre>
  63. * @see autoCreateSessionTable
  64. */
  65. public $sessionTableName='YiiSession';
  66. /**
  67. * @var boolean whether the session DB table should be automatically created if not exists. Defaults to true.
  68. * @see sessionTableName
  69. */
  70. public $autoCreateSessionTable=true;
  71. /**
  72. * @var CDbConnection the DB connection instance
  73. */
  74. private $_db;
  75. /**
  76. * Returns a value indicating whether to use custom session storage.
  77. * This method overrides the parent implementation and always returns true.
  78. * @return boolean whether to use custom storage.
  79. */
  80. public function getUseCustomStorage()
  81. {
  82. return true;
  83. }
  84. /**
  85. * Updates the current session id with a newly generated one.
  86. * Please refer to {@link http://php.net/session_regenerate_id} for more details.
  87. * @param boolean $deleteOldSession Whether to delete the old associated session file or not.
  88. * @since 1.1.8
  89. */
  90. public function regenerateID($deleteOldSession=false)
  91. {
  92. $oldID=session_id();
  93. // if no session is started, there is nothing to regenerate
  94. if(empty($oldID))
  95. return;
  96. parent::regenerateID(false);
  97. $newID=session_id();
  98. $db=$this->getDbConnection();
  99. $row=$db->createCommand()
  100. ->select()
  101. ->from($this->sessionTableName)
  102. ->where('id=:id',array(':id'=>$oldID))
  103. ->queryRow();
  104. if($row!==false)
  105. {
  106. if($deleteOldSession)
  107. $db->createCommand()->update($this->sessionTableName,array(
  108. 'id'=>$newID
  109. ),'id=:oldID',array(':oldID'=>$oldID));
  110. else
  111. {
  112. $row['id']=$newID;
  113. $db->createCommand()->insert($this->sessionTableName, $row);
  114. }
  115. }
  116. else
  117. {
  118. // shouldn't reach here normally
  119. $db->createCommand()->insert($this->sessionTableName, array(
  120. 'id'=>$newID,
  121. 'expire'=>time()+$this->getTimeout(),
  122. 'data'=>'',
  123. ));
  124. }
  125. }
  126. /**
  127. * Creates the session DB table.
  128. * @param CDbConnection $db the database connection
  129. * @param string $tableName the name of the table to be created
  130. */
  131. protected function createSessionTable($db,$tableName)
  132. {
  133. switch($db->getDriverName())
  134. {
  135. case 'mysql':
  136. $blob='LONGBLOB';
  137. break;
  138. case 'pgsql':
  139. $blob='BYTEA';
  140. break;
  141. case 'sqlsrv':
  142. case 'mssql':
  143. case 'dblib':
  144. $blob='VARBINARY(MAX)';
  145. break;
  146. default:
  147. $blob='BLOB';
  148. break;
  149. }
  150. $db->createCommand()->createTable($tableName,array(
  151. 'id'=>'CHAR(32) PRIMARY KEY',
  152. "coach_id" => "bigint",
  153. "update_time" => "bigint",
  154. 'expire'=>'integer',
  155. 'data'=>$blob,
  156. ));
  157. }
  158. /**
  159. * @return CDbConnection the DB connection instance
  160. * @throws CException if {@link connectionID} does not point to a valid application component.
  161. */
  162. protected function getDbConnection()
  163. {
  164. if($this->_db!==null)
  165. return $this->_db;
  166. elseif(($id=$this->connectionID)!==null)
  167. {
  168. if(($this->_db=Yii::app()->getComponent($id)) instanceof CDbConnection)
  169. return $this->_db;
  170. else
  171. throw new CException(Yii::t('yii','CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.',
  172. array('{id}'=>$id)));
  173. }
  174. else
  175. {
  176. $dbFile=Yii::app()->getRuntimePath().DIRECTORY_SEPARATOR.'session-'.Yii::getVersion().'.db';
  177. return $this->_db=new CDbConnection('sqlite:'.$dbFile);
  178. }
  179. }
  180. /**
  181. * Session open handler.
  182. * Do not call this method directly.
  183. * @param string $savePath session save path
  184. * @param string $sessionName session name
  185. * @return boolean whether session is opened successfully
  186. */
  187. public function openSession($savePath,$sessionName)
  188. {
  189. if($this->autoCreateSessionTable)
  190. {
  191. $db=$this->getDbConnection();
  192. $db->setActive(true);
  193. try
  194. {
  195. $db->createCommand()->delete($this->sessionTableName,'expire<:expire',array(':expire'=>time()));
  196. }
  197. catch(Exception $e)
  198. {
  199. $this->createSessionTable($db,$this->sessionTableName);
  200. }
  201. }
  202. return true;
  203. }
  204. /**
  205. * Session read handler.
  206. * Do not call this method directly.
  207. * @param string $id session ID
  208. * @return string the session data
  209. */
  210. public function readSession($id)
  211. {
  212. $expire=time()+$this->getTimeout();
  213. $db=$this->getDbConnection();
  214. if($db->getDriverName()=='sqlsrv' || $db->getDriverName()=='mssql' || $db->getDriverName()=='dblib')
  215. $select='CONVERT(VARCHAR(MAX), data)';
  216. else
  217. $select='data';
  218. $db->createCommand()->update($this->sessionTableName,array(
  219. "update_time" => time(),
  220. 'expire'=>$expire,
  221. ),'id=:id',array(':id'=>$id));
  222. $data=$db->createCommand()
  223. ->select($select)
  224. ->from($this->sessionTableName)
  225. ->where('expire>:expire AND id=:id',array(':expire'=>time(),':id'=>$id))
  226. ->queryScalar();
  227. return $data===false?'':$data;
  228. }
  229. /**
  230. * Session write handler.
  231. * Do not call this method directly.
  232. * @param string $id session ID
  233. * @param string $data session data
  234. * @return boolean whether session write is successful
  235. */
  236. public function writeSession($id,$data)
  237. {
  238. // exception must be caught in session write handler
  239. // http://us.php.net/manual/en/function.session-set-save-handler.php
  240. try
  241. {
  242. $expire=time()+$this->getTimeout();
  243. $db=$this->getDbConnection();
  244. $coachId = isset($_SESSION["coachInfo"]["coach_id"]) ? $_SESSION["coachInfo"]["coach_id"] : null;
  245. if($db->getDriverName()=='sqlsrv' || $db->getDriverName()=='mssql' || $db->getDriverName()=='dblib')
  246. $data=new CDbExpression('CONVERT(VARBINARY(MAX), '.$db->quoteValue($data).')');
  247. if($db->createCommand()->select('id')->from($this->sessionTableName)->where('id=:id',array(':id'=>$id))->queryScalar()===false)
  248. $db->createCommand()->insert($this->sessionTableName,array(
  249. 'id'=>$id,
  250. "coach_id" => $coachId,
  251. "update_time" => time(),
  252. 'data'=>$data,
  253. 'expire'=>$expire,
  254. ));
  255. else
  256. $db->createCommand()->update($this->sessionTableName,array(
  257. "coach_id" => $coachId,
  258. "update_time" => time(),
  259. 'data'=>$data,
  260. 'expire'=>$expire
  261. ),'id=:id',array(':id'=>$id));
  262. }
  263. catch(Exception $e)
  264. {
  265. if(YII_DEBUG)
  266. echo $e->getMessage();
  267. // it is too late to log an error message here
  268. return false;
  269. }
  270. return true;
  271. }
  272. /**
  273. * Session destroy handler.
  274. * Do not call this method directly.
  275. * @param string $id session ID
  276. * @return boolean whether session is destroyed successfully
  277. */
  278. public function destroySession($id)
  279. {
  280. $this->getDbConnection()->createCommand()
  281. ->delete($this->sessionTableName,'id=:id',array(':id'=>$id));
  282. return true;
  283. }
  284. /**
  285. * Session GC (garbage collection) handler.
  286. * Do not call this method directly.
  287. * @param integer $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up.
  288. * @return boolean whether session is GCed successfully
  289. */
  290. public function gcSession($maxLifetime)
  291. {
  292. $time = time() - $this->getTimeout();
  293. $this->getDbConnection()->createCommand()
  294. ->delete($this->sessionTableName,'update_time<=:time',array(':time'=>$time));
  295. return true;
  296. }
  297. }