Completed
Push — master ( f6cf33...b7d869 )
by Steevan
05:23 queued 02:44
created

DumpBacktrace.php (5 issues)

Upgrade to new PHP Analysis Engine

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
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
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::getBacktracesDump(static::getBacktraces($offset + 1, $limit));
23
    }
24
25
    /**
26
     * @param int $offset
27
     * @param int|null $limit
28
     * @return array
29
     */
30
    public static function getBacktraces($offset = 0, $limit = null)
31
    {
32
        if ($limit !== null) {
33
            $limit += $offset;
34
        }
35
36
        $filteredBacktraces = [];
37
        foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, $limit) as $dumpIndex => $backtrace) {
38
            if ($dumpIndex > $offset) {
39
                $filteredBacktrace = [
40
                    'file' => isset($backtrace['file']) ? $backtrace['file'] : null,
41
                    'line' => isset($backtrace['line']) ? $backtrace['line'] : null,
42
                ];
43
                if (isset($backtrace['class'])) {
44
                    $filteredBacktrace['call'] =
45
                        $backtrace['class'] . $backtrace['type'] . $backtrace['function'] . '()';
46
                } elseif (isset($backtrace['function'])) {
47
                    $filteredBacktrace['call'] = $backtrace['function'] . '()';
48
                } else {
49
                    $filteredBacktrace['call'] = '(Unknow call)';
50
                }
51
52
                $filteredBacktraces[] = $filteredBacktrace;
53
            }
54
        }
55
56
        return $filteredBacktraces;
57
    }
58
59
    /**
60
     * @return array|null
61
     */
62
    protected static function getCaller()
63
    {
64
        $backtraces = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 7);
65
        $nextIsCaller = false;
66
        $caller = null;
67
        foreach ($backtraces as $backtrace) {
68
            if (
69
                isset($backtrace['file'])
70
                && strpos($backtrace['file'], __FILE__) !== false
71
            ) {
72
                $nextIsCaller = true;
73
            } elseif (
74
                $nextIsCaller
75
                && (
76
                    (isset($backtrace['file']) && strpos($backtrace['file'], __FILE__) === false)
77
                    || isset($backtrace['file']) === false
78
                )
79
            ) {
80
                $caller = $backtrace;
81
                break;
82
            }
83
        }
84
        if ($nextIsCaller === false && count($backtraces) > 0) {
85
            $caller = $backtraces[0];
86
        }
87
88
        return $caller;
89
    }
90
91
    /**
92
     * @param array $backtraces
93
     * @return string
94
     */
95
    protected static function getBacktracesDump(array $backtraces)
96
    {
97
        $return = static::getStylesDump();
98
        $return .= static::getJavascriptDump();
99
100
        $return .= '<div class="steevanb-backtrace-container">';
101
        $return .= static::getCallerDump();
102
103
        $return .= '
104
            <table class="table-backtrace">
105
                <tr>
106
                    <th>#</th>
107
                    <th>File::Line</th>
108
                    <th>Call</th>
109
                </tr>
110
        ';
111
        $previewPrefix = uniqid('steevanb_backtrace_preview');
112
        foreach ($backtraces as $index => $backtrace) {
113
            $return .= static::getBacktraceDump($backtrace, $index, $previewPrefix);
114
        }
115
        $return .= '</table>';
116
117
        $return .= '</div>';
118
119
        return $return;
120
    }
121
122
    /**
123
     * @return string
124
     */
125
    protected static function getStylesDump()
126
    {
127
        return '
128
            <style type="text/css">
129
                .steevanb-backtrace-container {
130
                    padding: 5px;
131
                    border: solid 2px #9e9e9e;
132
                    background-color: #F5F5F5;
133
                    cursor: default;
134
                    font-family: monospace;
135
                }
136
                .steevanb-backtrace-container table {
137
                    border-collapse: collapse;
138
                }
139
                .steevanb-backtrace-container table.table-backtrace tr.dark {
140
                    background-color: #e5e5e5;
141
                }
142
                .steevanb-backtrace-container table.table-backtrace td {
143
                    padding: 2px !important;
144
                }
145
                .steevanb-backtrace-container table.table-backtrace td a,
146
                .steevanb-backtrace-container table.table-backtrace td a:hover,
147
                .steevanb-backtrace-container table.table-backtrace td a:visited
148
                {
149
                    color: #4e7ca9 !important;
150
                    text-decoration: none !important;
151
                    cursor: pointer !important;
152
                }
153
                .steevanb-backtrace-container table.table-backtrace td a:hover {
154
                    text-decoration: underline !important;
155
                }
156
            </style>';
157
    }
158
159
    /**
160
     * @return string
161
     */
162
    protected static function getJavascriptDump()
163
    {
164
        return '
165
            <script type="text/javascript">
166
                function steevanb_dev_showCodePreview(id)
167
                {
168
                    var element = document.getElementById(id);
169
                    element.style.display = (element.style.display === "none") ? "" : "none";
170
                }
171
            </script>';
172
    }
173
174
    /**
175
     * @param array $backtrace
176
     * @param int $index
177
     * @param string $previewPrefix
178
     * @return string
179
     */
180
    protected static function getBacktraceDump(array $backtrace, $index, $previewPrefix)
181
    {
182
        if (isset($backtrace['file'])) {
183
            $file = basename($backtrace['file']);
184
            $filePath = $backtrace['file'];
185
            $fileFound = true;
186
        } else {
187
            $filePath = null;
188
            $fileFound = false;
189
        }
190
191
        if (isset($backtrace['line'])) {
192
            $line = $backtrace['line'];
193
            $lineFound = true;
194
        } else {
195
            $lineFound = false;
196
        }
197
198
        if ($fileFound === false && $lineFound === false) {
199
            $fileLineHtml = '\Closure';
200
        } else {
201
            $previewId = $previewPrefix . '_' . $index;
202
            $codePreview = static::getCodePreview($filePath, $line);
0 ignored issues
show
The variable $line does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
203
            $fileLineHtml = '
204
                <a
205
                    title="' . static::getFilePath($filePath) . '"
206
                    onclick="steevanb_dev_showCodePreview(\'' . $previewId . '\')"
207
                >
208
                    ' . $file . '::' . $line . '
0 ignored issues
show
The variable $file does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
209
                </a>
210
            ';
211
        }
212
213
        $html = '
214
            <tr' . ($index % 2 ? null : ' class="dark"') . '>
215
                <td>' . $index . '</td>
216
                <td>' . $fileLineHtml . '</td>
217
                <td>' . $backtrace['call'] . '</td>
218
            </tr>
219
        ';
220
        if ($fileFound && $lineFound) {
221
            $html .= '
222
                <tr' . ($index % 2 ? null : ' class="dark"') . ' id="' . $previewId . '" style="display: none">
0 ignored issues
show
The variable $previewId does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
223
                    <td colspan="3"><pre>' . $codePreview . '</pre></td>
0 ignored issues
show
The variable $codePreview does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
224
                </tr>
225
            ';
226
        }
227
228
        return $html;
229
    }
230
231
    /**
232
     * @return string
233
     */
234
    protected static function getCallerDump()
235
    {
236
        $caller = static::getCaller();
237
238
        $return = '<div style="padding: 5px; background-color: #78a1c9; color: white; font-weight: bold">';
239
        $header = null;
240
        if (is_array($caller)) {
241
            $header .= isset($caller['file']) ? static::getFilePath($caller['file']) : '(Unknow file)';
242
            $header .= isset($caller['line']) ? '::' . $caller['line'] : '::(Unknow line)';
243
        } else {
244
            $header= 'Unkonw caller';
245
        }
246
        $return .= $header;
247
        $return .= '</div>';
248
249
        return $return;
250
    }
251
252
    /**
253
     * @param string $code
254
     * @return string
255
     */
256
    protected static function highlightCode($code)
257
    {
258
        $highlight = highlight_string('<?php ' . $code, true);
259
        $highlight = str_replace('>&lt;?php&nbsp;', null, $highlight);
260
261
        return $highlight;
262
    }
263
264
    /**
265
     * @param string $file
266
     * @param int $line
267
     * @return string
268
     */
269
    protected static function getCodePreview($file, $line)
270
    {
271
        $preview = [];
272
        $lineMin = $line - 6;
273
        $lineMax = $line + 4;
274
        foreach (file($file) as $index => $codeLine) {
275
            if ($index >= $lineMin && $index <= $lineMax) {
276
                if ($index === $line - 1) {
277
                    $preview[] = '<span style="background-color: #7fd189">' . rtrim($codeLine) . '</span>';
278
                } else {
279
                    $preview[] = rtrim($codeLine);
280
                }
281
            } elseif ($index > $lineMax) {
282
                break;
283
            }
284
        }
285
286
        return implode('<br />', $preview);
287
    }
288
289
    /**
290
     * @param string $path
291
     * @return string
292
     */
293
    protected static function getFilePath($path)
294
    {
295
        $path = realpath($path);
296
297
        if (static::$removePathPrefix === false) {
298
            $return = $path;
299
        } else {
300
            // assume that we are in vendor/ dir
301
            $prefix = (static::$removePathPrefix === true)
302
                ? realpath(__DIR__ . '/../../../')
303
                : static::$removePathPrefix;
304
            $return = (substr($path, 0, strlen($prefix)) === $prefix) ? substr($path, strlen($prefix) + 1) : $path;
305
        }
306
307
        return $return;
308
    }
309
}
310