Debugger   A
last analyzed

Complexity

Total Complexity 28

Size/Duplication

Total Lines 177
Duplicated Lines 0 %

Importance

Changes 29
Bugs 4 Features 1
Metric Value
eloc 93
c 29
b 4
f 1
dl 0
loc 177
rs 10
wmc 28

12 Methods

Rating   Name   Duplication   Size   Complexity  
A visualDump() 0 22 3
A toText() 0 10 2
A addSkipClasses() 0 4 1
A traceToString() 0 21 3
A toHTML() 0 17 1
A tracesToString() 0 12 3
A traceArgsToString() 0 8 2
A formatFilename() 0 3 1
A displayErrors() 0 11 3
A formatExceptionAsTrace() 0 9 1
B traceArgToString() 0 21 7
A isShortcutCall() 0 3 1
1
<?php
2
3
namespace HexMakina\Debugger
4
{
5
    class Debugger
6
    {
7
        private static array $skip_classes = [__CLASS__];
8
9
        public function addSkipClasses($skip_classes): void
10
        {
11
            self::$skip_classes = array_merge(self::$skip_classes, $skip_classes);
12
            self::$skip_classes = array_unique(self::$skip_classes);
13
        }
14
15
        // -- visual dump (display depends on env)
16
        // return the var itself, for easy code debugging
17
        public static function visualDump($var, $var_name = null, $full_backtrace = false)
18
        {
19
            if ($var instanceof \Throwable) {
20
                $traces = $var->getTrace();
21
                $dump = self::formatExceptionAsTrace($var);
22
            } else {
23
                $traces = debug_backtrace();
24
25
                ob_start();
26
                var_dump($var);
27
                $dump = ob_get_clean();
28
            }
29
            // purge traces from skipped classes
30
            $traces = array_filter(
31
                $traces,
32
                static fn($trace): bool
33
                => empty($trace['class']) || !in_array($trace['class'], self::$skip_classes)
34
            );
35
36
            $message = self::toHTML($dump, $var_name, $traces, $full_backtrace);
37
            self::displayErrors($message);
38
            return $var;
39
        }
40
41
        private static function formatExceptionAsTrace($var): string
42
        {
43
            return self::traceToString([
44
              'class' => get_class($var),
45
              'line' => $var->getLine(),
46
              'file' => $var->getFile(),
47
              'function' => 'getCode',
48
              'args' => [$var->getCode()]
49
            ]) . PHP_EOL . $var->getMessage();
50
        }
51
52
        // should we display something ?
53
        public static function displayErrors($error_message): void
54
        {
55
            if (empty($error_message)) {
56
                return;
57
            }
58
59
            if (ini_get('display_errors') != '1') {
60
                return;
61
            }
62
63
            echo $error_message;
64
        }
65
66
        // -- formatting
67
        public static function toText($var_dump, $var_name, $backtrace, $full_backtrace): string
68
        {
69
            return PHP_EOL
70
            . "******* "
71
            . (empty($var_name) ? ($backtrace[1]['function'] ?? '?') . '()' : sprintf(' (%s) ', $var_name))
72
            . " *******"
73
            . PHP_EOL
74
            . self::tracesToString($backtrace, $full_backtrace)
75
            . PHP_EOL
76
            . $var_dump;
77
        }
78
79
        public static function toHTML($var_dump, $var_name, $backtrace, $full_backtrace): string
80
        {
81
            $css = [
82
            'text-align:left',
83
            'z-index:9999',
84
            'background-color:#FFF',
85
            'color:#000',
86
            'padding:0.5em',
87
            'font-size:0.7em',
88
            'margin:0 0 1em 0',
89
            'font-family:courier'
90
            ];
91
92
            return sprintf(
93
                '<pre style="%s">%s</pre>',
94
                implode(';', $css),
95
                self::toText($var_dump, $var_name, $backtrace, $full_backtrace)
96
            );
97
        }
98
99
        // reduce_file_depth_to allows for short filepath, cause it gets crazy sometimes
100
        private static function formatFilename($file, $reduce_file_depth_to = 5): string
101
        {
102
            return implode('/', array_slice(explode('/', $file), -$reduce_file_depth_to, $reduce_file_depth_to));
103
        }
104
105
        // -- formatting : nice backtrace
106
        private static function tracesToString($traces, $full_backtrace): string
107
        {
108
            $formated_traces = [];
109
110
            foreach ($traces as $trace) {
111
                $formated_traces [] = self::traceToString($trace);
112
                if ($full_backtrace === false) {
113
                    break;
114
                }
115
            }
116
117
            return implode(PHP_EOL, array_reverse($formated_traces));
118
        }
119
120
        private static function traceToString($trace): string
121
        {
122
            $function_name = $trace['function'] ?? '?';
123
            $class_name = $trace['class'] ?? '';
124
125
            if (self::isShortcutCall($function_name)) {
126
                $args = date_format(date_create(), 'ymd:his');
127
            } else {
128
                $args = self::traceArgsToString($trace['args'] ?? []);
129
            }
130
131
            $call_file = isset($trace['file']) ? self::formatFilename($trace['file'], 2) : '?';
132
            $call_line = $trace['line'] ?? '?';
133
134
            return sprintf(
135
                '[%-33.33s %3s]  %s%s(%s)',
136
                $call_file,
137
                $call_line,
138
                sprintf('%s::', $class_name),
139
                $function_name,
140
                $args
141
            );
142
        }
143
144
        private static function traceArgsToString($trace_args): string
145
        {
146
            $ret = [];
147
            foreach ($trace_args as $trace_arg) {
148
                $ret[] = self::traceArgToString($trace_arg);
149
            }
150
151
            return implode(', ', $ret);
152
        }
153
154
        private static function traceArgToString($arg): string
155
        {
156
            switch (true) {
157
                case is_null($arg):
158
                    return 'NULL';
159
160
                case is_bool($arg):
161
                    return '[bool]' . (int) $arg;
162
163
                case is_scalar($arg):
164
                    return '[scalar]' . $arg;
165
166
                case is_object($arg):
167
                    return get_class($arg);
168
169
                case is_array($arg):
170
                    $count = count($arg);
171
                    return 'Array #' . $count . ($count > 0 ? ' json:' . json_encode(array_keys($arg)) : '');
172
173
                default:
174
                    return 'unknown type';
175
            }
176
        }
177
178
179
        private static function isShortcutCall($function_name): bool
180
        {
181
            return in_array($function_name, ['vd', 'dd','vdt', 'ddt']);
182
        }
183
    }
184
}
185
186
namespace
187
{
188
    use HexMakina\Debugger\Debugger;
189
190
    if (!function_exists('vd')) {
191
        function vd($var, $var_name = null): void
192
        {
193
            Debugger::visualDump($var, $var_name, false);
194
        }
195
    }
196
197
    if (!function_exists('dd')) {
198
        function dd($var, $var_name = null): void
199
        {
200
            Debugger::visualDump($var, $var_name, false);
201
            die;
202
        }
203
    }
204
205
    if (!function_exists('vdt')) {
206
        function vdt($var, $var_name = null): void
207
        {
208
            Debugger::visualDump($var, $var_name, true);
209
        }
210
    }
211
212
    if (!function_exists('ddt')) {
213
        function ddt($var, $var_name = null): void
214
        {
215
            Debugger::visualDump($var, $var_name, true);
216
            die;
217
        }
218
    }
219
}
220