Passed
Push — master ( a8aa44...c5c6d0 )
by Sergey
01:55
created

SlackTarget::formatMessageAttachment()   C

Complexity

Conditions 9
Paths 48

Size

Total Lines 59
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 59
rs 6.9133
c 0
b 0
f 0
cc 9
eloc 41
nc 48
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Slack log target for Yii 2
4
 *
5
 * @see       https://github.com/sergeymakinen/yii2-slack-log
6
 * @copyright Copyright (c) 2016 Sergey Makinen (https://makinen.ru)
7
 * @license   https://github.com/sergeymakinen/yii2-slack-log/blob/master/LICENSE The MIT License
8
 */
9
10
namespace sergeymakinen\log;
11
12
use yii\helpers\Json;
13
use yii\helpers\Url;
14
use yii\helpers\VarDumper;
15
use yii\log\Logger;
16
use yii\log\Target;
17
use yii\web\Request;
18
19
class SlackTarget extends Target
20
{
21
    /**
22
     * Incoming Webhook URL.
23
     *
24
     * @var string
25
     */
26
    public $webhookUrl;
27
28
    /**
29
     * Displayed username.
30
     *
31
     * @var string
32
     */
33
    public $username;
34
35
    /**
36
     * Icon URL.
37
     *
38
     * @var string
39
     */
40
    public $iconUrl;
41
42
    /**
43
     * Icon Emoji.
44
     *
45
     * @var string
46
     */
47
    public $iconEmoji;
48
49
    /**
50
     * Channel or Direct Message.
51
     *
52
     * @var string
53
     */
54
    public $channel;
55
56
    /**
57
     * Colors per a Logger level.
58
     *
59
     * @var array
60
     */
61
    public $colors = [
62
        Logger::LEVEL_ERROR => 'danger',
63
        Logger::LEVEL_WARNING => 'warning'
64
    ];
65
66
    /**
67
     * @inheritDoc
68
     */
69
    public function export()
70
    {
71
        if (!isset($this->webhookUrl)) {
72
            return;
73
        }
74
75
        $payload = [
76
            'parse' => 'none',
77
            'attachments' => array_map([$this, 'formatMessageAttachment'], $this->messages)
78
        ];
79
        $this->copyToPayload($payload, 'username', $this->username);
80
        $this->copyToPayload($payload, 'icon_url', $this->iconUrl);
81
        $this->copyToPayload($payload, 'icon_emoji', $this->iconEmoji);
82
        $this->copyToPayload($payload, 'channel', $this->channel);
83
        if (extension_loaded('curl')) {
84
            $curl = curl_init();
85
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
86
            curl_setopt($curl, CURLOPT_POST, true);
87
            curl_setopt($curl, CURLOPT_HTTPHEADER, ['Content-Type: application/json; charset=UTF-8']);
88
            curl_setopt($curl, CURLOPT_POSTFIELDS, Json::encode($payload));
89
            curl_setopt($curl, CURLOPT_URL, $this->webhookUrl);
90
            curl_exec($curl);
91
            curl_close($curl);
92
        } else {
93
            $context = stream_context_create([
94
                'http' => [
95
                    'method' => 'POST',
96
                    'header' => "Content-Type: application/json; charset=UTF-8\r\n",
97
                    'content' => Json::encode($payload)
98
                ]
99
            ]);
100
            file_get_contents($this->webhookUrl, false, $context);
101
        }
102
    }
103
104
    /**
105
     * Encodes special chars in a message as HTML entities.
106
     *
107
     * @param string $message
108
     *
109
     * @return string
110
     */
111
    protected static function encodeMessage($message)
112
    {
113
        return htmlspecialchars($message, ENT_NOQUOTES, 'UTF-8');
114
    }
115
116
    /**
117
     * Returns a properly formatted message attachment for Slack API.
118
     *
119
     * @param array $message
120
     *
121
     * @return array
122
     */
123
    protected function formatMessageAttachment($message)
124
    {
125
        list($text, $level, $category, $timestamp) = $message;
126
        $attachment = [
127
            'fallback' => static::encodeMessage($this->formatMessage($message)),
128
            'title' => ucwords(Logger::getLevelName($level)),
129
            'fields' => [
130
                [
131
                    'title' => 'Level',
132
                    'value' => Logger::getLevelName($level),
133
                    'short' => true
134
                ],
135
                [
136
                    'title' => 'Category',
137
                    'value' => '`' . static::encodeMessage($category) . '`',
138
                    'short' => true
139
                ]
140
            ],
141
            'footer' => static::class,
142
            'ts' => (integer) round($timestamp),
143
            'mrkdwn_in' => [
144
                'fields',
145
                'text'
146
            ]
147
        ];
148
        if (isset($this->prefix)) {
149
            $attachment['fields'][] = [
150
                'title' => 'Prefix',
151
                'value' => '`' . static::encodeMessage(call_user_func($this->prefix, $message)) . '`',
152
                'short' => true,
153
            ];
154
        }
155
        $this->applyApplicationFormatToAttachment($attachment);
156
        if (isset($this->colors[$level])) {
157
            $attachment['color'] = $this->colors[$level];
158
        }
159
        if (!is_string($text)) {
160
            if ($text instanceof \Throwable || $text instanceof \Exception) {
161
                $text = (string) $text;
162
            } else {
163
                $text = VarDumper::export($text);
164
            }
165
        }
166
        $attachment['text'] = "```\n" . static::encodeMessage($text) . "\n```";
167
        $traces = [];
168
        if (isset($message[4])) {
169
            foreach ($message[4] as $trace) {
170
                $traces[] = "in {$trace['file']}:{$trace['line']}";
171
            }
172
        }
173
        if (!empty($traces)) {
174
            $attachment['fields'][] = [
175
                'title' => 'Stack Trace',
176
                'value' => "```\n" . static::encodeMessage(implode("\n", $traces)) . "\n```",
177
                'short' => false,
178
            ];
179
        }
180
        return $attachment;
181
    }
182
183
    /**
184
     * Inserts current application details to the attachment.
185
     *
186
     * @param array $attachment
187
     */
188
    private function applyApplicationFormatToAttachment(array &$attachment)
189
    {
190
        if (!isset(\Yii::$app)) {
191
            return;
192
        }
193
194
        if (\Yii::$app->getRequest() instanceof Request) {
195
            $attachment['author_name'] = Url::current([], true);
196
            $attachment['author_link'] = $attachment['author_name'];
197
            $attachment['fields'][] = [
198
                'title' => 'User IP',
199
                'value' => \Yii::$app->getRequest()->getUserIP(),
200
                'short' => true,
201
            ];
202
        } else {
203
            $attachment['author_name'] = implode(' ', \Yii::$app->getRequest()->getParams());
204
        }
205
        if (\Yii::$app->has('user', true) && !is_null(\Yii::$app->getUser())) {
206
            $user = \Yii::$app->getUser()->getIdentity(false);
207
            if (isset($user)) {
208
                $attachment['fields'][] = [
209
                    'title' => 'User ID',
210
                    'value' => $user->getId(),
211
                    'short' => true,
212
                ];
213
            }
214
        }
215
        if (
216
            \Yii::$app->has('session', true)
217
            && !is_null(\Yii::$app->getSession())
218
            && \Yii::$app->getSession()->getIsActive()
219
        ) {
220
            $attachment['fields'][] = [
221
                'title' => 'Session ID',
222
                'value' => \Yii::$app->getSession()->getId(),
223
                'short' => true,
224
            ];
225
        }
226
    }
227
228
    /**
229
     * Copies the value to the payload if the value is set.
230
     *
231
     * @param array $payload
232
     * @param string $name
233
     * @param mixed $value
234
     */
235
    private function copyToPayload(array &$payload, $name, $value)
236
    {
237
        if (isset($value)) {
238
            $payload[$name] = $value;
239
        }
240
    }
241
}
242