14
14
use MongoDB \BSON \Binary ;
15
15
use MongoDB \BSON \UTCDateTime ;
16
16
use MongoDB \Client ;
17
- use MongoDB \Collection ;
17
+ use MongoDB \Driver \BulkWrite ;
18
+ use MongoDB \Driver \Manager ;
19
+ use MongoDB \Driver \Query ;
18
20
19
21
/**
20
- * Session handler using the mongodb/mongodb package and MongoDB driver extension.
22
+ * Session handler using the MongoDB driver extension.
21
23
*
22
24
* @author Markus Bachmann <markus.bachmann@bachi.biz>
25
+ * @author Jérôme Tamarelle <jerome@tamarelle.net>
23
26
*
24
- * @see https://packagist.org/packages/mongodb/mongodb
25
27
* @see https://php.net/mongodb
26
28
*/
27
29
class MongoDbSessionHandler extends AbstractSessionHandler
28
30
{
29
- private Client $ mongo ;
30
- private Collection $ collection ;
31
+ private Manager $ manager ;
32
+ private string $ namespace ;
31
33
private array $ options ;
32
34
private int |\Closure |null $ ttl ;
33
35
@@ -62,13 +64,18 @@ class MongoDbSessionHandler extends AbstractSessionHandler
62
64
*
63
65
* @throws \InvalidArgumentException When "database" or "collection" not provided
64
66
*/
65
- public function __construct (Client $ mongo , array $ options )
67
+ public function __construct (Client | Manager $ mongo , array $ options )
66
68
{
67
69
if (!isset ($ options ['database ' ]) || !isset ($ options ['collection ' ])) {
68
70
throw new \InvalidArgumentException ('You must provide the "database" and "collection" option for MongoDBSessionHandler. ' );
69
71
}
70
72
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 ' ];
72
79
73
80
$ this ->options = array_merge ([
74
81
'id_field ' => '_id ' ,
@@ -86,77 +93,97 @@ public function close(): bool
86
93
87
94
protected function doDestroy (#[\SensitiveParameter] string $ sessionId ): bool
88
95
{
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 );
92
103
93
104
return true ;
94
105
}
95
106
96
107
public function gc (int $ maxlifetime ): int |false
97
108
{
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 ;
101
116
}
102
117
103
118
protected function doWrite (#[\SensitiveParameter] string $ sessionId , string $ data ): bool
104
119
{
105
120
$ 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 );
107
122
108
123
$ fields = [
109
- $ this ->options ['time_field ' ] => new UTCDateTime (),
124
+ $ this ->options ['time_field ' ] => $ this -> getUTCDateTime (),
110
125
$ this ->options ['expiry_field ' ] => $ expiry ,
111
126
$ this ->options ['data_field ' ] => new Binary ($ data , Binary::TYPE_OLD_BINARY ),
112
127
];
113
128
114
- $ this ->getCollection ()->updateOne (
129
+ $ write = new BulkWrite ();
130
+ $ write ->update (
115
131
[$ this ->options ['id_field ' ] => $ sessionId ],
116
132
['$set ' => $ fields ],
117
133
['upsert ' => true ]
118
134
);
119
135
136
+ $ this ->manager ->executeBulkWrite ($ this ->namespace , $ write );
137
+
120
138
return true ;
121
139
}
122
140
123
141
public function updateTimestamp (#[\SensitiveParameter] string $ sessionId , string $ data ): bool
124
142
{
125
143
$ 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 );
127
145
128
- $ this ->getCollection ()->updateOne (
146
+ $ write = new BulkWrite ();
147
+ $ write ->update (
129
148
[$ this ->options ['id_field ' ] => $ sessionId ],
130
149
['$set ' => [
131
- $ this ->options ['time_field ' ] => new UTCDateTime (),
150
+ $ this ->options ['time_field ' ] => $ this -> getUTCDateTime (),
132
151
$ this ->options ['expiry_field ' ] => $ expiry ,
133
- ]]
152
+ ]],
153
+ ['multi ' => false ],
134
154
);
135
155
156
+ $ this ->manager ->executeBulkWrite ($ this ->namespace , $ write );
157
+
136
158
return true ;
137
159
}
138
160
139
161
protected function doRead (#[\SensitiveParameter] string $ sessionId ): string
140
162
{
141
- $ dbData = $ this ->getCollection ()-> findOne ([
163
+ $ dbData = $ this ->manager -> executeQuery ( $ this -> namespace , new Query ([
142
164
$ this ->options ['id_field ' ] => $ sessionId ,
143
- $ this ->options ['expiry_field ' ] => ['$gte ' => new UTCDateTime ()],
144
- ]);
145
-
146
- if (null === $ dbData ) {
165
+ $ this ->options ['expiry_field ' ] => ['$gte ' => $ this ->getUTCDateTime ()],
166
+ ], [
167
+ 'projection ' => [
168
+ '_id ' => false ,
169
+ $ this ->options ['data_field ' ] => true ,
170
+ ],
171
+ ]));
172
+ $ dbData ->setTypeMap (['root ' => 'object ' ]);
173
+ $ dbData ->rewind ();
174
+ $ data = $ dbData ->current ();
175
+
176
+ if (!$ data ) {
147
177
return '' ;
148
178
}
149
179
150
- return $ dbData [ $ this ->options ['data_field ' ]]-> getData () ;
180
+ return ( string ) $ data ->{ $ this ->options ['data_field ' ]} ;
151
181
}
152
182
153
- private function getCollection ( ): Collection
183
+ private function getUTCDateTime ( int $ additionalSeconds = 0 ): UTCDateTime
154
184
{
155
- return $ this ->collection ??= $ this ->mongo ->selectCollection ($ this ->options ['database ' ], $ this ->options ['collection ' ]);
156
- }
185
+ $ timestamp = new \DateTimeImmutable ('now ' , new \DateTimeZone ('UTC ' ));
157
186
158
- protected function getMongo (): Client
159
- {
160
- return $ this ->mongo ;
187
+ return new UTCDateTime (($ timestamp ->getTimestamp () + $ additionalSeconds ) * 1000 );
161
188
}
162
189
}
0 commit comments