Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
43 / 43
100.00% covered (success)
100.00%
9 / 9
CRAP
100.00% covered (success)
100.00%
1 / 1
Memory
100.00% covered (success)
100.00%
43 / 43
100.00% covered (success)
100.00%
9 / 9
16
100.00% covered (success)
100.00%
1 / 1
 getItemTtl
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 saveItem
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 getItem
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 hasItem
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 deleteItem
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 clear
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 destroy
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 incrementItem
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 decrementItem
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <nick@popphp.org>
8 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Cache\Adapter;
16
17/**
18 * Memory adapter cache class
19 *
20 * @category   Pop
21 * @package    Pop\Cache
22 * @author     Nick Sagona, III <nick@popphp.org>
23 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
24 * @license    https://www.popphp.org/license     New BSD License
25 * @version    5.0.0
26 */
27class Memory extends AbstractAdapter
28{
29
30    /**
31     * Cached items, keyed by sha1($id)
32     * @var array
33     */
34    protected array $items = [];
35
36    /**
37     * Get the time-to-live for an item in cache
38     *
39     * @param  string $id
40     * @param  int    $default
41     * @return int
42     */
43    public function getItemTtl(string $id, int $default = 0): int
44    {
45        $key = sha1($id);
46        $ttl = $default;
47
48        if (isset($this->items[$key])) {
49            $ttl = $this->items[$key]['ttl'];
50        }
51
52        return $ttl;
53    }
54
55    /**
56     * Save an item to cache
57     *
58     * @param  string $id
59     * @param  mixed  $value
60     * @param  ?int   $ttl
61     * @return Memory
62     */
63    public function saveItem(string $id, mixed $value, ?int $ttl = null): Memory
64    {
65        $this->items[sha1($id)] = [
66            'start' => $this->clock->now(),
67            'ttl'   => ($ttl !== null) ? $ttl : $this->ttl,
68            'value' => $value
69        ];
70
71        return $this;
72    }
73
74    /**
75     * Get an item from cache
76     *
77     * @param  string $id
78     * @param  mixed  $default
79     * @return mixed
80     */
81    public function getItem(string $id, mixed $default = false): mixed
82    {
83        $key   = sha1($id);
84        $value = $default;
85
86        if (isset($this->items[$key])) {
87            $cacheValue = $this->items[$key];
88            if ($this->isFresh($cacheValue)) {
89                $value = $cacheValue['value'];
90            } else {
91                $this->deleteItem($id);
92            }
93        }
94
95        return $value;
96    }
97
98    /**
99     * Determine if the item exist in cache
100     *
101     * @param  string $id
102     * @return bool
103     */
104    public function hasItem(string $id): bool
105    {
106        $key    = sha1($id);
107        $result = false;
108
109        if (isset($this->items[$key])) {
110            $cacheValue = $this->items[$key];
111            $result     = $this->isFresh($cacheValue);
112        }
113
114        return $result;
115    }
116
117    /**
118     * Delete a value in cache
119     *
120     * @param  string $id
121     * @return Memory
122     */
123    public function deleteItem(string $id): Memory
124    {
125        unset($this->items[sha1($id)]);
126        return $this;
127    }
128
129    /**
130     * Clear all stored values from cache
131     *
132     * @return Memory
133     */
134    public function clear(): Memory
135    {
136        $this->items = [];
137        return $this;
138    }
139
140    /**
141     * Destroy cache resource
142     *
143     * @return Memory
144     */
145    public function destroy(): Memory
146    {
147        $this->clear();
148        return $this;
149    }
150
151    /**
152     * Atomically increment a counter in cache, creating it at $initial if it doesn't exist
153     *
154     * Non-atomic read-modify-write through the same start/ttl/value envelope used by saveItem()/getItem() —
155     * Memory has no native atomic primitive, so a counter here is an ordinary cached integer, fully
156     * interoperable with getItem()/hasItem()/deleteItem().
157     *
158     * @param  string $id
159     * @param  int    $amount
160     * @param  int    $initial
161     * @param  ?int   $ttl
162     * @throws Exception
163     * @return int
164     */
165    public function incrementItem(string $id, int $amount = 1, int $initial = 0, ?int $ttl = null): int
166    {
167        $current = $this->getItem($id, $initial);
168
169        if (!is_int($current)) {
170            throw new Exception('Error: The value at that key is not numeric.');
171        }
172
173        $value = $current + $amount;
174        $this->saveItem($id, $value, $ttl);
175
176        return $value;
177    }
178
179    /**
180     * Atomically decrement a counter in cache, creating it at $initial if it doesn't exist
181     *
182     * Non-atomic read-modify-write through the same start/ttl/value envelope used by saveItem()/getItem() —
183     * Memory has no native atomic primitive, so a counter here is an ordinary cached integer, fully
184     * interoperable with getItem()/hasItem()/deleteItem().
185     *
186     * @param  string $id
187     * @param  int    $amount
188     * @param  int    $initial
189     * @param  ?int   $ttl
190     * @throws Exception
191     * @return int
192     */
193    public function decrementItem(string $id, int $amount = 1, int $initial = 0, ?int $ttl = null): int
194    {
195        $current = $this->getItem($id, $initial);
196
197        if (!is_int($current)) {
198            throw new Exception('Error: The value at that key is not numeric.');
199        }
200
201        $value = $current - $amount;
202        $this->saveItem($id, $value, $ttl);
203
204        return $value;
205    }
206
207}