annotate vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 /*
Chris@0 4 * This file is part of the Symfony package.
Chris@0 5 *
Chris@0 6 * (c) Fabien Potencier <fabien@symfony.com>
Chris@0 7 *
Chris@0 8 * For the full copyright and license information, please view the LICENSE
Chris@0 9 * file that was distributed with this source code.
Chris@0 10 */
Chris@0 11
Chris@0 12 namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
Chris@0 13
Chris@0 14 /**
Chris@14 15 * Session handler using the mongodb/mongodb package and MongoDB driver extension.
Chris@0 16 *
Chris@0 17 * @author Markus Bachmann <markus.bachmann@bachi.biz>
Chris@14 18 *
Chris@14 19 * @see https://packagist.org/packages/mongodb/mongodb
Chris@14 20 * @see http://php.net/manual/en/set.mongodb.php
Chris@0 21 */
Chris@14 22 class MongoDbSessionHandler extends AbstractSessionHandler
Chris@0 23 {
Chris@0 24 private $mongo;
Chris@0 25
Chris@0 26 /**
Chris@0 27 * @var \MongoCollection
Chris@0 28 */
Chris@0 29 private $collection;
Chris@0 30
Chris@0 31 /**
Chris@0 32 * @var array
Chris@0 33 */
Chris@0 34 private $options;
Chris@0 35
Chris@0 36 /**
Chris@0 37 * Constructor.
Chris@0 38 *
Chris@0 39 * List of available options:
Chris@0 40 * * database: The name of the database [required]
Chris@0 41 * * collection: The name of the collection [required]
Chris@0 42 * * id_field: The field name for storing the session id [default: _id]
Chris@0 43 * * data_field: The field name for storing the session data [default: data]
Chris@0 44 * * time_field: The field name for storing the timestamp [default: time]
Chris@14 45 * * expiry_field: The field name for storing the expiry-timestamp [default: expires_at].
Chris@0 46 *
Chris@0 47 * It is strongly recommended to put an index on the `expiry_field` for
Chris@0 48 * garbage-collection. Alternatively it's possible to automatically expire
Chris@0 49 * the sessions in the database as described below:
Chris@0 50 *
Chris@0 51 * A TTL collections can be used on MongoDB 2.2+ to cleanup expired sessions
Chris@0 52 * automatically. Such an index can for example look like this:
Chris@0 53 *
Chris@0 54 * db.<session-collection>.ensureIndex(
Chris@0 55 * { "<expiry-field>": 1 },
Chris@0 56 * { "expireAfterSeconds": 0 }
Chris@0 57 * )
Chris@0 58 *
Chris@0 59 * More details on: http://docs.mongodb.org/manual/tutorial/expire-data/
Chris@0 60 *
Chris@0 61 * If you use such an index, you can drop `gc_probability` to 0 since
Chris@0 62 * no garbage-collection is required.
Chris@0 63 *
Chris@14 64 * @param \MongoDB\Client $mongo A MongoDB\Client instance
Chris@14 65 * @param array $options An associative array of field options
Chris@0 66 *
Chris@0 67 * @throws \InvalidArgumentException When MongoClient or Mongo instance not provided
Chris@0 68 * @throws \InvalidArgumentException When "database" or "collection" not provided
Chris@0 69 */
Chris@0 70 public function __construct($mongo, array $options)
Chris@0 71 {
Chris@14 72 if ($mongo instanceof \MongoClient || $mongo instanceof \Mongo) {
Chris@14 73 @trigger_error(sprintf('Using %s with the legacy mongo extension is deprecated as of 3.4 and will be removed in 4.0. Use it with the mongodb/mongodb package and ext-mongodb instead.', __CLASS__), E_USER_DEPRECATED);
Chris@14 74 }
Chris@14 75
Chris@0 76 if (!($mongo instanceof \MongoDB\Client || $mongo instanceof \MongoClient || $mongo instanceof \Mongo)) {
Chris@0 77 throw new \InvalidArgumentException('MongoClient or Mongo instance required');
Chris@0 78 }
Chris@0 79
Chris@0 80 if (!isset($options['database']) || !isset($options['collection'])) {
Chris@0 81 throw new \InvalidArgumentException('You must provide the "database" and "collection" option for MongoDBSessionHandler');
Chris@0 82 }
Chris@0 83
Chris@0 84 $this->mongo = $mongo;
Chris@0 85
Chris@17 86 $this->options = array_merge([
Chris@0 87 'id_field' => '_id',
Chris@0 88 'data_field' => 'data',
Chris@0 89 'time_field' => 'time',
Chris@0 90 'expiry_field' => 'expires_at',
Chris@17 91 ], $options);
Chris@0 92 }
Chris@0 93
Chris@0 94 /**
Chris@0 95 * {@inheritdoc}
Chris@0 96 */
Chris@0 97 public function close()
Chris@0 98 {
Chris@0 99 return true;
Chris@0 100 }
Chris@0 101
Chris@0 102 /**
Chris@0 103 * {@inheritdoc}
Chris@0 104 */
Chris@14 105 protected function doDestroy($sessionId)
Chris@0 106 {
Chris@0 107 $methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteOne' : 'remove';
Chris@0 108
Chris@17 109 $this->getCollection()->$methodName([
Chris@0 110 $this->options['id_field'] => $sessionId,
Chris@17 111 ]);
Chris@0 112
Chris@0 113 return true;
Chris@0 114 }
Chris@0 115
Chris@0 116 /**
Chris@0 117 * {@inheritdoc}
Chris@0 118 */
Chris@0 119 public function gc($maxlifetime)
Chris@0 120 {
Chris@14 121 $methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteMany' : 'remove';
Chris@0 122
Chris@17 123 $this->getCollection()->$methodName([
Chris@17 124 $this->options['expiry_field'] => ['$lt' => $this->createDateTime()],
Chris@17 125 ]);
Chris@0 126
Chris@0 127 return true;
Chris@0 128 }
Chris@0 129
Chris@0 130 /**
Chris@0 131 * {@inheritdoc}
Chris@0 132 */
Chris@14 133 protected function doWrite($sessionId, $data)
Chris@0 134 {
Chris@0 135 $expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime'));
Chris@0 136
Chris@17 137 $fields = [
Chris@0 138 $this->options['time_field'] => $this->createDateTime(),
Chris@0 139 $this->options['expiry_field'] => $expiry,
Chris@17 140 ];
Chris@0 141
Chris@17 142 $options = ['upsert' => true];
Chris@0 143
Chris@0 144 if ($this->mongo instanceof \MongoDB\Client) {
Chris@0 145 $fields[$this->options['data_field']] = new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY);
Chris@0 146 } else {
Chris@0 147 $fields[$this->options['data_field']] = new \MongoBinData($data, \MongoBinData::BYTE_ARRAY);
Chris@0 148 $options['multiple'] = false;
Chris@0 149 }
Chris@0 150
Chris@0 151 $methodName = $this->mongo instanceof \MongoDB\Client ? 'updateOne' : 'update';
Chris@0 152
Chris@0 153 $this->getCollection()->$methodName(
Chris@17 154 [$this->options['id_field'] => $sessionId],
Chris@17 155 ['$set' => $fields],
Chris@0 156 $options
Chris@0 157 );
Chris@0 158
Chris@0 159 return true;
Chris@0 160 }
Chris@0 161
Chris@0 162 /**
Chris@0 163 * {@inheritdoc}
Chris@0 164 */
Chris@14 165 public function updateTimestamp($sessionId, $data)
Chris@14 166 {
Chris@14 167 $expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime'));
Chris@14 168
Chris@14 169 if ($this->mongo instanceof \MongoDB\Client) {
Chris@14 170 $methodName = 'updateOne';
Chris@17 171 $options = [];
Chris@14 172 } else {
Chris@14 173 $methodName = 'update';
Chris@17 174 $options = ['multiple' => false];
Chris@14 175 }
Chris@14 176
Chris@14 177 $this->getCollection()->$methodName(
Chris@17 178 [$this->options['id_field'] => $sessionId],
Chris@17 179 ['$set' => [
Chris@14 180 $this->options['time_field'] => $this->createDateTime(),
Chris@14 181 $this->options['expiry_field'] => $expiry,
Chris@17 182 ]],
Chris@14 183 $options
Chris@14 184 );
Chris@14 185
Chris@14 186 return true;
Chris@14 187 }
Chris@14 188
Chris@14 189 /**
Chris@14 190 * {@inheritdoc}
Chris@14 191 */
Chris@14 192 protected function doRead($sessionId)
Chris@0 193 {
Chris@17 194 $dbData = $this->getCollection()->findOne([
Chris@0 195 $this->options['id_field'] => $sessionId,
Chris@17 196 $this->options['expiry_field'] => ['$gte' => $this->createDateTime()],
Chris@17 197 ]);
Chris@0 198
Chris@0 199 if (null === $dbData) {
Chris@0 200 return '';
Chris@0 201 }
Chris@0 202
Chris@0 203 if ($dbData[$this->options['data_field']] instanceof \MongoDB\BSON\Binary) {
Chris@0 204 return $dbData[$this->options['data_field']]->getData();
Chris@0 205 }
Chris@0 206
Chris@0 207 return $dbData[$this->options['data_field']]->bin;
Chris@0 208 }
Chris@0 209
Chris@0 210 /**
Chris@0 211 * Return a "MongoCollection" instance.
Chris@0 212 *
Chris@0 213 * @return \MongoCollection
Chris@0 214 */
Chris@0 215 private function getCollection()
Chris@0 216 {
Chris@0 217 if (null === $this->collection) {
Chris@0 218 $this->collection = $this->mongo->selectCollection($this->options['database'], $this->options['collection']);
Chris@0 219 }
Chris@0 220
Chris@0 221 return $this->collection;
Chris@0 222 }
Chris@0 223
Chris@0 224 /**
Chris@0 225 * Return a Mongo instance.
Chris@0 226 *
Chris@0 227 * @return \Mongo|\MongoClient|\MongoDB\Client
Chris@0 228 */
Chris@0 229 protected function getMongo()
Chris@0 230 {
Chris@0 231 return $this->mongo;
Chris@0 232 }
Chris@0 233
Chris@0 234 /**
Chris@0 235 * Create a date object using the class appropriate for the current mongo connection.
Chris@0 236 *
Chris@0 237 * Return an instance of a MongoDate or \MongoDB\BSON\UTCDateTime
Chris@0 238 *
Chris@0 239 * @param int $seconds An integer representing UTC seconds since Jan 1 1970. Defaults to now.
Chris@0 240 *
Chris@0 241 * @return \MongoDate|\MongoDB\BSON\UTCDateTime
Chris@0 242 */
Chris@0 243 private function createDateTime($seconds = null)
Chris@0 244 {
Chris@0 245 if (null === $seconds) {
Chris@0 246 $seconds = time();
Chris@0 247 }
Chris@0 248
Chris@0 249 if ($this->mongo instanceof \MongoDB\Client) {
Chris@0 250 return new \MongoDB\BSON\UTCDateTime($seconds * 1000);
Chris@0 251 }
Chris@0 252
Chris@0 253 return new \MongoDate($seconds);
Chris@0 254 }
Chris@0 255 }