Session.php 5.33 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

namespace yii\mongodb;

use Yii;
use yii\base\InvalidConfigException;

/**
 * Session extends [[\yii\web\Session]] by using MongoDB as session data storage.
 *
 * By default, Session stores session data in a collection named 'session' inside the default database.
 * This collection is better to be pre-created with fields 'id' and 'expire' indexed.
 * The collection name can be changed by setting [[sessionCollection]].
 *
 * The following example shows how you can configure the application to use Session:
 * Add the following to your application config under `components`:
 *
 * ~~~
 * 'session' => [
 *     'class' => 'yii\mongodb\Session',
 *     // 'db' => 'mymongodb',
 *     // 'sessionCollection' => 'my_session',
 * ]
 * ~~~
 *
31 32
 * @property boolean $useCustomStorage Whether to use custom storage. This property is read-only.
 *
33 34 35 36 37 38 39 40 41 42 43 44 45
 * @author Paul Klimov <klimov.paul@gmail.com>
 * @since 2.0
 */
class Session extends \yii\web\Session
{
	/**
	 * @var Connection|string the MongoDB connection object or the application component ID of the MongoDB connection.
	 * After the Session object is created, if you want to change this property, you should only assign it
	 * with a MongoDB connection object.
	 */
	public $db = 'mongodb';
	/**
	 * @var string|array the name of the MongoDB collection that stores the session data.
46 47
	 * Please refer to [[Connection::getCollection()]] on how to specify this parameter.
	 * This collection is better to be pre-created with fields 'id' and 'expire' indexed.
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
	 */
	public $sessionCollection = 'session';

	/**
	 * Initializes the Session component.
	 * This method will initialize the [[db]] property to make sure it refers to a valid MongoDB connection.
	 * @throws InvalidConfigException if [[db]] is invalid.
	 */
	public function init()
	{
		if (is_string($this->db)) {
			$this->db = Yii::$app->getComponent($this->db);
		}
		if (!$this->db instanceof Connection) {
			throw new InvalidConfigException($this->className() . "::db must be either a MongoDB connection instance or the application component ID of a MongoDB connection.");
		}
		parent::init();
	}

	/**
	 * 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();

94 95 96
		$collection = $this->db->getCollection($this->sessionCollection);
		$row = $collection->findOne(['id' => $oldID]);
		if ($row !== null) {
97
			if ($deleteOldSession) {
98
				$collection->update(['id' => $oldID], ['id' => $newID]);
99 100 101
			} else {
				unset($row['_id']);
				$row['id'] = $newID;
102
				$collection->insert($row);
103 104 105
			}
		} else {
			// shouldn't reach here normally
106 107 108 109
			$collection->insert([
				'id' => $newID,
				'expire' => time() + $this->getTimeout()
			]);
110 111 112 113 114 115 116 117 118 119 120
		}
	}

	/**
	 * Session read handler.
	 * Do not call this method directly.
	 * @param string $id session ID
	 * @return string the session data
	 */
	public function readSession($id)
	{
121 122 123 124
		$collection = $this->db->getCollection($this->sessionCollection);
		$doc = $collection->findOne(
			[
				'id' => $id,
125
				'expire' => ['$gt' => time()],
126 127 128 129
			],
			['data' => 1, '_id' => 0]
		);
		return isset($doc['data']) ? $doc['data'] : '';
130 131 132 133 134 135 136 137 138 139 140 141 142 143
	}

	/**
	 * 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 {
144 145 146 147 148 149 150 151 152
			$this->db->getCollection($this->sessionCollection)->update(
				['id' => $id],
				[
					'id' => $id,
					'data' => $data,
					'expire' => time() + $this->getTimeout(),
				],
				['upsert' => true]
			);
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
		} 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)
	{
171 172 173 174
		$this->db->getCollection($this->sessionCollection)->remove(
			['id' => $id],
			['justOne' => true]
		);
175 176 177 178 179 180 181 182 183 184 185
		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)
	{
186 187
		$this->db->getCollection($this->sessionCollection)
			->remove(['expire' => ['$lt' => time()]]);
188 189 190
		return true;
	}
}