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,143 @@ 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
+ * @return int|float
168
+ */
169
+ #[Override]
170
+ public function increment ($ key , $ value = 1 ): int |float
171
+ {
172
+ $ this ->forgetIfExpired ($ key );
173
+
174
+ $ result = $ this ->collection ->findOneAndUpdate (
175
+ [
176
+ 'key ' => $ this ->prefix . $ key ,
177
+ 'expiration ' => ['$gte ' => $ this ->currentTime ()],
178
+ ],
179
+ [
180
+ '$inc ' => ['value ' => $ value ],
181
+ ],
182
+ [
183
+ 'upsert ' => true ,
184
+ 'returnDocument ' => FindOneAndUpdate::RETURN_DOCUMENT_AFTER ,
185
+ ],
88
186
);
187
+
188
+ return $ result ? $ result ->value : false ;
189
+ }
190
+
191
+ /**
192
+ * Decrement the value of an item in the cache.
193
+ *
194
+ * @param string $key
195
+ * @param int|float $value
196
+ *
197
+ * @return int|float
198
+ */
199
+ #[Override]
200
+ public function decrement ($ key , $ value = 1 )
201
+ {
202
+ return $ this ->increment ($ key , -1 * $ value );
203
+ }
204
+
205
+ /**
206
+ * Store an item in the cache indefinitely.
207
+ *
208
+ * @param string $key
209
+ * @param mixed $value
210
+ */
211
+ #[Override]
212
+ public function forever ($ key , $ value ): bool
213
+ {
214
+ return $ this ->put ($ key , $ value , 315360000 );
215
+ }
216
+
217
+ /**
218
+ * Remove an item from the cache.
219
+ *
220
+ * @param string $key
221
+ */
222
+ #[Override]
223
+ public function forget ($ key ): bool
224
+ {
225
+ $ result = $ this ->collection ->deleteOne ([
226
+ 'key ' => $ this ->prefix . $ key ,
227
+ ]);
228
+
229
+ return $ result ->getDeletedCount () > 0 ;
230
+ }
231
+
232
+ /**
233
+ * Remove an item from the cache if it is expired.
234
+ *
235
+ * @param string $key
236
+ */
237
+ public function forgetIfExpired ($ key ): bool
238
+ {
239
+ $ result = $ this ->collection ->deleteOne ([
240
+ 'key ' => $ this ->prefix . $ key ,
241
+ 'expiration ' => ['$lte ' => $ this ->currentTime ()],
242
+ ]);
243
+
244
+ return $ result ->getDeletedCount () > 0 ;
245
+ }
246
+
247
+ public function flush (): bool
248
+ {
249
+ $ this ->collection ->deleteMany ([]);
250
+
251
+ return true ;
252
+ }
253
+
254
+ public function getPrefix ()
255
+ {
256
+ return $ this ->prefix ;
257
+ }
258
+
259
+ private function serialize ($ value ): string |int
260
+ {
261
+ // Don't serialize numbers, so they can be incremented
262
+ if (is_int ($ value ) || is_float ($ value )) {
263
+ return $ value ;
264
+ }
265
+
266
+ return serialize ($ value );
267
+ }
268
+
269
+ private function unserialize ($ value ): string |int
270
+ {
271
+ if (! is_string ($ value ) || ! str_contains ($ value , '; ' )) {
272
+ return $ value ;
273
+ }
274
+
275
+ return unserialize ($ value );
89
276
}
90
277
}
0 commit comments