CacheProvider   A
last analyzed

Complexity

Total Complexity 29

Size/Duplication

Total Lines 288
Duplicated Lines 0 %

Test Coverage

Coverage 97.26%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 65
c 2
b 0
f 0
dl 0
loc 288
ccs 71
cts 73
cp 0.9726
rs 10
wmc 29

12 Methods

Rating   Name   Duplication   Size   Complexity  
A getType() 0 3 1
A setMultiple() 0 13 3
A isExpired() 0 12 3
A gc() 0 28 6
A getMultiple() 0 11 2
A get() 0 16 3
A delete() 0 5 1
A clear() 0 3 1
A deleteMultiple() 0 13 3
A getAll() 0 3 1
A has() 0 9 2
A set() 0 17 3
1
<?php
2
/*
3
 * This file is part of the Shieldon Simple Cache package.
4
 *
5
 * (c) Terry L. <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
declare(strict_types=1);
12
13
namespace Shieldon\SimpleCache;
14
15
use Psr\SimpleCache\CacheInterface;
16
use Shieldon\SimpleCache\AssertTrait;
17
use DateInterval;
18
use Datetime;
19
use function intval;
20
use function is_null;
21
use function rand;
22
use function time;
23
24
/**
25
 * The abstract class for cache service providers.
26
 */
27
abstract class CacheProvider implements CacheInterface
28
{
29
    use AssertTrait;
30
31
    /**
32
     * The type of cache driver.
33
     *
34
     * @var string
35
     */
36
    protected $type = '';
37
38
    /**
39
     * Get a cache by an extended Cache Driver.
40
     *
41
     * @inheritDoc
42
     */
43 88
    public function get($key, $default = null)
44
    {
45 88
        $this->assertArgumentString($key);
46
47 86
        $data = $this->doGet($key);
48
49 86
        if (!empty($data)) {
50 82
            if ($this->isExpired($data['ttl'], $data['timestamp'])) {
51 12
                $this->delete($key);
52 12
                $data['value'] = $default;
53
            }
54
55 82
            $default = $data['value'];
56
        }
57
58 86
        return $default;
59
    }
60
61
    /**
62
     * Set a cache by an extended Cache Driver.
63
     *
64
     * @inheritDoc
65
     */
66 104
    public function set($key, $value, $ttl = null)
67
    {
68 104
        $this->assertArgumentString($key);
69 100
        $this->assertValidTypeOfTtl($ttl);
70
71 96
        $timestamp = time();
72
73 96
        if (is_null($ttl)) {
74 20
            $ttl = 0;
75 96
        } elseif ($ttl instanceof DateInterval) {
76 20
            $datetimeObj = new DateTime();
77 20
            $datetimeObj->add($ttl);
78
79 20
            $ttl = $datetimeObj->getTimestamp() - $timestamp;
80
        }
81
82 96
        return $this->doSet($key, $value, $ttl, $timestamp);
83
    }
84
85
    /**
86
     * Delete a cache by an extended Cache Driver.
87
     *
88
     * @inheritDoc
89
     */
90 42
    public function delete($key)
91
    {
92 42
        $this->assertArgumentString($key);
93
94 42
        return $this->doDelete($key);
95
    }
96
97
    /**
98
     * Clear all caches by an extended Cache Driver.
99
     *
100
     * @inheritDoc
101
     */
102 54
    public function clear()
103
    {
104 54
        return $this->doClear();
105
    }
106
107
    /**
108
     * Check if a cache exists by an extended Cache Driver.
109
     *
110
     * @inheritDoc
111
     */
112 44
    public function has($key)
113
    {
114 44
        $this->assertArgumentString($key);
115
116 42
        if ($this->doHas($key)) {
117 42
            return true;
118
        }
119
120 42
        return false;
121
    }
122
123
    /**
124
     * Get multiple caches by an extended Cache Driver.
125
     *
126
     * @inheritDoc
127
     */
128 22
    public function getMultiple($keys, $default = null)
129
    {
130 22
        $this->assertArgumentIterable($keys);
131
132 22
        $data = [];
133
134 22
        foreach ($keys as $key) {
135 22
            $data[$key] = $this->get($key, $default);
136
        }
137
138 22
        return $data;
139
    }
140
141
    /**
142
     * Set multiple caches by an extended Cache Driver.
143
     *
144
     * @inheritDoc
145
     */
146 38
    public function setMultiple($values, $ttl = null)
147
    {
148 38
        $this->assertArgumentIterable($values);
149
150 38
        foreach ($values as $key => $value) {
151 38
            if (!$this->set($key, $value, $ttl)) {
152
                // @codeCoverageIgnoreStart
153
                return false;
154
                // @codeCoverageIgnoreEnd
155
            }
156
        }
157
158 34
        return true;
159
    }
160
161
    /**
162
     * Delete multiple caches by an extended Cache Driver.
163
     *
164
     * @inheritDoc
165
     */
166 24
    public function deleteMultiple($keys)
167
    {
168 24
        $this->assertArgumentIterable($keys);
169
170 22
        foreach ($keys as $key) {
171 22
            if (!$this->doDelete($key)) {
172
                // @codeCoverageIgnoreStart
173
                return false;
174
                // @codeCoverageIgnoreEnd
175
            }
176
        }
177
178 22
        return true;
179
    }
180
181
    /**
182
     * Performing cache data garbage collection for drivers that don't have
183
     * ability to remove expired items automatically.
184
     * This method is not needed for Redis and Memcached driver.
185
     *
186
     * @param int $probability Numerator.
187
     * @param int $divisor     Denominator.
188
     *
189
     * @return array
190
     */
191 20
    public function gc(int $probability, int $divisor): array
192
    {
193 20
        if ($probability > $divisor) {
194 18
            $probability = $divisor;
195
        }
196
197 20
        $chance = intval($divisor / $probability);
198 20
        $hit    = rand(1, $chance);
199 20
        $list   = [];
200
201 20
        if ($hit === 1) {
202
            // Always return [] from Redis and Memcached driver.
203 20
            $data = $this->getAll();
204
205 20
            if (!empty($data)) {
206 10
                foreach ($data as $key => $value) {
207 10
                    $ttl      = (int) $value['ttl'];
208 10
                    $lasttime = (int) $value['timestamp'];
209
210 10
                    if ($this->isExpired($ttl, $lasttime)) {
211 10
                        $this->delete($key);
212
213 10
                        $list[] = $key;
214
                    }
215
                }
216
            }
217
        }
218 20
        return $list;
219
    }
220
221
    public function getType(): string
222
    {
223
        return $this->type;
224
    }
225
226
    /**
227
     * Check if the TTL is expired or not.
228
     *
229
     * @param int $ttl       The time to live of a cached data.
230
     * @param int $timestamp The unix timesamp that want to check.
231
     *
232
     * @return bool
233
     */
234 84
    protected function isExpired(int $ttl, int $timestamp): bool
235
    {
236 84
        $now = time();
237
238
        // If $ttl equal to 0 means that never expires.
239 84
        if (empty($ttl)) {
240 22
            return false;
241 82
        } elseif ($now - $timestamp < $ttl) {
242 82
            return false;
243
        }
244
245 20
        return true;
246
    }
247
248
    /**
249
     * Fetch all cache items to prepare removing expired items.
250
     * This method is not needed for Redis and Memcached driver because that
251
     * it is used only in `gc()`.
252
     *
253
     * @return array
254
     */
255 6
    protected function getAll(): array
256
    {
257 6
        return [];
258
    }
259
260
    /**
261
     * Fetch a cache by an extended Cache Driver.
262
     *
263
     * @param string $key     The key of a cache.
264
     * @param mixed  $default Default value to return if the key does not exist.
265
     *
266
     * @return array The data structure looks like:
267
     *
268
     * [
269
     *   [
270
     *     'value'     => (mixed) $value
271
     *     'ttl'       => (int)   $ttl,
272
     *     'timestamp' => (int)   $timestamp,
273
     *   ],
274
     *   ...
275
     * ]
276
     */
277
    abstract protected function doGet(string $key): array;
278
279
    /**
280
     * Set a cache by an extended Cache Driver.
281
     *
282
     * @param string $key       The key of a cache.
283
     * @param mixed  $value     The value of a cache. (serialized)
284
     * @param int    $ttl       The time to live for a cache.
285
     * @param int    $timestamp The time to store a cache.
286
     *
287
     * @return bool
288
     */
289
    abstract protected function doSet(string $key, $value, int $ttl, int $timestamp): bool;
290
291
    /**
292
     * Delete a cache by an extended Cache Driver.
293
     *
294
     * @param string $key The key of a cache.
295
     *
296
     * @return bool
297
     */
298
    abstract protected function doDelete(string $key): bool;
299
300
    /**
301
     * Delete all caches by an extended Cache Driver.
302
     *
303
     * @return bool
304
     */
305
    abstract protected function doClear(): bool;
306
307
    /**
308
     * Check if a cahce exists or not.
309
     *
310
     * @param string $key The key of a cache.
311
     *
312
     * @return bool
313
     */
314
    abstract protected function doHas(string $key): bool;
315
}
316