Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
87.50% covered (success)
87.50%
105 / 120
64.29% covered (warning)
64.29%
9 / 14
CRAP
0.00% covered (danger)
0.00%
0 / 1
Database
87.50% covered (success)
87.50%
105 / 120
64.29% covered (warning)
64.29%
9 / 14
40.82
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 setDb
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getDb
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getTable
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setTable
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 save
74.07% covered (success)
74.07%
20 / 27
0.00% covered (danger)
0.00%
0 / 1
3.16
 getById
90.91% covered (success)
90.91%
20 / 22
0.00% covered (danger)
0.00%
0 / 1
9.06
 getByType
86.67% covered (success)
86.67%
13 / 15
0.00% covered (danger)
0.00%
0 / 1
4.04
 has
83.33% covered (success)
83.33%
10 / 12
0.00% covered (danger)
0.00%
0 / 1
4.07
 delete
80.00% covered (success)
80.00%
8 / 10
0.00% covered (danger)
0.00%
0 / 1
3.07
 clear
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 encodeValue
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 decodeValue
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 createTable
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
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 */
14namespace Pop\Debug\Storage;
15
16use Pop\Db\Adapter\AbstractAdapter;
17
18/**
19 * Debug database storage class
20 *
21 * @category   Pop
22 * @package    Pop\Debug
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    2.0.0
27 */
28class Database extends AbstractStorage
29{
30
31    /**
32     * DB adapter
33     * @var ?AbstractAdapter
34     */
35    protected ?AbstractAdapter $db = null;
36
37    /**
38     * Table
39     * @var string
40     */
41    protected string $table = 'pop_debug';
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     *     value TEXT, VARCHAR, etc.
52     *
53     * @param  AbstractAdapter $db
54     * @param  string          $format
55     * @param  string          $table
56     */
57    public function __construct(AbstractAdapter $db, string $format = 'text', string $table = 'pop_debug')
58    {
59        parent::__construct($format);
60
61        $this->setDb($db);
62        $this->setTable($table);
63
64        if (!$db->hasTable($this->table)) {
65            $this->createTable();
66        }
67    }
68
69    /**
70     * Set the current debug db adapter.
71     *
72     * @param  AbstractAdapter $db
73     * @return Database
74     */
75    public function setDb(AbstractAdapter $db): Database
76    {
77        $this->db = $db;
78        return $this;
79    }
80
81    /**
82     * Get the current debug db adapter.
83     *
84     * @return ?AbstractAdapter
85     */
86    public function getDb(): ?AbstractAdapter
87    {
88        return $this->db;
89    }
90
91    /**
92     * Get the current debug db table.
93     *
94     * @return string
95     */
96    public function getTable(): string
97    {
98        return $this->table;
99    }
100
101    /**
102     * Set the debug db table
103     *
104     * @param  string $table
105     * @return Database
106     */
107    public function setTable(string $table): Database
108    {
109        $this->table = $table;
110        return $this;
111    }
112
113    /**
114     * Save debug data
115     *
116     * @param  string $id
117     * @param  mixed  $value
118     * @return void
119     */
120    public function save(string $id, mixed $value): void
121    {
122        $sql = $this->db->createSql();
123        $sql->reset();
124        $placeholder = $sql->getPlaceholder();
125
126        if ($placeholder == ':') {
127            $placeholder1 = ':key';
128            $placeholder2 = ':value';
129            $placeholder3 = ':timestamp';
130        } else if ($placeholder == '$') {
131            $placeholder1 = '$1';
132            $placeholder2 = '$2';
133            $placeholder3 = '$3';
134        } else {
135            $placeholder1 = $placeholder;
136            $placeholder2 = $placeholder;
137            $placeholder3 = $placeholder;
138        }
139
140        $sql->insert($this->table)->values([
141            'key'       => $placeholder1,
142            'value'     => $placeholder2,
143            'timestamp' => $placeholder3
144        ]);
145        $params = [
146            'key'       => $id,
147            'value'     => $this->encodeValue($value),
148            'timestamp' => date('Y-m-d H:i:s')
149        ];
150
151        // Save value
152        $this->db->prepare($sql)
153            ->bindParams($params)
154            ->execute();
155    }
156
157    /**
158     * Get debug data by ID
159     *
160     * @param  string $id
161     * @return mixed
162     */
163    public function getById(string $id): mixed
164    {
165        $sql         = $this->db->createSql();
166        $placeholder = $sql->getPlaceholder();
167        $value       = false;
168        $isWildcard  = false;
169
170        if ($placeholder == ':') {
171            $placeholder .= 'key';
172        } else if ($placeholder == '$') {
173            $placeholder .= '1';
174        }
175
176        if (str_ends_with($id, '*') || str_ends_with($id, '%')) {
177            $sql->select()->from($this->table)->where('key LIKE ' . $placeholder);
178            $id = substr($id, 0, -1) . '%';
179            $isWildcard = true;
180        } else {
181            $sql->select()->from($this->table)->where('key = ' . $placeholder);
182        }
183
184        $this->db->prepare($sql)
185            ->bindParams(['key' => $id])
186            ->execute();
187
188        $rows = $this->db->fetchAll();
189
190        // If the value is found, return.
191        if (($isWildcard) && isset($rows[0])) {
192            $value = $rows;
193        } else if (isset($rows[0]) && isset($rows[0]['value'])) {
194            $value = $this->decodeValue($rows[0]['value']);
195        }
196
197        return $value;
198    }
199
200    /**
201     * Get debug data by type
202     *
203     * @param  string $type
204     * @return mixed
205     */
206    public function getByType(string $type): mixed
207    {
208        $sql         = $this->db->createSql();
209        $placeholder = $sql->getPlaceholder();
210        $value       = false;
211
212        if ($placeholder == ':') {
213            $placeholder .= 'key';
214        } else if ($placeholder == '$') {
215            $placeholder .= '1';
216        }
217
218        $sql->select()->from($this->table)->where('key LIKE ' . $placeholder);
219        $this->db->prepare($sql)
220            ->bindParams(['key' => '%' . $type])
221            ->execute();
222
223        $rows = $this->db->fetchAll();
224
225        // If the value is found, return.
226        if (isset($rows[0])) {
227            $value = $rows;
228        }
229
230        return $value;
231    }
232
233    /**
234     * Determine if debug data exists by ID
235     *
236     * @param  string $id
237     * @return bool
238     */
239    public function has(string $id): bool
240    {
241        $sql         = $this->db->createSql();
242        $placeholder = $sql->getPlaceholder();
243
244        if ($placeholder == ':') {
245            $placeholder .= 'key';
246        } else if ($placeholder == '$') {
247            $placeholder .= '1';
248        }
249
250        $sql->select()->from($this->table)->where('key = ' . $placeholder);
251
252        $this->db->prepare($sql)
253            ->bindParams(['key' => $id])
254            ->execute();
255
256        $rows = $this->db->fetchAll();
257
258        return (isset($rows[0]) && isset($rows[0]['value']));
259    }
260
261    /**
262     * Delete debug data by ID
263     *
264     * @param  string $id
265     * @return void
266     */
267    public function delete(string $id): void
268    {
269        $sql         = $this->db->createSql();
270        $placeholder = $sql->getPlaceholder();
271
272        if ($placeholder == ':') {
273            $placeholder .= 'key';
274        } else if ($placeholder == '$') {
275            $placeholder .= '1';
276        }
277
278        $sql->delete($this->table)->where('key = ' . $placeholder);
279
280        $this->db->prepare($sql)
281            ->bindParams(['key' => $id])
282            ->execute();
283    }
284
285    /**
286     * Clear all debug data
287     *
288     * @return void
289     */
290    public function clear(): void
291    {
292        $sql = $this->db->createSql();
293        $sql->delete($this->table);
294        $this->db->query($sql);
295    }
296
297    /**
298     * Encode the value based on the format
299     *
300     * @param  mixed  $value
301     * @throws Exception
302     * @return string
303     */
304    public function encodeValue(mixed $value): string
305    {
306        if ($this->format == self::JSON) {
307            $value = json_encode($value, JSON_PRETTY_PRINT);
308        } else if ($this->format == self::PHP) {
309            $value = serialize($value);
310        } else if (!is_string($value)) {
311            throw new Exception('Error: The value must be a string if storing in text format.');
312        }
313
314        return $value;
315    }
316
317    /**
318     * Decode the value based on the format
319     *
320     * @param  mixed  $value
321     * @return mixed
322     */
323    public function decodeValue(mixed $value): mixed
324    {
325        if ($this->format == self::JSON) {
326            $value = json_decode($value, true);
327        } else if ($this->format == self::PHP) {
328            $value = unserialize($value);
329        }
330
331        return $value;
332    }
333
334    /**
335     * Create table in database
336     *
337     * @return void
338     */
339    protected function createTable(): void
340    {
341        $schema = $this->db->createSchema();
342        $schema->create($this->table)
343            ->int('id')->increment()
344            ->varchar('key', 255)
345            ->text('value')
346            ->datetime('timestamp')
347            ->primary('id');
348
349        $this->db->query($schema);
350    }
351
352}