yii\web\MultiFieldSession created

This commit is contained in:
Klimov Paul
2015-06-08 17:25:24 +03:00
parent 71b4786850
commit 4283f29a5a
2 changed files with 160 additions and 30 deletions

View File

@ -30,12 +30,13 @@ use yii\di\Instance;
* ]
* ~~~
*
* @property boolean $useCustomStorage Whether to use custom storage. This property is read-only.
* DbSession extends [[MultiFieldSession]], thus it allows saving extra fields into the [[sessionTable]].
* Refer to [[MultiFieldSession]] for more details.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class DbSession extends Session
class DbSession extends MultiFieldSession
{
/**
* @var Connection|array|string the DB connection object or the application component ID of the DB connection.
@ -85,16 +86,6 @@ class DbSession extends Session
$this->db = Instance::ensure($this->db, Connection::className());
}
/**
* 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 <http://php.net/session_regenerate_id> for more details.
@ -112,7 +103,7 @@ class DbSession extends Session
parent::regenerateID(false);
$newID = session_id();
$query = new Query;
$query = new Query();
$row = $query->from($this->sessionTable)
->where(['id' => $oldID])
->createCommand($this->db)
@ -131,10 +122,8 @@ class DbSession extends Session
} else {
// shouldn't reach here normally
$this->db->createCommand()
->insert($this->sessionTable, [
'id' => $newID,
'expire' => time() + $this->getTimeout(),
])->execute();
->insert($this->sessionTable, $this->composeFields($newID, ''))
->execute();
}
}
@ -146,13 +135,16 @@ class DbSession extends Session
*/
public function readSession($id)
{
$query = new Query;
$data = $query->select(['data'])
->from($this->sessionTable)
->where('[[expire]]>:expire AND [[id]]=:id', [':expire' => time(), ':id' => $id])
->createCommand($this->db)
->queryScalar();
$query = new Query();
$query->from($this->sessionTable)
->where('[[expire]]>:expire AND [[id]]=:id', [':expire' => time(), ':id' => $id]);
if (isset($this->readCallback)) {
$fields = $query->one($this->db);
return $fields === false ? '' : $this->extractData($fields);
}
$data = $query->select(['data'])->scalar($this->db);
return $data === false ? '' : $data;
}
@ -168,23 +160,21 @@ class DbSession extends Session
// 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();
$query = new Query;
$exists = $query->select(['id'])
->from($this->sessionTable)
->where(['id' => $id])
->createCommand($this->db)
->queryScalar();
$fields = $this->composeFields($id, $data);
if ($exists === false) {
$this->db->createCommand()
->insert($this->sessionTable, [
'id' => $id,
'data' => $data,
'expire' => $expire,
])->execute();
->insert($this->sessionTable, $fields)
->execute();
} else {
unset($fields['id']);
$this->db->createCommand()
->update($this->sessionTable, ['data' => $data, 'expire' => $expire], ['id' => $id])
->update($this->sessionTable, $fields, ['id' => $id])
->execute();
}
} catch (\Exception $e) {

View File

@ -0,0 +1,140 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\web;
/**
* MultiDataSession is a base class for the sessions, which are using multi-field data storage, e.g.
* session data can be split between several fields in the storage record.
* For example a relational database such as MySQL supports this.
* Using such storage allows saving particular session data into separated field, which then can be used
* to manipulate sessions in the way plain PHP does not allows.
* For example: currently authenticated user ID can be saved as separated column in the MySQL 'session' table,
* which allows to query all active sessions for particular user or terminate them at will.
*
* Customizing of the session writing is performed via [[writeCallback]], reading - via [[readCallback]].
*
* While extending this class you should use [[composeFields()]] method - while writing the session data into the storage and
* [[extractData()]] - while reading session data from the storage.
*
* @property boolean $useCustomStorage Whether to use custom storage. This property is read-only.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0.5
*/
abstract class MultiFieldSession extends Session
{
/**
* @var callable a callback that will be called during session data reading.
* The signature of the callback should be as follows:
*
* ```
* function ($fields)
* ```
*
* where `$fields` is the storage field set for read session and `$session` is this session instance.
* If callback returns an array, it will be merged into the session data.
*
* For example:
*
* ```php
* function ($fields) {
* return [
* 'expireDate' => Yii::$app->formatter->asDate($fields['expire']),
* ];
* }
* ```
*/
public $readCallback;
/**
* @var callable a callback that will be called during session data writing.
* The signature of the callback should be as follows:
*
* ```
* function ($session)
* ```
*
* where `$session` is this session instance, this variable can be used to retrieve session data.
* Callback should return the actual fields set, which should be saved into the session storage.
*
* For example:
*
* ```php
* function ($session) {
* return [
* 'user_id' => Yii::$app->user->id,
* 'ip' => $_SERVER['REMOTE_ADDR'],
* 'is_trusted' => $session->get('is_trusted', false),
* ];
* }
* ```
*/
public $writeCallback;
/**
* 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;
}
/**
* Composes storage field set for session writing.
* @param string $id session id
* @param string $data session data
* @return array storage fields
*/
protected function composeFields($id, $data)
{
$fields = [
'data' => $data,
];
if (isset($this->writeCallback)) {
$fields = array_merge(
$fields,
call_user_func($this->writeCallback, $this)
);
if (!is_string($fields['data'])) {
$_SESSION = $fields['data'];
$fields['data'] = session_encode();
}
}
// ensure 'id' and 'expire' are never affected by [[writeCallback]]
$fields = array_merge($fields, [
'id' => $id,
'expire' => time() + $this->getTimeout(),
]);
return $fields;
}
/**
* Extracts session data from storage field set.
* @param array $fields storage fields.
* @return string session data.
*/
protected function extractData($fields)
{
if (isset($this->readCallback)) {
if (!isset($fields['data'])) {
$fields['data'] = '';
}
$extraData = call_user_func($this->readCallback, $fields);
if (!empty($extraData)) {
session_decode($fields['data']);
$_SESSION = array_merge((array)$_SESSION, (array)$extraData);
return session_encode();
}
return $fields['data'];
} else {
return isset($fields['data']) ? $fields['data'] : '';
}
}
}