PresenceChannel::subscribe()   A
last analyzed

Complexity

Conditions 3
Paths 1

Size

Total Lines 72
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 3
eloc 44
c 4
b 0
f 0
nc 1
nop 2
dl 0
loc 72
rs 9.216

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
namespace BeyondCode\LaravelWebSockets\Channels;
4
5
use BeyondCode\LaravelWebSockets\DashboardLogger;
6
use BeyondCode\LaravelWebSockets\Events\SubscribedToChannel;
7
use BeyondCode\LaravelWebSockets\Events\UnsubscribedFromChannel;
8
use BeyondCode\LaravelWebSockets\Server\Exceptions\InvalidSignature;
9
use Ratchet\ConnectionInterface;
10
use stdClass;
11
12
class PresenceChannel extends PrivateChannel
13
{
14
    /**
15
     * Subscribe to the channel.
16
     *
17
     * @see    https://pusher.com/docs/pusher_protocol#presence-channel-events
18
     *
19
     * @param  \Ratchet\ConnectionInterface  $connection
20
     * @param  \stdClass  $payload
21
     * @return bool
22
     *
23
     * @throws InvalidSignature
24
     */
25
    public function subscribe(ConnectionInterface $connection, stdClass $payload): bool
26
    {
27
        $this->verifySignature($connection, $payload);
28
29
        $this->saveConnection($connection);
30
31
        $user = json_decode($payload->channel_data);
32
33
        $this->channelManager
34
            ->userJoinedPresenceChannel($connection, $user, $this->getName(), $payload)
35
            ->then(function () use ($connection) {
36
                $this->channelManager
37
                    ->getChannelMembers($connection->app->id, $this->getName())
0 ignored issues
show
Bug introduced by
Accessing app on the interface Ratchet\ConnectionInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
38
                    ->then(function ($users) use ($connection) {
39
                        $hash = [];
40
41
                        foreach ($users as $socketId => $user) {
42
                            $hash[$user->user_id] = $user->user_info ?? [];
43
                        }
44
45
                        $connection->send(json_encode([
46
                            'event' => 'pusher_internal:subscription_succeeded',
47
                            'channel' => $this->getName(),
48
                            'data' => json_encode([
49
                                'presence' => [
50
                                    'ids' => collect($users)->map(function ($user) {
51
                                        return (string) $user->user_id;
52
                                    })->values(),
53
                                    'hash' => $hash,
54
                                    'count' => count($users),
55
                                ],
56
                            ]),
57
                        ]));
58
                    });
59
            })
60
            ->then(function () use ($connection, $user, $payload) {
61
                // The `pusher_internal:member_added` event is triggered when a user joins a channel.
62
                // It's quite possible that a user can have multiple connections to the same channel
63
                // (for example by having multiple browser tabs open)
64
                // and in this case the events will only be triggered when the first tab is opened.
65
                $this->channelManager
66
                    ->getMemberSockets($user->user_id, $connection->app->id, $this->getName())
0 ignored issues
show
Bug introduced by
Accessing app on the interface Ratchet\ConnectionInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
67
                    ->then(function ($sockets) use ($payload, $connection, $user) {
68
                        if (count($sockets) === 1) {
69
                            $memberAddedPayload = [
70
                                'event' => 'pusher_internal:member_added',
71
                                'channel' => $this->getName(),
72
                                'data' => $payload->channel_data,
73
                            ];
74
75
                            $this->broadcastToEveryoneExcept(
76
                                (object) $memberAddedPayload, $connection->socketId,
0 ignored issues
show
Bug introduced by
Accessing socketId on the interface Ratchet\ConnectionInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
77
                                $connection->app->id
0 ignored issues
show
Bug introduced by
Accessing app on the interface Ratchet\ConnectionInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
78
                            );
79
80
                            SubscribedToChannel::dispatch(
81
                                $connection->app->id,
82
                                $connection->socketId,
83
                                $this->getName(),
84
                                $user
85
                            );
86
                        }
87
88
                        DashboardLogger::log($connection->app->id, DashboardLogger::TYPE_SUBSCRIBED, [
89
                            'socketId' => $connection->socketId,
90
                            'channel' => $this->getName(),
91
                            'duplicate-connection' => count($sockets) > 1,
92
                        ]);
93
                    });
94
            });
95
96
        return true;
97
    }
98
99
    /**
100
     * Unsubscribe connection from the channel.
101
     *
102
     * @param  \Ratchet\ConnectionInterface  $connection
103
     * @return bool
104
     */
105
    public function unsubscribe(ConnectionInterface $connection): bool
106
    {
107
        $truth = parent::unsubscribe($connection);
108
109
        $this->channelManager
110
            ->getChannelMember($connection, $this->getName())
111
            ->then(function ($user) {
112
                return @json_decode($user);
113
            })
114
            ->then(function ($user) use ($connection) {
115
                if (! $user) {
116
                    return;
117
                }
118
119
                $this->channelManager
120
                    ->userLeftPresenceChannel($connection, $user, $this->getName())
121
                    ->then(function () use ($connection, $user) {
122
                        // The `pusher_internal:member_removed` is triggered when a user leaves a channel.
123
                        // It's quite possible that a user can have multiple connections to the same channel
124
                        // (for example by having multiple browser tabs open)
125
                        // and in this case the events will only be triggered when the last one is closed.
126
                        $this->channelManager
127
                            ->getMemberSockets($user->user_id, $connection->app->id, $this->getName())
0 ignored issues
show
Bug introduced by
Accessing app on the interface Ratchet\ConnectionInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
128
                            ->then(function ($sockets) use ($connection, $user) {
129
                                if (count($sockets) === 0) {
130
                                    $memberRemovedPayload = [
131
                                        'event' => 'pusher_internal:member_removed',
132
                                        'channel' => $this->getName(),
133
                                        'data' => json_encode([
134
                                            'user_id' => $user->user_id,
135
                                        ]),
136
                                    ];
137
138
                                    $this->broadcastToEveryoneExcept(
139
                                        (object) $memberRemovedPayload, $connection->socketId,
0 ignored issues
show
Bug introduced by
Accessing socketId on the interface Ratchet\ConnectionInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
140
                                        $connection->app->id
0 ignored issues
show
Bug introduced by
Accessing app on the interface Ratchet\ConnectionInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
141
                                    );
142
143
                                    UnsubscribedFromChannel::dispatch(
144
                                        $connection->app->id,
145
                                        $connection->socketId,
146
                                        $this->getName(),
147
                                        $user
148
                                    );
149
                                }
150
                            });
151
                    });
152
            });
153
154
        return $truth;
155
    }
156
}
157