Completed
Push — separate-soap-handler ( aa8a6b...a6aaf5 )
by Denis
62:14 queued 01:08
created

PlainTextHandler   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 250
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 84.81%

Importance

Changes 4
Bugs 2 Features 1
Metric Value
wmc 27
c 4
b 2
f 1
lcom 1
cbo 4
dl 0
loc 250
ccs 67
cts 79
cp 0.8481
rs 10

12 Methods

Rating   Name   Duplication   Size   Complexity  
A getTraceFunctionArgsOutputLimit() 0 4 1
A __construct() 0 4 1
A setLogger() 0 13 3
A getLogger() 0 4 1
A addTraceToOutput() 0 9 2
A addTraceFunctionArgsToOutput() 0 12 3
A setTraceFunctionArgsOutputLimit() 0 4 1
A loggerOnly() 0 8 2
A canOutput() 0 4 1
B getFrameArgsOutput() 0 25 4
B getTraceOutput() 0 36 4
B handle() 0 28 4
1
<?php
2
/**
3
* Whoops - php errors for cool kids
4
* @author Filipe Dobreira <http://github.com/filp>
5
* Plaintext handler for command line and logs.
6
* @author Pierre-Yves Landuré <https://howto.biapy.com/>
7
*/
8
9
namespace Whoops\Handler;
10
11
use InvalidArgumentException;
12
use Psr\Log\LoggerInterface;
13
use Whoops\Exception\Frame;
14
15
/**
16
* Handler outputing plaintext error messages. Can be used
17
* directly, or will be instantiated automagically by Whoops\Run
18
* if passed to Run::pushHandler
19
*/
20
class PlainTextHandler extends Handler
21
{
22
    const VAR_DUMP_PREFIX = '   | ';
23
24
    /**
25
     * @var \Psr\Log\LoggerInterface
26
     */
27
    protected $logger;
28
29
    /**
30
     * @var bool
31
     */
32
    private $addTraceToOutput = true;
33
34
    /**
35
     * @var bool|integer
36
     */
37
    private $addTraceFunctionArgsToOutput = false;
38
39
    /**
40
     * @var integer
41
     */
42
    private $traceFunctionArgsOutputLimit = 1024;
43
44
    /**
45
     * @var bool
46
     */
47
    private $loggerOnly = false;
48
49
    /**
50
     * Constructor.
51
     * @throws InvalidArgumentException     If argument is not null or a LoggerInterface
52
     * @param  \Psr\Log\LoggerInterface|null $logger
53
     */
54
    public function __construct($logger = null)
55
    {
56
        $this->setLogger($logger);
57
    }
58
59
    /**
60
     * Set the output logger interface.
61
     * @throws InvalidArgumentException     If argument is not null or a LoggerInterface
62
     * @param  \Psr\Log\LoggerInterface|null $logger
63
     */
64 1
    public function setLogger($logger = null)
65
    {
66 1
        if (! (is_null($logger)
67
            || $logger instanceof LoggerInterface)) {
68
            throw new InvalidArgumentException(
69
                'Argument to ' . __METHOD__ .
70
                " must be a valid Logger Interface (aka. Monolog), " .
71
                get_class($logger) . ' given.'
72
            );
73
        }
74 2
75
        $this->logger = $logger;
76 2
    }
77 2
78 2
    /**
79 2
     * @return \Psr\Log\LoggerInterface|null
80 2
     */
81 2
    public function getLogger()
82 2
    {
83
        return $this->logger;
84
    }
85 1
86 1
    /**
87
     * Add error trace to output.
88
     * @param  bool|null  $addTraceToOutput
89
     * @return bool|$this
90
     */
91
    public function addTraceToOutput($addTraceToOutput = null)
92
    {
93
        if (func_num_args() == 0) {
94
            return $this->addTraceToOutput;
95
        }
96
97
        $this->addTraceToOutput = (bool) $addTraceToOutput;
98
        return $this;
99
    }
100
101 4
    /**
102
     * Add error trace function arguments to output.
103 4
     * Set to True for all frame args, or integer for the n first frame args.
104 4
     * @param  bool|integer|null $addTraceFunctionArgsToOutput
105
     * @return null|bool|integer
106
     */
107 4
    public function addTraceFunctionArgsToOutput($addTraceFunctionArgsToOutput = null)
108 4
    {
109
        if (func_num_args() == 0) {
110
            return $this->addTraceFunctionArgsToOutput;
111
        }
112
113
        if (! is_integer($addTraceFunctionArgsToOutput)) {
114
            $this->addTraceFunctionArgsToOutput = (bool) $addTraceFunctionArgsToOutput;
115
        } else {
116
            $this->addTraceFunctionArgsToOutput = $addTraceFunctionArgsToOutput;
117 2
        }
118
    }
119 2
120 2
    /**
121
     * Set the size limit in bytes of frame arguments var_dump output.
122
     * If the limit is reached, the var_dump output is discarded.
123 2
     * Prevent memory limit errors.
124 1
     * @var integer
125 1
     */
126 2
    public function setTraceFunctionArgsOutputLimit($traceFunctionArgsOutputLimit)
127
    {
128 2
        $this->traceFunctionArgsOutputLimit = (integer) $traceFunctionArgsOutputLimit;
129
    }
130
131
    /**
132
     * Get the size limit in bytes of frame arguments var_dump output.
133
     * If the limit is reached, the var_dump output is discarded.
134
     * Prevent memory limit errors.
135
     * @return integer
136 1
     */
137
    public function getTraceFunctionArgsOutputLimit()
138 1
    {
139 1
        return $this->traceFunctionArgsOutputLimit;
140
    }
141
142
    /**
143
     * Only output to logger.
144
     * @param  bool|null $loggerOnly
145
     * @return null|bool
146
     */
147 1
    public function loggerOnly($loggerOnly = null)
148
    {
149 1
        if (func_num_args() == 0) {
150
            return $this->loggerOnly;
151
        }
152
153
        $this->loggerOnly = (bool) $loggerOnly;
154
    }
155
156
    /**
157 1
     * Test if handler can output to stdout.
158
     * @return bool
159 1
     */
160 1
    private function canOutput()
161
    {
162 1
        return !$this->loggerOnly();
163 1
    }
164
165
    /**
166
     * Get the frame args var_dump.
167
     * @param  \Whoops\Exception\Frame $frame [description]
168
     * @param  integer                 $line  [description]
169
     * @return string
170
     */
171
    private function getFrameArgsOutput(Frame $frame, $line)
172 1
    {
173
        if ($this->addTraceFunctionArgsToOutput() === false
174 1
            || $this->addTraceFunctionArgsToOutput() < $line) {
175 1
            return '';
176
        }
177 1
178 1
        // Dump the arguments:
179
        ob_start();
180
        var_dump($frame->getArgs());
181
        if (ob_get_length() > $this->getTraceFunctionArgsOutputLimit()) {
182
            // The argument var_dump is to big.
183
            // Discarded to limit memory usage.
184
            ob_clean();
185 1
            return sprintf(
186
                "\n%sArguments dump length greater than %d Bytes. Discarded.",
187 1
                self::VAR_DUMP_PREFIX,
188 1
                $this->getTraceFunctionArgsOutputLimit()
189
            );
190
        }
191 1
192 1
        return sprintf("\n%s",
193
            preg_replace('/^/m', self::VAR_DUMP_PREFIX, ob_get_clean())
194
        );
195
    }
196
197
    /**
198
     * Get the exception trace as plain text.
199
     * @return string
200
     */
201
    private function getTraceOutput()
202
    {
203
        if (! $this->addTraceToOutput()) {
204
            return '';
205
        }
206
        $inspector = $this->getInspector();
207 2
        $frames = $inspector->getFrames();
208
209 2
        $response = "\nStack trace:";
210
211
        $line = 1;
212
        foreach ($frames as $frame) {
213
            /** @var Frame $frame */
214
            $class = $frame->getClass();
215
216 2
            $template = "\n%3d. %s->%s() %s:%d%s";
217
            if (! $class) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
218 2
                // Remove method arrow (->) from output.
219 2
                $template = "\n%3d. %s%s() %s:%d%s";
220
            }
221
222
            $response .= sprintf(
223
                $template,
224
                $line,
225
                $class,
226
                $frame->getFunction(),
227
                $frame->getFile(),
228 1
                $frame->getLine(),
229
                $this->getFrameArgsOutput($frame, $line)
230 1
            );
231 1
232 1
            $line++;
233
        }
234
235
        return $response;
236 1
    }
237 1
238 1
    /**
239
     * @return int
240
     */
241
    public function handle()
242
    {
243
        $exception = $this->getException();
244
245
        $response = sprintf("%s: %s in file %s on line %d%s\n",
246
                get_class($exception),
247
                $exception->getMessage(),
248
                $exception->getFile(),
249 1
                $exception->getLine(),
250 1
                $this->getTraceOutput()
251 1
            );
252
253
        if ($this->getLogger()) {
254
            $this->getLogger()->error($response);
255
        }
256
257
        if (! $this->canOutput()) {
258 2
            return Handler::DONE;
259
        }
260 2
261
        if (\Whoops\Util\Misc::canSendHeaders()) {
262
            header('Content-Type: text/plain');
263 2
        }
264 2
265
        echo $response;
266 2
267
        return Handler::QUIT;
268 2
    }
269
}
270