XLogger::setUser()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 1
1
<?php
2
declare(strict_types=1);
3
4
namespace SKien\XLogger;
5
6
use Psr\Log\AbstractLogger;
7
use Psr\Log\InvalidArgumentException;
8
use Psr\Log\LogLevel;
9
10
/**
11
 * abstract class for several types of logger.
12
 *
13
 * Class implements the PSR-3 LoggerInferface through AbstractLogger.
14
 *
15
 * @package XLogger
16
 * @author Stefanius <[email protected]>
17
 * @copyright MIT License - see the LICENSE file for details
18
 */
19
abstract class XLogger extends AbstractLogger
20
{
21
    /** include IP-adress in log item     */
22
    const LOG_IP        = 0x01;
23
    /** include backtrace (caller) in log item     */
24
    const LOG_BT        = 0x02;
25
    /** include user in log item     */
26
    const LOG_USER      = 0x04;
27
    /** include user-agent in log item     */
28
    const LOG_UA        = 0x08;
29
30
    /** @var int levels to include in the log (bitmask of internal levels)     */
31
    protected int $iLogLevel = 0;
32
    /** @var int options bitmask     */
33
    protected int $iOptions = self::LOG_IP | self::LOG_USER | self::LOG_UA;
34
    /** @var string username for logging (if LOG_USER is set)     */
35
    protected string $strUser = '';
36
    /** @var string path to store the logfile     */
37
    protected string $strPath = '.';
38
    /** @var string filename of the logfile     */
39
    protected string $strFilename = '';
40
41
    /**
42
     * Init logging level and remote username (if set).
43
     * @see XLogger::setLogLevel()
44
     * @param string $level the min. `LogLevel` to be logged
45
     */
46
    public function __construct(string $level = LogLevel::DEBUG)
47
    {
48
        $this->setLogLevel($level);
49
        // init with remote user, if available
50
        $this->strUser = isset($_SERVER["REMOTE_USER"]) ? $_SERVER["REMOTE_USER"] : '';
51
    }
52
53
    /**
54
     * Set the level from witch items should be logged.
55
     * All entries with a lower level than the specified are ignored. <br/>
56
     * The relevance of the defined PSR-3 level from hight to low are: <br/><ul>
57
     * <li> EMERGENCY </li>
58
     * <li> ALERT </li>
59
     * <li> CRITICAL </li>
60
     * <li> ERROR </li>
61
     * <li> WARNING </li>
62
     * <li> NOTICE </li>
63
     * <li>INFO </li>
64
     * <li> DEBUG </li></ul>
65
     *
66
     * @param string $strLogLevel   the min. `LogLevel` to be logged
67
     * @return void
68
     */
69
    public function setLogLevel(string $strLogLevel) : void
70
    {
71
        $this->iLogLevel = $this->getIntLevel($strLogLevel);
72
    }
73
74
    /**
75
     * Set Options for logging.
76
     * Use any combination of: <ul>
77
     * <li> XLogger::LOG_IP :      include IP-adress in log item </li>
78
     * <li> XLogger::LOG_BT :      include backtrace 'filename (line)' in log item </li>
79
     * <li> XLogger::LOG_USER :    include user in log item </li>
80
     * <li> XLogger::LOG_UA :      include user-agent in log item </li></ul>
81
     *
82
     * @param int $iOptions any combination (bitwise or, '|') of the flags described
83
     * @return void
84
     */
85
    public function setOptions(int $iOptions) : void
86
    {
87
        $this->iOptions = $iOptions;
88
    }
89
90
    /**
91
     * Set the username.
92
     * @param string $strUser
93
     * @return void
94
     */
95
    public function setUser(string $strUser) : void
96
    {
97
        $this->strUser = $strUser;
98
    }
99
100
    /**
101
     * Set the path and filename for the logfile.
102
     * In order to be able to assign logfiles chronologically and / or to a user, it is
103
     * possible to use placeholders in the file name or path, which are replaced accordingly
104
     * before the file is created.
105
     * Following placeholders are supported:  <br/><ul>
106
     * <li> {date}   : will be replaced by current date (Format 'YYYY-MM-DD') </li>
107
     * <li> {month} : will be replaced by current month (Format 'YYYY-MM') </li>
108
     * <li> {year}   : will be replaced by current year (Format 'YYYY') </li>
109
     * <li> {week}   : will be replaced by current ISO-8601 week (Format 'YYYY_WW') </li>
110
     * <li> {name}   : will be replaced by the username </li></ul>
111
     *
112
     * > Note: <br/>
113
     * > If you use the placeholder for the user name, this have to be set BEFORE the call of
114
     *   this method. In the username, all characters except A-Z, a-z, 0-9, '_' and '-' are
115
     *   filtered out (to always get a valid file name)!
116
     * @param string $strFullpath
117
     * @return void
118
     */
119
    public function setFullpath(string $strFullpath) : void
120
    {
121
        $this->closeLogfile();
122
        if (strlen($strFullpath) > 0) {
123
            $strFullpath = $this->replacePathPlaceholder($strFullpath);
124
            // scrutinizer didn't (or can't...) analyse, that pathinfo returns allways string if the $options param is set!
125
            /** @scrutinizer ignore-type */ $this->strPath = pathinfo($strFullpath, PATHINFO_DIRNAME);
126
            /** @scrutinizer ignore-type */ $this->strFilename = pathinfo($strFullpath, PATHINFO_BASENAME);
127
        }
128
    }
129
130
    /**
131
     * Set the path for the logfile.
132
     * Some placeholders can be used for date/month/year/week.
133
     * @param string $strPath
134
     * @return void
135
     * @see XLogger::setFullpath()
136
     */
137
    public function setPath(string $strPath) : void
138
    {
139
        $this->closeLogfile();
140
        $this->strPath = $this->replacePathPlaceholder($strPath);
141
    }
142
143
    /**
144
     * Set the filename for the logfile.
145
     * Some placeholders can be used for date/month/year/week.
146
     * @param string $strFilename
147
     * @return void
148
     * @see XLogger::setFullpath()
149
     */
150
    public function setFilename(string $strFilename) : void
151
    {
152
        $this->closeLogfile();
153
        $this->strFilename = $this->replacePathPlaceholder($strFilename);
154
    }
155
156
    /**
157
     * Get current filename (may be some placeholders are supplemented).
158
     * @return string
159
     * @see XLogger::setFullpath()
160
     */
161
    public function getFilename() : string
162
    {
163
        return $this->strFilename;
164
    }
165
166
    /**
167
     * Get full path of the logfile.
168
     * @return string
169
     * @see XLogger::setFullpath()
170
     */
171
    public function getFullpath() : string
172
    {
173
        $strFullPath = rtrim($this->strPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $this->strFilename;
174
        return $strFullPath;
175
    }
176
177
    /**
178
     * may be implemented in extending classes
179
     * @internal
180
     */
181
    public function reset() : void
182
    {
183
    }
184
185
    /**
186
     * Check, if item for requested level should be logged.
187
     * @param string $level
188
     * @return bool
189
     */
190
    protected function logLevel($level) : bool
191
    {
192
        return ($this->iLogLevel <= $this->getIntLevel($level));
193
    }
194
195
    /**
196
     * Replace placeholders with the coresponding values.
197
     * @param mixed $message    string or object implements __toString() method
198
     * @param array<string,mixed> $context
199
     * @return string
200
     */
201
    protected function replaceContext($message, array $context = []) : string
202
    {
203
        // build a replacement array with braces around the context keys
204
        $replace = array();
205
        foreach ($context as $key => $val) {
206
            // check that the value can be cast to string
207
            if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) {
208
                $replace['{' . $key . '}'] = $val;
209
            }
210
        }
211
        // interpolate replacement values into the message and return
212
        $strMessage = (string)$message;
213
        $strMessage = strtr($strMessage, $replace);
214
215
        return $strMessage;
216
    }
217
218
    /**
219
     * Get the caller from backtrace in format "<filename>(<line>)".
220
     * Document root is cut off from the full path.
221
     * @return string
222
     */
223
    protected function getCaller() : string
224
    {
225
        $strCaller = '';
226
        $aBacktrace = debug_backtrace();
227
        while (($aCaller = array_shift($aBacktrace)) !== null) {
228
            // just get latest call outside of the logger interface
229
            // if ($aCaller['class'] != get_class() ) {
230
            if (!is_subclass_of($aCaller['class'], 'Psr\Log\AbstractLogger')) {
231
                break;
232
            }
233
        }
234
        if ($aCaller) {
235
            // the base path on server isn't from interest..
236
            $strFile = str_replace($_SERVER['DOCUMENT_ROOT'], '', $aCaller['file']);
237
            $strCaller = $strFile . ' (' . $aCaller['line'] . ')';
238
        }
239
        return $strCaller;
240
    }
241
242
    /**
243
     *
244
     * @param string $level
245
     * @throws InvalidArgumentException
246
     * @return int
247
     */
248
    protected function getIntLevel(string $level) : int
249
    {
250
        $aLevel = array(
251
            LogLevel::EMERGENCY => 7,
252
            LogLevel::ALERT => 6,
253
            LogLevel::CRITICAL => 5,
254
            LogLevel::ERROR => 4,
255
            LogLevel::WARNING => 3,
256
            LogLevel::NOTICE => 2,
257
            LogLevel::INFO => 1,
258
            LogLevel::DEBUG => 0
259
        );
260
        if (!isset($aLevel[$level])) {
261
            throw new InvalidArgumentException("Unknown logging level ($level)");
262
        }
263
        return $aLevel[$level];
264
    }
265
266
    /**
267
     * Replaces placeholders in path/filename/fullpath.
268
     * @see XLogger::setFullPath()
269
     * @param string $strPath
270
     * @return string
271
     */
272
    protected function replacePathPlaceholder(string $strPath) : string
273
    {
274
        $strPath = str_replace('{date}', date('Y-m-d'), $strPath);
275
        $strPath = str_replace('{month}', date('Y-m'), $strPath);
276
        $strPath = str_replace('{year}', date('Y'), $strPath);
277
        $strPath = str_replace('{week}', date('Y_W'), $strPath);
278
279
        $strUser = preg_replace("/[^A-Za-z0-9_-]/", '', $this->strUser) ?? '';
280
        $strPath = str_replace('{name}', $strUser, $strPath);
281
282
        return $strPath;
283
    }
284
285
    /**
286
     * Has nothing to do in the abstract class...
287
     * ...but does not necessarily have to be implemented in extended classes
288
     * and is therefore not declared as an abstract at this point.
289
     * @return void
290
     */
291
    protected function openLogfile() : void
292
    {
293
    }
294
295
    /**
296
     * Has nothing to do in the abstract class...
297
     * ...but does not necessarily have to be implemented in extended classes
298
     * and is therefore not declared as an abstract at this point.
299
     * @return void
300
     */
301
    protected function createLogfile() : void
302
    {
303
    }
304
305
    /**
306
     * Has nothing to do in the abstract class...
307
     * ...but does not necessarily have to be implemented in extended classes
308
     * and is therefore not declared as an abstract at this point.
309
     * @return void
310
     */
311
    protected function closeLogfile() : void
312
    {
313
    }
314
}
315