Passed
Push — master ( ab1cd3...08c723 )
by Stefan
02:14
created

XLogger::reset()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 0
c 0
b 0
f 0
dl 0
loc 2
rs 10
cc 1
nc 1
nop 0
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
            $this->strPath = pathinfo($strFullpath, PATHINFO_DIRNAME);
1 ignored issue
show
Documentation Bug introduced by
It seems like pathinfo($strFullpath, S...ogger\PATHINFO_DIRNAME) can also be of type array. However, the property $strPath is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
125
            $this->strFilename = pathinfo($strFullpath, PATHINFO_BASENAME);
1 ignored issue
show
Documentation Bug introduced by
It seems like pathinfo($strFullpath, S...gger\PATHINFO_BASENAME) can also be of type array. However, the property $strFilename is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
126
        }
127
    }
128
129
    /**
130
     * Set the path for the logfile.
131
     * Some placeholders can be used for date/month/year/week.
132
     * @param string $strPath
133
     * @return void
134
     * @see XLogger::setFullpath()
135
     */
136
    public function setPath(string $strPath) : void
137
    {
138
        $this->closeLogfile();
139
        $this->strPath = $this->replacePathPlaceholder($strPath);
140
    }
141
142
    /**
143
     * Set the filename for the logfile.
144
     * Some placeholders can be used for date/month/year/week.
145
     * @param string $strFilename
146
     * @return void
147
     * @see XLogger::setFullpath()
148
     */
149
    public function setFilename(string $strFilename) : void
150
    {
151
        $this->closeLogfile();
152
        $this->strFilename = $this->replacePathPlaceholder($strFilename);
153
    }
154
155
    /**
156
     * Get current filename (may be some placeholders are supplemented).
157
     * @return string
158
     * @see XLogger::setFullpath()
159
     */
160
    public function getFilename() : string
161
    {
162
        return $this->strFilename;
163
    }
164
165
    /**
166
     * Get full path of the logfile.
167
     * @return string
168
     * @see XLogger::setFullpath()
169
     */
170
    public function getFullpath() : string
171
    {
172
        $strFullPath = rtrim($this->strPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $this->strFilename;
173
        return $strFullPath;
174
    }
175
176
    /**
177
     * may be implemented in extending classes
178
     * @internal
179
     */
180
    public function reset() : void
181
    {
182
    }
183
184
    /**
185
     * Check, if item for requested level should be logged.
186
     * @param string $level
187
     * @return bool
188
     */
189
    protected function logLevel($level) : bool
190
    {
191
        return ($this->iLogLevel <= $this->getIntLevel($level));
192
    }
193
194
    /**
195
     * Replace placeholders with the coresponding values.
196
     * @param mixed $message    string or object implements __toString() method
197
     * @param array $context
198
     * @return string
199
     */
200
    protected function replaceContext($message, array $context = array()) : string
201
    {
202
        // build a replacement array with braces around the context keys
203
        $replace = array();
204
        foreach ($context as $key => $val) {
205
            // check that the value can be cast to string
206
            if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) {
207
                $replace['{' . $key . '}'] = $val;
208
            }
209
        }
210
        // interpolate replacement values into the message and return
211
        $strMessage = (string)$message;
212
        $strMessage = strtr($strMessage, $replace);
213
214
        return $strMessage;
215
    }
216
217
    /**
218
     * Get the caller from backtrace in format "<filename>(<line>)".
219
     * Document root is cut off from the full path.
220
     * @return string
221
     */
222
    protected function getCaller() : string
223
    {
224
        $strCaller = '';
225
        $aBacktrace = debug_backtrace();
226
        while (($aCaller = array_shift($aBacktrace)) !== null) {
227
            // just get latest call outside of the logger interface
228
            // if ($aCaller['class'] != get_class() ) {
229
            if (!is_subclass_of($aCaller['class'], 'Psr\Log\AbstractLogger')) {
230
                break;
231
            }
232
        }
233
        if ($aCaller) {
234
            // the base path on server isn't from interest..
235
            $strFile = str_replace($_SERVER['DOCUMENT_ROOT'], '', $aCaller['file']);
236
            $strCaller = $strFile . ' (' . $aCaller['line'] . ')';
237
        }
238
        return $strCaller;
239
    }
240
241
    /**
242
     *
243
     * @param string $level
244
     * @throws InvalidArgumentException
245
     * @return int
246
     */
247
    protected function getIntLevel(string $level) : int
248
    {
249
        $aLevel = array(
250
            LogLevel::EMERGENCY => 7,
251
            LogLevel::ALERT => 6,
252
            LogLevel::CRITICAL => 5,
253
            LogLevel::ERROR => 4,
254
            LogLevel::WARNING => 3,
255
            LogLevel::NOTICE => 2,
256
            LogLevel::INFO => 1,
257
            LogLevel::DEBUG => 0
258
        );
259
        if (!isset($aLevel[$level])) {
260
            throw new InvalidArgumentException("Unknown logging level ($level)");
261
        }
262
        return $aLevel[$level];
263
    }
264
265
    /**
266
     * Replaces placeholders in path/filename/fullpath.
267
     * @see XLogger::setFullPath()
268
     * @param string $strPath
269
     * @return string
270
     */
271
    protected function replacePathPlaceholder(string $strPath) : string
272
    {
273
        $strPath = str_replace('{date}', date('Y-m-d'), $strPath);
274
        $strPath = str_replace('{month}', date('Y-m'), $strPath);
275
        $strPath = str_replace('{year}', date('Y'), $strPath);
276
        $strPath = str_replace('{week}', date('Y_W'), $strPath);
277
278
        $strUser = preg_replace("/[^A-Za-z0-9_-]/",'', $this->strUser);
279
        $strPath = str_replace('{name}', $strUser, $strPath);
280
281
        return $strPath;
282
    }
283
284
    /**
285
     * Has nothing to do in the abstract class...
286
     * ...but does not necessarily have to be implemented in extended classes
287
     * and is therefore not declared as an abstract at this point.
288
     * @return void
289
     */
290
    protected function openLogfile() : void
291
    {
292
    }
293
294
    /**
295
     * Has nothing to do in the abstract class...
296
     * ...but does not necessarily have to be implemented in extended classes
297
     * and is therefore not declared as an abstract at this point.
298
     * @return void
299
     */
300
    protected function createLogfile() : void
301
    {
302
    }
303
304
    /**
305
     * Has nothing to do in the abstract class...
306
     * ...but does not necessarily have to be implemented in extended classes
307
     * and is therefore not declared as an abstract at this point.
308
     * @return void
309
     */
310
    protected function closeLogfile() : void
311
    {
312
    }
313
}
314