ExceptionLogger   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 173
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 68
dl 0
loc 173
rs 9.92
c 1
b 0
f 0
wmc 31

6 Methods

Rating   Name   Duplication   Size   Complexity  
A _write() 0 3 1
A _addUser() 0 17 4
A _filterData() 0 17 5
A _add() 0 13 4
F write() 0 55 15
A _message() 0 10 2
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Saito - The Threaded Web Forum
7
 *
8
 * @copyright Copyright (c) the Saito Project Developers
9
 * @link https://github.com/Schlaefer/Saito
10
 * @license http://opensource.org/licenses/MIT
11
 */
12
13
namespace Saito\Exception\Logger;
14
15
use Cake\Log\Log;
16
use Cake\Routing\Router;
17
use Saito\User\CurrentUser\CurrentUserInterface;
18
19
class ExceptionLogger
20
{
21
22
    private $__lines = [];
23
24
    /**
25
     * Write
26
     *
27
     * @param string $message message
28
     * @param array $data data
29
     * - `msgs` array with additional message-lines
30
     * @return void
31
     * @throws \InvalidArgumentException
32
     */
33
    public function write($message, $data = null)
34
    {
35
        //# process message(s)
36
        $msgs = [$message];
37
        if (isset($data['msgs'])) {
38
            $msgs = array_merge($msgs, $data['msgs']);
39
        }
40
        // prepend main message in front of metadata added by subclasses
41
        foreach (array_reverse($msgs) as $key => $msg) {
42
            $this->_add($msg, $key, true);
43
        }
44
45
        //# add exception data
46
        if (isset($data['e'])) {
47
            /* @var $Exception \Exception */
48
            $Exception = $data['e'];
49
            unset($data['e']);
50
            $message = $Exception->getMessage();
51
            if (!empty($message)) {
52
                $this->_add($message);
53
            }
54
        }
55
56
        //# add request data
57
        $request = (php_sapi_name() !== 'cli') ? Router::getRequest() : false;
58
59
        $url = false;
60
        if (isset($data['URL'])) {
61
            $url = $data['URL'];
62
        } elseif ($request) {
63
            $url = $request->getRequestTarget();
64
        }
65
66
        $requestMethod = $request ? $request->getMethod() : false;
67
        if ($url && $requestMethod) {
68
            $url .= ' ' . $requestMethod;
69
        }
70
        if ($url) {
71
            $this->_add($url, 'Request URL');
72
        }
73
74
        if (isset($_SERVER['HTTP_USER_AGENT'])) {
75
            $this->_add($_SERVER['HTTP_USER_AGENT'], 'User-Agent');
76
        }
77
78
        $this->_addUser($data);
79
80
        if ($request) {
81
            $data = $request->getData();
82
            if (!empty($data)) {
83
                $this->_add($this->_filterData($data), 'Data');
84
            }
85
        }
86
87
        $this->_write();
88
    }
89
90
    /**
91
     * adds data about current user to log entry
92
     *
93
     * @param mixed $data data
94
     * @return void
95
     *
96
     * @throws \InvalidArgumentException
97
     */
98
    protected function _addUser($data)
99
    {
100
        if (!isset($data['CurrentUser'])) {
101
            return;
102
        }
103
        if (!($data['CurrentUser'] instanceof CurrentUserInterface)) {
104
            throw new \InvalidArgumentException();
105
        }
106
        $CurrentUser = $data['CurrentUser'];
107
        if ($CurrentUser->isLoggedIn()) {
108
            $username = $CurrentUser->get('username');
109
            $userId = $CurrentUser->getId();
110
            $username = "{$username} (id: {$userId})";
111
        } else {
112
            $username = 'anonymous';
113
        }
114
        $this->_add($username, 'Current user');
115
    }
116
117
    /**
118
     * Filters request-data which should not be in server logs
119
     *
120
     * esp. cleartext passwords in $_POST data
121
     *
122
     * @param mixed $data data
123
     * @return array
124
     */
125
    protected function _filterData($data)
126
    {
127
        if (!is_array($data)) {
128
            return $data;
129
        }
130
        foreach ($data as $key => $datum) {
131
            if (is_array($datum)) {
132
                $data[$key] = $this->_filterData($datum);
133
                continue;
134
            }
135
136
            if (stripos($key, 'password') !== false) {
137
                $data[$key] = '***********';
138
            }
139
        }
140
141
        return $data;
142
    }
143
144
    /**
145
     * Write
146
     *
147
     * @return void
148
     */
149
    protected function _write()
150
    {
151
        Log::write('error', $this->_message(), ['scope' => ['saito.error']]);
152
    }
153
154
    /**
155
     * Message
156
     *
157
     * @return string
158
     */
159
    protected function _message()
160
    {
161
        $message = [];
162
        $i = 1;
163
        foreach ($this->__lines as $line) {
164
            $message[] = sprintf("  #%d %s", $i, $line);
165
            $i++;
166
        }
167
168
        return "\n" . implode("\n", $message);
169
    }
170
171
    /**
172
     * Add
173
     *
174
     * @param mixed $val value
175
     * @param mixed $key key
176
     * @param bool $prepend prepend
177
     * @return void
178
     */
179
    protected function _add($val, $key = null, $prepend = false)
180
    {
181
        if (is_array($val)) {
182
            $val = print_r($this->_filterData($val), true);
183
        }
184
        if (is_string($key)) {
185
            $val = "$key: $val";
186
        }
187
188
        if ($prepend) {
189
            array_unshift($this->__lines, $val);
190
        } else {
191
            $this->__lines[] = $val;
192
        }
193
    }
194
}
195