Passed
Push — main ( 04b043...77724e )
by Sammy
01:30
created

Debugger::traces()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 4
eloc 4
c 1
b 1
f 0
nc 4
nop 1
dl 0
loc 10
rs 10
1
<?php
2
3
namespace HexMakina\Debugger
4
{
5
    class Debugger
6
    {
7
        private static $meta_methods = [];
8
9
        public function __construct()
10
        {
11
            $debugger = new \ReflectionClass(__CLASS__);
12
            $methods = $debugger->getMethods();
13
            self::$meta_methods = array_map(function ($m) {
14
                return $m->name;
15
            }, $methods);
16
        }
17
18
        // -- visual dump (display depends on env)
19
        // return the var itself, for easy code debugging
20
        public static function visualDump($var, $var_name = null, $full_backtrace = false)
21
        {
22
            $dump = self::dump($var);
23
            $backtrace = self::traces($var);
24
            $message = self::toHTML($dump, $var_name, $backtrace, $full_backtrace);
25
26
            self::displayErrors($message);
27
            return $var;
28
        }
29
30
        // should we display something ?
31
        public static function displayErrors($error_message = null)
32
        {
33
            if (!empty($error_message) && ini_get('display_errors') == '1') {
34
                echo $error_message;
35
            }
36
        }
37
38
        // creates a dump according to variable type (Throwables & anything else)
39
        private static function dump($var): string
40
        {
41
            if ($var instanceof \Throwable) {
42
                return self::formatThrowable($var);
43
            }
44
45
            ob_start();
46
            var_dump($var);
47
            return ob_get_clean();
48
        }
49
50
        public static function traces($var)
51
        {
52
            $traces = $var instanceof \Throwable ? $var->getTrace() : debug_backtrace();
53
54
            // removes all internal calls
55
            while (!empty($traces[0]['class']) && $traces[0]['class'] == __CLASS__) {
56
                array_shift($traces);
57
            }
58
59
            return $traces;
60
        }
61
62
        // -- formatting
63
64
        // -- formatting : first line of \Throwable-based error
65
        public static function formatThrowable(\Throwable $err)
66
        {
67
            return sprintf(
68
                '%s (%d) in file %s:%d' . PHP_EOL . '%s',
69
                get_class($err),
70
                $err->getCode(),
71
                self::formatFilename($err->getFile()),
72
                $err->getLine(),
73
                $err->getMessage()
74
            );
75
        }
76
77
        // reduce_file_depth_to allows for short filepath, cause it gets crazy sometimes
78
        public static function formatFilename($file, $reduce_file_depth_to = 5)
79
        {
80
            return implode('/', array_slice(explode('/', $file), -$reduce_file_depth_to, $reduce_file_depth_to));
81
        }
82
83
        // -- formatting : nice backtrace
84
        public static function tracesToString($traces, $full_backtrace)
85
        {
86
            $formated_traces = [];
87
88
            foreach ($traces as $depth => $trace) {
89
                if (!empty($trace_string = self::traceToString($trace))) {
90
                    $formated_traces [] = $trace_string;
91
                }
92
                if (!empty($formated_traces) && $full_backtrace === false) {
93
                    break;
94
                }
95
            }
96
97
            return implode(PHP_EOL, array_reverse($formated_traces));
98
        }
99
100
        private static function traceToString($trace)
101
        {
102
            $function_name = $trace['function'] ?? '?';
103
            $class_name = $trace['class'] ?? '';
104
105
            if (self::isShortcutCall($function_name)) {
106
                $args = date_format(date_create(), 'ymd:his');
107
            } else {
108
                $args = self::traceArgsToString($trace['args'] ?? []);
109
            }
110
111
            $call_file = isset($trace['file']) ? basename($trace['file']) : '?';
112
            $call_line = $trace['line'] ?? '?';
113
114
            return sprintf(
115
                '[%-23.23s %3s]  %s%s(%s)',
116
                $call_file,
117
                $call_line,
118
                "$class_name::",
119
                $function_name,
120
                $args
121
            );
122
        }
123
124
        private static function traceArgsToString($trace_args)
125
        {
126
            $ret = [];
127
            foreach ($trace_args as $arg) {
128
                $ret[] = self::traceArgToString($arg);
129
            }
130
            return implode(', ', $ret);
131
        }
132
133
        private static function traceArgToString($arg)
134
        {
135
            $ret = 'unknown type';
136
137
            if (is_null($arg)) {
138
                $ret = 'NULL';
139
            } elseif (is_bool($arg)) {
140
                $ret = 'bool:' . ((int)$arg);
141
            } elseif (is_scalar($arg)) {
142
                $ret = $arg;
143
            } elseif (is_object($arg)) {
144
                $ret = get_class($arg);
145
            } elseif (is_array($arg)) {
146
                $ret = 'Array #' . count($arg);
147
            }
148
            return $ret;
149
        }
150
151
        // private static function isInternalFunctionCall($class_name, $function_name): bool
152
        // {
153
        //     return $class_name === __CLASS__ && in_array($function_name, self::$meta_methods);
154
        // }
155
156
        private static function isShortcutCall($function_name): bool
157
        {
158
            return in_array($function_name, ['vd', 'dd','vdt', 'ddt']);
159
        }
160
161
162
        public static function toText($var_dump, $var_name, $backtrace, $full_backtrace)
163
        {
164
            return PHP_EOL
165
            . "******* "
166
            . (empty($var_name) ? $backtrace[1]['function'] . '()' : " ($var_name) ")
167
            . " *******"
168
            . PHP_EOL
169
            . self::tracesToString($backtrace, $full_backtrace)
170
            . PHP_EOL
171
            . $var_dump;
172
        }
173
174
        public static function toHTML($var_dump, $var_name, $backtrace, $full_backtrace)
175
        {
176
            $css = [
177
            'text-align:left',
178
            'z-index:9999',
179
            'background-color:#FFF',
180
            'color:#000',
181
            'padding:0.5em',
182
            'font-size:0.7em',
183
            'margin:0 0 1em 0',
184
            'font-family:courier'
185
            ];
186
187
            return sprintf(
188
                '<pre style="%s">%s</pre>',
189
                implode(';', $css),
190
                self::toText($var_dump, $var_name, $backtrace, $full_backtrace)
191
            );
192
        }
193
    }
194
}
195
namespace
196
{
197
    use HexMakina\Debugger\Debugger;
198
199
    if (!function_exists('vd')) {
200
        function vd($var, $var_name = null)
201
        {
202
            Debugger::visualDump($var, $var_name, false);
203
        }
204
    }
205
    if (!function_exists('dd')) {
206
        function dd($var, $var_name = null)
207
        {
208
            Debugger::visualDump($var, $var_name, false);
209
            die;
1 ignored issue
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
210
        }
211
    }
212
    if (!function_exists('vdt')) {
213
        function vdt($var, $var_name = null)
214
        {
215
            Debugger::visualDump($var, $var_name, true);
216
        }
217
    }
218
    if (!function_exists('ddt')) {
219
        function ddt($var, $var_name = null)
220
        {
221
            Debugger::visualDump($var, $var_name, true);
222
            die;
1 ignored issue
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
223
        }
224
    }
225
}
226