Completed
Push — master ( cdeef2...f03c61 )
by Justin
03:14
created

SlackRecord::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 10
nc 2
nop 8
dl 0
loc 13
rs 9.9332
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/*
4
 * This file is part of the Monolog package.
5
 *
6
 * (c) Jordi Boggiano <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Monolog\Handler\Slack;
13
14
use Monolog\Logger;
15
use Monolog\Formatter\NormalizerFormatter;
16
use Monolog\Formatter\FormatterInterface;
17
18
/**
19
 * Slack record utility helping to log to Slack webhooks or API.
20
 *
21
 * @author Greg Kedzierski <[email protected]>
22
 * @author Haralan Dobrev <[email protected]>
23
 * @see    https://api.slack.com/incoming-webhooks
24
 * @see    https://api.slack.com/docs/message-attachments
25
 */
26
class SlackRecord
27
{
28
    const COLOR_DANGER = 'danger';
29
30
    const COLOR_WARNING = 'warning';
31
32
    const COLOR_GOOD = 'good';
33
34
    const COLOR_DEFAULT = '#e3e4e6';
35
36
    /**
37
     * Slack channel (encoded ID or name)
38
     * @var string|null
39
     */
40
    private $channel;
41
42
    /**
43
     * Name of a bot
44
     * @var string|null
45
     */
46
    private $username;
47
48
    /**
49
     * User icon e.g. 'ghost', 'http://example.com/user.png'
50
     * @var string
51
     */
52
    private $userIcon;
53
54
    /**
55
     * Whether the message should be added to Slack as attachment (plain text otherwise)
56
     * @var bool
57
     */
58
    private $useAttachment;
59
60
    /**
61
     * Whether the the context/extra messages added to Slack as attachments are in a short style
62
     * @var bool
63
     */
64
    private $useShortAttachment;
65
66
    /**
67
     * Whether the attachment should include context and extra data
68
     * @var bool
69
     */
70
    private $includeContextAndExtra;
71
72
    /**
73
     * Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
74
     * @var array
75
     */
76
    private $excludeFields;
77
78
    /**
79
     * @var FormatterInterface
80
     */
81
    private $formatter;
82
83
    /**
84
     * @var NormalizerFormatter
85
     */
86
    private $normalizerFormatter;
87
88
    public function __construct($channel = null, $username = null, $useAttachment = true, $userIcon = null, $useShortAttachment = false, $includeContextAndExtra = false, array $excludeFields = array(), FormatterInterface $formatter = null)
89
    {
90
        $this->channel = $channel;
91
        $this->username = $username;
92
        $this->userIcon = trim($userIcon, ':');
93
        $this->useAttachment = $useAttachment;
94
        $this->useShortAttachment = $useShortAttachment;
95
        $this->includeContextAndExtra = $includeContextAndExtra;
96
        $this->excludeFields = $excludeFields;
97
        $this->formatter = $formatter;
98
99
        if ($this->includeContextAndExtra) {
100
            $this->normalizerFormatter = new NormalizerFormatter();
101
        }
102
    }
103
104
    public function getSlackData(array $record)
105
    {
106
        $dataArray = array();
107
        $record = $this->excludeFields($record);
108
109
        if ($this->username) {
110
            $dataArray['username'] = $this->username;
111
        }
112
113
        if ($this->channel) {
114
            $dataArray['channel'] = $this->channel;
115
        }
116
117
        if ($this->formatter && !$this->useAttachment) {
118
            $message = $this->formatter->format($record);
119
        } else {
120
            $message = $record['message'];
121
        }
122
123
        if ($this->useAttachment) {
124
            $attachment = array(
125
                'fallback'  => $message,
126
                'text'      => $message,
127
                'color'     => $this->getAttachmentColor($record['level']),
128
                'fields'    => array(),
129
                'mrkdwn_in' => array('fields'),
130
                'ts'        => $record['datetime']->getTimestamp()
131
            );
132
133
            if ($this->useShortAttachment) {
134
                $attachment['title'] = $record['level_name'];
135
            } else {
136
                $attachment['title'] = 'Message';
137
                $attachment['fields'][] = $this->generateAttachmentField('Level', $record['level_name']);
138
            }
139
140
141
            if ($this->includeContextAndExtra) {
142
                foreach (array('extra', 'context') as $key) {
143
                    if (empty($record[$key])) {
144
                        continue;
145
                    }
146
147
                    if ($this->useShortAttachment) {
148
                        $attachment['fields'][] = $this->generateAttachmentField(
149
                            ucfirst($key),
150
                            $record[$key]
151
                        );
152
                    } else {
153
                        // Add all extra fields as individual fields in attachment
154
                        $attachment['fields'] = array_merge(
155
                            $attachment['fields'],
156
                            $this->generateAttachmentFields($record[$key])
157
                        );
158
                    }
159
                }
160
            }
161
162
            $dataArray['attachments'] = array($attachment);
163
        } else {
164
            $dataArray['text'] = $message;
165
        }
166
167
        if ($this->userIcon) {
168
            if (filter_var($this->userIcon, FILTER_VALIDATE_URL)) {
169
                $dataArray['icon_url'] = $this->userIcon;
170
            } else {
171
                $dataArray['icon_emoji'] = ":{$this->userIcon}:";
172
            }
173
        }
174
175
        return $dataArray;
176
    }
177
178
    /**
179
     * Returned a Slack message attachment color associated with
180
     * provided level.
181
     *
182
     * @param  int    $level
183
     * @return string
184
     */
185
    public function getAttachmentColor($level)
186
    {
187
        switch (true) {
188
            case $level >= Logger::ERROR:
189
                return self::COLOR_DANGER;
190
            case $level >= Logger::WARNING:
191
                return self::COLOR_WARNING;
192
            case $level >= Logger::INFO:
193
                return self::COLOR_GOOD;
194
            default:
195
                return self::COLOR_DEFAULT;
196
        }
197
    }
198
199
    /**
200
     * Stringifies an array of key/value pairs to be used in attachment fields
201
     *
202
     * @param array $fields
203
     *
204
     * @return string
205
     */
206
    public function stringify($fields)
207
    {
208
        $normalized = $this->normalizerFormatter->format($fields);
209
        $prettyPrintFlag = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 128;
210
211
        $hasSecondDimension = count(array_filter($normalized, 'is_array'));
212
        $hasNonNumericKeys = !count(array_filter(array_keys($normalized), 'is_numeric'));
213
214
        return $hasSecondDimension || $hasNonNumericKeys
215
            ? json_encode($normalized, $prettyPrintFlag)
216
            : json_encode($normalized);
217
    }
218
219
    /**
220
     * Sets the formatter
221
     *
222
     * @param FormatterInterface $formatter
223
     */
224
    public function setFormatter(FormatterInterface $formatter)
225
    {
226
        $this->formatter = $formatter;
227
    }
228
229
    /**
230
     * Generates attachment field
231
     *
232
     * @param string $title
233
     * @param string|array $value\
234
     *
235
     * @return array
236
     */
237
    private function generateAttachmentField($title, $value)
238
    {
239
        $value = is_array($value)
240
            ? sprintf('```%s```', $this->stringify($value))
241
            : $value;
242
243
        return array(
244
            'title' => $title,
245
            'value' => $value,
246
            'short' => false
247
        );
248
    }
249
250
    /**
251
     * Generates a collection of attachment fields from array
252
     *
253
     * @param array $data
254
     *
255
     * @return array
256
     */
257
    private function generateAttachmentFields(array $data)
258
    {
259
        $fields = array();
260
        foreach ($data as $key => $value) {
261
            $fields[] = $this->generateAttachmentField($key, $value);
262
        }
263
264
        return $fields;
265
    }
266
267
    /**
268
     * Get a copy of record with fields excluded according to $this->excludeFields
269
     *
270
     * @param array $record
271
     *
272
     * @return array
273
     */
274
    private function excludeFields(array $record)
275
    {
276
        foreach ($this->excludeFields as $field) {
277
            $keys = explode('.', $field);
278
            $node = &$record;
279
            $lastKey = end($keys);
280
            foreach ($keys as $key) {
281
                if (!isset($node[$key])) {
282
                    break;
283
                }
284
                if ($lastKey === $key) {
285
                    unset($node[$key]);
286
                    break;
287
                }
288
                $node = &$node[$key];
289
            }
290
        }
291
292
        return $record;
293
    }
294
}
295