These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | class DumpBacktrace |
||
4 | { |
||
5 | /** @var bool|string */ |
||
6 | protected static $removePathPrefix = true; |
||
7 | |||
8 | /** |
||
9 | * @param bool|string $remove |
||
10 | */ |
||
11 | public static function setRemovePathPrefix($remove) |
||
12 | { |
||
13 | static::$removePathPrefix = $remove; |
||
14 | } |
||
15 | |||
16 | /** |
||
17 | * @param int $offset |
||
18 | * @param int|null $limit |
||
19 | */ |
||
20 | public static function dump($offset = 0, $limit = null) |
||
21 | { |
||
22 | echo static::getDump(static::getBacktraces($offset + 1, $limit)); |
||
23 | } |
||
24 | |||
25 | /** |
||
26 | * @param int $offset |
||
27 | * @param int|null $limit |
||
28 | */ |
||
29 | public static function eDump($offset = 0, $limit = null) |
||
30 | { |
||
31 | static::dump($offset + 1, $limit); |
||
32 | exit(); |
||
1 ignored issue
–
show
|
|||
33 | } |
||
34 | |||
35 | /** |
||
36 | * @param int $offset |
||
37 | * @param int|null $limit |
||
38 | * @return array |
||
39 | */ |
||
40 | public static function getBacktraces($offset = 0, $limit = null) |
||
41 | { |
||
42 | if ($limit !== null) { |
||
43 | $limit += $offset; |
||
44 | } |
||
45 | |||
46 | $filteredBacktraces = []; |
||
47 | foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, $limit) as $dumpIndex => $backtrace) { |
||
48 | if ($dumpIndex > $offset) { |
||
49 | $filteredBacktrace = [ |
||
50 | 'file' => isset($backtrace['file']) ? $backtrace['file'] : null, |
||
51 | 'line' => isset($backtrace['line']) ? $backtrace['line'] : null, |
||
52 | ]; |
||
53 | if (isset($backtrace['class'])) { |
||
54 | $filteredBacktrace['call'] = |
||
55 | $backtrace['class'] . $backtrace['type'] . $backtrace['function'] . '()'; |
||
56 | } elseif (isset($backtrace['function'])) { |
||
57 | $filteredBacktrace['call'] = $backtrace['function'] . '()'; |
||
58 | } else { |
||
59 | $filteredBacktrace['call'] = '(Unknow call)'; |
||
60 | } |
||
61 | |||
62 | $filteredBacktraces[] = $filteredBacktrace; |
||
63 | } |
||
64 | } |
||
65 | |||
66 | return $filteredBacktraces; |
||
67 | } |
||
68 | |||
69 | /** |
||
70 | * @param array $backtraces |
||
71 | * @return string |
||
72 | */ |
||
73 | public static function getDump(array $backtraces) |
||
74 | { |
||
75 | $return = static::getStylesDump(); |
||
76 | $return .= static::getJavascriptDump(); |
||
77 | |||
78 | $return .= '<div class="steevanb-backtrace-container">'; |
||
79 | $return .= static::getCallerDump(); |
||
80 | |||
81 | $return .= ' |
||
82 | <table class="table-backtrace"> |
||
83 | <tr> |
||
84 | <th>#</th> |
||
85 | <th>File::Line</th> |
||
86 | <th>Call</th> |
||
87 | </tr> |
||
88 | '; |
||
89 | $previewPrefix = uniqid('steevanb_backtrace_preview'); |
||
90 | foreach ($backtraces as $index => $backtrace) { |
||
91 | $return .= static::getBacktraceDump($backtrace, $index, $previewPrefix); |
||
92 | } |
||
93 | $return .= '</table>'; |
||
94 | |||
95 | $return .= '</div>'; |
||
96 | |||
97 | return $return; |
||
98 | } |
||
99 | |||
100 | /** |
||
101 | * @return array|null |
||
102 | */ |
||
103 | protected static function getCaller() |
||
104 | { |
||
105 | $backtraces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 7); |
||
106 | $nextIsCaller = false; |
||
107 | $caller = null; |
||
108 | foreach ($backtraces as $backtrace) { |
||
109 | if ( |
||
110 | isset($backtrace['file']) |
||
111 | && strpos($backtrace['file'], __FILE__) !== false |
||
112 | ) { |
||
113 | $nextIsCaller = true; |
||
114 | } elseif ( |
||
115 | $nextIsCaller |
||
116 | && ( |
||
117 | (isset($backtrace['file']) && strpos($backtrace['file'], __FILE__) === false) |
||
118 | || isset($backtrace['file']) === false |
||
119 | ) |
||
120 | ) { |
||
121 | $caller = $backtrace; |
||
122 | break; |
||
123 | } |
||
124 | } |
||
125 | if ($nextIsCaller === false && count($backtraces) > 0) { |
||
126 | $caller = $backtraces[0]; |
||
127 | } |
||
128 | |||
129 | return $caller; |
||
130 | } |
||
131 | |||
132 | /** |
||
133 | * @return string |
||
134 | */ |
||
135 | protected static function getStylesDump() |
||
136 | { |
||
137 | return ' |
||
138 | <style type="text/css"> |
||
139 | .steevanb-backtrace-container { |
||
140 | padding: 5px; |
||
141 | border: solid 2px #9e9e9e; |
||
142 | background-color: #F5F5F5; |
||
143 | cursor: default; |
||
144 | font-family: monospace; |
||
145 | } |
||
146 | .steevanb-backtrace-container table { |
||
147 | border-collapse: collapse; |
||
148 | } |
||
149 | .steevanb-backtrace-container table.table-backtrace tr.dark { |
||
150 | background-color: #e5e5e5; |
||
151 | } |
||
152 | .steevanb-backtrace-container table.table-backtrace td { |
||
153 | padding: 2px !important; |
||
154 | } |
||
155 | .steevanb-backtrace-container table.table-backtrace td a, |
||
156 | .steevanb-backtrace-container table.table-backtrace td a:hover, |
||
157 | .steevanb-backtrace-container table.table-backtrace td a:visited |
||
158 | { |
||
159 | color: #4e7ca9 !important; |
||
160 | text-decoration: none !important; |
||
161 | cursor: pointer !important; |
||
162 | } |
||
163 | .steevanb-backtrace-container table.table-backtrace td a:hover { |
||
164 | text-decoration: underline !important; |
||
165 | } |
||
166 | </style>'; |
||
167 | } |
||
168 | |||
169 | /** |
||
170 | * @return string |
||
171 | */ |
||
172 | protected static function getJavascriptDump() |
||
173 | { |
||
174 | return ' |
||
175 | <script type="text/javascript"> |
||
176 | function steevanb_dev_showCodePreview(id) |
||
177 | { |
||
178 | var element = document.getElementById(id); |
||
179 | element.style.display = (element.style.display === "none") ? "" : "none"; |
||
180 | } |
||
181 | </script>'; |
||
182 | } |
||
183 | |||
184 | /** |
||
185 | * @param array $backtrace |
||
186 | * @param int $index |
||
187 | * @param string $previewPrefix |
||
188 | * @return string |
||
189 | */ |
||
190 | protected static function getBacktraceDump(array $backtrace, $index, $previewPrefix) |
||
191 | { |
||
192 | if (isset($backtrace['file'])) { |
||
193 | $file = basename($backtrace['file']); |
||
194 | $filePath = $backtrace['file']; |
||
195 | $fileFound = true; |
||
196 | } else { |
||
197 | $filePath = null; |
||
198 | $fileFound = false; |
||
199 | } |
||
200 | |||
201 | if (isset($backtrace['line'])) { |
||
202 | $line = $backtrace['line']; |
||
203 | $lineFound = true; |
||
204 | } else { |
||
205 | $lineFound = false; |
||
206 | } |
||
207 | |||
208 | if ($fileFound === false && $lineFound === false) { |
||
209 | $fileLineHtml = '\Closure'; |
||
210 | } else { |
||
211 | $previewId = $previewPrefix . '_' . $index; |
||
212 | $codePreview = static::getCodePreview($filePath, $line); |
||
213 | $fileLineHtml = ' |
||
214 | <a |
||
215 | title="' . static::getFilePath($filePath) . '" |
||
216 | onclick="steevanb_dev_showCodePreview(\'' . $previewId . '\')" |
||
217 | > |
||
218 | ' . $file . '::' . $line . ' |
||
219 | </a> |
||
220 | '; |
||
221 | } |
||
222 | |||
223 | $html = ' |
||
224 | <tr' . ($index % 2 ? null : ' class="dark"') . '> |
||
225 | <td>' . $index . '</td> |
||
226 | <td>' . $fileLineHtml . '</td> |
||
227 | <td>' . $backtrace['call'] . '</td> |
||
228 | </tr> |
||
229 | '; |
||
230 | if ($fileFound && $lineFound) { |
||
231 | $html .= ' |
||
232 | <tr' . ($index % 2 ? null : ' class="dark"') . ' id="' . $previewId . '" style="display: none"> |
||
233 | <td colspan="3"><pre>' . $codePreview . '</pre></td> |
||
234 | </tr> |
||
235 | '; |
||
236 | } |
||
237 | |||
238 | return $html; |
||
239 | } |
||
240 | |||
241 | /** |
||
242 | * @return string |
||
243 | */ |
||
244 | protected static function getCallerDump() |
||
245 | { |
||
246 | $caller = static::getCaller(); |
||
247 | |||
248 | $return = '<div style="padding: 5px; background-color: #78a1c9; color: white; font-weight: bold">'; |
||
249 | $header = null; |
||
250 | if (is_array($caller)) { |
||
251 | $header .= isset($caller['file']) ? static::getFilePath($caller['file']) : '(Unknow file)'; |
||
252 | $header .= isset($caller['line']) ? '::' . $caller['line'] : '::(Unknow line)'; |
||
253 | } else { |
||
254 | $header= 'Unkonw caller'; |
||
255 | } |
||
256 | $return .= $header; |
||
257 | $return .= '</div>'; |
||
258 | |||
259 | return $return; |
||
260 | } |
||
261 | |||
262 | /** |
||
263 | * @param string $code |
||
264 | * @return string |
||
265 | */ |
||
266 | protected static function highlightCode($code) |
||
267 | { |
||
268 | $highlight = highlight_string('<?php ' . $code, true); |
||
269 | $highlight = str_replace('><?php ', null, $highlight); |
||
270 | |||
271 | return $highlight; |
||
272 | } |
||
273 | |||
274 | /** |
||
275 | * @param string $file |
||
276 | * @param int $line |
||
277 | * @return string |
||
278 | */ |
||
279 | protected static function getCodePreview($file, $line) |
||
280 | { |
||
281 | $preview = []; |
||
282 | $lineMin = $line - 6; |
||
283 | $lineMax = $line + 4; |
||
284 | foreach (file($file) as $index => $codeLine) { |
||
285 | if ($index >= $lineMin && $index <= $lineMax) { |
||
286 | if ($index === $line - 1) { |
||
287 | $preview[] = '<span style="background-color: #7fd189">' . rtrim($codeLine) . '</span>'; |
||
288 | } else { |
||
289 | $preview[] = rtrim($codeLine); |
||
290 | } |
||
291 | } elseif ($index > $lineMax) { |
||
292 | break; |
||
293 | } |
||
294 | } |
||
295 | |||
296 | return implode('<br />', $preview); |
||
297 | } |
||
298 | |||
299 | /** |
||
300 | * @param string $path |
||
301 | * @return string |
||
302 | */ |
||
303 | protected static function getFilePath($path) |
||
304 | { |
||
305 | $path = realpath($path); |
||
306 | |||
307 | if (static::$removePathPrefix === false) { |
||
308 | $return = $path; |
||
309 | } else { |
||
310 | // assume that we are in vendor/ dir |
||
311 | $prefix = (static::$removePathPrefix === true) |
||
312 | ? realpath(__DIR__ . '/../../../') |
||
313 | : static::$removePathPrefix; |
||
314 | $return = (substr($path, 0, strlen($prefix)) === $prefix) ? substr($path, strlen($prefix) + 1) : $path; |
||
315 | } |
||
316 | |||
317 | return $return; |
||
318 | } |
||
319 | } |
||
320 |
An exit expression should only be used in rare cases. For example, if you write a short command line script.
In most cases however, using an
exit
expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.