DbSession.php 6.96 KB
Newer Older
Qiang Xue committed
1 2 3
<?php
/**
 * @link http://www.yiiframework.com/
Qiang Xue committed
4
 * @copyright Copyright (c) 2008 Yii Software LLC
Qiang Xue committed
5 6 7 8 9
 * @license http://www.yiiframework.com/license/
 */

namespace yii\web;

Qiang Xue committed
10 11 12 13
use Yii;
use yii\db\Connection;
use yii\db\Query;
use yii\base\InvalidConfigException;
14
use yii\di\Instance;
Qiang Xue committed
15

Qiang Xue committed
16
/**
Qiang Xue committed
17
 * DbSession extends [[Session]] by using database as session data storage.
Qiang Xue committed
18
 *
19
 * By default, DbSession stores session data in a DB table named 'session'. This table
20
 * must be pre-created. The table name can be changed by setting [[sessionTable]].
21
 *
22
 * The following example shows how you can configure the application to use DbSession:
23
 * Add the following to your application config under `components`:
24
 *
Qiang Xue committed
25
 * ~~~
Alexander Makarov committed
26
 * 'session' => [
27 28 29
 *     'class' => 'yii\web\DbSession',
 *     // 'db' => 'mydb',
 *     // 'sessionTable' => 'my_session',
Alexander Makarov committed
30
 * ]
Qiang Xue committed
31
 * ~~~
Qiang Xue committed
32
 *
33
 * @property boolean $useCustomStorage Whether to use custom storage. This property is read-only.
34
 *
Qiang Xue committed
35
 * @author Qiang Xue <qiang.xue@gmail.com>
Qiang Xue committed
36
 * @since 2.0
Qiang Xue committed
37 38 39
 */
class DbSession extends Session
{
40 41 42 43 44 45 46 47 48 49 50
    /**
     * @var Connection|string the DB connection object or the application component ID of the DB connection.
     * After the DbSession object is created, if you want to change this property, you should only assign it
     * with a DB connection object.
     */
    public $db = 'db';
    /**
     * @var string the name of the DB table that stores the session data.
     * The table should be pre-created as follows:
     *
     * ~~~
51
     * CREATE TABLE session
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
     * (
     *     id CHAR(40) NOT NULL PRIMARY KEY,
     *     expire INTEGER,
     *     data BLOB
     * )
     * ~~~
     *
     * where 'BLOB' refers to the BLOB-type of your preferred DBMS. Below are the BLOB type
     * that can be used for some popular DBMS:
     *
     * - MySQL: LONGBLOB
     * - PostgreSQL: BYTEA
     * - MSSQL: BLOB
     *
     * When using DbSession in a production server, we recommend you create a DB index for the 'expire'
     * column in the session table to improve the performance.
     */
    public $sessionTable = '{{%session}}';

71

72 73 74 75 76 77 78 79
    /**
     * Initializes the DbSession component.
     * This method will initialize the [[db]] property to make sure it refers to a valid DB connection.
     * @throws InvalidConfigException if [[db]] is invalid.
     */
    public function init()
    {
        parent::init();
80
        $this->db = Instance::ensure($this->db, Connection::className());
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
    }

    /**
     * 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.
     * @param boolean $deleteOldSession Whether to delete the old associated session file or not.
     */
    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();

        $query = new Query;
        $row = $query->from($this->sessionTable)
            ->where(['id' => $oldID])
            ->createCommand($this->db)
            ->queryOne();
        if ($row !== false) {
            if ($deleteOldSession) {
                $this->db->createCommand()
                    ->update($this->sessionTable, ['id' => $newID], ['id' => $oldID])
                    ->execute();
            } else {
                $row['id'] = $newID;
                $this->db->createCommand()
                    ->insert($this->sessionTable, $row)
                    ->execute();
            }
        } else {
            // shouldn't reach here normally
            $this->db->createCommand()
                ->insert($this->sessionTable, [
                    'id' => $newID,
                    'expire' => time() + $this->getTimeout(),
                ])->execute();
        }
    }

    /**
     * Session read handler.
     * Do not call this method directly.
139
     * @param string $id session ID
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
     * @return string the session data
     */
    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();

        return $data === false ? '' : $data;
    }

    /**
     * Session write handler.
     * Do not call this method directly.
157 158
     * @param string $id session ID
     * @param string $data session data
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
     * @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();
            $query = new Query;
            $exists = $query->select(['id'])
                ->from($this->sessionTable)
                ->where(['id' => $id])
                ->createCommand($this->db)
                ->queryScalar();
            if ($exists === false) {
                $this->db->createCommand()
                    ->insert($this->sessionTable, [
                        'id' => $id,
                        'data' => $data,
                        'expire' => $expire,
                    ])->execute();
            } else {
                $this->db->createCommand()
                    ->update($this->sessionTable, ['data' => $data, 'expire' => $expire], ['id' => $id])
                    ->execute();
            }
        } catch (\Exception $e) {
186 187 188 189 190
            $exception = ErrorHandler::convertExceptionToString($e);
            // its too late to use Yii logging here
            error_log($exception);
            echo $exception;

191 192 193 194 195 196 197 198 199
            return false;
        }

        return true;
    }

    /**
     * Session destroy handler.
     * Do not call this method directly.
200
     * @param string $id session ID
201 202 203 204 205 206 207 208 209 210 211 212 213 214
     * @return boolean whether session is destroyed successfully
     */
    public function destroySession($id)
    {
        $this->db->createCommand()
            ->delete($this->sessionTable, ['id' => $id])
            ->execute();

        return true;
    }

    /**
     * Session GC (garbage collection) handler.
     * Do not call this method directly.
215
     * @param integer $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up.
216 217 218 219 220 221 222 223 224 225
     * @return boolean whether session is GCed successfully
     */
    public function gcSession($maxLifetime)
    {
        $this->db->createCommand()
            ->delete($this->sessionTable, '[[expire]]<:expire', [':expire' => time()])
            ->execute();

        return true;
    }
Qiang Xue committed
226
}