Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Illuminate\Cache;
4
 
5
use Closure;
6
use Exception;
7
use Illuminate\Contracts\Cache\LockProvider;
8
use Illuminate\Contracts\Cache\Store;
9
use Illuminate\Database\ConnectionInterface;
10
use Illuminate\Database\PostgresConnection;
11
use Illuminate\Database\QueryException;
12
use Illuminate\Support\InteractsWithTime;
13
use Illuminate\Support\Str;
14
 
15
class DatabaseStore implements LockProvider, Store
16
{
17
    use InteractsWithTime, RetrievesMultipleKeys;
18
 
19
    /**
20
     * The database connection instance.
21
     *
22
     * @var \Illuminate\Database\ConnectionInterface
23
     */
24
    protected $connection;
25
 
26
    /**
27
     * The database connection instance that should be used to manage locks.
28
     *
29
     * @var \Illuminate\Database\ConnectionInterface
30
     */
31
    protected $lockConnection;
32
 
33
    /**
34
     * The name of the cache table.
35
     *
36
     * @var string
37
     */
38
    protected $table;
39
 
40
    /**
41
     * A string that should be prepended to keys.
42
     *
43
     * @var string
44
     */
45
    protected $prefix;
46
 
47
    /**
48
     * The name of the cache locks table.
49
     *
50
     * @var string
51
     */
52
    protected $lockTable;
53
 
54
    /**
55
     * An array representation of the lock lottery odds.
56
     *
57
     * @var array
58
     */
59
    protected $lockLottery;
60
 
61
    /**
62
     * Create a new database store.
63
     *
64
     * @param  \Illuminate\Database\ConnectionInterface  $connection
65
     * @param  string  $table
66
     * @param  string  $prefix
67
     * @param  string  $lockTable
68
     * @param  array  $lockLottery
69
     * @return void
70
     */
71
    public function __construct(ConnectionInterface $connection,
72
                                $table,
73
                                $prefix = '',
74
                                $lockTable = 'cache_locks',
75
                                $lockLottery = [2, 100])
76
    {
77
        $this->table = $table;
78
        $this->prefix = $prefix;
79
        $this->connection = $connection;
80
        $this->lockTable = $lockTable;
81
        $this->lockLottery = $lockLottery;
82
    }
83
 
84
    /**
85
     * Retrieve an item from the cache by key.
86
     *
87
     * @param  string|array  $key
88
     * @return mixed
89
     */
90
    public function get($key)
91
    {
92
        $prefixed = $this->prefix.$key;
93
 
94
        $cache = $this->table()->where('key', '=', $prefixed)->first();
95
 
96
        // If we have a cache record we will check the expiration time against current
97
        // time on the system and see if the record has expired. If it has, we will
98
        // remove the records from the database table so it isn't returned again.
99
        if (is_null($cache)) {
100
            return;
101
        }
102
 
103
        $cache = is_array($cache) ? (object) $cache : $cache;
104
 
105
        // If this cache expiration date is past the current time, we will remove this
106
        // item from the cache. Then we will return a null value since the cache is
107
        // expired. We will use "Carbon" to make this comparison with the column.
108
        if ($this->currentTime() >= $cache->expiration) {
109
            $this->forget($key);
110
 
111
            return;
112
        }
113
 
114
        return $this->unserialize($cache->value);
115
    }
116
 
117
    /**
118
     * Store an item in the cache for a given number of seconds.
119
     *
120
     * @param  string  $key
121
     * @param  mixed  $value
122
     * @param  int  $seconds
123
     * @return bool
124
     */
125
    public function put($key, $value, $seconds)
126
    {
127
        $key = $this->prefix.$key;
128
        $value = $this->serialize($value);
129
        $expiration = $this->getTime() + $seconds;
130
 
131
        try {
132
            return $this->table()->insert(compact('key', 'value', 'expiration'));
133
        } catch (Exception $e) {
134
            $result = $this->table()->where('key', $key)->update(compact('value', 'expiration'));
135
 
136
            return $result > 0;
137
        }
138
    }
139
 
140
    /**
141
     * Store an item in the cache if the key doesn't exist.
142
     *
143
     * @param  string  $key
144
     * @param  mixed  $value
145
     * @param  int  $seconds
146
     * @return bool
147
     */
148
    public function add($key, $value, $seconds)
149
    {
150
        $key = $this->prefix.$key;
151
        $value = $this->serialize($value);
152
        $expiration = $this->getTime() + $seconds;
153
 
154
        try {
155
            return $this->table()->insert(compact('key', 'value', 'expiration'));
156
        } catch (QueryException $e) {
157
            return $this->table()
158
                ->where('key', $key)
159
                ->where('expiration', '<=', $this->getTime())
160
                ->update([
161
                    'value' => $value,
162
                    'expiration' => $expiration,
163
                ]) >= 1;
164
        }
165
    }
166
 
167
    /**
168
     * Increment the value of an item in the cache.
169
     *
170
     * @param  string  $key
171
     * @param  mixed  $value
172
     * @return int|bool
173
     */
174
    public function increment($key, $value = 1)
175
    {
176
        return $this->incrementOrDecrement($key, $value, function ($current, $value) {
177
            return $current + $value;
178
        });
179
    }
180
 
181
    /**
182
     * Decrement the value of an item in the cache.
183
     *
184
     * @param  string  $key
185
     * @param  mixed  $value
186
     * @return int|bool
187
     */
188
    public function decrement($key, $value = 1)
189
    {
190
        return $this->incrementOrDecrement($key, $value, function ($current, $value) {
191
            return $current - $value;
192
        });
193
    }
194
 
195
    /**
196
     * Increment or decrement an item in the cache.
197
     *
198
     * @param  string  $key
199
     * @param  mixed  $value
200
     * @param  \Closure  $callback
201
     * @return int|bool
202
     */
203
    protected function incrementOrDecrement($key, $value, Closure $callback)
204
    {
205
        return $this->connection->transaction(function () use ($key, $value, $callback) {
206
            $prefixed = $this->prefix.$key;
207
 
208
            $cache = $this->table()->where('key', $prefixed)
209
                        ->lockForUpdate()->first();
210
 
211
            // If there is no value in the cache, we will return false here. Otherwise the
212
            // value will be decrypted and we will proceed with this function to either
213
            // increment or decrement this value based on the given action callbacks.
214
            if (is_null($cache)) {
215
                return false;
216
            }
217
 
218
            $cache = is_array($cache) ? (object) $cache : $cache;
219
 
220
            $current = $this->unserialize($cache->value);
221
 
222
            // Here we'll call this callback function that was given to the function which
223
            // is used to either increment or decrement the function. We use a callback
224
            // so we do not have to recreate all this logic in each of the functions.
225
            $new = $callback((int) $current, $value);
226
 
227
            if (! is_numeric($current)) {
228
                return false;
229
            }
230
 
231
            // Here we will update the values in the table. We will also encrypt the value
232
            // since database cache values are encrypted by default with secure storage
233
            // that can't be easily read. We will return the new value after storing.
234
            $this->table()->where('key', $prefixed)->update([
235
                'value' => $this->serialize($new),
236
            ]);
237
 
238
            return $new;
239
        });
240
    }
241
 
242
    /**
243
     * Get the current system time.
244
     *
245
     * @return int
246
     */
247
    protected function getTime()
248
    {
249
        return $this->currentTime();
250
    }
251
 
252
    /**
253
     * Store an item in the cache indefinitely.
254
     *
255
     * @param  string  $key
256
     * @param  mixed  $value
257
     * @return bool
258
     */
259
    public function forever($key, $value)
260
    {
261
        return $this->put($key, $value, 315360000);
262
    }
263
 
264
    /**
265
     * Get a lock instance.
266
     *
267
     * @param  string  $name
268
     * @param  int  $seconds
269
     * @param  string|null  $owner
270
     * @return \Illuminate\Contracts\Cache\Lock
271
     */
272
    public function lock($name, $seconds = 0, $owner = null)
273
    {
274
        return new DatabaseLock(
275
            $this->lockConnection ?? $this->connection,
276
            $this->lockTable,
277
            $this->prefix.$name,
278
            $seconds,
279
            $owner,
280
            $this->lockLottery
281
        );
282
    }
283
 
284
    /**
285
     * Restore a lock instance using the owner identifier.
286
     *
287
     * @param  string  $name
288
     * @param  string  $owner
289
     * @return \Illuminate\Contracts\Cache\Lock
290
     */
291
    public function restoreLock($name, $owner)
292
    {
293
        return $this->lock($name, 0, $owner);
294
    }
295
 
296
    /**
297
     * Remove an item from the cache.
298
     *
299
     * @param  string  $key
300
     * @return bool
301
     */
302
    public function forget($key)
303
    {
304
        $this->table()->where('key', '=', $this->prefix.$key)->delete();
305
 
306
        return true;
307
    }
308
 
309
    /**
310
     * Remove all items from the cache.
311
     *
312
     * @return bool
313
     */
314
    public function flush()
315
    {
316
        $this->table()->delete();
317
 
318
        return true;
319
    }
320
 
321
    /**
322
     * Get a query builder for the cache table.
323
     *
324
     * @return \Illuminate\Database\Query\Builder
325
     */
326
    protected function table()
327
    {
328
        return $this->connection->table($this->table);
329
    }
330
 
331
    /**
332
     * Get the underlying database connection.
333
     *
334
     * @return \Illuminate\Database\ConnectionInterface
335
     */
336
    public function getConnection()
337
    {
338
        return $this->connection;
339
    }
340
 
341
    /**
342
     * Specify the name of the connection that should be used to manage locks.
343
     *
344
     * @param  \Illuminate\Database\ConnectionInterface  $connection
345
     * @return $this
346
     */
347
    public function setLockConnection($connection)
348
    {
349
        $this->lockConnection = $connection;
350
 
351
        return $this;
352
    }
353
 
354
    /**
355
     * Get the cache key prefix.
356
     *
357
     * @return string
358
     */
359
    public function getPrefix()
360
    {
361
        return $this->prefix;
362
    }
363
 
364
    /**
365
     * Serialize the given value.
366
     *
367
     * @param  mixed  $value
368
     * @return string
369
     */
370
    protected function serialize($value)
371
    {
372
        $result = serialize($value);
373
 
374
        if ($this->connection instanceof PostgresConnection && str_contains($result, "\0")) {
375
            $result = base64_encode($result);
376
        }
377
 
378
        return $result;
379
    }
380
 
381
    /**
382
     * Unserialize the given value.
383
     *
384
     * @param  string  $value
385
     * @return mixed
386
     */
387
    protected function unserialize($value)
388
    {
389
        if ($this->connection instanceof PostgresConnection && ! Str::contains($value, [':', ';'])) {
390
            $value = base64_decode($value);
391
        }
392
 
393
        return unserialize($value);
394
    }
395
}