Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.44% covered (success)
98.44%
63 / 64
96.15% covered (success)
96.15%
25 / 26
CRAP
0.00% covered (danger)
0.00%
0 / 1
Debugger
98.44% covered (success)
98.44%
63 / 64
96.15% covered (success)
96.15%
25 / 26
45
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
8
 setRequestId
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getRequestId
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 hasRequestId
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 generateId
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 addHandlers
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 addHandler
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 hasHandler
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getHandler
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getHandlers
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setStorage
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 hasStorage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getStorage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addLogger
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 save
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
 clear
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 count
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getIterator
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __set
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __get
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __isset
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __unset
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 offsetSet
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 offsetGet
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 offsetExists
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 offsetUnset
100.00% covered (success)
100.00%
2 / 2
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 <dev@noladev.com>
8 * @copyright  Copyright (c) 2009-2027 NOLA Interactive, LLC.
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Debug;
16
17use Pop\Debug\Storage\StorageInterface;
18use Psr\Log\LoggerInterface;
19use Pop\Utils\DebuggerInterface;
20use Pop\Utils\DebuggerHandlerInterface;
21use ArrayIterator;
22
23/**
24 * Debugger class
25 *
26 * @category   Pop
27 * @package    Pop\Debug
28 * @author     Nick Sagona, III <dev@noladev.com>
29 * @copyright  Copyright (c) 2009-2027 NOLA Interactive, LLC.
30 * @license    https://www.popphp.org/license     New BSD License
31 * @version    4.0.0
32 */
33class Debugger implements DebuggerInterface, \ArrayAccess, \Countable, \IteratorAggregate
34{
35
36    /**
37     * Debugger handlers
38     * @var array
39     */
40    protected array $handlers = [];
41
42    /**
43     * Debugger storage object
44     * @var ?StorageInterface
45     */
46    protected ?StorageInterface $storage = null;
47
48    /**
49     * Debugger request ID
50     * @var ?string
51     */
52    protected ?string $requestId = null;
53
54    /**
55     * Constructor
56     *
57     * Instantiate a debug object
58     */
59    public function __construct()
60    {
61        $args = func_get_args();
62
63        foreach ($args as $arg) {
64            if (is_array($arg)) {
65                foreach ($arg as $a) {
66                    if ($a instanceof DebuggerHandlerInterface) {
67                        $this->addHandler($a);
68                    } else if ($a instanceof StorageInterface) {
69                        $this->setStorage($a);
70                    }
71                }
72            } else if ($arg instanceof DebuggerHandlerInterface) {
73                $this->addHandler($arg);
74            } else if ($arg instanceof StorageInterface) {
75                $this->setStorage($arg);
76            }
77        }
78    }
79
80    /**
81     * Set request ID
82     *
83     * @param  string $requestId
84     * @return Debugger
85     */
86    public function setRequestId(string $requestId): Debugger
87    {
88        $this->requestId = $requestId;
89        return $this;
90    }
91
92    /**
93     * Get current request ID
94     *
95     * @return string
96     */
97    public function getRequestId(): string
98    {
99        if (!$this->hasRequestId()) {
100            $this->setRequestId($this->generateId());
101        }
102
103        return $this->requestId;
104    }
105
106    /**
107     * Has request ID
108     *
109     * @return bool
110     */
111    public function hasRequestId(): bool
112    {
113        if ($this->requestId === null) {
114            $this->requestId = $this->generateId();
115        }
116
117        return !empty($this->requestId);
118    }
119
120    /**
121     * Generate unique ID
122     *
123     * @return string
124     */
125    public function generateId(): string
126    {
127        if (function_exists('random_bytes')) {
128            return bin2hex(random_bytes(16));
129        } else {
130            return md5(uniqid());
131        }
132    }
133
134    /**
135     * Add handlers
136     *
137     * @param  array $handlers
138     * @return Debugger
139     */
140    public function addHandlers(array $handlers): Debugger
141    {
142        foreach ($handlers as $handler) {
143            $this->addHandler($handler);
144        }
145
146        return $this;
147    }
148
149    /**
150     * Add a handler
151     *
152     * @param  DebuggerHandlerInterface $handler
153     * @return Debugger
154     */
155    public function addHandler(DebuggerHandlerInterface $handler): Debugger
156    {
157        $type = strtolower(str_replace('Handler', '', get_class($handler)));
158        if (strrpos($type, '\\') !== false) {
159            $type = substr($type, (strrpos($type, '\\') + 1));
160            if (!empty($handler->getName())) {
161                $type = str_replace(' ', '-', strtolower($handler->getName())) . '-' . $type;
162            }
163        }
164
165        $this->handlers[$type] = $handler;
166
167        return $this;
168    }
169
170    /**
171     * Determine if the debug object has a handler
172     *
173     * @param  string $name
174     * @return bool
175     */
176    public function hasHandler(string $name): bool
177    {
178        return isset($this->handlers[$name]);
179    }
180
181    /**
182     * Get a handler
183     *
184     * @param  string $name
185     * @return ?DebuggerHandlerInterface
186     */
187    public function getHandler(string $name): ?DebuggerHandlerInterface
188    {
189        return $this->handlers[$name] ?? null;
190    }
191
192    /**
193     * Get all handlers
194     *
195     * @return array
196     */
197    public function getHandlers(): array
198    {
199        return $this->handlers;
200    }
201
202    /**
203     * Set the storage object
204     *
205     * @param  StorageInterface $storage
206     * @return Debugger
207     */
208    public function setStorage(StorageInterface $storage): Debugger
209    {
210        $this->storage = $storage;
211        return $this;
212    }
213
214    /**
215     * Determine if the debug object has storage
216     *
217     * @return bool
218     */
219    public function hasStorage(): bool
220    {
221        return ($this->storage !== null);
222    }
223
224    /**
225     * Get the storage object
226     *
227     * @return StorageInterface
228     */
229    public function getStorage(): StorageInterface
230    {
231        return $this->storage;
232    }
233
234    /**
235     * Add logger to handler(s)
236     *
237     * @param  LoggerInterface $logger
238     * @param  array           $loggingParams
239     * @return Debugger
240     */
241    public function addLogger(LoggerInterface $logger, array $loggingParams): Debugger
242    {
243        foreach ($this->handlers as $handler) {
244            $handler->setLogger($logger);
245            $handler->setLoggingParams($loggingParams);
246        }
247        return $this;
248    }
249
250    /**
251     * Save the debug handlers' data to storage (returns the request ID of what was saved)
252     *
253     * @return string
254     */
255    public function save(): string
256    {
257        foreach ($this->handlers as $name => $handler) {
258            // Storage debug info
259            if ($this->hasStorage()) {
260                $this->storage->save($this->getRequestId(), $name, $handler);
261            }
262            // Log debug events
263            if ($handler->hasLogger()) {
264                $handler->log();
265            }
266        }
267
268        return $this->getRequestId();
269    }
270
271    /**
272     * Clear storage
273     *
274     * @return void
275     */
276    public function clear(): void
277    {
278        $this->storage->clear();
279    }
280
281    /**
282     * Method to get the count of the handlers
283     *
284     * @return int
285     */
286    public function count(): int
287    {
288        return count($this->handlers);
289    }
290
291    /**
292     * Method to iterate over the handlers
293     *
294     * @return ArrayIterator
295     */
296    public function getIterator(): ArrayIterator
297    {
298        return new ArrayIterator($this->handlers);
299    }
300
301    /**
302     * Set a handler
303     *
304     * @param  string $name
305     * @param  mixed $value
306     * @throws Exception
307     * @return void
308     */
309    public function __set(string $name, mixed $value): void
310    {
311        $this->offsetSet($name, $value);
312    }
313
314    /**
315     * Get a handler
316     *
317     * @param  string $name
318     * @return mixed
319     */
320    public function __get(string $name): mixed
321    {
322        return $this->offsetGet($name);
323    }
324
325    /**
326     * Is handler set
327     *
328     * @param  string $name
329     * @return bool
330     */
331    public function __isset(string $name): bool
332    {
333        return $this->offsetExists($name);
334    }
335
336    /**
337     * Unset a handler
338     *
339     * @param  string $name
340     * @return void
341     */
342    public function __unset(string $name): void
343    {
344        $this->offsetUnset($name);
345    }
346
347    /**
348     * ArrayAccess offsetSet
349     *
350     * @param  mixed $offset
351     * @param  mixed $value
352     * @throws Exception
353     * @return void
354     */
355    public function offsetSet(mixed $offset, mixed $value): void
356    {
357        if (!($value instanceof DebuggerHandlerInterface)) {
358            throw new Exception('Error: The value passed must be an instance of DebuggerHandlerInterface');
359        }
360        $this->handlers[$offset] = $value;
361    }
362
363    /**
364     * ArrayAccess offsetGet
365     *
366     * @param  mixed $offset
367     * @return mixed
368     */
369    public function offsetGet(mixed $offset): mixed
370    {
371        return $this->handlers[$offset] ?? null;
372    }
373
374    /**
375     * ArrayAccess offsetExists
376     *
377     * @param  mixed $offset
378     * @return bool
379     */
380    public function offsetExists(mixed $offset): bool
381    {
382        return isset($this->handlers[$offset]);
383    }
384
385    /**
386     * ArrayAccess offsetUnset
387     *
388     * @param  mixed $offset
389     * @return void
390     */
391    public function offsetUnset(mixed $offset): void
392    {
393        if (isset($this->handlers[$offset])) {
394            unset($this->handlers[$offset]);
395        }
396    }
397
398}