Completed
Push — master ( ef09e8...2a4db2 )
by Jaap
8s
created

ExceptionPrinter::linesFromException()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 9.2
c 0
b 0
f 0
cc 4
eloc 10
nc 4
nop 2
1
<?php
2
3
namespace PolderKnowledge\LogModule\Formatter;
4
5
class ExceptionPrinter
6
{
7
    public static function linesFromException(\Throwable $exception, bool $nested = false): array
8
    {
9
        $lines = [];
10
        $lines[] = self::printFirstLineFromException($exception, $nested);
11
12
        foreach ($exception->getTrace() as $traceLine) {
13
            $lines[] = self::formatTraceLine($traceLine);
14
        }
15
16
        if ($exception->getPrevious()) {
17
            $previousLines = self::linesFromException($exception->getPrevious(), true);
18
19
            // recursion makes sure deeper nesting has longer prefixes
20
            foreach ($previousLines as $previousLine) {
21
                $lines[] = '  ' . $previousLine;
22
            }
23
        }
24
25
        return $lines;
26
    }
27
28
    public static function printFirstLineFromException(\Throwable $exception, bool $nested): string
29
    {
30
        return sprintf('[%s] %s: %s in %s:%s', ...[
31
            // only the root exception has a timestamp
32
            $nested ? 'Previous Exception' : date('r'),
33
            get_class($exception),
34
            $exception->getMessage(),
35
            $exception->getFile(),
36
            $exception->getLine()
37
        ]);
38
    }
39
40
    /**
41
     * Transform an element of the array Exception::getTrace
42
     * to a string of a single line
43
     *
44
     * @param mixed[] $trace element of Exception::getTrace
45
     * @return string of a single line
46
     */
47
    public static function formatTraceLine(array $trace): string
48
    {
49
        $output = '';
50
51
        // only display file if we're not in the context of a class
52
        // to save space for the sake for readability of the stack trace
53
        if (isset($trace['file']) && !isset($trace['class'])) {
54
            $output .= $trace['file'];
55
56
            if (isset($trace['line'])) {
57
                $output .= '(' . $trace['line'] . ')';
58
            }
59
60
            if (!isset($trace['function'])) {
61
                return $output;
62
            }
63
        }
64
65
        $output .= $trace['class'] ?? '';
66
        $output .= $trace['type'] ?? '';
67
        $output .= $trace['function'] ?? '';
68
69
        if (isset($trace['function'])) {
70
            $arguments = self::formatArguments($trace['args'] ?? []);
71
            $output .= '(' . $arguments . ')';
72
        }
73
74
        // print line number now if we skipped it
75
        if (isset($trace['class']) && isset($trace['line'])) {
76
            $output .= ':' . $trace['line'];
77
        }
78
79
        return $output;
80
    }
81
82
    /**
83
     * Convert function arguments of any type to a short readable string
84
     *
85
     * @param mixed[] $arguments
86
     * @return string a summary of the list of arguments
87
     */
88
    public static function formatArguments(array $arguments): string
89
    {
90
        return implode(', ', array_map([__CLASS__, 'formatArgument'], $arguments));
91
    }
92
93
    /**
94
     * Summarize a variable
95
     *
96
     * @param mixed $argument anything at all
97
     * @return string a summary of the argument
98
     */
99
    public static function formatArgument($argument): string
100
    {
101
        if (is_object($argument)) {
102
            return get_class($argument);
103
        }
104
105
        if (is_int($argument) || is_float($argument)) {
106
            return $argument;
107
        }
108
109
        if (is_string($argument)) {
110
            return self::truncateString($argument);
111
        }
112
113
        if ($argument === false) {
114
            return 'false';
115
        }
116
117
        if ($argument === true) {
118
            return 'true';
119
        }
120
121
        if (is_array($argument)) {
122
            return 'array(' . count($argument) . ')';
123
        }
124
125
        // resource or null
126
        return gettype($argument);
127
    }
128
129
    public static function truncateString(string $argument): string
130
    {
131
        if (strlen($argument) < 80) {
132
            $truncated = $argument;
133
        } else {
134
            $truncated = substr($argument, 0, 30) . '...' . substr($argument, -30, 30);
135
        }
136
137
        return "'$truncated'";
138
    }
139
}
140