Completed
Push — master ( d83ba1...8a9b13 )
by Sergey
02:13
created

SlackTarget.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace sergeymakinen\log;
4
5
use yii\helpers\Json;
6
use yii\helpers\Url;
7
use yii\helpers\VarDumper;
8
use yii\log\Logger;
9
use yii\log\Target;
10
use yii\web\Request;
11
12
class SlackTarget extends Target
13
{
14
    /**
15
     * Incoming Webhook URL.
16
     *
17
     * @var string
18
     */
19
    public $webhookUrl;
20
21
    /**
22
     * Displayed username.
23
     *
24
     * @var string
25
     */
26
    public $username;
27
28
    /**
29
     * Icon URL.
30
     *
31
     * @var string
32
     */
33
    public $iconUrl;
34
35
    /**
36
     * Icon Emoji.
37
     *
38
     * @var string
39
     */
40
    public $iconEmoji;
41
42
    /**
43
     * Channel or Direct Message.
44
     *
45
     * @var string
46
     */
47
    public $channel;
48
49
    /**
50
     * Colors per a Logger level.
51
     *
52
     * @var array
53
     */
54
    public $colors = [
55
        Logger::LEVEL_ERROR => 'danger',
56
        Logger::LEVEL_WARNING => 'warning'
57
    ];
58
59
    /**
60
     * @inheritDoc
61
     */
62
    public function export()
63
    {
64
        if (!isset($this->webhookUrl)) {
65
            return;
66
        }
67
68
        $payload = [
69
            'parse' => 'none',
70
            'attachments' => array_map([$this, 'formatMessageAttachment'], $this->messages)
71
        ];
72
        if (isset($this->username)) {
73
            $payload['username'] = $this->username;
74
        }
75
        if (isset($this->iconUrl)) {
76
            $payload['icon_url'] = $this->iconUrl;
77
        }
78
        if (isset($this->iconEmoji)) {
79
            $payload['icon_emoji'] = $this->iconEmoji;
80
        }
81
        if (isset($this->channel)) {
82
            $payload['channel'] = $this->channel;
83
        }
84
        $context  = stream_context_create([
85
            'http' => [
86
                'method' => 'POST',
87
                'header' => "Content-Type: application/json; charset=UTF-8\r\n",
88
                'content' => Json::encode($payload)
89
            ]
90
        ]);
91
        @file_get_contents($this->webhookUrl, false, $context);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
92
    }
93
94
    /**
95
     * Encodes special chars in a message as HTML entities.
96
     *
97
     * @param string $message
98
     *
99
     * @return string
100
     */
101
    protected static function encodeMessage($message)
102
    {
103
        return htmlspecialchars($message, ENT_NOQUOTES, 'UTF-8');
104
    }
105
106
    /**
107
     * Returns a properly formatted message attachment for Slack API.
108
     *
109
     * @param array $message
110
     *
111
     * @return array
112
     */
113
    protected function formatMessageAttachment($message)
114
    {
115
        list($text, $level, $category, $timestamp) = $message;
116
        $attachment = [
117
            'fallback' => static::encodeMessage($this->formatMessage($message)),
118
            'title' => ucwords(Logger::getLevelName($level)),
119
            'fields' => [
120
                [
121
                    'title' => 'Level',
122
                    'value' => Logger::getLevelName($level),
123
                    'short' => true
124
                ],
125
                [
126
                    'title' => 'Category',
127
                    'value' => '`' . static::encodeMessage($category) . '`',
128
                    'short' => true
129
                ]
130
            ],
131
            'footer' => static::class,
132
            'ts' => (integer) round($timestamp),
133
            'mrkdwn_in' => [
134
                'fields',
135
                'text'
136
            ]
137
        ];
138
        if ($this->prefix !== null) {
139
            $attachment['fields'][] = [
140
                'title' => 'Prefix',
141
                'value' => '`' . static::encodeMessage(call_user_func($this->prefix, $message)) . '`',
142
                'short' => true,
143
            ];
144
        }
145
        if (isset(\Yii::$app)) {
146
            if (isset($_SERVER['argv'])) {
147
                $attachment['author_name'] = implode(' ', $_SERVER['argv']);
148
            } elseif (\Yii::$app->request instanceof Request) {
149
                $attachment['author_name'] = Url::current([], true);
150
                $attachment['author_link'] = $attachment['author_name'];
151
                $attachment['fields'][] = [
152
                    'title' => 'User IP',
153
                    'value' => \Yii::$app->request->userIP,
154
                    'short' => true,
155
                ];
156
            }
157
            if (\Yii::$app->has('user', true) && isset(\Yii::$app->user)) {
158
                $user = \Yii::$app->user->getIdentity(false);
159
                if (isset($user)) {
160
                    $attachment['fields'][] = [
161
                        'title' => 'User ID',
162
                        'value' => $user->getId(),
163
                        'short' => true,
164
                    ];
165
                }
166
            }
167
            if (\Yii::$app->has('session', true) && isset(\Yii::$app->session)) {
168
                if (\Yii::$app->session->isActive) {
169
                    $attachment['fields'][] = [
170
                        'title' => 'Session ID',
171
                        'value' => \Yii::$app->session->id,
172
                        'short' => true,
173
                    ];
174
                }
175
            }
176
        }
177
        if (isset($this->colors[$level])) {
178
            $attachment['color'] = $this->colors[$level];
179
        }
180
        if (!is_string($text)) {
181
            if ($text instanceof \Throwable || $text instanceof \Exception) {
182
                $text = (string) $text;
183
            } else {
184
                $text = VarDumper::export($text);
185
            }
186
        }
187
        $attachment['text'] = "```\n" . static::encodeMessage($text) . "\n```";
188
        $traces = [];
189
        if (isset($message[4])) {
190
            foreach ($message[4] as $trace) {
191
                $traces[] = "in {$trace['file']}:{$trace['line']}";
192
            }
193
        }
194
        if (!empty($traces)) {
195
            $attachment['fields'][] = [
196
                'title' => 'Stack Trace',
197
                'value' => "```\n" . static::encodeMessage(implode("\n", $traces)) . "\n```",
198
                'short' => false,
199
            ];
200
        }
201
        return $attachment;
202
    }
203
}
204