Passed
Push — master ( 068bff...27cbbc )
by Kirill
03:24
created

ExceptionRenderer::renderHeader()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 0
cts 6
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
1
<?php
2
/**
3
 * This file is part of Railt package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
declare(strict_types=1);
9
10
namespace Railt\Console;
11
use Railt\Io\Exception\NotReadableException;
12
use Railt\Io\File;
13
14
/**
15
 * Class ExceptionRenderer
16
 */
17
class ExceptionRenderer
18
{
19
    /**
20
     * @var Highlighter
21
     */
22
    private $highlighter;
23
24
    /**
25
     * ExceptionRenderer constructor.
26
     * @param Highlighter $highlighter
27
     */
28
    public function __construct(Highlighter $highlighter)
29
    {
30
        $this->highlighter = $highlighter;
31
    }
32
33
    /**
34
     * @param \Throwable $e
35
     * @return string
36
     */
37
    public function render(\Throwable $e): string
38
    {
39
        return
40
            $this->renderHeader($e) .
41
            $this->renderFileHeader($e) .
42
            $this->renderCode($e) .
43
            $this->renderTrace($e);
44
    }
45
46
    /**
47
     * @param \Throwable $e
48
     * @return string
49
     */
50
    private function renderCode(\Throwable $e): string
51
    {
52
        try {
53
            $file = File::fromPathname($e->getFile());
54
        } catch (NotReadableException $error) {
55
            $file = File::fromSources('', $e->getFile());
56
        }
57
58
        return $this->highlighter->highlight($file, $e->getLine()) .
0 ignored issues
show
Bug introduced by
It seems like $file defined by \Railt\Io\File::fromPathname($e->getFile()) on line 53 can also be of type object<Railt\Io\File>; however, Railt\Console\Highlighter::highlight() does only seem to accept object<Railt\Io\Readable>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
59
            \PHP_EOL;
60
    }
61
62
    /**
63
     * @param \Throwable $e
64
     * @return string
65
     */
66
    private function renderHeader(\Throwable $e): string
67
    {
68
        [$name, $msg] = [$this->classname($e), $e->getMessage()];
0 ignored issues
show
Bug introduced by
The variable $name does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $msg does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
69
70
        return \sprintf('<error> %s </error> <comment>%s</comment>', $name, $msg) .
71
            \PHP_EOL . \PHP_EOL;
72
    }
73
74
    /**
75
     * @param string|object $class
76
     * @return string
77
     */
78
    private function classname($class): string
79
    {
80
        $class = \is_object($class) ? \get_class($class) : $class;
81
82
        return \basename(\str_replace('\\', '/', $class));
83
    }
84
85
    /**
86
     * @param \Throwable $e
87
     * @return string
88
     */
89
    private function renderFileHeader(\Throwable $e): string
90
    {
91
        $message = 'in <fg=cyan;options=underscore>%s</> at line <fg=cyan>%d</>';
92
93
        return \sprintf($message, $e->getFile(), $e->getLine()) .
94
            \PHP_EOL . \PHP_EOL;
95
    }
96
97
    /**
98
     * @param \Throwable $e
99
     * @return string
100
     */
101
    private function renderTrace(\Throwable $e): string
102
    {
103
        $result = ['<comment>Stack Trace:</comment>'];
104
105
        foreach ($e->getTrace() as $i => $trace) {
106
            $line = $this->traceInvocation($trace);
107
            $args = $this->traceArguments($trace);
108
109
            $result[] = \sprintf('<fg=blue>%3s</> <comment>%s(%s)</comment>', '#' . $i, $line, $args);
110
            $result[] = \sprintf('    <fg=cyan>%s</>:<fg=cyan>%d</>', $trace['file'], $trace['line']);
111
        }
112
113
        return \implode(\PHP_EOL, $result) .
114
            \PHP_EOL;
115
    }
116
117
    /**
118
     * @param array $trace
119
     * @return string
120
     */
121
    private function traceArguments(array $trace): string
122
    {
123
        $result = [];
124
125
        foreach ($trace['args'] as $arg) {
126
            $result[] = \is_object($arg) ? 'Object(' . $this->classname($arg) . ')' : \gettype($arg);
127
        }
128
129
        return \implode(', ', $result);
130
    }
131
132
    /**
133
     * @param array $trace
134
     * @return string
135
     */
136
    private function traceInvocation(array $trace): string
137
    {
138
        return isset($trace['class'])
139
            ? $trace['class'] . $trace['type'] . $trace['function']
140
            : $trace['function'];
141
    }
142
}
143