-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[HttpFoundation][Lock] Makes MongoDB adapters usable with ext-mongodb
directly
#52336
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
…b` only
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,20 +14,22 @@ | |
use MongoDB\BSON\Binary; | ||
use MongoDB\BSON\UTCDateTime; | ||
use MongoDB\Client; | ||
use MongoDB\Collection; | ||
use MongoDB\Driver\BulkWrite; | ||
use MongoDB\Driver\Manager; | ||
use MongoDB\Driver\Query; | ||
|
||
/** | ||
* Session handler using the mongodb/mongodb package and MongoDB driver extension. | ||
* Session handler using the MongoDB driver extension. | ||
* | ||
* @author Markus Bachmann <markus.bachmann@bachi.biz> | ||
* @author Jérôme Tamarelle <jerome@tamarelle.net> | ||
* | ||
* @see https://packagist.org/packages/mongodb/mongodb | ||
* @see https://php.net/mongodb | ||
*/ | ||
class MongoDbSessionHandler extends AbstractSessionHandler | ||
{ | ||
private Client $mongo; | ||
private Collection $collection; | ||
private Manager $manager; | ||
private string $namespace; | ||
private array $options; | ||
private int|\Closure|null $ttl; | ||
|
||
|
@@ -62,13 +64,18 @@ class MongoDbSessionHandler extends AbstractSessionHandler | |
* | ||
* @throws \InvalidArgumentException When "database" or "collection" not provided | ||
*/ | ||
public function __construct(Client $mongo, array $options) | ||
public function __construct(Client|Manager $mongo, array $options) | ||
{ | ||
if (!isset($options['database']) || !isset($options['collection'])) { | ||
throw new \InvalidArgumentException('You must provide the "database" and "collection" option for MongoDBSessionHandler.'); | ||
} | ||
|
||
$this->mongo = $mongo; | ||
if ($mongo instanceof Client) { | ||
$mongo = $mongo->getManager(); | ||
} | ||
|
||
$this->manager = $mongo; | ||
$this->namespace = $options['database'].'.'.$options['collection']; | ||
|
||
$this->options = array_merge([ | ||
'id_field' => '_id', | ||
|
@@ -86,77 +93,94 @@ public function close(): bool | |
|
||
protected function doDestroy(#[\SensitiveParameter] string $sessionId): bool | ||
{ | ||
$this->getCollection()->deleteOne([ | ||
$this->options['id_field'] => $sessionId, | ||
]); | ||
$write = new BulkWrite(); | ||
$write->delete( | ||
[$this->options['id_field'] => $sessionId], | ||
['limit' => 1] | ||
); | ||
|
||
$this->manager->executeBulkWrite($this->namespace, $write); | ||
|
||
return true; | ||
} | ||
|
||
public function gc(int $maxlifetime): int|false | ||
{ | ||
return $this->getCollection()->deleteMany([ | ||
$this->options['expiry_field'] => ['$lt' => new UTCDateTime()], | ||
])->getDeletedCount(); | ||
$write = new BulkWrite(); | ||
$write->delete( | ||
[$this->options['expiry_field'] => ['$lt' => $this->getUTCDateTime()]], | ||
); | ||
$result = $this->manager->executeBulkWrite($this->namespace, $write); | ||
|
||
return $result->getDeletedCount() ?? false; | ||
} | ||
|
||
protected function doWrite(#[\SensitiveParameter] string $sessionId, string $data): bool | ||
{ | ||
$ttl = ($this->ttl instanceof \Closure ? ($this->ttl)() : $this->ttl) ?? \ini_get('session.gc_maxlifetime'); | ||
$expiry = new UTCDateTime((time() + (int) $ttl) * 1000); | ||
$expiry = $this->getUTCDateTime($ttl); | ||
|
||
$fields = [ | ||
$this->options['time_field'] => new UTCDateTime(), | ||
$this->options['time_field'] => $this->getUTCDateTime(), | ||
$this->options['expiry_field'] => $expiry, | ||
$this->options['data_field'] => new Binary($data, Binary::TYPE_OLD_BINARY), | ||
$this->options['data_field'] => new Binary($data, Binary::TYPE_GENERIC), | ||
]; | ||
|
||
$this->getCollection()->updateOne( | ||
$write = new BulkWrite(); | ||
$write->update( | ||
[$this->options['id_field'] => $sessionId], | ||
['$set' => $fields], | ||
['upsert' => true] | ||
); | ||
|
||
$this->manager->executeBulkWrite($this->namespace, $write); | ||
|
||
return true; | ||
} | ||
|
||
public function updateTimestamp(#[\SensitiveParameter] string $sessionId, string $data): bool | ||
{ | ||
$ttl = ($this->ttl instanceof \Closure ? ($this->ttl)() : $this->ttl) ?? \ini_get('session.gc_maxlifetime'); | ||
$expiry = new UTCDateTime((time() + (int) $ttl) * 1000); | ||
$expiry = $this->getUTCDateTime($ttl); | ||
|
||
$this->getCollection()->updateOne( | ||
$write = new BulkWrite(); | ||
$write->update( | ||
[$this->options['id_field'] => $sessionId], | ||
['$set' => [ | ||
$this->options['time_field'] => new UTCDateTime(), | ||
$this->options['time_field'] => $this->getUTCDateTime(), | ||
$this->options['expiry_field'] => $expiry, | ||
]] | ||
]], | ||
['multi' => false], | ||
); | ||
|
||
$this->manager->executeBulkWrite($this->namespace, $write); | ||
|
||
return true; | ||
} | ||
|
||
protected function doRead(#[\SensitiveParameter] string $sessionId): string | ||
{ | ||
$dbData = $this->getCollection()->findOne([ | ||
$cursor = $this->manager->executeQuery($this->namespace, new Query([ | ||
$this->options['id_field'] => $sessionId, | ||
$this->options['expiry_field'] => ['$gte' => new UTCDateTime()], | ||
]); | ||
|
||
if (null === $dbData) { | ||
return ''; | ||
$this->options['expiry_field'] => ['$gte' => $this->getUTCDateTime()], | ||
], [ | ||
'projection' => [ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does anything enforce that I assume this will only ever return one or no documents, but I noticed you use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess this options were copied from |
||
'_id' => false, | ||
$this->options['data_field'] => true, | ||
], | ||
'limit' => 1, | ||
])); | ||
|
||
foreach ($cursor as $document) { | ||
return (string) $document->{$this->options['data_field']} ?? ''; | ||
} | ||
|
||
return $dbData[$this->options['data_field']]->getData(); | ||
} | ||
|
||
private function getCollection(): Collection | ||
{ | ||
return $this->collection ??= $this->mongo->selectCollection($this->options['database'], $this->options['collection']); | ||
// Not found | ||
return ''; | ||
} | ||
|
||
protected function getMongo(): Client | ||
private function getUTCDateTime(int $additionalSeconds = 0): UTCDateTime | ||
{ | ||
return $this->mongo; | ||
return new UTCDateTime((time() + $additionalSeconds) * 1000); | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.