2
2
3
3
namespace MongoDB \Laravel \Cache ;
4
4
5
- use Illuminate \Cache \DatabaseStore ;
5
+ use Illuminate \Cache \RetrievesMultipleKeys ;
6
+ use Illuminate \Contracts \Cache \LockProvider ;
7
+ use Illuminate \Contracts \Cache \Store ;
8
+ use Illuminate \Support \InteractsWithTime ;
6
9
use MongoDB \Laravel \Collection ;
7
10
use MongoDB \Laravel \Connection ;
11
+ use MongoDB \Operation \FindOneAndUpdate ;
12
+ use Override ;
8
13
9
- use function assert ;
14
+ use function is_float ;
15
+ use function is_int ;
16
+ use function is_string ;
17
+ use function serialize ;
18
+ use function str_contains ;
19
+ use function unserialize ;
10
20
11
- class MongoStore extends DatabaseStore
21
+ final class MongoStore implements LockProvider, Store
12
22
{
23
+ use InteractsWithTime;
24
+ // Provides "many" and "putMany" in a non-optimized way
25
+ use RetrievesMultipleKeys;
26
+
27
+ private Collection $ collection ;
28
+
13
29
public function __construct (
14
- Connection $ connection ,
15
- string $ table ,
16
- string $ prefix = '' ,
17
- string $ lockTable = 'cache_locks ' ,
18
- array $ lockLottery = [2 , 100 ],
19
- int $ defaultLockTimeoutInSeconds = 86400 ,
30
+ private Connection $ connection ,
31
+ private string $ collectionName ,
32
+ private string $ prefix = '' ,
33
+ private ?Connection $ lockConnection = null ,
34
+ private string $ lockCollectionName = 'cache_locks ' ,
35
+ private array $ lockLottery = [2 , 100 ],
36
+ private int $ defaultLockTimeoutInSeconds = 86400 ,
20
37
) {
21
- parent ::__construct ($ connection , $ table , $ prefix , $ lockTable , $ lockLottery , $ defaultLockTimeoutInSeconds );
38
+ $ this ->collection = $ this ->connection ->getCollection ($ this ->collectionName );
39
+ }
40
+
41
+ /**
42
+ * Get a lock instance.
43
+ *
44
+ * @param string $name
45
+ * @param int $seconds
46
+ * @param string|null $owner
47
+ */
48
+ #[Override]
49
+ public function lock ($ name , $ seconds = 0 , $ owner = null ): MongoLock
50
+ {
51
+ return new MongoLock (
52
+ ($ this ->lockConnection ?? $ this ->connection )->getCollection ($ this ->lockCollectionName ),
53
+ $ this ->prefix . $ name ,
54
+ $ seconds ,
55
+ $ owner ,
56
+ $ this ->lockLottery ,
57
+ $ this ->defaultLockTimeoutInSeconds ,
58
+ );
59
+ }
60
+
61
+ /**
62
+ * Restore a lock instance using the owner identifier.
63
+ */
64
+ #[Override]
65
+ public function restoreLock ($ name , $ owner ): MongoLock
66
+ {
67
+ return $ this ->lock ($ name , 0 , $ owner );
22
68
}
23
69
70
+ /**
71
+ * Store an item in the cache for a given number of seconds.
72
+ *
73
+ * @param string $key
74
+ * @param mixed $value
75
+ * @param int $seconds
76
+ */
77
+ #[Override]
24
78
public function put ($ key , $ value , $ seconds ): bool
25
79
{
26
- $ key = $ this ->prefix . $ key ;
27
- $ value = $ this ->serialize ($ value );
28
- $ expiration = $ this ->getTime () + $ seconds ;
29
- $ collection = $ this ->table ()->raw (null );
30
- assert ($ collection instanceof Collection);
31
-
32
- $ result = $ collection ->updateOne (
33
- ['key ' => ['$eq ' => $ key ]],
34
- ['$set ' => ['value ' => $ value , 'expiration ' => $ expiration ]],
35
- ['upsert ' => true ],
80
+ $ result = $ this ->collection ->updateOne (
81
+ [
82
+ 'key ' => $ this ->prefix . $ key ,
83
+ ],
84
+ [
85
+ '$set ' => [
86
+ 'value ' => $ this ->serialize ($ value ),
87
+ 'expiration ' => $ this ->currentTime () + $ seconds ,
88
+ ],
89
+ ],
90
+ [
91
+ 'upsert ' => true ,
92
+
93
+ ],
36
94
);
37
95
38
96
return $ result ->getUpsertedCount () > 0 || $ result ->getModifiedCount () > 0 ;
39
97
}
40
98
99
+ /**
100
+ * Store an item in the cache if the key doesn't exist.
101
+ *
102
+ * @param string $key
103
+ * @param mixed $value
104
+ * @param int $seconds
105
+ */
41
106
public function add ($ key , $ value , $ seconds ): bool
42
107
{
43
- $ key = $ this ->prefix . $ key ;
44
- $ value = $ this ->serialize ($ value );
45
- $ expiration = $ this ->getTime () + $ seconds ;
46
- $ collection = $ this ->table ()->raw (null );
47
- assert ($ collection instanceof Collection);
48
-
49
- $ result = $ collection ->updateOne (
50
- ['key ' => ['$eq ' => $ key ]],
108
+ $ result = $ this ->collection ->updateOne (
109
+ [
110
+ 'key ' => $ this ->prefix . $ key ,
111
+ ],
51
112
[
52
113
[
53
114
'$set ' => [
54
115
'value ' => [
55
116
'$cond ' => [
56
- 'if ' => ['$lte ' => ['$expiration ' , $ this ->getTime ()]],
57
- 'then ' => $ value ,
117
+ 'if ' => ['$lte ' => ['$expiration ' , $ this ->currentTime ()]],
118
+ 'then ' => $ this -> serialize ( $ value) ,
58
119
'else ' => '$value ' ,
59
120
],
60
121
],
61
122
'expiration ' => [
62
123
'$cond ' => [
63
- 'if ' => ['$lte ' => ['$expiration ' , $ this ->getTime ()]],
64
- 'then ' => $ expiration ,
124
+ 'if ' => ['$lte ' => ['$expiration ' , $ this ->currentTime ()]],
125
+ 'then ' => $ this -> currentTime () + $ seconds ,
65
126
'else ' => '$expiration ' ,
66
127
],
67
128
],
@@ -74,17 +135,148 @@ public function add($key, $value, $seconds): bool
74
135
return $ result ->getUpsertedCount () > 0 || $ result ->getModifiedCount () > 0 ;
75
136
}
76
137
77
- public function lock ($ name , $ seconds = 0 , $ owner = null )
138
+ /**
139
+ * Retrieve an item from the cache by key.
140
+ *
141
+ * @param string $key
142
+ */
143
+ #[Override]
144
+ public function get ($ key ): mixed
78
145
{
79
- assert ($ this ->connection instanceof Connection);
146
+ $ result = $ this ->collection ->findOne (
147
+ [
148
+ 'key ' => $ this ->prefix . $ key ,
149
+ ],
150
+ );
80
151
81
- return new MongoLock (
82
- ($ this ->lockConnection ?? $ this ->connection )->getCollection ($ this ->lockTable ),
83
- $ this ->prefix . $ name ,
84
- $ seconds ,
85
- $ owner ,
86
- $ this ->lockLottery ,
87
- $ this ->defaultLockTimeoutInSeconds ,
152
+ if ($ result ->expiration <= $ this ->currentTime ()) {
153
+ $ this ->forgetIfExpired ($ key );
154
+
155
+ return null ;
156
+ }
157
+
158
+ return $ this ->unserialize ($ result ->value );
159
+ }
160
+
161
+ /**
162
+ * Increment the value of an item in the cache.
163
+ *
164
+ * @param string $key
165
+ * @param int|float $value
166
+ */
167
+ #[Override]
168
+ public function increment ($ key , $ value = 1 ): int |float |false
169
+ {
170
+ $ this ->forgetIfExpired ($ key );
171
+
172
+ $ result = $ this ->collection ->findOneAndUpdate (
173
+ [
174
+ 'key ' => $ this ->prefix . $ key ,
175
+ 'expiration ' => ['$gte ' => $ this ->currentTime ()],
176
+ ],
177
+ [
178
+ '$inc ' => ['value ' => $ value ],
179
+ ],
180
+ [
181
+ 'returnDocument ' => FindOneAndUpdate::RETURN_DOCUMENT_AFTER ,
182
+ ],
88
183
);
184
+
185
+ if (! $ result ) {
186
+ return false ;
187
+ }
188
+
189
+ if ($ result ->expiration <= $ this ->currentTime ()) {
190
+ $ this ->forgetIfExpired ($ key );
191
+
192
+ return false ;
193
+ }
194
+
195
+ return $ result ->value ;
196
+ }
197
+
198
+ /**
199
+ * Decrement the value of an item in the cache.
200
+ *
201
+ * @param string $key
202
+ * @param int|float $value
203
+ */
204
+ #[Override]
205
+ public function decrement ($ key , $ value = 1 ): int |float |false
206
+ {
207
+ return $ this ->increment ($ key , -1 * $ value );
208
+ }
209
+
210
+ /**
211
+ * Store an item in the cache indefinitely.
212
+ *
213
+ * @param string $key
214
+ * @param mixed $value
215
+ */
216
+ #[Override]
217
+ public function forever ($ key , $ value ): bool
218
+ {
219
+ return $ this ->put ($ key , $ value , 315360000 );
220
+ }
221
+
222
+ /**
223
+ * Remove an item from the cache.
224
+ *
225
+ * @param string $key
226
+ */
227
+ #[Override]
228
+ public function forget ($ key ): bool
229
+ {
230
+ $ result = $ this ->collection ->deleteOne ([
231
+ 'key ' => $ this ->prefix . $ key ,
232
+ ]);
233
+
234
+ return $ result ->getDeletedCount () > 0 ;
235
+ }
236
+
237
+ /**
238
+ * Remove an item from the cache if it is expired.
239
+ *
240
+ * @param string $key
241
+ */
242
+ public function forgetIfExpired ($ key ): bool
243
+ {
244
+ $ result = $ this ->collection ->deleteOne ([
245
+ 'key ' => $ this ->prefix . $ key ,
246
+ 'expiration ' => ['$lte ' => $ this ->currentTime ()],
247
+ ]);
248
+
249
+ return $ result ->getDeletedCount () > 0 ;
250
+ }
251
+
252
+ public function flush (): bool
253
+ {
254
+ $ this ->collection ->deleteMany ([]);
255
+
256
+ return true ;
257
+ }
258
+
259
+ public function getPrefix ()
260
+ {
261
+ return $ this ->prefix ;
262
+ }
263
+
264
+ private function serialize ($ value ): string |int |float
265
+ {
266
+ // Don't serialize numbers, so they can be incremented
267
+ if (is_int ($ value ) || is_float ($ value )) {
268
+ return $ value ;
269
+ }
270
+
271
+ return serialize ($ value );
272
+ }
273
+
274
+ private function unserialize ($ value ): mixed
275
+ {
276
+ if (! is_string ($ value ) || ! str_contains ($ value , '; ' )) {
277
+ return $ value ;
278
+ }
279
+
280
+ return unserialize ($ value );
89
281
}
90
282
}
0 commit comments