Passed
Branch main (6fd149)
by Sílvio
02:57
created

ArrayCacheStore::isExpired()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Silviooosilva\CacheerPhp\CacheStore;
4
5
use Silviooosilva\CacheerPhp\Interface\CacheerInterface;
6
use Silviooosilva\CacheerPhp\Utils\CacheLogger;
7
use Silviooosilva\CacheerPhp\CacheStore\Support\ArrayCacheBatchWriter;
8
use Silviooosilva\CacheerPhp\CacheStore\Support\ArrayCacheCodec;
9
use Silviooosilva\CacheerPhp\CacheStore\Support\ArrayCacheKeyspace;
10
use Silviooosilva\CacheerPhp\CacheStore\Support\ArrayCacheTagIndex;
11
use Silviooosilva\CacheerPhp\CacheStore\Support\OperationStatus;
12
13
/**
14
 * Class ArrayCacheStore
15
 * @author Sílvio Silva <https://github.com/silviooosilva>
16
 * @package Silviooosilva\CacheerPhp
17
 */
18
class ArrayCacheStore implements CacheerInterface
19
{
20
21
    /**
22
     * @param array $arrayStore
23
     */
24
    private array $arrayStore = [];
25
26
    /**
27
     * @var OperationStatus
28
     */
29
    private OperationStatus $status;
30
31
    /**
32
     * @var ArrayCacheKeyspace
33
     */
34
    private ArrayCacheKeyspace $keyspace;
35
36
    /**
37
     * @var ArrayCacheCodec
38
     */
39
    private ArrayCacheCodec $codec;
40
41
    /**
42
     * @var ArrayCacheTagIndex
43
     */
44
    private ArrayCacheTagIndex $tagIndex;
45
46
  /**
47
   * ArrayCacheStore constructor.
48
   * 
49
   * @param string $logPath
50
   */
51
    public function __construct(string $logPath)
52
    {
53
        $logger = new CacheLogger($logPath);
54
        $this->status = new OperationStatus($logger, 'array');
55
        $this->keyspace = new ArrayCacheKeyspace();
56
        $this->codec = new ArrayCacheCodec();
57
        $this->tagIndex = new ArrayCacheTagIndex($this->keyspace, $this->status);
58
    }
59
60
  /**
61
   * Appends data to an existing cache item.
62
   * 
63
   * @param string $cacheKey
64
   * @param mixed  $cacheData
65
   * @param string $namespace
66
   * @return bool
67
   */
68
    public function appendCache(string $cacheKey, mixed $cacheData, string $namespace = ''): bool
69
    {
70
        $arrayStoreKey = $this->keyspace->build($cacheKey, $namespace);
71
        $entry = $this->arrayStore[$arrayStoreKey] ?? null;
72
73
        if ($entry === null || $this->keyspace->isExpired($entry)) {
74
            $this->status->record("cacheData can't be appended, because doesn't exist or expired", false);
75
            return false;
76
        }
77
78
        $this->arrayStore[$arrayStoreKey]['cacheData'] = $this->codec->encode($cacheData);
79
        $this->status->record("Cache appended successfully", true);
80
        return true;
81
    }
82
83
  /**
84
   * Clears a specific cache item.
85
   * 
86
   * @param string $cacheKey
87
   * @param string $namespace
88
   * @return void
89
   */
90
    public function clearCache(string $cacheKey, string $namespace = ''): void
91
    {
92
        $arrayStoreKey = $this->keyspace->build($cacheKey, $namespace);
93
        unset($this->arrayStore[$arrayStoreKey]);
94
        $this->status->record("Cache cleared successfully", true);
95
    }
96
97
  /**
98
   * Decrements a cache item by a specified amount.
99
   * 
100
   * @param string $cacheKey
101
   * @param int $amount
102
   * @param string $namespace
103
   * @return bool
104
   */
105
    public function decrement(string $cacheKey, int $amount = 1, string $namespace = ''): bool
106
    {
107
        return $this->increment($cacheKey, ($amount * -1), $namespace);
108
    }
109
110
  /**
111
   * Flushes all cache items.
112
   * 
113
   * @return void
114
   */
115
    public function flushCache(): void
116
    {
117
        $this->arrayStore = [];
118
        $this->tagIndex->reset();
119
        $this->status->record("Cache flushed successfully", true);
120
    }
121
122
    /**
123
     * Stores a cache item permanently.
124
     *
125
     * @param string $cacheKey
126
     * @param mixed $cacheData
127
     * @return void
128
     */
129
    public function forever(string $cacheKey, mixed $cacheData): void
130
    {
131
        $this->putCache($cacheKey, $cacheData, ttl: 31536000 * 1000);
132
    }
133
134
  /**
135
   * Retrieves a single cache item.
136
   * 
137
   * @param string $cacheKey
138
   * @param string $namespace
139
   * @param int|string $ttl
140
   * @return mixed
141
   */
142
    public function getCache(string $cacheKey, string $namespace = '', string|int $ttl = 3600): mixed
143
    {
144
        $arrayStoreKey = $this->keyspace->build($cacheKey, $namespace);
145
        $cacheData = $this->arrayStore[$arrayStoreKey] ?? null;
146
147
        if ($cacheData === null) {
148
            $this->status->record("cacheData not found, does not exists or expired", false);
149
            return false;
150
        }
151
152
        if ($this->keyspace->isExpired($cacheData)) {
153
            $this->clearCache($cacheKey, $namespace);
154
            $this->status->record("cacheKey: {$cacheKey} has expired.", false);
155
            return false;
156
        }
157
158
        $this->status->record("Cache retrieved successfully", true);
159
        return $this->codec->decode($cacheData['cacheData']);
160
    }
161
162
  /**
163
   * Gets all items in a specific namespace.
164
   * 
165
   * @param string $namespace
166
   * @return array
167
   */
168
    public function getAll(string $namespace = ''): array
169
    {
170
        $results = [];
171
        foreach ($this->arrayStore as $key => $data) {
172
            if (str_starts_with($key, $namespace . ':') || $namespace === '') {
173
                $results[$key] = $this->codec->decode($data['cacheData']);
174
            }
175
        }
176
        return $results;
177
    }
178
179
  /**
180
   * Retrieves multiple cache items by their keys.
181
   * 
182
   * @param array $cacheKeys
183
   * @param string $namespace
184
   * @param string|int $ttl
185
   * @return array
186
   */
187
    public function getMany(array $cacheKeys, string $namespace = '', string|int $ttl = 3600): array
188
    {
189
        $results = [];
190
        foreach ($cacheKeys as $cacheKey) {
191
            $results[$cacheKey] = $this->getCache($cacheKey, $namespace, $ttl);
192
        }
193
        return $results;
194
    }
195
196
  /**
197
   * Checks if a cache item exists.
198
   * 
199
   * @param string $cacheKey
200
   * @param string $namespace
201
   * @return bool
202
   */
203
    public function has(string $cacheKey, string $namespace = ''): bool
204
    {
205
        $arrayStoreKey = $this->keyspace->build($cacheKey, $namespace);
206
        $entry = $this->arrayStore[$arrayStoreKey] ?? null;
207
        $exists = $entry !== null && !$this->keyspace->isExpired($entry);
208
209
        $this->status->record(
210
            $exists ? "Cache key: {$cacheKey} exists and it's available!" : "Cache key: {$cacheKey} does not exist or it's expired!",
211
            $exists
212
        );
213
214
        return $exists;
215
    }
216
217
  /**
218
   * Increments a cache item by a specified amount.
219
   * 
220
   * @param string $cacheKey
221
   * @param int $amount
222
   * @param string $namespace
223
   * @return bool
224
   */
225
    public function increment(string $cacheKey, int $amount = 1, string $namespace = ''): bool
226
    {
227
        $cacheData = $this->getCache($cacheKey, $namespace);
228
229
        if (!empty($cacheData) && is_numeric($cacheData)) {
230
            $this->putCache($cacheKey, (int) ($cacheData + $amount), $namespace);
231
            return true;
232
        }
233
234
        return false;
235
    }
236
237
  /**
238
   * Checks if the operation was successful.
239
   * 
240
   * @return boolean
241
   */
242
    public function isSuccess(): bool
243
    {
244
        return $this->status->isSuccess();
245
    }
246
247
  /**
248
   * Gets the last message.
249
   * 
250
   * @return string
251
   */
252
    public function getMessage(): string
253
    {
254
        return $this->status->getMessage();
255
    }
256
257
  /**
258
   * Stores an item in the cache with a specific TTL.
259
   * 
260
   * @param string $cacheKey
261
   * @param mixed $cacheData
262
   * @param string $namespace
263
   * @param int|string $ttl
264
   * @return bool
265
   */
266
    public function putCache(string $cacheKey, mixed $cacheData, string $namespace = '', int|string $ttl = 3600): bool
267
    {
268
        $arrayStoreKey = $this->keyspace->build($cacheKey, $namespace);
269
270
        $this->arrayStore[$arrayStoreKey] = [
271
            'cacheData' => $this->codec->encode($cacheData),
272
            'expirationTime' => time() + $ttl
273
        ];
274
275
        $this->status->record("Cache stored successfully", true);
276
        return true;
277
    }
278
279
  /**
280
   * Stores multiple items in the cache in batches.
281
   * 
282
   * @param array $items
283
   * @param string $namespace
284
   * @param int $batchSize
285
   * @return void
286
   */
287
    public function putMany(array $items, string $namespace = '', int $batchSize = 100): void
288
    {
289
        $writer = new ArrayCacheBatchWriter($batchSize);
290
        $writer->write($items, $namespace, function (string $cacheKey, mixed $cacheData, string $namespace): void {
291
            $this->putCache($cacheKey, $cacheData, $namespace);
292
        });
293
294
        $this->status->record($this->getMessage(), $this->isSuccess());
295
    }
296
297
  /**
298
   * Renews the expiration time of a cache item.
299
   * 
300
   * @param string $cacheKey
301
   * @param string|int $ttl
302
   * @param string $namespace
303
   * @return void
304
   */
305
    public function renewCache(string $cacheKey, int|string $ttl = 3600, string $namespace = ''): void
306
    {
307
        $arrayStoreKey = $this->keyspace->build($cacheKey, $namespace);
308
309
        if (isset($this->arrayStore[$arrayStoreKey])) {
310
            $ttlSeconds = is_numeric($ttl) ? (int) $ttl : strtotime($ttl) - time();
311
            $this->arrayStore[$arrayStoreKey]['expirationTime'] = time() + $ttlSeconds;
312
            $this->status->record("cacheKey: {$cacheKey} renewed successfully", true);
313
        }
314
    }
315
316
  /**
317
   * Associates one or more keys to a tag.
318
   *
319
   * @param string $tag
320
   * @param string ...$keys
321
   * @return bool
322
   */
323
    public function tag(string $tag, string ...$keys): bool
324
    {
325
        return $this->tagIndex->tag($tag, ...$keys);
326
    }
327
328
  /**
329
   * Flushes all keys associated with a tag.
330
   *
331
   * @param string $tag
332
   * @return void
333
   */
334
    public function flushTag(string $tag): void
335
    {
336
        $this->tagIndex->flush($tag, function (string $cacheKey, string $namespace): void {
337
            $this->clearCache($cacheKey, $namespace);
338
        });
339
    }
340
}
341