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