Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
22 / 22
90.91% covered (success)
90.91%
30 / 33
39.13% covered (danger)
39.13%
9 / 23
33.33% covered (danger)
33.33%
1 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
PreparesValue
100.00% covered (success)
100.00%
22 / 22
90.91% covered (success)
90.91%
30 / 33
39.13% covered (danger)
39.13%
9 / 23
100.00% covered (success)
100.00%
3 / 3
58.20
100.00% covered (success)
100.00%
1 / 1
 prepare
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
15 / 15
54.55% covered (warning)
54.55%
6 / 11
100.00% covered (success)
100.00%
1 / 1
14.01
 prepareArray
100.00% covered (success)
100.00%
2 / 2
75.00% covered (warning)
75.00%
3 / 4
50.00% covered (danger)
50.00%
1 / 2
100.00% covered (success)
100.00%
1 / 1
2.50
 getExceptionMessage
100.00% covered (success)
100.00%
10 / 10
85.71% covered (warning)
85.71%
12 / 14
20.00% covered (danger)
20.00%
2 / 10
100.00% covered (success)
100.00%
1 / 1
12.19
1<?php
2
3declare(strict_types=1);
4
5namespace Celema\Log\Formatter;
6
7use DateTimeInterface;
8use Stringable;
9use Throwable;
10
11trait PreparesValue
12{
13    private function prepare(
14        mixed $value,
15        bool $includeTraceback,
16        string $tracebackIndent = '',
17    ): string {
18        return match (true) {
19            // Exceptions must be first as they are Stringable
20            $value instanceof Throwable => $this->getExceptionMessage(
21                $value,
22                $includeTraceback,
23                $tracebackIndent,
24            ),
25            is_scalar($value) || $value instanceof Stringable => (string) $value,
26            $value instanceof DateTimeInterface => $value->format('Y-m-d H:i:s T'),
27            is_object($value) => '[Instance of ' . $value::class . ']',
28            is_array($value) => $this->prepareArray($value),
29            default => '[' . get_debug_type($value) . ']',
30        };
31    }
32
33    /** @param array<array-key, mixed> $value */
34    private function prepareArray(array $value): string
35    {
36        $encoded = json_encode($value, JSON_UNESCAPED_SLASHES);
37
38        return '[Array ' . ($encoded !== false ? $encoded : '...') . ']';
39    }
40
41    /**
42     * The exception, then each previous exception it wraps, so the original
43     * cause of a rethrown error stays in the log.
44     */
45    private function getExceptionMessage(
46        Throwable $exception,
47        bool $includeTraceback,
48        string $tracebackIndent,
49    ): string {
50        $parts = [];
51
52        for ($current = $exception; $current !== null; $current = $current->getPrevious()) {
53            $part = $current::class . ': ' . $current->getMessage();
54
55            if ($includeTraceback) {
56                $trace = $current->getTraceAsString();
57
58                if ($tracebackIndent) {
59                    // Indent each frame: split on '#', rejoin with indent+'#'
60                    $trace = implode($tracebackIndent . '#', explode('#', $trace));
61                }
62
63                $part .= "\n" . $trace;
64            }
65
66            $parts[] = $part;
67        }
68
69        return implode("\n" . $tracebackIndent . 'Caused by: ', $parts);
70    }
71}