Completed
Push — master ( aef97c...07ee05 )
by Sergey
02:58
created

Message::getStackTrace()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 7
cts 7
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 7
nc 2
nop 0
crap 3
1
<?php
2
/**
3
 * Yii 2 log message object
4
 *
5
 * @see       https://github.com/sergeymakinen/yii2-log-message
6
 * @copyright Copyright (c) 2017 Sergey Makinen (https://makinen.ru)
7
 * @license   https://github.com/sergeymakinen/yii2-log-message/blob/master/LICENSE MIT License
8
 */
9
10
namespace sergeymakinen\log;
11
12
use yii\base\InvalidConfigException;
13
use yii\base\Object;
14
use yii\helpers\Url;
15
use yii\helpers\VarDumper;
16
use yii\log\Logger;
17
use yii\log\Target;
18
19
/**
20
 * This class wraps a log message and exposes its properties as well as the current request/user details.
21
 * @property string $category the message category.
22
 * @property string|null $commandLine the command line.
23
 * @property bool $isConsoleRequest whether the current request is a console request.
24
 * @property string $level the text display of the message level.
25
 * @property string $prefix the messsage prefix string.
26
 * @property string|null $sessionId the session ID.
27
 * @property string|null $stackTrace the stack trace.
28
 * @property string $text the message text.
29
 * @property float $timestamp the message creation timestamp.
30
 * @property string|null $url the current absolute URL.
31
 * @property int|string|null $userId the user identity ID.
32
 * @property string|null $userIp the user IP address.
33
 */
34
class Message extends Object
35
{
36
    /**
37
     * @var array the message.
38
     */
39
    public $message;
40
41
    /**
42
     * @var Target the message target, a [[Target]] instance. May be `null`.
43
     */
44
    public $target;
45
46
    /**
47
     * @var bool whether the current request is a console request.
48
     */
49
    private $_isConsoleRequest;
50
51
    /**
52
     * Constructor.
53
     * @param array $message the message.
54
     * @param Target|null $target the message target, a [[Target]] instance.
55
     * @param array $config name-value pairs that will be used to initialize the object properties.
56
     */
57 143
    public function __construct(array $message, $target = null, $config = [])
58
    {
59 143
        $this->message = $message;
60 143
        $this->target = $target;
61 143
        parent::__construct($config);
62 142
    }
63
64
    /**
65
     * @inheritDoc
66
     */
67 143
    public function init()
68
    {
69 143
        parent::init();
70 143
        if ($this->target !== null && !$this->target instanceof Target) {
71 1
            throw new InvalidConfigException('`' . get_class($this) . '::target` should be an instance of `' . Target::className() . '`.');
72
        }
73 142
    }
74
75
    /**
76
     * Returns the message category.
77
     * @return string the message category.
78
     */
79 12
    public function getCategory()
80
    {
81 12
        return $this->message[2];
82
    }
83
84
    /**
85
     * Returns the command line.
86
     * @return string|null the command line, `null` if not available.
87
     */
88 12
    public function getCommandLine()
89
    {
90 12
        if (\Yii::$app === null || !$this->getIsConsoleRequest()) {
91 8
            return null;
92
        }
93
94 4
        if (isset($_SERVER['argv'])) {
95 4
            $params = $_SERVER['argv'];
96 4
        } else {
97 4
            $params = [];
98
        }
99 4
        return implode(' ', $params);
100
    }
101
102
    /**
103
     * Returns whether the current request is a console request.
104
     * @return bool whether the current request is a console request.
105
     * @throws InvalidConfigException if unable to determine.
106
     */
107 33
    public function getIsConsoleRequest()
108
    {
109 33
        if ($this->_isConsoleRequest === null && \Yii::$app !== null) {
110 32
            if (\Yii::$app->getRequest() instanceof \yii\console\Request) {
111 16
                $this->_isConsoleRequest = true;
112 32
            } elseif (\Yii::$app->getRequest() instanceof \yii\web\Request) {
113 16
                $this->_isConsoleRequest = false;
114 16
            }
115 32
        }
116 33
        if ($this->_isConsoleRequest === null) {
117 1
            throw new InvalidConfigException('Unable to determine if the application is a console or web application.');
118
        }
119
120 32
        return $this->_isConsoleRequest;
121
    }
122
123
    /**
124
     * Returns the text display of the message level.
125
     * @return string the text display of the message level.
126
     */
127 12
    public function getLevel()
128
    {
129 12
        return Logger::getLevelName($this->message[1]);
130
    }
131
132
    /**
133
     * Returns the string to be prefixed to the message.
134
     * @return string the messsage prefix string.
135
     */
136 12
    public function getPrefix()
137
    {
138 12
        if ($this->target !== null) {
139 8
            return $this->target->getMessagePrefix($this->message);
140
        } else {
141 12
            return '';
142
        }
143
    }
144
145
    /**
146
     * Returns the session ID.
147
     * @return string|null the session ID, `null` if not available.
148
     */
149 12
    public function getSessionId()
150
    {
151
        if (
152 12
            \Yii::$app !== null
153 12
            && \Yii::$app->has('session', true)
154 12
            && \Yii::$app->getSession() !== null
155 12
            && \Yii::$app->getSession()->getIsActive()
156 12
        ) {
157 4
            return \Yii::$app->getSession()->getId();
1 ignored issue
show
Bug introduced by
The method getSession does only exist in yii\web\Application, but not in yii\console\Application.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
158
        } else {
159 8
            return null;
160
        }
161
    }
162
163
    /**
164
     * Returns the additional stack trace as a string.
165
     * @return string|null the stack trace, `null` if not available.
166
     */
167 13
    public function getStackTrace()
168
    {
169 13
        if (!isset($this->message[4]) || empty($this->message[4])) {
170 12
            return null;
171
        }
172
173 1
        $traces = array_map(function ($trace) {
174 1
            return "in {$trace['file']}:{$trace['line']}";
175 1
        }, $this->message[4]);
176 1
        return implode("\n", $traces);
177
    }
178
179
    /**
180
     * Returns the message text.
181
     * @return string the message text.
182
     */
183 12
    public function getText()
184
    {
185 12
        $text = $this->message[0];
186 12
        if (!is_string($text)) {
187 6
            if ($text instanceof \Throwable || $text instanceof \Exception) {
188 3
                $text = (string) $text;
189 3
            } else {
190 3
                $text = VarDumper::export($text);
191
            }
192 6
        }
193 12
        return $text;
194
    }
195
196
    /**
197
     * Returns the message creation timestamp.
198
     * @return float the message creation timestamp.
199
     */
200 12
    public function getTimestamp()
201
    {
202 12
        return $this->message[3];
203
    }
204
205
    /**
206
     * Returns the current absolute URL.
207
     * @return null|string the current absolute URL, `null` if not available.
208
     */
209 12
    public function getUrl()
210
    {
211 12
        if (\Yii::$app === null || $this->getIsConsoleRequest()) {
212 8
            return null;
213
        }
214
215 4
        return Url::current([], true);
216
    }
217
218
    /**
219
     * Returns the user identity ID.
220
     * @return int|string|null the user identity ID, `null` if not available.
221
     */
222 12
    public function getUserId()
223
    {
224
        if (
225 12
            \Yii::$app !== null
226 12
            && \Yii::$app->has('user', true)
227 12
            && \Yii::$app->getUser() !== null
228 12
        ) {
229 4
            $user = \Yii::$app->getUser()->getIdentity(false);
1 ignored issue
show
Bug introduced by
The method getUser does only exist in yii\web\Application, but not in yii\console\Application.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
230 4
            if ($user !== null) {
231 4
                return $user->getId();
232
            }
233
        }
234 8
        return null;
235
    }
236
237
    /**
238
     * Returns the user IP address.
239
     * @return string|null the user IP address, `null` if not available.
240
     */
241 12
    public function getUserIp()
242
    {
243 12
        if (\Yii::$app === null || $this->getIsConsoleRequest()) {
244 8
            return null;
245
        }
246
247 4
        return \Yii::$app->getRequest()->getUserIP();
248
    }
249
}
250