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