Payload::detectAndCleanUtf8()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 3
eloc 10
c 3
b 0
f 0
nc 2
nop 1
dl 0
loc 14
ccs 0
cts 11
cp 0
crap 12
rs 9.9332
1
<?php declare(strict_types=1);
2
3
namespace Logfile;
4
5
use DateTimeImmutable;
6
use DateTimeInterface;
7
use Throwable;
8
9
class Payload
10
{
11
    protected $message;
12
13
    protected $config;
14
15
    protected $id;
16
17
    protected $dateTime;
18
19
    protected $severity;
20
21
    protected $exceptions = [];
22
23
    public function __construct(string $message, Config $config)
24
    {
25
        $this->message = $message;
26
        $this->config = $config;
27
        $this->id = $this->uuid4();
28
        $this->dateTime = new DateTimeImmutable;
29
        $this->severity = 'debug';
30
    }
31
32
    public static function createFromException(Throwable $exception, Config $config): self
33
    {
34
        $payload = new self($exception->getMessage(), $config);
35
        $payload->pushException($exception);
36
        return $payload;
37
    }
38
39
    public function getMessage(): string
40
    {
41
        return $this->message;
42
    }
43
44
    public function newId(string $id): string
45
    {
46
        $old = $this->id;
47
        $this->id = $id;
48
        return $old;
49
    }
50
51
    public function getId(): string
52
    {
53
        return $this->id;
54
    }
55
56
    public function setDateTime(DateTimeInterface $dateTime): void
57
    {
58
        $this->dateTime = $dateTime;
59
    }
60
61
    public function getDateTime(): DateTimeInterface
62
    {
63
        return $this->dateTime;
64
    }
65
66
    public function setSeverity(string $severity): void
67
    {
68
        $this->severity = $severity;
69
    }
70
71
    public function getSeverity(): string
72
    {
73
        return $this->severity;
74
    }
75
76
    public function pushException(Throwable $exception): void
77
    {
78
        $trace = new Inspection\Stacktrace($exception);
79
        $trace->setPath($this->config->getPath());
80
81
        $this->exceptions[] = [
82
            'exception' => \get_class($exception),
83
            'message' => $exception->getMessage(),
84
            'trace' => $trace->getFrames(),
85
        ];
86
87
        if ($previousException = $exception->getPrevious()) {
88
            $this->pushException($previousException);
89
        }
90
    }
91
92
    public function getExceptions(): array
93
    {
94
        return $this->exceptions;
95
    }
96
97
    /**
98
     * Get payload data
99
     *
100
     * @return array
101
     */
102
    public function getData(): array
103
    {
104
        return [
105
            'id' => $this->getId(),
106
            'severity' => $this-> getSeverity(),
107
            'datetime' => $this->getDateTime(),
108
            'message' => $this->getMessage(),
109
            'tags' => $this->config->getTags(),
110
            'exceptions' => $this->getExceptions(),
111
        ];
112
    }
113
114
    /**
115
     * Get payload json encoded
116
     *
117
     * @return string
118
     */
119
    public function getEncodedData(): string
120
    {
121
        $data = $this->getData();
122
        $encoded = \json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
123
124
        if (JSON_ERROR_UTF8 === \json_last_error()) {
125
            if (\is_string($data)) {
0 ignored issues
show
introduced by
The condition is_string($data) is always false.
Loading history...
126
                $this->detectAndCleanUtf8($data);
127
            } elseif (\is_array($data)) {
0 ignored issues
show
introduced by
The condition is_array($data) is always true.
Loading history...
128
                \array_walk_recursive($data, [$this, 'detectAndCleanUtf8']);
129
            }
130
            $encoded = \json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
131
        }
132
133
        if (JSON_ERROR_NONE !== \json_last_error()) {
134
            $error = \json_last_error_msg();
135
            throw new \LogicException(\sprintf('Failed to encode json data: %s.', $error));
136
        }
137
138
        return $encoded;
139
    }
140
141
    /**
142
     * Detect invalid UTF-8 string characters and convert to valid UTF-8.
143
     * @see https://github.com/Seldaek/monolog/blob/master/src/Monolog/Formatter/NormalizerFormatter.php
144
     */
145
    public function detectAndCleanUtf8(&$data)
146
    {
147
        if (\is_string($data) && !\preg_match('//u', $data)) {
148
            $data = \preg_replace_callback(
149
                '/[\x80-\xFF]+/',
150
                function ($m) {
151
                    return \utf8_encode($m[0]);
152
                },
153
                $data
154
            );
155
            $data = \str_replace(
156
                ['¤', '¦', '¨', '´', '¸', '¼', '½', '¾'],
157
                ['€', 'Š', 'š', 'Ž', 'ž', 'Œ', 'œ', 'Ÿ'],
158
                $data
159
            );
160
        }
161
    }
162
163
    /**
164
     * Get uuid v4
165
     *
166
     * @see http://www.php.net/manual/en/function.uniqid.php#94959
167
     * @return string
168
     */
169
    protected function uuid4(): string
170
    {
171
        \mt_srand();
172
        return \sprintf(
173
            '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
174
            // 32 bits for "time_low"
175
            \mt_rand(0, 0xffff),
176
            \mt_rand(0, 0xffff),
177
            // 16 bits for "time_mid"
178
            \mt_rand(0, 0xffff),
179
            // 16 bits for "time_hi_and_version",
180
            // four most significant bits holds version number 4
181
            \mt_rand(0, 0x0fff) | 0x4000,
182
            // 16 bits, 8 bits for "clk_seq_hi_res",
183
            // 8 bits for "clk_seq_low",
184
            // two most significant bits holds zero and one for variant DCE1.1
185
            \mt_rand(0, 0x3fff) | 0x8000,
186
            // 48 bits for "node"
187
            \mt_rand(0, 0xffff),
188
            \mt_rand(0, 0xffff),
189
            \mt_rand(0, 0xffff)
190
        );
191
    }
192
}
193