Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
88.06% |
118 / 134 |
|
61.54% |
8 / 13 |
CRAP | |
0.00% |
0 / 1 |
Database | |
88.06% |
118 / 134 |
|
61.54% |
8 / 13 |
40.46 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
setDb | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getDb | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getTable | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getItemTtl | |
83.33% |
10 / 12 |
|
0.00% |
0 / 1 |
5.12 | |||
saveItem | |
84.31% |
43 / 51 |
|
0.00% |
0 / 1 |
10.39 | |||
getItem | |
88.89% |
16 / 18 |
|
0.00% |
0 / 1 |
6.05 | |||
hasItem | |
87.50% |
14 / 16 |
|
0.00% |
0 / 1 |
5.05 | |||
deleteItem | |
81.82% |
9 / 11 |
|
0.00% |
0 / 1 |
3.05 | |||
clear | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
destroy | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setTable | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
createTable | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * Pop PHP Framework (http://www.popphp.org/) |
4 | * |
5 | * @link https://github.com/popphp/popphp-framework |
6 | * @author Nick Sagona, III <dev@nolainteractive.com> |
7 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
8 | * @license http://www.popphp.org/license New BSD License |
9 | */ |
10 | |
11 | /** |
12 | * @namespace |
13 | */ |
14 | namespace Pop\Cache\Adapter; |
15 | |
16 | use Pop\Db\Adapter; |
17 | |
18 | /** |
19 | * Database cache adapter class |
20 | * |
21 | * @category Pop |
22 | * @package Pop\Cache |
23 | * @author Nick Sagona, III <dev@nolainteractive.com> |
24 | * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
25 | * @license http://www.popphp.org/license New BSD License |
26 | * @version 4.0.0 |
27 | */ |
28 | class Database extends AbstractAdapter |
29 | { |
30 | |
31 | /** |
32 | * Database adapter |
33 | * @var ?Adapter\AbstractAdapter |
34 | */ |
35 | protected ?Adapter\AbstractAdapter $db = null; |
36 | |
37 | /** |
38 | * Cache db table |
39 | * @var string |
40 | */ |
41 | protected string $table = 'pop_cache'; |
42 | |
43 | /** |
44 | * Constructor |
45 | * |
46 | * Instantiate the DB writer object |
47 | * |
48 | * The DB table requires the following fields at a minimum: |
49 | |
50 | * id INT |
51 | * key VARCHAR |
52 | * start INT |
53 | * ttl INT |
54 | * value TEXT, VARCHAR, etc. |
55 | * |
56 | * @param Adapter\AbstractAdapter $db |
57 | * @param int $ttl |
58 | * @param string $table |
59 | */ |
60 | public function __construct(Adapter\AbstractAdapter $db, int $ttl = 0, string $table = 'pop_cache') |
61 | { |
62 | parent::__construct($ttl); |
63 | |
64 | $this->setDb($db); |
65 | $this->setTable($table); |
66 | |
67 | if (!$db->hasTable($this->table)) { |
68 | $this->createTable(); |
69 | } |
70 | } |
71 | |
72 | /** |
73 | * Set the current cache db adapter. |
74 | * |
75 | * @param Adapter\AbstractAdapter $db |
76 | * @return Database |
77 | */ |
78 | public function setDb(Adapter\AbstractAdapter $db): Database |
79 | { |
80 | $this->db = $db; |
81 | return $this; |
82 | } |
83 | |
84 | /** |
85 | * Get the current cache db adapter. |
86 | * |
87 | * @return Adapter\AbstractAdapter |
88 | */ |
89 | public function getDb(): Adapter\AbstractAdapter |
90 | { |
91 | return $this->db; |
92 | } |
93 | |
94 | /** |
95 | * Get the current cache db table. |
96 | * |
97 | * @return string |
98 | */ |
99 | public function getTable(): string |
100 | { |
101 | return $this->table; |
102 | } |
103 | |
104 | /** |
105 | * Get the time-to-live for an item in cache |
106 | * |
107 | * @param string $id |
108 | * @return int |
109 | */ |
110 | public function getItemTtl(string $id): int |
111 | { |
112 | $sql = $this->db->createSql(); |
113 | $placeholder = $sql->getPlaceholder(); |
114 | |
115 | if ($placeholder == ':') { |
116 | $placeholder .= 'key'; |
117 | } else if ($placeholder == '$') { |
118 | $placeholder .= '1'; |
119 | } |
120 | |
121 | $sql->select()->from($this->table)->where('key = ' . $placeholder); |
122 | |
123 | $this->db->prepare($sql) |
124 | ->bindParams(['key' => sha1($id)]) |
125 | ->execute(); |
126 | |
127 | $rows = $this->db->fetchAll(); |
128 | |
129 | return (isset($rows[0]) && isset($rows[0]['ttl'])) ? $rows[0]['ttl'] : 0; |
130 | } |
131 | |
132 | /** |
133 | * Save an item to cache |
134 | * |
135 | * @param string $id |
136 | * @param mixed $value |
137 | * @param ?int $ttl |
138 | * @return Database |
139 | */ |
140 | public function saveItem(string $id, mixed $value, ?int $ttl = null): Database |
141 | { |
142 | // Determine if the value already exists. |
143 | $sql = $this->db->createSql(); |
144 | $placeholder = $sql->getPlaceholder(); |
145 | |
146 | if ($placeholder == ':') { |
147 | $placeholder .= 'key'; |
148 | } else if ($placeholder == '$') { |
149 | $placeholder .= '1'; |
150 | } |
151 | |
152 | $sql->select()->from($this->table)->where('key = ' . $placeholder); |
153 | |
154 | $this->db->prepare($sql) |
155 | ->bindParams(['key' => sha1($id)]) |
156 | ->execute(); |
157 | |
158 | $rows = $this->db->fetchAll(); |
159 | |
160 | $sql->reset(); |
161 | $placeholder = $sql->getPlaceholder(); |
162 | |
163 | // If the value doesn't exist, save the new value. |
164 | if (count($rows) == 0) { |
165 | if ($placeholder == ':') { |
166 | $placeholders = [':key', ':start', ':ttl', ':value']; |
167 | } else if ($placeholder == '$') { |
168 | $placeholders = ['$1', '$2', '$3', '$4']; |
169 | } else { |
170 | $placeholders = ['?', '?', '?', '?']; |
171 | } |
172 | $sql->insert($this->table)->values([ |
173 | 'key' => $placeholders[0], |
174 | 'start' => $placeholders[1], |
175 | 'ttl' => $placeholders[2], |
176 | 'value' => $placeholders[3] |
177 | ]); |
178 | $params = [ |
179 | 'key' => sha1($id), |
180 | 'start' => time(), |
181 | 'ttl' => ($ttl !== null) ? $ttl : $this->ttl, |
182 | 'value' => serialize($value) |
183 | ]; |
184 | // Else, update it. |
185 | } else { |
186 | if ($placeholder == ':') { |
187 | $placeholders = [':start', ':ttl', ':value', ':key']; |
188 | } else if ($placeholder == '$') { |
189 | $placeholders = ['$1', '$2', '$3', '$4']; |
190 | } else { |
191 | $placeholders = ['?', '?', '?', '?']; |
192 | } |
193 | $sql->update($this->table)->values([ |
194 | 'start' => $placeholders[0], |
195 | 'ttl' => $placeholders[1], |
196 | 'value' => $placeholders[2] |
197 | ])->where('key = ' . $placeholders[3]); |
198 | $params = [ |
199 | 'start' => time(), |
200 | 'ttl' => ($ttl !== null) ? $ttl : $this->ttl, |
201 | 'value' => serialize($value), |
202 | 'key' => sha1($id) |
203 | ]; |
204 | } |
205 | |
206 | // Save value |
207 | $this->db->prepare($sql) |
208 | ->bindParams($params) |
209 | ->execute(); |
210 | |
211 | return $this; |
212 | } |
213 | |
214 | /** |
215 | * Get an item from cache |
216 | * |
217 | * @param string $id |
218 | * @return mixed |
219 | */ |
220 | public function getItem(string $id): mixed |
221 | { |
222 | $sql = $this->db->createSql(); |
223 | $placeholder = $sql->getPlaceholder(); |
224 | $value = false; |
225 | |
226 | if ($placeholder == ':') { |
227 | $placeholder .= 'key'; |
228 | } else if ($placeholder == '$') { |
229 | $placeholder .= '1'; |
230 | } |
231 | |
232 | $sql->select()->from($this->table)->where('key = ' . $placeholder); |
233 | |
234 | $this->db->prepare($sql) |
235 | ->bindParams(['key' => sha1($id)]) |
236 | ->execute(); |
237 | |
238 | $rows = $this->db->fetchAll(); |
239 | |
240 | // If the value is found, check expiration and return. |
241 | if (count($rows) > 0) { |
242 | $cacheValue = $rows[0]; |
243 | if (($cacheValue['ttl'] == 0) || ((time() - $cacheValue['start']) <= $cacheValue['ttl'])) { |
244 | $value = unserialize($cacheValue['value']); |
245 | } else { |
246 | $this->deleteItem($id); |
247 | } |
248 | } |
249 | |
250 | return $value; |
251 | } |
252 | |
253 | /** |
254 | * Determine if the item exist in cache |
255 | * |
256 | * @param string $id |
257 | * @return bool |
258 | */ |
259 | public function hasItem(string $id): bool |
260 | { |
261 | $sql = $this->db->createSql(); |
262 | $placeholder = $sql->getPlaceholder(); |
263 | $result = false; |
264 | |
265 | if ($placeholder == ':') { |
266 | $placeholder .= 'key'; |
267 | } else if ($placeholder == '$') { |
268 | $placeholder .= '1'; |
269 | } |
270 | |
271 | $sql->select()->from($this->table)->where('key = ' . $placeholder); |
272 | |
273 | $this->db->prepare($sql) |
274 | ->bindParams(['key' => sha1($id)]) |
275 | ->execute(); |
276 | |
277 | $rows = $this->db->fetchAll(); |
278 | |
279 | // If the value is found, check expiration and return. |
280 | if (count($rows) > 0) { |
281 | $cacheValue = $rows[0]; |
282 | $result = (($cacheValue['ttl'] == 0) || ((time() - $cacheValue['start']) <= $cacheValue['ttl'])); |
283 | } |
284 | |
285 | return $result; |
286 | } |
287 | |
288 | /** |
289 | * Delete a value in cache |
290 | * |
291 | * @param string $id |
292 | * @return Database |
293 | */ |
294 | public function deleteItem(string $id): Database |
295 | { |
296 | $sql = $this->db->createSql(); |
297 | $placeholder = $sql->getPlaceholder(); |
298 | |
299 | if ($placeholder == ':') { |
300 | $placeholder .= 'key'; |
301 | } else if ($placeholder == '$') { |
302 | $placeholder .= '1'; |
303 | } |
304 | |
305 | $sql->delete($this->table)->where('key = ' . $placeholder); |
306 | |
307 | $this->db->prepare($sql) |
308 | ->bindParams(['key' => sha1($id)]) |
309 | ->execute(); |
310 | |
311 | return $this; |
312 | } |
313 | |
314 | /** |
315 | * Clear all stored values from cache |
316 | * |
317 | * @return Database |
318 | */ |
319 | public function clear(): Database |
320 | { |
321 | $sql = $this->db->createSql(); |
322 | $sql->delete($this->table); |
323 | $this->db->query($sql); |
324 | |
325 | return $this; |
326 | } |
327 | |
328 | /** |
329 | * Destroy cache resource |
330 | * |
331 | * @return Database |
332 | */ |
333 | public function destroy(): Database |
334 | { |
335 | $this->clear(); |
336 | return $this; |
337 | } |
338 | |
339 | /** |
340 | * Set the cache db table |
341 | * |
342 | * @param string $table |
343 | * @return Database |
344 | */ |
345 | public function setTable(string $table): Database |
346 | { |
347 | $this->table = $table; |
348 | return $this; |
349 | } |
350 | |
351 | /** |
352 | * Create table in database |
353 | * |
354 | * @return void |
355 | */ |
356 | protected function createTable(): void |
357 | { |
358 | $schema = $this->db->createSchema(); |
359 | $schema->create($this->table) |
360 | ->int('id')->increment() |
361 | ->varchar('key', 255) |
362 | ->int('start') |
363 | ->int('ttl') |
364 | ->text('value') |
365 | ->primary('id'); |
366 | |
367 | $this->db->query($schema); |
368 | } |
369 | } |