diff options
Diffstat (limited to 'framework/web/CDbHttpSession.php')
| -rw-r--r-- | framework/web/CDbHttpSession.php | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/framework/web/CDbHttpSession.php b/framework/web/CDbHttpSession.php new file mode 100644 index 0000000..de42d71 --- /dev/null +++ b/framework/web/CDbHttpSession.php @@ -0,0 +1,267 @@ +<?php +/** + * CDbHttpSession class + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CDbHttpSession extends {@link CHttpSession} by using database as session data storage. + * + * CDbHttpSession stores session data in a DB table named 'YiiSession'. The table name + * can be changed by setting {@link sessionTableName}. If the table does not exist, + * it will be automatically created if {@link autoCreateSessionTable} is set true. + * + * The following is the table structure: + * + * <pre> + * CREATE TABLE YiiSession + * ( + * id CHAR(32) PRIMARY KEY, + * expire INTEGER, + * data TEXT + * ) + * </pre> + * + * CDbHttpSession relies on {@link http://www.php.net/manual/en/ref.pdo.php PDO} to access database. + * + * By default, it will use an SQLite3 database named 'session-YiiVersion.db' under the application runtime directory. + * You can also specify {@link connectionID} so that it makes use of a DB application component to access database. + * + * When using CDbHttpSession in a production server, we recommend you pre-create the session DB table + * and set {@link autoCreateSessionTable} to be false. This will greatly improve the performance. + * You may also create a DB index for the 'expire' column in the session table to further improve the performance. + * + * @property boolean $useCustomStorage Whether to use custom storage. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CDbHttpSession.php 3426 2011-10-25 00:01:09Z alexander.makarow $ + * @package system.web + * @since 1.0 + */ +class CDbHttpSession extends CHttpSession +{ + /** + * @var string the ID of a {@link CDbConnection} application component. If not set, a SQLite database + * will be automatically created and used. The SQLite database file is + * is <code>protected/runtime/session-YiiVersion.db</code>. + */ + public $connectionID; + /** + * @var string the name of the DB table to store session content. + * Note, if {@link autoCreateSessionTable} is false and you want to create the DB table manually by yourself, + * you need to make sure the DB table is of the following structure: + * <pre> + * (id CHAR(32) PRIMARY KEY, expire INTEGER, data TEXT) + * </pre> + * @see autoCreateSessionTable + */ + public $sessionTableName='YiiSession'; + /** + * @var boolean whether the session DB table should be automatically created if not exists. Defaults to true. + * @see sessionTableName + */ + public $autoCreateSessionTable=true; + /** + * @var CDbConnection the DB connection instance + */ + private $_db; + + + /** + * Returns a value indicating whether to use custom session storage. + * This method overrides the parent implementation and always returns true. + * @return boolean whether to use custom storage. + */ + public function getUseCustomStorage() + { + return true; + } + + /** + * Updates the current session id with a newly generated one. + * Please refer to {@link http://php.net/session_regenerate_id} for more details. + * @param boolean $deleteOldSession Whether to delete the old associated session file or not. + * @since 1.1.8 + */ + public function regenerateID($deleteOldSession=false) + { + $oldID=session_id(); + + // if no session is started, there is nothing to regenerate + if(empty($oldID)) + return; + + parent::regenerateID(false); + $newID=session_id(); + $db=$this->getDbConnection(); + + $sql="SELECT * FROM {$this->sessionTableName} WHERE id=:id"; + $row=$db->createCommand($sql)->bindValue(':id',$oldID)->queryRow(); + if($row!==false) + { + if($deleteOldSession) + { + $sql="UPDATE {$this->sessionTableName} SET id=:newID WHERE id=:oldID"; + $db->createCommand($sql)->bindValue(':newID',$newID)->bindValue(':oldID',$oldID)->execute(); + } + else + { + $row['id']=$newID; + $db->createCommand()->insert($this->sessionTableName, $row); + } + } + else + { + // shouldn't reach here normally + $db->createCommand()->insert($this->sessionTableName, array( + 'id'=>$newID, + 'expire'=>time()+$this->getTimeout(), + )); + } + } + + /** + * Creates the session DB table. + * @param CDbConnection $db the database connection + * @param string $tableName the name of the table to be created + */ + protected function createSessionTable($db,$tableName) + { + $sql=" +CREATE TABLE $tableName +( + id CHAR(32) PRIMARY KEY, + expire INTEGER, + data TEXT +)"; + $db->createCommand($sql)->execute(); + } + + /** + * @return CDbConnection the DB connection instance + * @throws CException if {@link connectionID} does not point to a valid application component. + */ + protected function getDbConnection() + { + if($this->_db!==null) + return $this->_db; + else if(($id=$this->connectionID)!==null) + { + if(($this->_db=Yii::app()->getComponent($id)) instanceof CDbConnection) + return $this->_db; + else + throw new CException(Yii::t('yii','CDbHttpSession.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.', + array('{id}'=>$id))); + } + else + { + $dbFile=Yii::app()->getRuntimePath().DIRECTORY_SEPARATOR.'session-'.Yii::getVersion().'.db'; + return $this->_db=new CDbConnection('sqlite:'.$dbFile); + } + } + + /** + * Session open handler. + * Do not call this method directly. + * @param string $savePath session save path + * @param string $sessionName session name + * @return boolean whether session is opened successfully + */ + public function openSession($savePath,$sessionName) + { + if($this->autoCreateSessionTable) + { + $db=$this->getDbConnection(); + $db->setActive(true); + $sql="DELETE FROM {$this->sessionTableName} WHERE expire<".time(); + try + { + $db->createCommand($sql)->execute(); + } + catch(Exception $e) + { + $this->createSessionTable($db,$this->sessionTableName); + } + } + return true; + } + + /** + * Session read handler. + * Do not call this method directly. + * @param string $id session ID + * @return string the session data + */ + public function readSession($id) + { + $now=time(); + $sql=" +SELECT data FROM {$this->sessionTableName} +WHERE expire>$now AND id=:id +"; + $data=$this->getDbConnection()->createCommand($sql)->bindValue(':id',$id)->queryScalar(); + return $data===false?'':$data; + } + + /** + * Session write handler. + * Do not call this method directly. + * @param string $id session ID + * @param string $data session data + * @return boolean whether session write is successful + */ + public function writeSession($id,$data) + { + // exception must be caught in session write handler + // http://us.php.net/manual/en/function.session-set-save-handler.php + try + { + $expire=time()+$this->getTimeout(); + $db=$this->getDbConnection(); + $sql="SELECT id FROM {$this->sessionTableName} WHERE id=:id"; + if($db->createCommand($sql)->bindValue(':id',$id)->queryScalar()===false) + $sql="INSERT INTO {$this->sessionTableName} (id, data, expire) VALUES (:id, :data, $expire)"; + else + $sql="UPDATE {$this->sessionTableName} SET expire=$expire, data=:data WHERE id=:id"; + $db->createCommand($sql)->bindValue(':id',$id)->bindValue(':data',$data)->execute(); + } + catch(Exception $e) + { + if(YII_DEBUG) + echo $e->getMessage(); + // it is too late to log an error message here + return false; + } + return true; + } + + /** + * Session destroy handler. + * Do not call this method directly. + * @param string $id session ID + * @return boolean whether session is destroyed successfully + */ + public function destroySession($id) + { + $sql="DELETE FROM {$this->sessionTableName} WHERE id=:id"; + $this->getDbConnection()->createCommand($sql)->bindValue(':id',$id)->execute(); + return true; + } + + /** + * Session GC (garbage collection) handler. + * Do not call this method directly. + * @param integer $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up. + * @return boolean whether session is GCed successfully + */ + public function gcSession($maxLifetime) + { + $sql="DELETE FROM {$this->sessionTableName} WHERE expire<".time(); + $this->getDbConnection()->createCommand($sql)->execute(); + return true; + } +} |
