8000 feature #52336 [HttpFoundation][Lock] Makes MongoDB adapters usable w… · symfony/symfony@8a69f67 · GitHub
[go: up one dir, main page]

Skip to content

Commit 8a69f67

Browse files
committed
feature #52336 [HttpFoundation][Lock] Makes MongoDB adapters usable with ext-mongodb only (GromNaN)
This PR was squashed before being merged into the 6.4 branch. Discussion ---------- [HttpFoundation][Lock] Makes MongoDB adapters usable with `ext-mongodb` only | Q | A | ------------- | --- | Branch? | 6.4 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | - | License | MIT [`mongodb/mongodb`](https://packagist.org/packages/mongodb/mongodb) is complex to handle for libraries with optional support of MongoDB, as it requires `ext-mongodb`. In order to reduce complexity for maintainers, I reimplemented the session and lock adapters to use only the C driver classes. Some features of `MongoDB\Client` are missing (server selection, session, transaction). But they are not necessary to store Sessions and Lock. Changes: - Lock & Session accept a `MongoDB\Driver\Manager` - The lock uses exclusively UTC date. Before, there was a mix of `time()` and `UTCDatetime` objects. - Session tests require a mongo server. - `mongodb/mongodb` not needed in the CI And of course also allows developers to use this adapters without installing `mongodb/mongodb` if they want, with the same features as before. Commits ------- bc24cb3 [HttpFoundation][Lock] Makes MongoDB adapters usable with `ext-mongodb` only
2 parents 8b13301 + bc24cb3 commit 8a69f67

File tree

12 files changed

+402
-256
lines changed

12 files changed

+402
-256
lines changed

.github/workflows/integration-tests.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,6 @@ jobs:
159159
echo COMPOSER_ROOT_VERSION=$COMPOSER_ROOT_VERSION >> $GITHUB_ENV
160160
161161
echo "::group::composer update"
162-
composer require --dev --no-update mongodb/mongodb
163162
composer update --no-progress --ansi
164163
echo "::endgroup::"
165164

.github/workflows/unit-tests.yml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,6 @@ jobs:
190190
exit 0
191191
fi
192192
193-
(cd src/Symfony/Component/HttpFoundation; cp composer.json composer.bak; composer require --dev --no-update mongodb/mongodb)
194-
(cd src/Symfony/Component/Lock; cp composer.json composer.bak; composer require --dev --no-update mongodb/mongodb)
195-
196193
# matrix.mode = high-deps
197194
echo "$COMPONENTS" | xargs -n1 | parallel -j +3 "_run_tests {} 'cd {} && $COMPOSER_UP && $PHPUNIT$LEGACY'" || X=1
198195
@@ -211,8 +208,6 @@ jobs:
211208
git fetch --depth=2 origin $SYMFONY_VERSION
212209
git checkout -m FETCH_HEAD
213210
PATCHED_COMPONENTS=$(echo "$PATCHED_COMPONENTS" | xargs dirname | xargs -n1 -I{} bash -c "[ -e '{}/phpunit.xml.dist' ] && echo '{}'" | sort || true)
214-
(cd src/Symfony/Component/HttpFoundation; composer require --dev --no-update mongodb/mongodb)
215-
(cd src/Symfony/Component/Lock; composer require --dev --no-update mongodb/mongodb)
216211
if [[ $PATCHED_COMPONENTS ]]; then
217212
echo "::group::install phpunit"
218213
./phpunit install

src/Symfony/Component/HttpFoundation/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ CHANGELOG
99
* Add `UriSigner` from the HttpKernel component
1010
* Add `partitioned` flag to `Cookie` (CHIPS Cookie)
1111
* Add argument `bool $flush = true` to `Response::send()`
12+
* Make `MongoDbSessionHandler` instantiable with the mongodb extension directly
1213

1314
6.3
1415
---

src/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.php

Lines changed: 59 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,22 @@
1414
use MongoDB\BSON\Binary;
1515
use MongoDB\BSON\UTCDateTime;
1616
use MongoDB\Client;
17-
use MongoDB\Collection;
17+
use MongoDB\Driver\BulkWrite;
18+
use MongoDB\Driver\Manager;
19+
use MongoDB\Driver\Query;
1820

1921
/**
20-
* Session handler using the mongodb/mongodb package and MongoDB driver extension.
22+
* Session handler using the MongoDB driver extension.
2123
*
2224
* @author Markus Bachmann <markus.bachmann@bachi.biz>
25+
* @author Jérôme Tamarelle <jerome@tamarelle.net>
2326
*
24-
* @see https://packagist.org/packages/mongodb/mongodb
2527
* @see https://php.net/mongodb
2628
*/
2729
class MongoDbSessionHandler extends AbstractSessionHandler
2830
{
29-
private Client $mongo;
30-
private Collection $collection;
31+
private Manager $manager;
32+
private string $namespace;
3133
private array $options;
3234
private int|\Closure|null $ttl;
3335

@@ -62,13 +64,18 @@ class MongoDbSessionHandler extends AbstractSessionHandler
6264
*
6365
* @throws \InvalidArgumentException When "database" or "collection" not provided
6466
*/
65-
public function __construct(Client $mongo, array $options)
67+
public function __construct(Client|Manager $mongo, array $options)
6668
{
6769
if (!isset($options['database']) || !isset($options['collection'])) {
6870
throw new \InvalidArgumentException('You must provide the "database" and "collection" option for MongoDBSessionHandler.');
6971
}
7072

71-
$this->mongo = $mongo;
73+
if ($mongo instanceof Client) {
74+
$mongo = $mongo->getManager();
75+
}
76+
77+
$this->manager = $mongo;
78+
$this->namespace = $options['database'].'.'.$options['collection'];
7279

7380
$this->options = array_merge([
7481
'id_field' => '_id',
@@ -86,77 +93,94 @@ public function close(): bool
8693

8794
protected function doDestroy(#[\SensitiveParameter] string $sessionId): bool
8895
{
89-
$this->getCollection()->deleteOne([
90-
$this->options['id_field'] => $sessionId,
91-
]);
96+
$write = new BulkWrite();
97+
$write->delete(
98+
[$this->options['id_field'] => $sessionId],
99+
['limit' => 1]
100+
);
101+
102+
$this->manager->executeBulkWrite($this->namespace, $write);
92103

93104
return true;
94105
}
95106

96107
public function gc(int $maxlifetime): int|false
97108
{
98-
return $this->getCollection()->deleteMany([
99-
$this->options['expiry_field'] => ['$lt' => new UTCDateTime()],
100-
])->getDeletedCount();
109+
$write = new BulkWrite();
110+
$write->delete(
111+
[$this->options['expiry_field'] => ['$lt' => $this->getUTCDateTime()]],
112+
);
113+
$result = $this->manager->executeBulkWrite($this->namespace, $write);
114+
115+
return $result->getDeletedCount() ?? false;
101116
}
102117

103118
protected function doWrite(#[\SensitiveParameter] string $sessionId, string $data): bool
104119
{
105120
$ttl = ($this->ttl instanceof \Closure ? ($this->ttl)() : $this->ttl) ?? \ini_get('session.gc_maxlifetime');
106-
$expiry = new UTCDateTime((time() + (int) $ttl) * 1000);
121+
$expiry = $this->getUTCDateTime($ttl);
107122

108123
$fields = [
109-
$this->options['time_field'] => new UTCDateTime(),
124+
$this->options['time_field'] => $this->getUTCDateTime(),
110125
$this->options['expiry_field'] => $expiry,
111-
$this->options['data_field'] => new Binary($data, Binary::TYPE_OLD_BINARY),
126+
$this->options['data_field'] => new Binary($data, Binary::TYPE_GENERIC),
112127
];
113128

114-
$this->getCollection()->updateOne(
129+
$write = new BulkWrite();
130+
$write->update(
115131
[$this->options['id_field'] => $sessionId],
116132
['$set' => $fields],
117133
['upsert' => true]
118134
);
119135

136+
$this->manager->executeBulkWrite($this->namespace, $write);
137+
120138
return true;
121139
}
122140

123141
public function updateTimestamp(#[\SensitiveParameter] string $sessionId, string $data): bool
124142
{
125143
$ttl = ($this->ttl instanceof \Closure ? ($this->ttl)() : $this->ttl) ?? \ini_get('session.gc_maxlifetime');
126-
$expiry = new UTCDateTime((time() + (int) $ttl) * 1000);
144+
$expiry = $this->getUTCDateTime($ttl);
127145

128-
$this->getCollection()->updateOne(
146+
$write = new BulkWrite();
147+
$write->update(
129148
[$this->options['id_field'] => $sessionId],
130149
['$set' => [
131-
$this->options['time_field'] => new UTCDateTime(),
150+
$this->options['time_field'] => $this->getUTCDateTime(),
132151
$this->options['expiry_field'] => $expiry,
133-
]]
152+
]],
153+
['multi' => false],
134154
);
135155

156+
$this->manager->executeBulkWrite($this->namespace, $write);
157+
136158
return true;
137159
}
138160

139161
protected function doRead(#[\SensitiveParameter] string $sessionId): string
140162
{
141-
$dbData = $this->getCollection()->findOne([
163+
$cursor = $this->manager->executeQuery($this->namespace, new Query([
142164
$this->options['id_field'] => $sessionId,
143-
$this->options['expiry_field'] => ['$gte' => new UTCDateTime()],
144-
]);
145-
146-
if (null === $dbData) {
147-
return '';
165+
$this->options['expiry_field'] => ['$gte' => $this->getUTCDateTime()],
166+
], [
167+
'projection' => [
168+
'_id' => false,
169+
$this->options['data_field'] => true,
170+
],
171+
'limit' => 1,
172+
]));
173+
174+
foreach ($cursor as $document) {
175+
return (string) $document->{$this->options['data_field']} ?? '';
148176
}
149177

150-
return $dbData[$this->options['data_field']]->getData();
151-
}
152-
153-
private function getCollection(): Collection
154-
{
155-
return $this->collection ??= $this->mongo->selectCollection($this->options['database'], $this->options['collection']);
178+
// Not found
179+
return '';
156180
}
157181

158-
protected function getMongo(): Client
182+
private function getUTCDateTime(int $additionalSeconds = 0): UTCDateTime
159183
{
160-
return $this->mongo;
184+
return new UTCDateTime((time() + $additionalSeconds) * 1000);
161185
}
162186
}

0 commit comments

Comments
 (0)
0