299 lines
8.6 KiB
PHP
299 lines
8.6 KiB
PHP
<?php
|
|
/**
|
|
* CDbHttpSession class
|
|
*
|
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
|
* @link http://www.yiiframework.com/
|
|
* @copyright 2008-2013 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 BLOB
|
|
* )
|
|
* </pre>
|
|
* Where 'BLOB' refers to the BLOB-type of your preffered database.
|
|
*
|
|
* Note that if your session IDs are more than 32 characters (can be changed via
|
|
* session.hash_bits_per_character or session.hash_function) you should modify
|
|
* SQL schema accordingly.
|
|
*
|
|
* 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>
|
|
* @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 BLOB)
|
|
* </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();
|
|
|
|
$row=$db->createCommand()
|
|
->select()
|
|
->from($this->sessionTableName)
|
|
->where('id=:id',array(':id'=>$oldID))
|
|
->queryRow();
|
|
if($row!==false)
|
|
{
|
|
if($deleteOldSession)
|
|
$db->createCommand()->update($this->sessionTableName,array(
|
|
'id'=>$newID
|
|
),'id=:oldID',array(':oldID'=>$oldID));
|
|
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(),
|
|
'data'=>'',
|
|
));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
switch($db->getDriverName())
|
|
{
|
|
case 'mysql':
|
|
$blob='LONGBLOB';
|
|
break;
|
|
case 'pgsql':
|
|
$blob='BYTEA';
|
|
break;
|
|
case 'sqlsrv':
|
|
case 'mssql':
|
|
case 'dblib':
|
|
$blob='VARBINARY(MAX)';
|
|
break;
|
|
default:
|
|
$blob='BLOB';
|
|
break;
|
|
}
|
|
$db->createCommand()->createTable($tableName,array(
|
|
'id'=>'CHAR(32) PRIMARY KEY',
|
|
'expire'=>'integer',
|
|
'data'=>$blob,
|
|
));
|
|
}
|
|
|
|
/**
|
|
* @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;
|
|
elseif(($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);
|
|
try
|
|
{
|
|
$db->createCommand()->delete($this->sessionTableName,'expire<:expire',array(':expire'=>time()));
|
|
}
|
|
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)
|
|
{
|
|
$db=$this->getDbConnection();
|
|
if($db->getDriverName()=='sqlsrv' || $db->getDriverName()=='mssql' || $db->getDriverName()=='dblib')
|
|
$select='CONVERT(VARCHAR(MAX), data)';
|
|
else
|
|
$select='data';
|
|
$data=$db->createCommand()
|
|
->select($select)
|
|
->from($this->sessionTableName)
|
|
->where('expire>:expire AND id=:id',array(':expire'=>time(),':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();
|
|
if($db->getDriverName()=='sqlsrv' || $db->getDriverName()=='mssql' || $db->getDriverName()=='dblib')
|
|
$data=new CDbExpression('CONVERT(VARBINARY(MAX), '.$db->quoteValue($data).')');
|
|
if($db->createCommand()->select('id')->from($this->sessionTableName)->where('id=:id',array(':id'=>$id))->queryScalar()===false)
|
|
$db->createCommand()->insert($this->sessionTableName,array(
|
|
'id'=>$id,
|
|
'data'=>$data,
|
|
'expire'=>$expire,
|
|
));
|
|
else
|
|
$db->createCommand()->update($this->sessionTableName,array(
|
|
'data'=>$data,
|
|
'expire'=>$expire
|
|
),'id=:id',array(':id'=>$id));
|
|
}
|
|
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)
|
|
{
|
|
$this->getDbConnection()->createCommand()
|
|
->delete($this->sessionTableName,'id=:id',array(':id'=>$id));
|
|
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)
|
|
{
|
|
$this->getDbConnection()->createCommand()
|
|
->delete($this->sessionTableName,'expire<:expire',array(':expire'=>time()));
|
|
return true;
|
|
}
|
|
}
|