ArrayCacheStore   A
last analyzed

Complexity

Total Complexity 41

Size/Duplication

Total Lines 377
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 97
c 1
b 0
f 0
dl 0
loc 377
rs 9.1199
wmc 41

22 Methods

Rating   Name   Duplication   Size   Complexity  
A flushCache() 0 6 1
A __construct() 0 3 1
A clearCache() 0 6 1
A decrement() 0 3 1
A appendCache() 0 13 2
A buildArrayKey() 0 3 2
A isExpired() 0 5 2
A getCache() 0 18 3
A getMany() 0 7 2
A forever() 0 4 1
A handleCacheNotFound() 0 4 1
A handleCacheExpired() 0 12 2
A getAll() 0 9 4
A putCache() 0 12 1
A setMessage() 0 4 1
A isSuccess() 0 3 1
A serialize() 0 3 2
A getMessage() 0 3 1
A renewCache() 0 9 3
A has() 0 12 3
A increment() 0 11 3
A putMany() 0 11 3

How to fix   Complexity   

Complex Class

Complex classes like ArrayCacheStore often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ArrayCacheStore, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Silviooosilva\CacheerPhp\CacheStore;
4
5
use Silviooosilva\CacheerPhp\Utils\CacheLogger;
6
use Silviooosilva\CacheerPhp\Interface\CacheerInterface;
7
8
/**
9
 * Class ArrayCacheStore
10
 * @author Sílvio Silva <https://github.com/silviooosilva>
11
 * @package Silviooosilva\CacheerPhp
12
 */
13
class ArrayCacheStore implements CacheerInterface
14
{
15
16
  /**
17
  * @param array $arrayStore
18
  */
19
  private array $arrayStore = [];
20
21
  /**
22
   * @var boolean
23
   */
24
  private bool $success = false;
25
26
  /**
27
   * @var string
28
   */
29
  private string $message = '';
30
31
  /**
32
   * @var ?CacheLogger
33
   */
34
  private ?CacheLogger $logger = null;
35
36
  /**
37
   * ArrayCacheStore constructor.
38
   * 
39
   * @param string $logPath
40
   */
41
  public function __construct(string $logPath)
42
  {
43
    $this->logger = new CacheLogger($logPath);
44
  }
45
46
  /**
47
   * Appends data to an existing cache item.
48
   * 
49
   * @param string $cacheKey
50
   * @param mixed  $cacheData
51
   * @param string $namespace
52
   * @return bool
53
   */
54
  public function appendCache(string $cacheKey, mixed $cacheData, string $namespace = ''): bool
55
  {
56
      $arrayStoreKey = $this->buildArrayKey($cacheKey, $namespace);
57
58
      if (!$this->has($cacheKey, $namespace)) {
59
          $this->setMessage("cacheData can't be appended, because doesn't exist or expired", false);
60
          $this->logger->debug("{$this->getMessage()} from array driver.");
0 ignored issues
show
Bug introduced by
The method debug() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

60
          $this->logger->/** @scrutinizer ignore-call */ 
61
                         debug("{$this->getMessage()} from array driver.");

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
61
          return false;
62
      }
63
64
      $this->arrayStore[$arrayStoreKey]['cacheData'] = serialize($cacheData);
65
      $this->setMessage("Cache appended successfully", true);
66
      return true;
67
  }
68
69
  /**
70
   * Builds a unique key for the array store.
71
   * 
72
   * @param string $cacheKey
73
   * @param string $namespace
74
   * @return string
75
   */
76
  private function buildArrayKey(string $cacheKey, string $namespace = ''): string
77
  {
78
    return !empty($namespace) ? ($namespace . ':' . $cacheKey) : $cacheKey;
79
  }
80
81
  /**
82
   * Clears a specific cache item.
83
   * 
84
   * @param string $cacheKey
85
   * @param string $namespace
86
   * @return void
87
   */
88
  public function clearCache(string $cacheKey, string $namespace = ''): void
89
  {
90
    $arrayStoreKey = $this->buildArrayKey($cacheKey, $namespace);
91
    unset($this->arrayStore[$arrayStoreKey]);
92
    $this->setMessage("Cache cleared successfully", true);
93
    $this->logger->debug("{$this->getMessage()} from array driver.");
94
  }
95
96
  /**
97
   * Decrements a cache item by a specified amount.
98
   * 
99
   * @param string $cacheKey
100
   * @param int $amount
101
   * @param string $namespace
102
   * @return bool
103
   */
104
  public function decrement(string $cacheKey, int $amount = 1, string $namespace = ''): bool
105
  {
106
    return $this->increment($cacheKey, ($amount * -1), $namespace);
107
  }
108
109
  /**
110
   * Flushes all cache items.
111
   * 
112
   * @return void
113
   */
114
  public function flushCache(): void
115
  {
116
    unset($this->arrayStore);
117
    $this->arrayStore = [];
118
    $this->setMessage("Cache flushed successfully", true);
119
    $this->logger->debug("{$this->getMessage()} from array driver.");
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
    $this->setMessage($this->getMessage(), $this->isSuccess());
133
  }
134
135
  /**
136
   * Retrieves a single cache item.
137
   * 
138
   * @param string $cacheKey
139
   * @param string $namespace
140
   * @param int|string $ttl
141
   * @return mixed
142
   */
143
  public function getCache(string $cacheKey, string $namespace = '', string|int $ttl = 3600): mixed
144
  {
145
    $arrayStoreKey = $this->buildArrayKey($cacheKey, $namespace);
146
147
    if (!$this->has($cacheKey, $namespace)) {
148
      $this->handleCacheNotFound();
149
      return false;
150
    }
151
152
    $cacheData = $this->arrayStore[$arrayStoreKey];
153
    if ($this->isExpired($cacheData)) {
154
      $this->handleCacheExpired($arrayStoreKey);
155
      return false;
156
    }
157
158
    $this->setMessage("Cache retrieved successfully", true);
159
    $this->logger->debug("{$this->getMessage()} from array driver.");
160
    return $this->serialize($cacheData['cacheData'], false);
161
  }
162
163
  /**
164
   * Verify if the cache is expired.
165
   * 
166
   * @param array $cacheData
167
   * @return bool
168
   */
169
  private function isExpired(array $cacheData): bool
170
  {
171
    $expirationTime = $cacheData['expirationTime'] ?? 0;
172
    $now = time();
173
    return $expirationTime !== 0 && $now >= $expirationTime;
174
  }
175
176
  /**
177
   * Handles the case when cache data is not found.
178
   * 
179
   * @return void
180
   */
181
  private function handleCacheNotFound(): void
182
  {
183
    $this->setMessage("cacheData not found, does not exists or expired", false);
184
    $this->logger->debug("{$this->getMessage()} from array driver.");
185
  }
186
187
  /**
188
   * Handles the case when cache data has expired.
189
   * 
190
   * @param string $arrayStoreKey
191
   * @return void
192
   */
193
  private function handleCacheExpired(string $arrayStoreKey): void
194
  {
195
    $parts = explode(':', $arrayStoreKey, 2);
196
    if (count($parts) === 2) {
197
      list($np, $key) = $parts;
198
    } else {
199
      $np = '';
200
      $key = $arrayStoreKey;
201
    }
202
    $this->clearCache($key, $np);
203
    $this->setMessage("cacheKey: {$key} has expired.", false);
204
    $this->logger->debug("{$this->getMessage()} from array driver.");
205
  }
206
207
  /**
208
   * Gets all items in a specific namespace.
209
   * 
210
   * @param string $namespace
211
   * @return array
212
   */
213
  public function getAll(string $namespace = ''): array
214
  {
215
    $results = [];
216
    foreach ($this->arrayStore as $key => $data) {
217
      if (str_starts_with($key, $namespace . ':') || empty($namespace)) {
218
        $results[$key] = $this->serialize($data['cacheData'], false);
219
      }
220
    }
221
    return $results;
222
  }
223
224
  /**
225
   * Retrieves multiple cache items by their keys.
226
   * 
227
   * @param array $cacheKeys
228
   * @param string $namespace
229
   * @param string|int $ttl
230
   * @return array
231
   */
232
  public function getMany(array $cacheKeys, string $namespace = '', string|int $ttl = 3600): array
233
  {
234
    $results = [];
235
    foreach ($cacheKeys as $cacheKey) {
236
      $results[$cacheKey] = $this->getCache($cacheKey, $namespace, $ttl);
237
    }
238
    return $results;
239
  }
240
241
  /**
242
   * Checks if a cache item exists.
243
   * 
244
   * @param string $cacheKey
245
   * @param string $namespace
246
   * @return bool
247
   */
248
  public function has(string $cacheKey, string $namespace = ''): bool
249
  {
250
    $arrayStoreKey = $this->buildArrayKey($cacheKey, $namespace);
251
    $exists = isset($this->arrayStore[$arrayStoreKey]) && time() < $this->arrayStore[$arrayStoreKey]['expirationTime'];
252
253
    $this->setMessage(
254
      $exists ? "Cache key: {$cacheKey} exists and it's available!" : "Cache key: {$cacheKey} does not exist or it's expired!",
255
      $exists
256
    );
257
    $this->logger->debug("{$this->getMessage()} from array driver.");
258
259
    return $exists;
260
  }
261
262
  /**
263
   * Increments a cache item by a specified amount.
264
   * 
265
   * @param string $cacheKey
266
   * @param int $amount
267
   * @param string $namespace
268
   * @return bool
269
   */
270
  public function increment(string $cacheKey, int $amount = 1, string $namespace = ''): bool
271
  {
272
    $cacheData = $this->getCache($cacheKey, $namespace);
273
274
    if(!empty($cacheData) && is_numeric($cacheData)) {
275
      $this->putCache($cacheKey, (int)($cacheData + $amount), $namespace);
276
      $this->setMessage($this->getMessage(), $this->isSuccess());
277
      return true;
278
    }
279
280
    return false;
281
  }
282
283
  /**
284
   * Checks if the operation was successful.
285
   * 
286
   * @return boolean
287
   */
288
  public function isSuccess(): bool
289
  {
290
    return $this->success;
291
  }
292
293
  /**
294
   * Gets the last message.
295
   * 
296
   * @return string
297
   */
298
  public function getMessage(): string
299
  {
300
    return $this->message;
301
  }
302
303
  /**
304
   * Stores an item in the cache with a specific TTL.
305
   * 
306
   * @param string $cacheKey
307
   * @param mixed $cacheData
308
   * @param string $namespace
309
   * @param int|string $ttl
310
   * @return bool
311
   */
312
  public function putCache(string $cacheKey, mixed $cacheData, string $namespace = '', int|string $ttl = 3600): bool
313
  {
314
    $arrayStoreKey = $this->buildArrayKey($cacheKey, $namespace);
315
316
    $this->arrayStore[$arrayStoreKey] = [
317
      'cacheData' => serialize($cacheData),
318
      'expirationTime' => time() + $ttl
319
    ];
320
321
    $this->setMessage("Cache stored successfully", true);
322
    $this->logger->debug("{$this->getMessage()} from Array driver.");
323
    return true;
324
  }
325
326
  /**
327
   * Stores multiple items in the cache in batches.
328
   * 
329
   * @param array $items
330
   * @param string $namespace
331
   * @param int $batchSize
332
   * @return void
333
   */
334
  public function putMany(array $items, string $namespace = '', int $batchSize = 100): void
335
  {
336
    $chunks = array_chunk($items, $batchSize, true);
337
338
    foreach ($chunks as $chunk) {
339
      foreach ($chunk as $key => $data) {
340
          $this->putCache($data['cacheKey'], $data['cacheData'], $namespace);
341
        }
342
      }
343
    $this->setMessage("{$this->getMessage()}", $this->isSuccess());
344
    $this->logger->debug("{$this->getMessage()} from Array driver.");
345
  }
346
347
  /**
348
   * Renews the expiration time of a cache item.
349
   * 
350
   * @param string $cacheKey
351
   * @param string|int $ttl
352
   * @param string $namespace
353
   * @return void
354
   */
355
  public function renewCache(string $cacheKey, int|string $ttl = 3600, string $namespace = ''): void
356
  {
357
    $arrayStoreKey = $this->buildArrayKey($cacheKey, $namespace);
358
359
    if (isset($this->arrayStore[$arrayStoreKey])) {
360
        $ttlSeconds = is_numeric($ttl) ? (int) $ttl : strtotime($ttl) - time();
361
        $this->arrayStore[$arrayStoreKey]['expirationTime'] = time() + $ttlSeconds;
362
        $this->setMessage("cacheKey: {$cacheKey} renewed successfully", true);
363
        $this->logger->debug("{$this->getMessage()} from array driver.");
364
      }
365
  }
366
367
  /**
368
   * Sets a message and its success status.
369
   * 
370
   * @param string  $message
371
   * @param boolean $success
372
   * @return void
373
   */
374
  private function setMessage(string $message, bool $success): void
375
  {
376
    $this->message = $message;
377
    $this->success = $success;
378
  }
379
380
  /**
381
   * Serializes or unserializes data based on the flag.
382
   * 
383
   * @param mixed $data
384
   * @param bool $serialize
385
   * @return mixed
386
   */
387
  private function serialize(mixed $data, bool $serialize = true): mixed
388
  {
389
    return $serialize ? serialize($data) : unserialize($data);
390
  }
391
}
392