Issues (762)

src/Monolog/Handler/SlackHandler.php (1 issue)

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;
13
14
use Monolog\Formatter\FormatterInterface;
15
use Monolog\Logger;
16
use Monolog\Handler\Slack\SlackRecord;
17
18
/**
19
 * Sends notifications through Slack API
20
 *
21
 * @author Greg Kedzierski <[email protected]>
22
 * @see    https://api.slack.com/
23
 */
24
class SlackHandler extends SocketHandler
25
{
26
    /**
27
     * Slack API token
28
     * @var string
29
     */
30
    private $token;
31
32
    /**
33
     * Instance of the SlackRecord util class preparing data for Slack API.
34
     * @var SlackRecord
35
     */
36
    private $slackRecord;
37
38
    /**
39
     * @param  string                    $token                  Slack API token
40
     * @param  string                    $channel                Slack channel (encoded ID or name)
41
     * @param  string|null               $username               Name of a bot
42
     * @param  bool                      $useAttachment          Whether the message should be added to Slack as attachment (plain text otherwise)
43
     * @param  string|null               $iconEmoji              The emoji name to use (or null)
44
     * @param  int                       $level                  The minimum logging level at which this handler will be triggered
45
     * @param  bool                      $bubble                 Whether the messages that are handled can bubble up the stack or not
46
     * @param  bool                      $useShortAttachment     Whether the the context/extra messages added to Slack as attachments are in a short style
47
     * @param  bool                      $includeContextAndExtra Whether the attachment should include context and extra data
48
     * @param  array                     $excludeFields          Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2']
49
     * @throws MissingExtensionException If no OpenSSL PHP extension configured
50
     */
51
    public function __construct($token, $channel, $username = null, $useAttachment = true, $iconEmoji = null, $level = Logger::CRITICAL, $bubble = true, $useShortAttachment = false, $includeContextAndExtra = false, array $excludeFields = array())
52
    {
53
        if (!extension_loaded('openssl')) {
54
            throw new MissingExtensionException('The OpenSSL PHP extension is required to use the SlackHandler');
55
        }
56
57
        parent::__construct('ssl://slack.com:443', $level, $bubble);
58
59
        $this->slackRecord = new SlackRecord(
60
            $channel,
61
            $username,
62
            $useAttachment,
63
            $iconEmoji,
64
            $useShortAttachment,
65
            $includeContextAndExtra,
66
            $excludeFields,
67
            $this->formatter
68
        );
69
70
        $this->token = $token;
71
    }
72
73
    public function getSlackRecord()
74
    {
75
        return $this->slackRecord;
76
    }
77
78
    /**
79
     * {@inheritdoc}
80
     *
81
     * @param  array  $record
82
     * @return string
83
     */
84
    protected function generateDataStream($record)
85
    {
86
        $content = $this->buildContent($record);
87
88
        return $this->buildHeader($content) . $content;
89
    }
90
91
    /**
92
     * Builds the body of API call
93
     *
94
     * @param  array  $record
95
     * @return string
96
     */
97
    private function buildContent($record)
98
    {
99
        $dataArray = $this->prepareContentData($record);
100
101
        return http_build_query($dataArray);
102
    }
103
104
    /**
105
     * Prepares content data
106
     *
107
     * @param  array $record
108
     * @return array
109
     */
110
    protected function prepareContentData($record)
111
    {
112
        $dataArray = $this->slackRecord->getSlackData($record);
113
        $dataArray['token'] = $this->token;
114
115
        if (!empty($dataArray['attachments'])) {
116
            $dataArray['attachments'] = json_encode($dataArray['attachments']);
117
        }
118
119
        return $dataArray;
120
    }
121
122
    /**
123
     * Builds the header of the API Call
124
     *
125
     * @param  string $content
126
     * @return string
127
     */
128
    private function buildHeader($content)
129
    {
130
        $header = "POST /api/chat.postMessage HTTP/1.1\r\n";
131
        $header .= "Host: slack.com\r\n";
132
        $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
133
        $header .= "Content-Length: " . strlen($content) . "\r\n";
134
        $header .= "\r\n";
135
136
        return $header;
137
    }
138
139
    /**
140
     * {@inheritdoc}
141
     *
142
     * @param array $record
143
     */
144
    protected function write(array $record)
145
    {
146
        parent::write($record);
147
        $this->finalizeWrite();
148
    }
149
150
    /**
151
     * Finalizes the request by reading some bytes and then closing the socket
152
     *
153
     * If we do not read some but close the socket too early, slack sometimes
154
     * drops the request entirely.
155
     */
156
    protected function finalizeWrite()
157
    {
158
        $res = $this->getResource();
159
        if (is_resource($res)) {
160
            @fread($res, 2048);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for fread(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

160
            /** @scrutinizer ignore-unhandled */ @fread($res, 2048);

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...
161
        }
162
        $this->closeSocket();
163
    }
164
165
    /**
166
     * Returned a Slack message attachment color associated with
167
     * provided level.
168
     *
169
     * @param  int    $level
170
     * @return string
171
     * @deprecated Use underlying SlackRecord instead
172
     */
173
    protected function getAttachmentColor($level)
174
    {
175
        trigger_error(
176
            'SlackHandler::getAttachmentColor() is deprecated. Use underlying SlackRecord instead.',
177
            E_USER_DEPRECATED
178
        );
179
180
        return $this->slackRecord->getAttachmentColor($level);
181
    }
182
183
    /**
184
     * Stringifies an array of key/value pairs to be used in attachment fields
185
     *
186
     * @param  array  $fields
187
     * @return string
188
     * @deprecated Use underlying SlackRecord instead
189
     */
190
    protected function stringify($fields)
191
    {
192
        trigger_error(
193
            'SlackHandler::stringify() is deprecated. Use underlying SlackRecord instead.',
194
            E_USER_DEPRECATED
195
        );
196
197
        return $this->slackRecord->stringify($fields);
198
    }
199
200
    public function setFormatter(FormatterInterface $formatter)
201
    {
202
        parent::setFormatter($formatter);
203
        $this->slackRecord->setFormatter($formatter);
204
205
        return $this;
206
    }
207
208
    public function getFormatter()
209
    {
210
        $formatter = parent::getFormatter();
211
        $this->slackRecord->setFormatter($formatter);
212
213
        return $formatter;
214
    }
215
}
216