Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
98.92% |
92 / 93 |
|
94.12% |
16 / 17 |
CRAP | |
0.00% |
0 / 1 |
Cache | |
98.92% |
92 / 93 |
|
94.12% |
16 / 17 |
36 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
2 | |||
initialize | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
log | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
makeTtl | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
get | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
getMulti | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
set | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
setMulti | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
delete | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
deleteMulti | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
flush | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
increment | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
decrement | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
renderKey | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
serialize | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
5 | |||
unserialize | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
5 | |||
setDebugCollector | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
addDebugGet | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
2 | |||
addDebugSet | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
2 | |||
addDebugDelete | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
2 | |||
addDebugFlush | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
2 |
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\Debug\Debugger; |
14 | use Framework\Log\Logger; |
15 | use Framework\Log\LogLevel; |
16 | use JetBrains\PhpStorm\Pure; |
17 | |
18 | /** |
19 | * Class Cache. |
20 | * |
21 | * @package cache |
22 | */ |
23 | abstract class Cache |
24 | { |
25 | /** |
26 | * Driver specific configurations. |
27 | * |
28 | * @var array<string,mixed> |
29 | */ |
30 | protected array $configs = []; |
31 | /** |
32 | * Keys prefix. |
33 | * |
34 | * @var string|null |
35 | */ |
36 | protected ?string $prefix; |
37 | /** |
38 | * Data serializer. |
39 | * |
40 | * @var Serializer |
41 | */ |
42 | protected Serializer $serializer; |
43 | /** |
44 | * The Logger instance if is set. |
45 | * |
46 | * @var Logger|null |
47 | */ |
48 | protected ?Logger $logger; |
49 | /** |
50 | * The default Time To Live value. |
51 | * |
52 | * Used when set methods has the $ttl param as null. |
53 | * |
54 | * @var int |
55 | */ |
56 | public int $defaultTtl = 60; |
57 | protected CacheCollector $debugCollector; |
58 | |
59 | /** |
60 | * Cache constructor. |
61 | * |
62 | * @param array<string,mixed> $configs Driver specific configurations |
63 | * @param string|null $prefix Keys prefix |
64 | * @param Serializer $serializer Data serializer |
65 | */ |
66 | public function __construct( |
67 | array $configs = [], |
68 | string $prefix = null, |
69 | Serializer $serializer = Serializer::PHP, |
70 | Logger $logger = null |
71 | ) { |
72 | if ($configs) { |
73 | $this->configs = \array_replace_recursive($this->configs, $configs); |
74 | } |
75 | $this->prefix = $prefix; |
76 | $this->serializer = $serializer; |
77 | $this->logger = $logger; |
78 | $this->initialize(); |
79 | } |
80 | |
81 | /** |
82 | * Initialize Cache handlers and configurations. |
83 | */ |
84 | protected function initialize() : void |
85 | { |
86 | } |
87 | |
88 | protected function log( |
89 | string $message, |
90 | LogLevel $level = LogLevel::ERROR |
91 | ) : void { |
92 | if (isset($this->logger)) { |
93 | $this->logger->log($level, $message); |
94 | } |
95 | } |
96 | |
97 | /** |
98 | * Make the Time To Live value. |
99 | * |
100 | * @param int|null $seconds TTL value or null to use the default |
101 | * |
102 | * @return int The input $seconds or the $defaultTtl as integer |
103 | */ |
104 | #[Pure] |
105 | protected function makeTtl(?int $seconds) : int |
106 | { |
107 | return $seconds ?? $this->defaultTtl; |
108 | } |
109 | |
110 | /** |
111 | * Gets one item from the cache storage. |
112 | * |
113 | * @param string $key The item name |
114 | * |
115 | * @return mixed The item value or null if not found |
116 | */ |
117 | abstract public function get(string $key) : mixed; |
118 | |
119 | /** |
120 | * Gets multi items from the cache storage. |
121 | * |
122 | * @param array<int,string> $keys List of items names to get |
123 | * |
124 | * @return array<string,mixed> associative array with key names and respective values |
125 | */ |
126 | public function getMulti(array $keys) : array |
127 | { |
128 | $values = []; |
129 | foreach ($keys as $key) { |
130 | $values[$key] = $this->get($key); |
131 | } |
132 | return $values; |
133 | } |
134 | |
135 | /** |
136 | * Sets one item to the cache storage. |
137 | * |
138 | * @param string $key The item name |
139 | * @param mixed $value The item value |
140 | * @param int|null $ttl The Time To Live for the item or null to use the default |
141 | * |
142 | * @return bool TRUE if the item was set, FALSE if fail to set |
143 | */ |
144 | abstract public function set(string $key, mixed $value, int $ttl = null) : bool; |
145 | |
146 | /** |
147 | * Sets multi items to the cache storage. |
148 | * |
149 | * @param array<string,mixed> $data Associative array with key names and respective values |
150 | * @param int|null $ttl The Time To Live for all the items or null to use the default |
151 | * |
152 | * @return array<string,bool> associative array with key names and respective set status |
153 | */ |
154 | public function setMulti(array $data, int $ttl = null) : array |
155 | { |
156 | foreach ($data as $key => &$value) { |
157 | $value = $this->set($key, $value, $ttl); |
158 | } |
159 | return $data; |
160 | } |
161 | |
162 | /** |
163 | * Deletes one item from the cache storage. |
164 | * |
165 | * @param string $key the item name |
166 | * |
167 | * @return bool TRUE if the item was deleted, FALSE if fail to delete |
168 | */ |
169 | abstract public function delete(string $key) : bool; |
170 | |
171 | /** |
172 | * Deletes multi items from the cache storage. |
173 | * |
174 | * @param array<int,string> $keys List of items names to be deleted |
175 | * |
176 | * @return array<string,bool> associative array with key names and respective delete status |
177 | */ |
178 | public function deleteMulti(array $keys) : array |
179 | { |
180 | $values = []; |
181 | foreach ($keys as $key) { |
182 | $values[$key] = $this->delete($key); |
183 | } |
184 | return $values; |
185 | } |
186 | |
187 | /** |
188 | * Flush the cache storage. |
189 | * |
190 | * @return bool TRUE if all items are deleted, otherwise FALSE |
191 | */ |
192 | abstract public function flush() : bool; |
193 | |
194 | /** |
195 | * Increments the value of one item. |
196 | * |
197 | * @param string $key The item name |
198 | * @param int $offset The value to increment |
199 | * @param int|null $ttl The Time To Live for the item or null to use the default |
200 | * |
201 | * @return int The current item value |
202 | */ |
203 | public function increment(string $key, int $offset = 1, int $ttl = null) : int |
204 | { |
205 | $offset = (int) \abs($offset); |
206 | $value = (int) $this->get($key); |
207 | $value = $value ? $value + $offset : $offset; |
208 | $this->set($key, $value, $ttl); |
209 | return $value; |
210 | } |
211 | |
212 | /** |
213 | * Decrements the value of one item. |
214 | * |
215 | * @param string $key The item name |
216 | * @param int $offset The value to decrement |
217 | * @param int|null $ttl The Time To Live for the item or null to use the default |
218 | * |
219 | * @return int The current item value |
220 | */ |
221 | public function decrement(string $key, int $offset = 1, int $ttl = null) : int |
222 | { |
223 | $offset = (int) \abs($offset); |
224 | $value = (int) $this->get($key); |
225 | $value = $value ? $value - $offset : -$offset; |
226 | $this->set($key, $value, $ttl); |
227 | return $value; |
228 | } |
229 | |
230 | #[Pure] |
231 | protected function renderKey(string $key) : string |
232 | { |
233 | return $this->prefix . $key; |
234 | } |
235 | |
236 | /** |
237 | * @param mixed $value |
238 | * |
239 | * @throws \JsonException |
240 | * |
241 | * @return string |
242 | */ |
243 | protected function serialize(mixed $value) : string |
244 | { |
245 | if ($this->serializer === Serializer::IGBINARY) { |
246 | return \igbinary_serialize($value); |
247 | } |
248 | if ($this->serializer === Serializer::JSON |
249 | || $this->serializer === Serializer::JSON_ARRAY |
250 | ) { |
251 | return \json_encode($value, \JSON_THROW_ON_ERROR); |
252 | } |
253 | if ($this->serializer === Serializer::MSGPACK) { |
254 | return \msgpack_serialize($value); |
255 | } |
256 | return \serialize($value); |
257 | } |
258 | |
259 | /** |
260 | * @param string $value |
261 | * |
262 | * @return mixed |
263 | */ |
264 | protected function unserialize(string $value) : mixed |
265 | { |
266 | if ($this->serializer === Serializer::IGBINARY) { |
267 | return @\igbinary_unserialize($value); |
268 | } |
269 | if ($this->serializer === Serializer::JSON) { |
270 | return \json_decode($value); |
271 | } |
272 | if ($this->serializer === Serializer::JSON_ARRAY) { |
273 | return \json_decode($value, true); |
274 | } |
275 | if ($this->serializer === Serializer::MSGPACK) { |
276 | return \msgpack_unserialize($value); |
277 | } |
278 | return \unserialize($value, ['allowed_classes' => true]); |
279 | } |
280 | |
281 | public function setDebugCollector(CacheCollector $debugCollector) : static |
282 | { |
283 | $this->debugCollector = $debugCollector; |
284 | $this->debugCollector->setInfo([ |
285 | 'class' => static::class, |
286 | 'serializer' => $this->serializer->value, |
287 | ]); |
288 | return $this; |
289 | } |
290 | |
291 | protected function addDebugGet(string $key, float $start, mixed $value) : mixed |
292 | { |
293 | $end = \microtime(true); |
294 | $this->debugCollector->addData([ |
295 | 'start' => $start, |
296 | 'end' => $end, |
297 | 'command' => 'GET', |
298 | 'status' => $value === null ? 'FAIL' : 'OK', |
299 | 'key' => $key, |
300 | 'value' => Debugger::makeDebugValue($value), |
301 | ]); |
302 | return $value; |
303 | } |
304 | |
305 | protected function addDebugSet(string $key, ?int $ttl, float $start, mixed $value, bool $status) : bool |
306 | { |
307 | $end = \microtime(true); |
308 | $this->debugCollector->addData([ |
309 | 'start' => $start, |
310 | 'end' => $end, |
311 | 'command' => 'SET', |
312 | 'status' => $status ? 'OK' : 'FAIL', |
313 | 'key' => $key, |
314 | 'value' => Debugger::makeDebugValue($value), |
315 | 'ttl' => $this->makeTtl($ttl), |
316 | ]); |
317 | return $status; |
318 | } |
319 | |
320 | protected function addDebugDelete(string $key, float $start, bool $status) : bool |
321 | { |
322 | $end = \microtime(true); |
323 | $this->debugCollector->addData([ |
324 | 'start' => $start, |
325 | 'end' => $end, |
326 | 'command' => 'DELETE', |
327 | 'status' => $status ? 'OK' : 'FAIL', |
328 | 'key' => $key, |
329 | ]); |
330 | return $status; |
331 | } |
332 | |
333 | protected function addDebugFlush(float $start, bool $status) : bool |
334 | { |
335 | $end = \microtime(true); |
336 | $this->debugCollector->addData([ |
337 | 'start' => $start, |
338 | 'end' => $end, |
339 | 'command' => 'FLUSH', |
340 | 'status' => $status ? 'OK' : 'FAIL', |
341 | ]); |
342 | return $status; |
343 | } |
344 | } |