Passed
Push — main ( e17d1e...7e8eba )
by Sammy
02:12
created

Debugger::traceToString()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 25
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

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