DebugBacktraceHtml   A
last analyzed

Complexity

Total Complexity 28

Size/Duplication

Total Lines 229
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
wmc 28
lcom 1
cbo 1
dl 0
loc 229
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A dump() 0 4 1
A eDump() 0 5 1
A getDump() 0 26 2
A getStyles() 0 39 1
A getJavascript() 0 11 1
C getBacktraceDump() 0 69 12
A getCallerDump() 0 17 4
A getCodePreview() 0 19 6
1
<?php
2
3
class DebugBacktraceHtml extends \DebugBacktrace
4
{
5
    /** @var ?string */
6
    protected static $callerRemoveFile = __FILE__;
7
8
    /**
9
     * @param int $offset
10
     * @param int|null $limit
11
     */
12
    public static function dump($offset = 0, $limit = null)
13
    {
14
        echo static::getDump(static::getBacktraces($offset + 1, $limit));
15
    }
16
17
    /**
18
     * @param int $offset
19
     * @param int|null $limit
20
     */
21
    public static function eDump($offset = 0, $limit = null)
22
    {
23
        static::dump($offset + 1, $limit);
24
        exit();
25
    }
26
27
    /**
28
     * @param array $backtraces
29
     * @return string
30
     */
31
    public static function getDump(array $backtraces)
32
    {
33
        $return = static::getStyles();
34
        $return .= static::getJavascript();
35
36
        $return .= '<div class="steevanb-backtrace-container">';
37
        $return .= static::getCallerDump();
38
39
        $return .= '
40
            <table class="table-backtrace">
41
                <tr>
42
                    <th>#</th>
43
                    <th>File#Line</th>
44
                    <th>Call</th>
45
                </tr>
46
        ';
47
        $previewPrefix = uniqid('steevanb_backtrace_preview');
48
        foreach ($backtraces as $index => $backtrace) {
49
            $return .= static::getBacktraceDump($backtrace, $index, $previewPrefix);
50
        }
51
        $return .= '</table>';
52
53
        $return .= '</div>';
54
55
        return $return;
56
    }
57
58
    /** @return string */
59
    protected static function getStyles()
60
    {
61
        return '
62
            <style type="text/css">
63
                .steevanb-backtrace-container {
64
                    padding: 3px;
65
                    border: solid 2px #9e9e9e;
66
                    background-color: #F5F5F5;
67
                    cursor: default;
68
                    font-family: monospace;
69
                }
70
                .steevanb-backtrace-container table {
71
                    border-collapse: collapse;
72
                }
73
                .steevanb-backtrace-container table.table-backtrace tr.dark {
74
                    background-color: #e5e5e5;
75
                }
76
                .steevanb-backtrace-container table.table-backtrace td {
77
                    padding: 2px !important;
78
                }
79
                .steevanb-backtrace-container table.table-backtrace td a,
80
                .steevanb-backtrace-container table.table-backtrace td a:hover,
81
                .steevanb-backtrace-container table.table-backtrace td a:visited
82
                {
83
                    color: #4e7ca9 !important;
84
                    text-decoration: none !important;
85
                    cursor: pointer !important;
86
                }
87
                .steevanb-backtrace-container table.table-backtrace td a:hover {
88
                    text-decoration: underline !important;
89
                }
90
                .steevanb-backtrace-caller {
91
                    padding: 3px;
92
                    background-color: #78a1c9;
93
                    color: white;
94
                    font-weight: bold;
95
                }
96
            </style>';
97
    }
98
99
    /** @return string */
100
    protected static function getJavascript()
101
    {
102
        return '
103
            <script type="text/javascript">
104
                function steevanb_dev_showCodePreview(id)
105
                {
106
                    var element = document.getElementById(id);
107
                    element.style.display = (element.style.display === "none") ? "" : "none";
108
                }
109
            </script>';
110
    }
111
112
    /**
113
     * @param array $backtrace
114
     * @param int $index
115
     * @param string $previewPrefix
116
     * @return string
117
     */
118
    protected static function getBacktraceDump(array $backtrace, $index, $previewPrefix)
119
    {
120
        $filePath = null;
121
        $line = null;
122
        $previewId = $previewPrefix . '_' . $index;
123
124
        if ($backtrace['file'] !== null) {
125
            if (file_exists($backtrace['file'])) {
126
                $filePath = $backtrace['file'];
127
                $fileFound = true;
128
129
                if ($backtrace['line'] !== null) {
130
                    $line = $backtrace['line'];
131
                    $lineFound = true;
132
                } else {
133
                    $lineFound = false;
134
                }
135
            } elseif (substr($backtrace['file'], -16) === ' : eval()\'d code') {
136
                $fileAndLine = substr($backtrace['file'], 0, -16);
137
                $filePath = substr($fileAndLine, 0, strrpos($fileAndLine, '('));
138
                if (file_exists($filePath)) {
139
                    $line = substr($fileAndLine, strrpos($fileAndLine, '(') + 1, -1);
140
                    $fileFound = true;
141
                    $lineFound = true;
142
                } else {
143
                    $fileFound = false;
144
                    $lineFound = false;
145
                }
146
            } else {
147
                $fileFound = false;
148
                $lineFound = false;
149
            }
150
        } else {
151
            $fileFound = false;
152
            $lineFound = false;
153
        }
154
155
        if ($fileFound === false && $lineFound === false) {
156
            $fileLineHtml = '\Closure';
157
            $codePreview = null;
158
        } else {
159
            $codePreview = static::getCodePreview($filePath, $line);
160
            $fileLineHtml = '
161
                <a
162
                    title="' . static::getFilePath($filePath) . '"
163
                    onclick="steevanb_dev_showCodePreview(\'' . $previewId . '\')"
164
                >
165
                    ' . basename($filePath) . '#' . $line . '
166
                </a>
167
            ';
168
        }
169
170
        $html = '
171
            <tr' . ($index % 2 ? null : ' class="dark"') . '>
172
                <td>' . ($index + 1) . '</td>
173
                <td>' . $fileLineHtml . '</td>
174
                <td>' . $backtrace['call'] . '</td>
175
            </tr>
176
        ';
177
        if ($fileFound && $lineFound) {
178
            $html .= '
179
                <tr' . ($index % 2 ? null : ' class="dark"') . ' id="' . $previewId . '" style="display: none">
180
                    <td colspan="3"><pre>' . $codePreview . '</pre></td>
181
                </tr>
182
            ';
183
        }
184
185
        return $html;
186
    }
187
188
    /** @return string */
189
    protected static function getCallerDump()
190
    {
191
        $caller = static::getCaller();
192
193
        $return = '<div class="steevanb-backtrace-caller">';
194
        $header = null;
195
        if (is_array($caller)) {
196
            $header .= isset($caller['file']) ? static::getFilePath($caller['file']) : '(Unknow file)';
197
            $header .= isset($caller['line']) ? '#' . $caller['line'] : '::(Unknow line)';
198
        } else {
199
            $header= 'Unkonw caller';
200
        }
201
        $return .= $header;
202
        $return .= '</div>';
203
204
        return $return;
205
    }
206
207
    /**
208
     * @param string $file
209
     * @param int $line
210
     * @return string
211
     */
212
    protected static function getCodePreview($file, $line)
213
    {
214
        $preview = [];
215
        $lineMin = $line - 6;
216
        $lineMax = $line + 4;
217
        foreach (file($file) as $index => $codeLine) {
218
            if ($index >= $lineMin && $index <= $lineMax) {
219
                if ($index === $line - 1) {
220
                    $preview[] = '<span style="background-color: #7fd189">' . rtrim($codeLine) . '</span>';
221
                } else {
222
                    $preview[] = rtrim($codeLine);
223
                }
224
            } elseif ($index > $lineMax) {
225
                break;
226
            }
227
        }
228
229
        return implode('<br />', $preview);
230
    }
231
}
232