Completed
Pull Request — master (#3)
by
unknown
12:16
created

HumanReadableExceptionFormatter::formatArgument()   D

Complexity

Conditions 9
Paths 8

Size

Total Lines 34
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 34
rs 4.909
c 0
b 0
f 0
cc 9
eloc 18
nc 8
nop 1
1
<?php
2
3
namespace PolderKnowledge\LogModule\Formatter;
4
5
use Monolog\Formatter\FormatterInterface;
6
use Monolog\Formatter\NormalizerFormatter;
7
8
/**
9
 * Format an Exception in a similar way PHP does by default when an exception bubbles to the top
10
 */
11
class HumanReadableExceptionFormatter extends NormalizerFormatter implements FormatterInterface
12
{
13
    public function format(array $record): string
14
    {
15
        //var_dump($record);exit;
0 ignored issues
show
Unused Code Comprehensibility introduced by
86% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
16
        $exception = $record['context']['exception'] ?? null;
17
        if ($exception) {
18
            return $this->printFromException($exception);
19
        } else {
20
            return $this->printWithoutException($record);
21
        }
22
    }
23
24
    protected function printWithoutException(array $record): string
25
    {
26
        return sprintf('[%s] %s: %s',
27
            date('r'),
28
            $record['level_name'],
29
            $record['message']
30
        );
31
    }
32
    
33
    protected function printFromException(\Throwable $exception)
34
    {
35
        return implode("\n", $this->linesFromException($exception)) . "\n"
36
            . "---------------------------------------\n";
37
    }
38
39
    protected function linesFromException(\Throwable $exception, bool $nested = false): array
40
    {
41
        $lines = [];
42
        $lines[] = $this->printFirstLineFromException($exception, $nested);
43
44
        foreach ($exception->getTrace() as $traceLine) {
45
            $lines[] = $this->formatTraceLine($traceLine);
46
        }
47
48
        if ($exception->getPrevious()) {
49
            $previousLines = $this->linesFromException($exception->getPrevious(), true);
50
51
            // recursion makes sure deeper nesting has longer prefixes
52
            foreach ($previousLines as $previousLine) {
53
                $lines[] = '……' . $previousLine;
54
            }
55
        }
56
57
        return $lines;
58
    }
59
60
    protected function printFirstLineFromException(\Throwable $exception, bool $nested): string
61
    {
62
        return sprintf('[%s] %s: %s in %s:%s',
63
            // only the root exception has a timestamp
64
            $nested ? 'Previous Exception' : date('r'),
65
            get_class($exception),
66
            $exception->getMessage(),
67
            $exception->getFile(),
68
            $exception->getLine()
69
        );
70
    }
71
72
    /**
73
     * Transform an element of the array Exception::getTrace
74
     * to a string of a single line
75
     *
76
     * @param mixed[] $trace element of Exception::getTrace
77
     * @return string of a single line
78
     */
79
    protected function formatTraceLine(array $trace): string
80
    {
81
        $arguments = $this->formatArguments($trace['args']);
0 ignored issues
show
Unused Code introduced by
$arguments is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
82
        $output = '';
83
84
        // only display file if we're not in the context of a class
85
        // to save space for the sake for readability of the stack trace
86
        if (isset($trace['file']) && !isset($trace['class'])) {
87
            $output .= $trace['file'];
88
89
            if (isset($trace['line'])) {
90
                $output .= '(' . $trace['line'] . ')';
91
            }
92
93
            $output .= ': ';
94
        }
95
96
        $output .= $trace['class'] ?? '';
97
        $output .= $trace['type'] ?? '';
98
        $output .= $trace['function'] ?? '';
99
100
        $arguments = $this->formatArguments($trace['args'] ?? []);
101
        $output .= '(' . $arguments . ')';
102
103
        if (isset($trace['class']) && isset($trace['line'])) {
104
            $output .= ':' . $trace['line'];
105
        }
106
107
        return $output;
108
    }
109
110
    /**
111
     * Convert function arguments of any type to a short readable string
112
     *
113
     * @param mixed[] $arguments
114
     * @return string a summary of the list of arguments
115
     */
116
    protected function formatArguments(array $arguments): string
117
    {
118
        return implode(', ', array_map([$this, 'formatArgument'], $arguments));
119
    }
120
121
    /**
122
     * Summarize a variable
123
     *
124
     * @param mixed $argument anything at all
125
     * @return string a summary of the argument
126
     */
127
    protected function formatArgument($argument): string
128
    {
129
        if (is_int($argument) || is_float($argument)) {
130
            return $argument;
131
        }
132
133
        if (is_string($argument)) {
134
            if (strlen($argument) < 80) {
135
                $truncated = $argument;
136
            } else {
137
                $truncated = substr($argument, 0, 30) . '...' . substr($argument, -30, 30);
138
            }
139
140
            return "'$truncated'";
141
        }
142
143
        if ($argument === false) {
144
            return 'false';
145
        }
146
147
        if ($argument === true) {
148
            return 'true';
149
        }
150
151
        if (is_array($argument)) {
152
            return 'Array(' . count($argument) . ')';
153
        }
154
155
        if (is_object($argument)) {
156
            return 'Object(' . get_class($argument) . ')';
157
        }
158
159
        return gettype($argument);
160
    }
161
}
162