• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

aplus-framework / cache / 16306742406

15 Jul 2025 11:44PM UTC coverage: 95.194%. Remained the same
16306742406

push

github

natanfelles
Add since tag

515 of 541 relevant lines covered (95.19%)

55.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/src/Cache.php
1
<?php declare(strict_types=1);
2
/*
3
 * This file is part of Aplus Framework Cache Library.
4
 *
5
 * (c) Natan Felles <natanfelles@gmail.com>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace Framework\Cache;
11

12
use Framework\Cache\Debug\CacheCollector;
13
use Framework\Log\Logger;
14
use Framework\Log\LogLevel;
15
use InvalidArgumentException;
16
use JetBrains\PhpStorm\Pure;
17
use SensitiveParameter;
18

19
/**
20
 * Class Cache.
21
 *
22
 * @todo Add way to use internal serializer in handlers
23
 *
24
 * @package cache
25
 */
26
abstract class Cache
27
{
28
    /**
29
     * Driver specific configurations.
30
     *
31
     * @var array<string,mixed>
32
     */
33
    protected array $configs = [];
34
    /**
35
     * Keys prefix.
36
     *
37
     * @var string|null
38
     */
39
    protected ?string $prefix = null;
40
    /**
41
     * Data serializer.
42
     *
43
     * @var Serializer
44
     */
45
    protected Serializer $serializer;
46
    /**
47
     * The Logger instance if is set.
48
     *
49
     * @var Logger|null
50
     */
51
    protected ?Logger $logger;
52
    /**
53
     * The default Time To Live value.
54
     *
55
     * Used when set methods has the $ttl param as null.
56
     *
57
     * @var int
58
     */
59
    protected int $defaultTtl = 60;
60
    protected CacheCollector $debugCollector;
61
    protected bool $autoClose = true;
62

63
    /**
64
     * Cache constructor.
65
     *
66
     * @param mixed $configs Driver specific configurations. Set
67
     * null to not initialize and set a custom object.
68
     * @param string|null $prefix Keys prefix
69
     * @param Serializer|string $serializer Data serializer
70
     * @param Logger|null $logger Logger instance
71
     */
72
    public function __construct(
73
        #[SensitiveParameter]
74
        mixed $configs = [],
75
        ?string $prefix = null,
76
        Serializer | string $serializer = Serializer::PHP,
77
        ?Logger $logger = null
78
    ) {
79
        $this->prefix = $prefix;
423✔
80
        $this->setSerializer($serializer);
423✔
81
        $this->logger = $logger;
423✔
82
        if (\is_array($configs)) {
423✔
83
            if ($configs) {
423✔
84
                $this->setConfigs($configs);
336✔
85
            }
86
            $this->initialize();
423✔
87
        }
88
    }
89

90
    public function __destruct()
91
    {
92
        if ($this->isAutoClose()) {
422✔
93
            $this->close();
422✔
94
        }
95
    }
96

97
    /**
98
     * @since 4.1
99
     *
100
     * @return bool
101
     */
102
    public function isAutoClose() : bool
103
    {
104
        return $this->autoClose;
422✔
105
    }
106

107
    /**
108
     * @since 4.1
109
     *
110
     * @param bool $autoClose True to enable auto close, false to disable
111
     *
112
     * @return static
113
     */
114
    public function setAutoClose(bool $autoClose) : static
115
    {
116
        $this->autoClose = $autoClose;
12✔
117
        return $this;
12✔
118
    }
119

120
    /**
121
     * @since 4.1
122
     *
123
     * @param array<string,mixed> $configs
124
     *
125
     * @return static
126
     */
127
    protected function setConfigs(array $configs) : static
128
    {
129
        $this->configs = \array_replace_recursive($this->configs, $configs);
336✔
130
        return $this;
336✔
131
    }
132

133
    /**
134
     * @since 4.1
135
     *
136
     * @param Serializer|string $serializer
137
     *
138
     * @return static
139
     */
140
    protected function setSerializer(Serializer | string $serializer) : static
141
    {
142
        if (\is_string($serializer)) {
423✔
143
            $serializer = Serializer::from($serializer);
21✔
144
        }
145
        $this->serializer = $serializer;
423✔
146
        return $this;
423✔
147
    }
148

149
    public function getSerializer() : Serializer
150
    {
151
        return $this->serializer;
21✔
152
    }
153

154
    /**
155
     * Initialize Cache handlers and configurations.
156
     */
157
    protected function initialize() : void
158
    {
159
    }
2✔
160

161
    protected function log(
162
        string $message,
163
        LogLevel $level = LogLevel::ERROR
164
    ) : void {
165
        if (isset($this->logger)) {
6✔
166
            $this->logger->log($level, $message);
6✔
167
        }
168
    }
169

170
    /**
171
     * Get the default Time To Live value in seconds.
172
     *
173
     * @return int
174
     */
175
    #[Pure]
176
    public function getDefaultTtl() : int
177
    {
178
        return $this->defaultTtl;
100✔
179
    }
180

181
    /**
182
     * Set the default Time To Live value in seconds.
183
     *
184
     * @param int $seconds An integer greater than zero
185
     *
186
     * @return static
187
     */
188
    public function setDefaultTtl(int $seconds) : static
189
    {
190
        if ($seconds < 1) {
1✔
191
            throw new InvalidArgumentException(
1✔
192
                'Default TTL must be greater than 0. ' . $seconds . ' given'
1✔
193
            );
1✔
194
        }
195
        $this->defaultTtl = $seconds;
1✔
196
        return $this;
1✔
197
    }
198

199
    /**
200
     * Make the Time To Live value.
201
     *
202
     * @param int|null $seconds TTL value or null to use the default
203
     *
204
     * @return int The input $seconds or the $defaultTtl as integer
205
     */
206
    #[Pure]
207
    protected function makeTtl(?int $seconds) : int
208
    {
209
        return $seconds ?? $this->getDefaultTtl();
230✔
210
    }
211

212
    /**
213
     * Gets one item from the cache storage.
214
     *
215
     * @param string $key The item name
216
     *
217
     * @return mixed The item value or null if not found
218
     */
219
    abstract public function get(string $key) : mixed;
220

221
    /**
222
     * Gets multi items from the cache storage.
223
     *
224
     * @param array<int,string> $keys List of items names to get
225
     *
226
     * @return array<string,mixed> associative array with key names and respective values
227
     */
228
    public function getMulti(array $keys) : array
229
    {
230
        $values = [];
63✔
231
        foreach ($keys as $key) {
63✔
232
            $values[$key] = $this->get($key);
63✔
233
        }
234
        return $values;
63✔
235
    }
236

237
    /**
238
     * Sets one item to the cache storage.
239
     *
240
     * @param string $key The item name
241
     * @param mixed $value The item value
242
     * @param int|null $ttl The Time To Live for the item or null to use the default
243
     *
244
     * @return bool TRUE if the item was set, FALSE if fail to set
245
     */
246
    abstract public function set(string $key, mixed $value, ?int $ttl = null) : bool;
247

248
    /**
249
     * Sets multi items to the cache storage.
250
     *
251
     * @param array<string,mixed> $data Associative array with key names and respective values
252
     * @param int|null $ttl The Time To Live for all the items or null to use the default
253
     *
254
     * @return array<string,bool> associative array with key names and respective set status
255
     */
256
    public function setMulti(array $data, ?int $ttl = null) : array
257
    {
258
        foreach ($data as $key => &$value) {
63✔
259
            $value = $this->set($key, $value, $ttl);
63✔
260
        }
261
        return $data;
63✔
262
    }
263

264
    /**
265
     * Deletes one item from the cache storage.
266
     *
267
     * @param string $key the item name
268
     *
269
     * @return bool TRUE if the item was deleted, FALSE if fail to delete
270
     */
271
    abstract public function delete(string $key) : bool;
272

273
    /**
274
     * Deletes multi items from the cache storage.
275
     *
276
     * @param array<int,string> $keys List of items names to be deleted
277
     *
278
     * @return array<string,bool> associative array with key names and respective delete status
279
     */
280
    public function deleteMulti(array $keys) : array
281
    {
282
        $values = [];
21✔
283
        foreach ($keys as $key) {
21✔
284
            $values[$key] = $this->delete($key);
21✔
285
        }
286
        return $values;
21✔
287
    }
288

289
    /**
290
     * Flush the cache storage.
291
     *
292
     * @return bool TRUE if all items are deleted, otherwise FALSE
293
     */
294
    abstract public function flush() : bool;
295

296
    /**
297
     * Increments the value of one item.
298
     *
299
     * @param string $key The item name
300
     * @param int $offset The value to increment
301
     * @param int|null $ttl The Time To Live for the item or null to use the default
302
     *
303
     * @return int The current item value
304
     */
305
    public function increment(string $key, int $offset = 1, ?int $ttl = null) : int
306
    {
307
        $offset = (int) \abs($offset);
42✔
308
        $value = (int) $this->get($key);
42✔
309
        $value = $value ? $value + $offset : $offset;
42✔
310
        $this->set($key, $value, $ttl);
42✔
311
        return $value;
42✔
312
    }
313

314
    /**
315
     * Decrements the value of one item.
316
     *
317
     * @param string $key The item name
318
     * @param int $offset The value to decrement
319
     * @param int|null $ttl The Time To Live for the item or null to use the default
320
     *
321
     * @return int The current item value
322
     */
323
    public function decrement(string $key, int $offset = 1, ?int $ttl = null) : int
324
    {
325
        $offset = (int) \abs($offset);
42✔
326
        $value = (int) $this->get($key);
42✔
327
        $value = $value ? $value - $offset : -$offset;
42✔
328
        $this->set($key, $value, $ttl);
42✔
329
        return $value;
42✔
330
    }
331

332
    /**
333
     * Close the cache storage.
334
     *
335
     * @since 4.1
336
     *
337
     * @return bool TRUE on success, otherwise FALSE
338
     */
339
    public function close() : bool
340
    {
341
        return true;
211✔
342
    }
343

344
    #[Pure]
345
    protected function renderKey(string $key) : string
346
    {
347
        return $this->prefix . $key;
176✔
348
    }
349

350
    /**
351
     * @param mixed $value
352
     *
353
     * @throws \JsonException
354
     *
355
     * @return string
356
     */
357
    protected function serialize(mixed $value) : string
358
    {
359
        if ($this->serializer === Serializer::IGBINARY) {
170✔
360
            return \igbinary_serialize($value);
34✔
361
        }
362
        if ($this->serializer === Serializer::JSON
136✔
363
            || $this->serializer === Serializer::JSON_ARRAY
136✔
364
        ) {
365
            return \json_encode($value, \JSON_THROW_ON_ERROR);
68✔
366
        }
367
        if ($this->serializer === Serializer::MSGPACK) {
68✔
368
            return \msgpack_serialize($value);
34✔
369
        }
370
        return \serialize($value);
34✔
371
    }
372

373
    /**
374
     * @param string $value
375
     *
376
     * @return mixed
377
     */
378
    protected function unserialize(string $value) : mixed
379
    {
380
        if ($this->serializer === Serializer::IGBINARY) {
150✔
381
            return @\igbinary_unserialize($value);
30✔
382
        }
383
        if ($this->serializer === Serializer::JSON) {
120✔
384
            return \json_decode($value);
30✔
385
        }
386
        if ($this->serializer === Serializer::JSON_ARRAY) {
90✔
387
            return \json_decode($value, true);
30✔
388
        }
389
        if ($this->serializer === Serializer::MSGPACK) {
60✔
390
            return \msgpack_unserialize($value);
30✔
391
        }
392
        return \unserialize($value, ['allowed_classes' => true]);
30✔
393
    }
394

395
    public function setDebugCollector(CacheCollector $debugCollector) : static
396
    {
397
        $this->debugCollector = $debugCollector;
84✔
398
        $this->debugCollector->setInfo([
84✔
399
            'class' => static::class,
84✔
400
            'configs' => $this->configs,
84✔
401
            'prefix' => $this->prefix,
84✔
402
            'serializer' => $this->serializer->value,
84✔
403
        ]);
84✔
404
        return $this;
84✔
405
    }
406

407
    protected function addDebugGet(string $key, float $start, mixed $value) : mixed
408
    {
409
        $end = \microtime(true);
42✔
410
        $this->debugCollector->addData([
42✔
411
            'start' => $start,
42✔
412
            'end' => $end,
42✔
413
            'command' => 'GET',
42✔
414
            'status' => $value === null ? 'FAIL' : 'OK',
42✔
415
            'key' => $key,
42✔
416
            'value' => \get_debug_type($value),
42✔
417
        ]);
42✔
418
        return $value;
42✔
419
    }
420

421
    protected function addDebugSet(string $key, ?int $ttl, float $start, mixed $value, bool $status) : bool
422
    {
423
        $end = \microtime(true);
21✔
424
        $this->debugCollector->addData([
21✔
425
            'start' => $start,
21✔
426
            'end' => $end,
21✔
427
            'command' => 'SET',
21✔
428
            'status' => $status ? 'OK' : 'FAIL',
21✔
429
            'key' => $key,
21✔
430
            'value' => \get_debug_type($value),
21✔
431
            'ttl' => $this->makeTtl($ttl),
21✔
432
        ]);
21✔
433
        return $status;
21✔
434
    }
435

436
    protected function addDebugDelete(string $key, float $start, bool $status) : bool
437
    {
438
        $end = \microtime(true);
21✔
439
        $this->debugCollector->addData([
21✔
440
            'start' => $start,
21✔
441
            'end' => $end,
21✔
442
            'command' => 'DELETE',
21✔
443
            'status' => $status ? 'OK' : 'FAIL',
21✔
444
            'key' => $key,
21✔
445
        ]);
21✔
446
        return $status;
21✔
447
    }
448

449
    protected function addDebugFlush(float $start, bool $status) : bool
450
    {
451
        $end = \microtime(true);
84✔
452
        $this->debugCollector->addData([
84✔
453
            'start' => $start,
84✔
454
            'end' => $end,
84✔
455
            'command' => 'FLUSH',
84✔
456
            'status' => $status ? 'OK' : 'FAIL',
84✔
457
        ]);
84✔
458
        return $status;
84✔
459
    }
460
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc