Passed
Push — stable ( 5e8422...b056b1 )
by Nuno
04:25
created

Writer::ignoreFilesIn()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This file is part of Collision.
5
 *
6
 * (c) Nuno Maduro <[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 NunoMaduro\Collision;
13
14
use Whoops\Exception\Frame;
15
use Whoops\Exception\FrameCollection;
16
use Whoops\Exception\Inspector;
17
use Symfony\Component\Console\Output\ConsoleOutput;
18
use Symfony\Component\Console\Output\OutputInterface;
19
use NunoMaduro\Collision\Contracts\Writer as WriterContract;
20
use NunoMaduro\Collision\Contracts\ArgumentFormatter as ArgumentFormatterContract;
21
22
/**
23
 * This is an Collision Writer implementation.
24
 *
25
 * @author Nuno Maduro <[email protected]>
26
 */
27
class Writer implements WriterContract
28
{
29
    /**
30
     * The number of frames if no verbosity is specified.
31
     */
32
    const VERBOSITY_NORMAL_FRAMES = 1;
33
34
    /**
35
     * Holds an instance of the Output.
36
     *
37
     * @var \Symfony\Component\Console\Output\OutputInterface
38
     */
39
    protected $output;
40
41
    /**
42
     * Holds an instance of the Argument Formatter.
43
     *
44
     * @var \NunoMaduro\Collision\Contracts\ArgumentFormatter
45
     */
46
    protected $argumentFormatter;
47
48
    /**
49
     * Ignores traces where the file string matches one
50
     * of the provided regex expressions.
51
     *
52
     * @var string[]
53
     */
54
    protected $ignore = [];
55
56
    /**
57
     * Declares whether or not the trace should appear.
58
     *
59
     * @var bool
60
     */
61
    protected $showTrace = true;
62
63
    /**
64
     * Creates an instance of the writer.
65
     *
66
     * @param \Symfony\Component\Console\Output\OutputInterface|null $output
67
     * @param \NunoMaduro\Collision\Contracts\ArgumentFormatter|null $argumentFormatter
68
     */
69
    public function __construct(OutputInterface $output = null, ArgumentFormatterContract $argumentFormatter = null)
70
    {
71
        $this->output = $output ?: new ConsoleOutput;
72
        $this->argumentFormatter = $argumentFormatter ?: new ArgumentFormatter;
73
    }
74
75
    /**
76
     * {@inheritdoc}
77
     */
78
    public function write(Inspector $inspector): void
79
    {
80
        $this->renderTitle($inspector);
81
82
        $frames = $this->getFrames($inspector);
83
        $this->renderEditor(array_shift($frames));
84
85
        if ($this->showTrace && ! empty($frames)) {
86
            $this->renderTrace($frames);
87
        } else {
88
            $this->output->writeln('');
89
        }
90
    }
91
92
    /**
93
     * {@inheritdoc}
94
     */
95
    public function ignoreFilesIn(array $ignore): WriterContract
96
    {
97
        $this->ignore = $ignore;
98
99
        return $this;
100
    }
101
102
    /**
103
     * {@inheritdoc}
104
     */
105
    public function showTrace(bool $show): WriterContract
106
    {
107
        $this->showTrace = $show;
108
109
        return $this;
110
    }
111
112
    /**
113
     * {@inheritdoc}
114
     */
115
    public function setOutput(OutputInterface $output): WriterContract
116
    {
117
        $this->output = $output;
118
119
        return $this;
120
    }
121
122
    /**
123
     * Returns pertinent frames.
124
     *
125
     * @param  \Whoops\Exception\Inspector $inspector
126
     *
127
     * @return array
128
     */
129
    protected function getFrames(Inspector $inspector): array
130
    {
131
        return $inspector->getFrames()
132
            ->filter(
133
                function ($frame) {
134
135
                    foreach ($this->ignore as $ignore) {
136
                        if (preg_match($ignore, $frame->getFile())) {
137
                            return false;
138
                        }
139
                    }
140
141
                    return true;
142
                }
143
            )
144
            ->getArray();
145
    }
146
147
    /**
148
     * Renders the title of the exception.
149
     *
150
     * @param \Whoops\Exception\Inspector $inspector
151
     *
152
     * @return \NunoMaduro\Collision\Contracts\Writer
153
     */
154
    protected function renderTitle(Inspector $inspector): WriterContract
155
    {
156
        $exception = $inspector->getException();
157
        $message = $exception->getMessage();
158
        $class = $inspector->getExceptionName();
159
160
        $this->render("<bg=red;options=bold>$class</> : <comment>$message</>");
161
162
        return $this;
163
    }
164
165
    /**
166
     * Renders the editor containing the code that was the
167
     * origin of the exception.
168
     *
169
     * @param \Whoops\Exception\Frame $frame
170
     *
171
     * @return \NunoMaduro\Collision\Contracts\Writer
172
     */
173
    protected function renderEditor(Frame $frame): WriterContract
174
    {
175
        $this->render('at <fg=green>'.$frame->getFile().'</>'.': <fg=green>'.$frame->getLine().'</>');
176
177
        $range = $frame->getFileLines($frame->getLine() - 5, 10);
178
179
        foreach ($range as $k => $code) {
180
            $line = $k + 1;
181
            $code = $line === $frame->getLine() ? "<bg=red>$code</>" : $code;
182
            $this->render("$line: $code", false);
183
        }
184
185
        return $this;
186
    }
187
188
    /**
189
     * Renders the trace of the exception.
190
     *
191
     * @param  array $frames
192
     *
193
     * @return \NunoMaduro\Collision\Contracts\Writer
194
     */
195
    protected function renderTrace(array $frames): WriterContract
196
    {
197
        $this->render('<comment>Exception trace:</comment>');
198
        foreach ($frames as $i => $frame) {
199
            if ($i > static::VERBOSITY_NORMAL_FRAMES && $this->output->getVerbosity(
200
                ) < OutputInterface::VERBOSITY_VERBOSE) {
201
                $this->render('<info>Please use the argument <fg=red>-v</> to see all trace.</info>');
202
                break;
203
            }
204
205
            $file = $frame->getFile();
206
            $line = $frame->getLine();
207
            $class = empty($frame->getClass()) ? '' : $frame->getClass().'::';
208
            $function = $frame->getFunction();
209
            $args = $this->argumentFormatter->format($frame->getArgs());
210
            $pos = str_pad($i + 1, 4, ' ');
211
212
            $this->render("<comment><fg=cyan>$pos</>$class$function($args)</comment>");
213
            $this->render("    <fg=green>$file</> : <fg=green>$line</>", false);
214
        }
215
216
        return $this;
217
    }
218
219
    /**
220
     * Renders an message into the console.
221
     *
222
     * @param  string $message
223
     * @param  bool $break
224
     *
225
     * @return $this
226
     */
227
    protected function render(string $message, bool $break = true): WriterContract
228
    {
229
        if ($break) {
230
            $this->output->writeln('');
231
        }
232
233
        $this->output->writeln("  $message");
234
235
        return $this;
236
    }
237
}
238