Slack::supports()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php namespace nyx\notify\transports;
2
3
// Internal dependencies
4
use nyx\notify\interfaces;
5
6
/**
7
 * Slack Transport
8
 *
9
 * @package     Nyx\Notify
10
 * @version     0.1.0
11
 * @author      Michal Chojnacki <[email protected]>
12
 * @copyright   2012-2017 Nyx Dev Team
13
 * @link        https://github.com/unyx/nyx
14
 */
15
class Slack implements interfaces\Transport
16
{
17
    /**
18
     * The types Message icons can be of.
19
     */
20
    const ICON_TYPE_URL   = 'icon_url';
21
    const ICON_TYPE_EMOJI = 'icon_emoji';
22
23
    /**
24
     * @var \GuzzleHttp\ClientInterface     The underlying HTTP Client instance.
25
     */
26
    protected $client;
27
28
    /**
29
     * @var string  The oAuth token to authorize API requests with (when not using webhook endpoints).
30
     */
31
    protected $token;
32
33
    /**
34
     * @var string  The (Webhook) endpoint to send messages to (when not using the Web API).
35
     */
36
    protected $endpoint;
37
38
    /**
39
     * @var string  The default username to send messages as.
40
     */
41
    protected $username;
42
43
    /**
44
     * @var string  The default icon to send messages with.
45
     */
46
    protected $icon;
47
48
    /**
49
     * @var string  The default parse mode of Messages. One of the slack\Message::PARSE_* class constants.
50
     */
51
    protected $parse;
52
53
    /**
54
     * @var bool    Whether names (like @someone) should be linked or left raw by Slack.
55
     */
56
    protected $linkNames;
57
58
    /**
59
     * @var bool    Whether Slack should unfurl text-based URLs.
60
     */
61
    protected $unfurlLinks;
62
63
    /**
64
     * @var bool    Whether Slack should unfurl media URLs.
65
     */
66
    protected $unfurlMedia;
67
68
    /**
69
     * @var bool    Whether the text of the messages sent should be parsed as Slack's markdown flavour or treated as
70
     *              raw text.
71
     */
72
    protected $allowMarkdown;
73
74
    /**
75
     * @var array   The attachment fields that should be parsed by Slack's markdown flavour.
76
     */
77
    protected $markdownInAttachments;
78
79
    /**
80
     * Parses an icon "definition" and determines whether it should be treated as an URL or a Slack-recognized
81
     * emoji.
82
     *
83
     * @param   string  $icon   The icon's "definition".
84
     * @return  string          One of the ICON_TYPE_* class constants.
85
     */
86
    public static function determineIconType(string $icon) : string
87
    {
88
        // Filter_var() will do the trick since we're not in a security-sensitive context.
89
        if (filter_var($icon, FILTER_VALIDATE_URL)) {
90
            return self::ICON_TYPE_URL;
91
        }
92
93
        return self::ICON_TYPE_EMOJI;
94
    }
95
96
    /**
97
     * Constructs a new Slack Transport instance.
98
     *
99
     * @param   array                       $config     The Transport's configuration.
100
     * @param   \GuzzleHttp\ClientInterface $client     A Guzzle HTTP Client instance.
101
     * @todo                                            Proper parsing of config options and error-recovery.
102
     */
103
    public function __construct(array $config, \GuzzleHttp\ClientInterface $client)
104
    {
105
        $this->token                 = $config['token']                   ?? null;
106
        $this->endpoint              = $config['endpoint']                ?? null;
107
        $this->username              = $config['username']                ?? null;
108
        $this->icon                  = $config['icon']                    ?? null;
109
        $this->parse                 = $config['parse']                   ?? slack\Message::PARSE_DEFAULT;
110
        $this->linkNames             = $config['link_names']              ?? true;
111
        $this->unfurlLinks           = $config['unfurl_links']            ?? false;
112
        $this->unfurlMedia           = $config['unfurl_media']            ?? true;
113
        $this->allowMarkdown         = $config['allow_markdown']          ?? true;
114
        $this->markdownInAttachments = $config['markdown_in_attachments'] ?? [];
115
116
        $this->client = $client;
117
    }
118
119
    /**
120
     * {@inheritDoc}
121
     *
122
     * @throws  \InvalidArgumentException   When the Notification casts down to a Message without text nor attachments.
123
     */
124
    public function send(interfaces\Notifiable $notifiable, interfaces\Notification $notification)
125
    {
126
        /* @var slack\interfaces\Slackable $notification */
127
        if (!$this->supports($notification)) {
0 ignored issues
show
Documentation introduced by
$notification is of type object<nyx\notify\transp...k\interfaces\Slackable>, but the function expects a object<nyx\notify\interfaces\Notification>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
128
            throw new \InvalidArgumentException('The given Notification is not supported (did you forget to implement the Slackable Interface?).');
129
        }
130
131
        if (false === $notifiable->routeNotification('slack', $message = $notification->toSlack($notifiable))) {
132
            return;
133
        }
134
135
        // Note: The dual 'to()' cast is intended - toSlack() above will let the Notification build the appropriate
136
        // Message while the latter toArray() call flattens the whole structure down into an array that we can more
137
        // easily digest and pass on to Slack itself.
138
        $message = $message->toArray();
139
140
        // We need text or an attachment for the message to actually be displayed in Slack.
141
        if (empty($message['text']) && empty($message['attachments'])) {
142
            throw new \RuntimeException('A message to Slack must contain at least either text or an attachment, got neither.');
143
        }
144
145
        // Apply our defaults where the Message doesn't override them.
146
        $message['token']        = $message['token']    ?? $this->token;
147
        $message['endpoint']     = $message['endpoint'] ?? $this->endpoint;
148
        $message['username']     = $message['username'] ?? $this->username;
149
        $message['parse']        = $message['parse']    ?? $this->parse;
150
        $message['link_names']   = $this->linkNames     ? 1 : 0;
151
        $message['unfurl_links'] = $this->unfurlLinks;
152
        $message['unfurl_media'] = $this->unfurlMedia;
153
        $message['mrkdwn']       = $this->allowMarkdown;
154
        $message['mrkdwn_in']    = $this->markdownInAttachments;
155
156
        // We're applying the icon separately since we need to know what key it's going to be sent as.
157
        $icon = $message['icon'] ?? $this->icon;
158
159
        if ($icon) {
160
            $message[static::determineIconType($icon)] = $icon;
161
        }
162
163
        if (isset($message['response_url'])) {
164
            $this->sendResponse($message);
165
        } elseif ($message['token']) {
166
            $this->sendApiMessage($message);
167
        } elseif ($message['endpoint']) {
168
            $this->sendWebhookMessage($message);
169
        } else {
170
            throw new \InvalidArgumentException('No oAuth token nor webhook endpoint given, could not send the Notification.');
171
        }
172
    }
173
174
    /**
175
     * Performs the actual sending of a Message to a Slack Webhook endpoint.
176
     *
177
     * @param   array   $message    The Message's data (toArray()'ed).
178
     */
179
    protected function sendWebhookMessage(array $message)
180
    {
181
        $this->client->request('POST', $message['endpoint'], [
182
            'json' => $message,
183
        ]);
184
    }
185
186
    /**
187
     * Performs the actual sending of a Message to Slack's Web API.
188
     *
189
     * @param   array   $message    The Message's data (toArray()'ed).
190
     */
191
    protected function sendApiMessage(array $message)
192
    {
193
        // Slack rejects PHP-style arrays so we need to encode them as JSON if they're present.
194
        if (!empty($message['mrkdwn_in'])) {
195
            $message['mrkdwn_in'] = json_encode($message['mrkdwn_in']);
196
        }
197
198
        if (!empty($message['attachments'])) {
199
            $message['attachments'] = json_encode($message['attachments']);
200
        }
201
202
        $this->client->request('POST', 'https://slack.com/api/chat.postMessage', [
203
            'form_params' => $message,
204
        ]);
205
    }
206
207
    /**
208
     * Performs the actual sending of a Response to its Response URL provided by Slack.
209
     *
210
     * @param   array   $message    The Message's data (toArray()'ed).
211
     */
212
    protected function sendResponse(array $message)
213
    {
214
        $this->client->request('POST', $message['response_url'], [
215
            'json' => $message,
216
        ]);
217
    }
218
219
    /**
220
     * {@inheritDoc}
221
     */
222
    public function supports(interfaces\Notification $notification) : bool
223
    {
224
        return ($notification instanceof slack\interfaces\Slackable);
225
    }
226
}
227