Skip to content

Commit

Permalink
Merge branch '6.4' into 7.0
Browse files Browse the repository at this point in the history
* 6.4: (28 commits)
  [Serializer] Fix `@method` annotation
  fix compatibility with Doctrine DBAL 4
  ensure string type with mbstring func overloading enabled
  [HttpKernel] Fix quotes expectations in tests
  [Validator] updated Greek translation
  [Cache][HttpFoundation][Lock] Fix empty username/password for PDO PostgreSQL
  [HttpClient][WebProfilerBundle] Do not generate cURL command when files are uploaded
  render newline in front of all script elements
  fix test fixture
  fix tests
  [Cache] Fix property types in PdoAdapter
  PHP files cannot be executable without shebang
  [TwigBridge] Mark CodeExtension as @internal
  Remove full DSNs from exception messages
  [Yaml] Fix uid binary parsing
  Disable the "Copy as cURL" button when the debug info are disabled
  [HttpClient] Replace `escapeshellarg` to prevent overpassing `ARG_MAX`
  Fix missing `profile` option for console commands
  [HttpFoundation][Lock] Makes MongoDB adapters usable with `ext-mongodb` only
  [HttpKernel] Preventing error 500 when function putenv is disabled
  ...
  • Loading branch information
nicolas-grekas committed Nov 7, 2023
2 parents 27c5310 + f0179cf commit 05d4e68
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 161 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ CHANGELOG
* Add `UriSigner` from the HttpKernel component
* Add `partitioned` flag to `Cookie` (CHIPS Cookie)
* Add argument `bool $flush = true` to `Response::send()`
* Make `MongoDbSessionHandler` instantiable with the mongodb extension directly

6.3
---
Expand Down
2 changes: 1 addition & 1 deletion HeaderUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ public static function parseQuery(string $query, bool $ignoreBrackets = false, s
private static function groupParts(array $matches, string $separators, bool $first = true): array
{
$separator = $separators[0];
$separators = substr($separators, 1);
$separators = substr($separators, 1) ?: '';
$i = 0;

if ('' === $separators && !$first) {
Expand Down
94 changes: 59 additions & 35 deletions Session/Storage/Handler/MongoDbSessionHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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',
Expand All @@ -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' => [
'_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);
}
}
4 changes: 2 additions & 2 deletions Session/Storage/Handler/PdoSessionHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,12 @@ class PdoSessionHandler extends AbstractSessionHandler
/**
* Username when lazy-connect.
*/
private string $username = '';
private ?string $username = null;

/**
* Password when lazy-connect.
*/
private string $password = '';
private ?string $password = null;

/**
* Connection options when lazy-connect.
Expand Down
Loading

0 comments on commit 05d4e68

Please sign in to comment.