Passed
Push — main ( 341e56...980c22 )
by Sammy
06:56
created

Debugger   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 181
Duplicated Lines 0 %

Importance

Changes 21
Bugs 3 Features 0
Metric Value
eloc 91
c 21
b 3
f 0
dl 0
loc 181
rs 9.6
wmc 35

14 Methods

Rating   Name   Duplication   Size   Complexity  
A traceArgToString() 0 16 6
A traceToString() 0 21 3
A traceArgsToString() 0 7 2
A dump() 0 9 2
A purgeTraces() 0 11 4
A visualDump() 0 10 2
A toText() 0 10 2
A setSkipClasses() 0 6 2
A formatThrowable() 0 9 1
A toHTML() 0 17 1
A tracesToString() 0 14 5
A formatFilename() 0 3 1
A isShortcutCall() 0 3 1
A displayErrors() 0 4 3
1
<?php
2
3
namespace HexMakina\Debugger
4
{
5
    class Debugger
6
    {
7
        private static $skip_classes = [__CLASS__];
8
9
        public function setSkipClasses($skip_classes)
10
        {
11
            foreach ($skip_classes as $class) {
12
                array_push(self::$skip_classes, $class);
13
            }
14
            self::$skip_classes = array_unique(self::$skip_classes);
15
        }
16
17
        // -- visual dump (display depends on env)
18
        // return the var itself, for easy code debugging
19
        public static function visualDump($var, $var_name = null, $full_backtrace = false)
20
        {
21
            $dump = self::dump($var);
22
            $traces = $var instanceof \Throwable ? $var->getTrace() : debug_backtrace();
23
            $traces = self::purgeTraces($traces, __CLASS__);
0 ignored issues
show
Unused Code introduced by
The call to HexMakina\Debugger\Debugger::purgeTraces() has too many arguments starting with __CLASS__. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

23
            /** @scrutinizer ignore-call */ 
24
            $traces = self::purgeTraces($traces, __CLASS__);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
24
25
            $message = self::toHTML($dump, $var_name, $traces, $full_backtrace);
26
27
            self::displayErrors($message);
28
            return $var;
29
        }
30
31
        // should we display something ?
32
        public static function displayErrors($error_message = null)
33
        {
34
            if (!empty($error_message) && ini_get('display_errors') == '1') {
35
                echo $error_message;
36
            }
37
        }
38
39
        private static function purgeTraces($traces)
40
        {
41
            $purged = [];
42
          // removes all internal calls
43
            foreach ($traces as $i => $trace) {
44
                if (empty($traces[$i]['class']) || !in_array($traces[$i]['class'], self::$skip_classes)) {
45
                    $purged[$i] = $trace;
46
                }
47
            }
48
49
            return $purged;
50
        }
51
52
        // -- formatting
53
        public static function toText($var_dump, $var_name, $backtrace, $full_backtrace)
54
        {
55
            return PHP_EOL
56
            . "******* "
57
            . (empty($var_name) ? $backtrace[1]['function'] . '()' : " ($var_name) ")
58
            . " *******"
59
            . PHP_EOL
60
            . self::tracesToString($backtrace, $full_backtrace)
61
            . PHP_EOL
62
            . $var_dump;
63
        }
64
65
        public static function toHTML($var_dump, $var_name, $backtrace, $full_backtrace)
66
        {
67
            $css = [
68
            'text-align:left',
69
            'z-index:9999',
70
            'background-color:#FFF',
71
            'color:#000',
72
            'padding:0.5em',
73
            'font-size:0.7em',
74
            'margin:0 0 1em 0',
75
            'font-family:courier'
76
            ];
77
78
            return sprintf(
79
                '<pre style="%s">%s</pre>',
80
                implode(';', $css),
81
                self::toText($var_dump, $var_name, $backtrace, $full_backtrace)
82
            );
83
        }
84
        // -- formatting : first line of \Throwable-based error
85
        public static function formatThrowable(\Throwable $err)
86
        {
87
            return sprintf(
88
                '%s (%d) in file %s:%d' . PHP_EOL . '%s',
89
                get_class($err),
90
                $err->getCode(),
91
                self::formatFilename($err->getFile()),
92
                $err->getLine(),
93
                $err->getMessage()
94
            );
95
        }
96
97
        // reduce_file_depth_to allows for short filepath, cause it gets crazy sometimes
98
        private static function formatFilename($file, $reduce_file_depth_to = 5)
99
        {
100
            return implode('/', array_slice(explode('/', $file), -$reduce_file_depth_to, $reduce_file_depth_to));
101
        }
102
103
        // -- formatting : nice backtrace
104
        private static function tracesToString($traces, $full_backtrace)
105
        {
106
            $formated_traces = [];
107
108
            foreach ($traces as $depth => $trace) {
109
                if (!empty($trace_string = self::traceToString($trace))) {
110
                    $formated_traces [] = $trace_string;
111
                }
112
                if (!empty($formated_traces) && $full_backtrace === false) {
113
                    break;
114
                }
115
            }
116
117
            return implode(PHP_EOL, array_reverse($formated_traces));
118
        }
119
120
        // creates a dump according to variable type (Throwables & anything else)
121
        private static function dump($var): string
122
        {
123
            if ($var instanceof \Throwable) {
124
                return self::formatThrowable($var);
125
            }
126
127
            ob_start();
128
            var_dump($var);
129
            return ob_get_clean();
130
        }
131
132
        private static function traceToString($trace)
133
        {
134
            $function_name = $trace['function'] ?? '?';
135
            $class_name = $trace['class'] ?? '';
136
137
            if (self::isShortcutCall($function_name)) {
138
                $args = date_format(date_create(), 'ymd:his');
139
            } else {
140
                $args = self::traceArgsToString($trace['args'] ?? []);
141
            }
142
143
            $call_file = isset($trace['file']) ? basename($trace['file']) : '?';
144
            $call_line = $trace['line'] ?? '?';
145
146
            return sprintf(
147
                '[%-23.23s %3s]  %s%s(%s)',
148
                $call_file,
149
                $call_line,
150
                "$class_name::",
151
                $function_name,
152
                $args
153
            );
154
        }
155
156
        private static function traceArgsToString($trace_args)
157
        {
158
            $ret = [];
159
            foreach ($trace_args as $arg) {
160
                $ret[] = self::traceArgToString($arg);
161
            }
162
            return implode(', ', $ret);
163
        }
164
165
        private static function traceArgToString($arg)
166
        {
167
            $ret = 'unknown type';
168
169
            if (is_null($arg)) {
170
                $ret = 'NULL';
171
            } elseif (is_bool($arg)) {
172
                $ret = 'bool:' . ((int)$arg);
173
            } elseif (is_scalar($arg)) {
174
                $ret = $arg;
175
            } elseif (is_object($arg)) {
176
                $ret = get_class($arg);
177
            } elseif (is_array($arg)) {
178
                $ret = 'Array #' . count($arg);
179
            }
180
            return $ret;
181
        }
182
183
        private static function isShortcutCall($function_name): bool
184
        {
185
            return in_array($function_name, ['vd', 'dd','vdt', 'ddt']);
186
        }
187
    }
188
}
189
namespace
190
{
191
    use HexMakina\Debugger\Debugger;
192
193
    if (!function_exists('vd')) {
194
        function vd($var, $var_name = null)
195
        {
196
            Debugger::visualDump($var, $var_name, false);
197
        }
198
    }
199
    if (!function_exists('dd')) {
200
        function dd($var, $var_name = null)
201
        {
202
            Debugger::visualDump($var, $var_name, false);
203
            die;
204
        }
205
    }
206
    if (!function_exists('vdt')) {
207
        function vdt($var, $var_name = null)
208
        {
209
            Debugger::visualDump($var, $var_name, true);
210
        }
211
    }
212
    if (!function_exists('ddt')) {
213
        function ddt($var, $var_name = null)
214
        {
215
            Debugger::visualDump($var, $var_name, true);
216
            die;
217
        }
218
    }
219
}
220