This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | namespace Slack; |
||
3 | |||
4 | use Devristo\Phpws\Client\WebSocket; |
||
5 | use Devristo\Phpws\Messaging\WebSocketMessageInterface; |
||
6 | use Evenement\EventEmitterTrait; |
||
7 | use React\Promise; |
||
8 | use Slack\Message\Message; |
||
9 | |||
10 | /** |
||
11 | * A client for the Slack real-time messaging API. |
||
12 | */ |
||
13 | class RealTimeClient extends ApiClient |
||
14 | { |
||
15 | use EventEmitterTrait; |
||
16 | |||
17 | /** |
||
18 | * @var WebSocket A websocket connection to the Slack API. |
||
19 | */ |
||
20 | protected $websocket; |
||
21 | |||
22 | /** |
||
23 | * @var int The ID of the last payload sent to Slack. |
||
24 | */ |
||
25 | protected $lastMessageId = 0; |
||
26 | |||
27 | /** |
||
28 | * @var array An array of pending messages waiting for successful confirmation |
||
29 | * from Slack. |
||
30 | */ |
||
31 | protected $pendingMessages = []; |
||
32 | |||
33 | /** |
||
34 | * @var bool Indicates if the client is connected. |
||
35 | */ |
||
36 | protected $connected = false; |
||
37 | |||
38 | /** |
||
39 | * @var Team The team logged in to. |
||
40 | */ |
||
41 | protected $team; |
||
42 | |||
43 | /** |
||
44 | * @var array A map of users. |
||
45 | */ |
||
46 | protected $users = []; |
||
47 | |||
48 | /** |
||
49 | * @var array A map of channels. |
||
50 | */ |
||
51 | protected $channels = []; |
||
52 | |||
53 | /** |
||
54 | * @var array A map of groups. |
||
55 | */ |
||
56 | protected $groups = []; |
||
57 | |||
58 | /** |
||
59 | * @var array A map of direct message channels. |
||
60 | */ |
||
61 | protected $dms = []; |
||
62 | |||
63 | /** |
||
64 | * @var array A map of bots. |
||
65 | */ |
||
66 | protected $bots = []; |
||
67 | |||
68 | /** |
||
69 | * Connects to the real-time messaging server. |
||
70 | * |
||
71 | * @return \React\Promise\PromiseInterface |
||
72 | */ |
||
73 | public function connect() |
||
74 | { |
||
75 | $deferred = new Promise\Deferred(); |
||
76 | |||
77 | // Request a real-time connection... |
||
78 | $this->apiCall('rtm.start') |
||
79 | |||
80 | // then connect to the socket... |
||
81 | ->then(function (Payload $response) { |
||
82 | $responseData = $response->getData(); |
||
83 | // get the team info |
||
84 | $this->team = new Team($this, $responseData['team']); |
||
85 | |||
86 | // Populate self user. |
||
87 | $this->users[$responseData['self']['id']] = new User($this, $responseData['self']); |
||
88 | |||
89 | // populate list of users |
||
90 | foreach ($responseData['users'] as $data) { |
||
91 | $this->users[$data['id']] = new User($this, $data); |
||
92 | } |
||
93 | |||
94 | // populate list of channels |
||
95 | foreach ($responseData['channels'] as $data) { |
||
96 | $this->channels[$data['id']] = new Channel($this, $data); |
||
97 | } |
||
98 | |||
99 | // populate list of groups |
||
100 | foreach ($responseData['groups'] as $data) { |
||
101 | $this->groups[$data['id']] = new Group($this, $data); |
||
102 | } |
||
103 | |||
104 | // populate list of dms |
||
105 | foreach ($responseData['ims'] as $data) { |
||
106 | $this->dms[$data['id']] = new DirectMessageChannel($this, $data); |
||
107 | } |
||
108 | |||
109 | // populate list of bots |
||
110 | foreach ($responseData['bots'] as $data) { |
||
111 | $this->bots[$data['id']] = new Bot($this, $data); |
||
112 | } |
||
113 | |||
114 | // Log PHPWS things to stderr |
||
115 | $logger = new \Zend\Log\Logger(); |
||
116 | $logger->addWriter(new \Zend\Log\Writer\Stream('php://stderr')); |
||
117 | |||
118 | // initiate the websocket connection |
||
119 | $this->websocket = new WebSocket($responseData['url'], $this->loop, $logger); |
||
120 | $this->websocket->on('message', function ($message) { |
||
121 | $this->onMessage($message); |
||
122 | }); |
||
123 | |||
124 | return $this->websocket->open(); |
||
125 | }, function($exception) use ($deferred) { |
||
126 | // if connection was not succesfull |
||
127 | $deferred->reject(new ConnectionException( |
||
128 | 'Could not connect to Slack API: '. $exception->getMessage(), |
||
129 | $exception->getCode() |
||
130 | )); |
||
131 | }) |
||
132 | |||
133 | // then wait for the connection to be ready. |
||
134 | ->then(function () use ($deferred) { |
||
135 | $this->once('hello', function () use ($deferred) { |
||
136 | $deferred->resolve(); |
||
137 | }); |
||
138 | |||
139 | $this->once('error', function ($data) use ($deferred) { |
||
140 | $deferred->reject(new ConnectionException( |
||
141 | 'Could not connect to WebSocket: '.$data['error']['msg'], |
||
142 | $data['error']['code'])); |
||
143 | }); |
||
144 | }); |
||
145 | |||
146 | return $deferred->promise(); |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * Disconnects the client. |
||
151 | */ |
||
152 | public function disconnect() |
||
153 | { |
||
154 | if (!$this->connected) { |
||
155 | return Promise\reject(new ConnectionException('Client not connected. Did you forget to call `connect()`?')); |
||
156 | } |
||
157 | |||
158 | $this->websocket->close(); |
||
159 | $this->connected = false; |
||
160 | } |
||
161 | |||
162 | /** |
||
163 | * {@inheritDoc} |
||
164 | */ |
||
165 | public function getTeam() |
||
166 | { |
||
167 | if (!$this->connected) { |
||
168 | return Promise\reject(new ConnectionException('Client not connected. Did you forget to call `connect()`?')); |
||
169 | } |
||
170 | |||
171 | return Promise\resolve($this->team); |
||
172 | } |
||
173 | |||
174 | /** |
||
175 | * {@inheritDoc} |
||
176 | */ |
||
177 | View Code Duplication | public function getChannels() |
|
178 | { |
||
179 | if (!$this->connected) { |
||
180 | return Promise\reject(new ConnectionException('Client not connected. Did you forget to call `connect()`?')); |
||
181 | } |
||
182 | |||
183 | return Promise\resolve(array_values($this->channels)); |
||
184 | } |
||
185 | |||
186 | /** |
||
187 | * {@inheritDoc} |
||
188 | */ |
||
189 | View Code Duplication | public function getChannelById($id) |
|
190 | { |
||
191 | if (!$this->connected) { |
||
192 | return Promise\reject(new ConnectionException('Client not connected. Did you forget to call `connect()`?')); |
||
193 | } |
||
194 | |||
195 | if (!isset($this->channels[$id])) { |
||
196 | return Promise\reject(new ApiException("No channel exists for ID '$id'.")); |
||
197 | } |
||
198 | |||
199 | return Promise\resolve($this->channels[$id]); |
||
200 | } |
||
201 | |||
202 | /** |
||
203 | * {@inheritDoc} |
||
204 | */ |
||
205 | View Code Duplication | public function getGroups() |
|
206 | { |
||
207 | if (!$this->connected) { |
||
208 | return Promise\reject(new ConnectionException('Client not connected. Did you forget to call `connect()`?')); |
||
209 | } |
||
210 | |||
211 | return Promise\resolve(array_values($this->groups)); |
||
212 | } |
||
213 | |||
214 | /** |
||
215 | * {@inheritDoc} |
||
216 | */ |
||
217 | View Code Duplication | public function getGroupById($id) |
|
218 | { |
||
219 | if (!$this->connected) { |
||
220 | return Promise\reject(new ConnectionException('Client not connected. Did you forget to call `connect()`?')); |
||
221 | } |
||
222 | |||
223 | if (!isset($this->groups[$id])) { |
||
224 | return Promise\reject(new ApiException("No group exists for ID '$id'.")); |
||
225 | } |
||
226 | |||
227 | return Promise\resolve($this->groups[$id]); |
||
228 | } |
||
229 | |||
230 | /** |
||
231 | * {@inheritDoc} |
||
232 | */ |
||
233 | View Code Duplication | public function getDMs() |
|
234 | { |
||
235 | if (!$this->connected) { |
||
236 | return Promise\reject(new ConnectionException('Client not connected. Did you forget to call `connect()`?')); |
||
237 | } |
||
238 | |||
239 | return Promise\resolve(array_values($this->dms)); |
||
240 | } |
||
241 | |||
242 | /** |
||
243 | * {@inheritDoc} |
||
244 | */ |
||
245 | View Code Duplication | public function getDMById($id) |
|
246 | { |
||
247 | if (!$this->connected) { |
||
248 | return Promise\reject(new ConnectionException('Client not connected. Did you forget to call `connect()`?')); |
||
249 | } |
||
250 | |||
251 | if (!isset($this->dms[$id])) { |
||
252 | return Promise\reject(new ApiException("No DM exists for ID '$id'.")); |
||
253 | } |
||
254 | |||
255 | return Promise\resolve($this->dms[$id]); |
||
256 | } |
||
257 | |||
258 | /** |
||
259 | * {@inheritDoc} |
||
260 | */ |
||
261 | View Code Duplication | public function getUsers() |
|
262 | { |
||
263 | if (!$this->connected) { |
||
264 | return Promise\reject(new ConnectionException('Client not connected. Did you forget to call `connect()`?')); |
||
265 | } |
||
266 | |||
267 | return Promise\resolve(array_values($this->users)); |
||
268 | } |
||
269 | |||
270 | /** |
||
271 | * {@inheritDoc} |
||
272 | */ |
||
273 | View Code Duplication | public function getUserById($id) |
|
274 | { |
||
275 | if (!$this->connected) { |
||
276 | return Promise\reject(new ConnectionException('Client not connected. Did you forget to call `connect()`?')); |
||
277 | } |
||
278 | |||
279 | if (!isset($this->users[$id])) { |
||
280 | return Promise\reject(new ApiException("No user exists for ID '$id'.")); |
||
281 | } |
||
282 | |||
283 | return Promise\resolve($this->users[$id]); |
||
284 | } |
||
285 | |||
286 | /** |
||
287 | * Gets all bots in the Slack team. |
||
288 | * |
||
289 | * @return \React\Promise\PromiseInterface A promise for an array of bots. |
||
290 | */ |
||
291 | View Code Duplication | public function getBots() |
|
292 | { |
||
293 | if (!$this->connected) { |
||
294 | return Promise\reject(new ConnectionException('Client not connected. Did you forget to call `connect()`?')); |
||
295 | } |
||
296 | |||
297 | return Promise\resolve(array_values($this->bots)); |
||
298 | } |
||
299 | |||
300 | /** |
||
301 | * Gets a bot by its ID. |
||
302 | * |
||
303 | * @param string $id A bot ID. |
||
304 | * |
||
305 | * @return \React\Promise\PromiseInterface A promise for a bot object. |
||
306 | */ |
||
307 | View Code Duplication | public function getBotById($id) |
|
308 | { |
||
309 | if (!$this->connected) { |
||
310 | return Promise\reject(new ConnectionException('Client not connected. Did you forget to call `connect()`?')); |
||
311 | } |
||
312 | |||
313 | if (!isset($this->bots[$id])) { |
||
314 | return Promise\reject(new ApiException("No bot exists for ID '$id'.")); |
||
315 | } |
||
316 | |||
317 | return Promise\resolve($this->bots[$id]); |
||
318 | } |
||
319 | |||
320 | /** |
||
321 | * {@inheritDoc} |
||
322 | */ |
||
323 | public function postMessage(Message $message) |
||
324 | { |
||
325 | if (!$this->connected) { |
||
326 | return Promise\reject(new ConnectionException('Client not connected. Did you forget to call `connect()`?')); |
||
327 | } |
||
328 | |||
329 | // We can't send attachments using the RTM API, so revert to the web API |
||
330 | // to send the message |
||
331 | if ($message->hasAttachments()) { |
||
332 | return parent::postMessage($message); |
||
333 | } |
||
334 | |||
335 | $data = [ |
||
336 | 'id' => ++$this->lastMessageId, |
||
337 | 'type' => 'message', |
||
338 | 'channel' => $message->data['channel'], |
||
339 | 'text' => $message->getText(), |
||
340 | ]; |
||
341 | $this->websocket->send(json_encode($data)); |
||
342 | |||
343 | // Create a deferred object and add message to pending list so when a |
||
344 | // success message arrives, we can de-queue it and resolve the promise. |
||
345 | $deferred = new Promise\Deferred(); |
||
346 | $this->pendingMessages[$this->lastMessageId] = $deferred; |
||
347 | |||
348 | return $deferred->promise(); |
||
349 | } |
||
350 | |||
351 | /** |
||
352 | * Returns whether the client is connected. |
||
353 | * |
||
354 | * @return bool |
||
355 | */ |
||
356 | public function isConnected() |
||
357 | { |
||
358 | return $this->connected; |
||
359 | } |
||
360 | |||
361 | /** |
||
362 | * Handles incoming websocket messages, parses them, and emits them as remote events. |
||
363 | * |
||
364 | * @param WebSocketMessageInterface $messageRaw A websocket message. |
||
0 ignored issues
–
show
|
|||
365 | */ |
||
366 | private function onMessage(WebSocketMessageInterface $message) |
||
367 | { |
||
368 | // parse the message and get the event name |
||
369 | $payload = Payload::fromJson($message->getData()); |
||
370 | |||
371 | if (isset($payload['type'])) { |
||
372 | switch ($payload['type']) { |
||
373 | case 'hello': |
||
374 | $this->connected = true; |
||
375 | break; |
||
376 | |||
377 | case 'team_rename': |
||
378 | $this->team->data['name'] = $payload['name']; |
||
379 | break; |
||
380 | |||
381 | case 'team_domain_change': |
||
382 | $this->team->data['domain'] = $payload['domain']; |
||
383 | break; |
||
384 | |||
385 | case 'channel_joined': |
||
386 | $channel = new Channel($this, $payload['channel']); |
||
387 | $this->channels[$channel->getId()] = $channel; |
||
388 | break; |
||
389 | |||
390 | case 'channel_created': |
||
391 | $this->getChannelById($payload['channel']['id'])->then(function (Channel $channel) { |
||
392 | $this->channels[$channel->getId()] = $channel; |
||
393 | }); |
||
394 | break; |
||
395 | |||
396 | case 'channel_deleted': |
||
397 | unset($this->channels[$payload['channel']['id']]); |
||
398 | break; |
||
399 | |||
400 | case 'channel_rename': |
||
401 | $this->channels[$payload['channel']['id']]->data['name'] |
||
402 | = $payload['channel']['name']; |
||
403 | break; |
||
404 | |||
405 | View Code Duplication | case 'channel_archive': |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
406 | $this->channels[$payload['channel']['id']]->data['is_archived'] = true; |
||
407 | break; |
||
408 | |||
409 | View Code Duplication | case 'channel_unarchive': |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
410 | $this->channels[$payload['channel']['id']]->data['is_archived'] = false; |
||
411 | break; |
||
412 | |||
413 | case 'group_joined': |
||
414 | $group = new Group($this, $payload['channel']); |
||
415 | $this->groups[$group->getId()] = $group; |
||
416 | break; |
||
417 | |||
418 | case 'group_rename': |
||
419 | $this->groups[$payload['group']['id']]->data['name'] |
||
420 | = $payload['channel']['name']; |
||
421 | break; |
||
422 | |||
423 | View Code Duplication | case 'group_archive': |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
424 | $this->groups[$payload['group']['id']]->data['is_archived'] = true; |
||
425 | break; |
||
426 | |||
427 | View Code Duplication | case 'group_unarchive': |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
428 | $this->groups[$payload['group']['id']]->data['is_archived'] = false; |
||
429 | break; |
||
430 | |||
431 | case 'im_created': |
||
432 | $dm = new DirectMessageChannel($this, $payload['channel']); |
||
433 | $this->dms[$dm->getId()] = $dm; |
||
434 | break; |
||
435 | |||
436 | case 'bot_added': |
||
437 | $bot = new Bot($this, $payload['bot']); |
||
438 | $this->bots[$bot->getId()] = $bot; |
||
439 | break; |
||
440 | |||
441 | case 'bot_changed': |
||
442 | $bot = new Bot($this, $payload['bot']); |
||
443 | $this->bots[$bot->getId()] = $bot; |
||
444 | break; |
||
445 | } |
||
446 | |||
447 | // emit an event with the attached json |
||
448 | $this->emit($payload['type'], [$payload]); |
||
449 | } else { |
||
450 | // If reply_to is set, then it is a server confirmation for a previously |
||
451 | // sent message |
||
452 | if (isset($payload['reply_to'])) { |
||
453 | if (isset($this->pendingMessages[$payload['reply_to']])) { |
||
454 | $deferred = $this->pendingMessages[$payload['reply_to']]; |
||
455 | |||
456 | // Resolve or reject the promise that was waiting for the reply. |
||
457 | if (isset($payload['ok']) && $payload['ok'] === true) { |
||
458 | $deferred->resolve(); |
||
459 | } else { |
||
460 | $deferred->reject($payload['error']); |
||
461 | } |
||
462 | |||
463 | unset($this->pendingMessages[$payload['reply_to']]); |
||
464 | } |
||
465 | } |
||
466 | } |
||
467 | } |
||
468 | } |
||
469 |
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.
Consider the following example. The parameter
$ireland
is not defined by the methodfinale(...)
.The most likely cause is that the parameter was changed, but the annotation was not.