1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the webmozart/console package. |
5
|
|
|
* |
6
|
|
|
* (c) Bernhard Schussek <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Webmozart\Console\UI\Component; |
13
|
|
|
|
14
|
|
|
use Exception; |
15
|
|
|
use Webmozart\Console\Api\IO\IO; |
16
|
|
|
use Webmozart\Console\UI\Component; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Renders the trace of an exception. |
20
|
|
|
* |
21
|
|
|
* @since 1.0 |
22
|
|
|
* |
23
|
|
|
* @author Bernhard Schussek <[email protected]> |
24
|
|
|
*/ |
25
|
|
|
class ExceptionTrace implements Component |
26
|
|
|
{ |
27
|
|
|
/** |
28
|
|
|
* @var Exception |
29
|
|
|
*/ |
30
|
|
|
private $exception; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Creates a renderer for the given exception. |
34
|
|
|
* |
35
|
|
|
* @param Exception $exception The exception to render. |
36
|
|
|
*/ |
37
|
6 |
|
public function __construct(Exception $exception) |
38
|
|
|
{ |
39
|
6 |
|
$this->exception = $exception; |
40
|
6 |
|
} |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* Renders the exception trace. |
44
|
|
|
* |
45
|
|
|
* @param IO $io The I/O. |
46
|
|
|
* @param int $indentation The number of spaces to indent. |
47
|
|
|
*/ |
48
|
6 |
|
public function render(IO $io, $indentation = 0) |
49
|
|
|
{ |
50
|
6 |
|
if (!$io->isVerbose()) { |
51
|
4 |
|
$io->errorLine('fatal: '.$this->exception->getMessage()); |
52
|
|
|
|
53
|
4 |
|
return; |
54
|
|
|
} |
55
|
|
|
|
56
|
2 |
|
$exception = $this->exception; |
57
|
|
|
|
58
|
2 |
|
$this->renderException($io, $exception); |
59
|
|
|
|
60
|
2 |
|
if ($io->isVeryVerbose()) { |
61
|
1 |
|
while ($exception = $exception->getPrevious()) { |
62
|
1 |
|
$io->errorLine('Caused by:'); |
63
|
|
|
|
64
|
1 |
|
$this->renderException($io, $exception); |
65
|
|
|
} |
66
|
|
|
} |
67
|
2 |
|
} |
68
|
|
|
|
69
|
2 |
|
private function renderException(IO $io, Exception $exception) |
70
|
|
|
{ |
71
|
2 |
|
$this->printBox($io, $exception); |
72
|
2 |
|
$this->printTrace($io, $exception); |
73
|
2 |
|
} |
74
|
|
|
|
75
|
2 |
|
private function printBox(IO $io, Exception $exception) |
76
|
|
|
{ |
77
|
2 |
|
$screenWidth = $io->getTerminalDimensions()->getWidth() - 1; |
78
|
2 |
|
$boxWidth = 0; |
79
|
|
|
|
80
|
2 |
|
$boxLines = array_merge( |
81
|
2 |
|
array(sprintf('[%s]', get_class($exception))), |
82
|
|
|
// TODO replace by implementation that is aware of format codes |
83
|
2 |
|
explode("\n", wordwrap($exception->getMessage(), $screenWidth - 4)) |
84
|
|
|
); |
85
|
|
|
|
86
|
2 |
|
foreach ($boxLines as $line) { |
87
|
2 |
|
$boxWidth = max($boxWidth, strlen($line)); |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
// TODO handle $boxWidth > $screenWidth |
91
|
2 |
|
$emptyLine = sprintf('<error>%s</error>', str_repeat(' ', $boxWidth + 4)); |
92
|
|
|
|
93
|
2 |
|
$io->errorLine(''); |
94
|
2 |
|
$io->errorLine(''); |
95
|
2 |
|
$io->errorLine($emptyLine); |
96
|
|
|
|
97
|
2 |
|
foreach ($boxLines as $boxLine) { |
98
|
2 |
|
$padding = str_repeat(' ', max(0, $boxWidth - strlen($boxLine))); |
99
|
2 |
|
$io->errorLine(sprintf('<error> %s%s </error>', $boxLine, $padding)); |
100
|
|
|
} |
101
|
|
|
|
102
|
2 |
|
$io->errorLine($emptyLine); |
103
|
2 |
|
$io->errorLine(''); |
104
|
2 |
|
$io->errorLine(''); |
105
|
2 |
|
} |
106
|
|
|
|
107
|
2 |
|
private function printTrace(IO $io, Exception $exception) |
108
|
|
|
{ |
109
|
2 |
|
$traces = $exception->getTrace(); |
110
|
2 |
|
$cwd = getcwd().DIRECTORY_SEPARATOR; |
111
|
2 |
|
$cwdLength = strlen($cwd); |
112
|
|
|
|
113
|
|
|
$lastTrace = array( |
114
|
2 |
|
'function' => '', |
115
|
|
|
'args' => array(), |
116
|
|
|
); |
117
|
|
|
|
118
|
2 |
|
if (null !== $exception->getFile()) { |
119
|
2 |
|
$lastTrace['file'] = $exception->getFile(); |
120
|
|
|
} |
121
|
|
|
|
122
|
2 |
|
if (null !== $exception->getLine()) { |
123
|
2 |
|
$lastTrace['line'] = $exception->getLine(); |
124
|
|
|
} |
125
|
|
|
|
126
|
2 |
|
array_unshift($traces, $lastTrace); |
127
|
|
|
|
128
|
2 |
|
$io->errorLine('<b>Exception trace:</b>'); |
129
|
|
|
|
130
|
2 |
|
foreach ($traces as $trace) { |
131
|
2 |
|
$namespace = ''; |
132
|
2 |
|
$class = ''; |
133
|
2 |
|
$location = 'n/a'; |
134
|
|
|
|
135
|
2 |
|
if (isset($trace['class'])) { |
136
|
2 |
|
if (false !== $pos = strrpos($trace['class'], '\\')) { |
137
|
2 |
|
$namespace = substr($trace['class'], 0, $pos + 1); |
138
|
2 |
|
$class = substr($trace['class'], $pos + 1); |
139
|
|
|
} else { |
140
|
2 |
|
$class = $trace['class']; |
141
|
|
|
} |
142
|
|
|
} |
143
|
|
|
|
144
|
2 |
|
if (isset($trace['file'])) { |
145
|
2 |
|
if (0 === strpos($trace['file'], $cwd)) { |
146
|
2 |
|
$location = substr($trace['file'], $cwdLength); |
147
|
|
|
} else { |
148
|
|
|
$location = $trace['file']; |
149
|
|
|
} |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
// class, operator, function |
|
|
|
|
153
|
2 |
|
$signature = $class.(isset($trace['type']) ? $trace['type'] : '').$trace['function']; |
154
|
2 |
|
$location .= ':'.(isset($trace['line']) ? $trace['line'] : 'n/a'); |
155
|
|
|
|
156
|
2 |
|
$io->errorLineRaw(sprintf(' %s%s()', $namespace, $io->format('<u>'.$signature.'</u>'))); |
157
|
2 |
|
$io->errorLineRaw(sprintf(' %s', $io->format('<c1>'.$location.'</c1>'))); |
158
|
|
|
} |
159
|
|
|
|
160
|
2 |
|
$io->errorLine(''); |
161
|
2 |
|
$io->errorLine(''); |
162
|
2 |
|
} |
163
|
|
|
} |
164
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.