Completed
Pull Request — master (#547)
by Alexandru
01:30
created

LocalChannelManager::findOrCreate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 2
1
<?php
2
3
namespace BeyondCode\LaravelWebSockets\ChannelManagers;
4
5
use BeyondCode\LaravelWebSockets\Channels\Channel;
6
use BeyondCode\LaravelWebSockets\Channels\PresenceChannel;
7
use BeyondCode\LaravelWebSockets\Channels\PrivateChannel;
8
use BeyondCode\LaravelWebSockets\Contracts\ChannelManager;
9
use BeyondCode\LaravelWebSockets\Helpers;
10
use Carbon\Carbon;
11
use Illuminate\Cache\ArrayLock;
12
use Illuminate\Cache\ArrayStore;
13
use Illuminate\Support\Str;
14
use Ratchet\ConnectionInterface;
15
use React\EventLoop\LoopInterface;
16
use React\Promise\PromiseInterface;
17
use stdClass;
18
19
class LocalChannelManager implements ChannelManager
20
{
21
    /**
22
     * The list of stored channels.
23
     *
24
     * @var array
25
     */
26
    protected $channels = [];
27
28
    /**
29
     * The list of users that joined the presence channel.
30
     *
31
     * @var array
32
     */
33
    protected $users = [];
34
35
    /**
36
     * The list of users by socket and their attached id.
37
     *
38
     * @var array
39
     */
40
    protected $userSockets = [];
41
42
    /**
43
     * Wether the current instance accepts new connections.
44
     *
45
     * @var bool
46
     */
47
    protected $acceptsNewConnections = true;
48
49
    /**
50
     * The lock name to use on Array to avoid multiple
51
     * actions that might lead to multiple processings.
52
     *
53
     * @var string
54
     */
55
    protected static $lockName = 'laravel-websockets:channel-manager:lock';
56
57
    /**
58
     * Create a new channel manager instance.
59
     *
60
     * @param  LoopInterface  $loop
61
     * @param  string|null  $factoryClass
62
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
63
     */
64
    public function __construct(LoopInterface $loop, $factoryClass = null)
65
    {
66
        //
67
    }
68
69
    /**
70
     * Find the channel by app & name.
71
     *
72
     * @param  string|int  $appId
73
     * @param  string  $channel
74
     * @return null|BeyondCode\LaravelWebSockets\Channels\Channel
75
     */
76
    public function find($appId, string $channel)
77
    {
78
        return $this->channels[$appId][$channel] ?? null;
79
    }
80
81
    /**
82
     * Find a channel by app & name or create one.
83
     *
84
     * @param  string|int  $appId
85
     * @param  string  $channel
86
     * @return BeyondCode\LaravelWebSockets\Channels\Channel
87
     */
88
    public function findOrCreate($appId, string $channel)
89
    {
90
        if (! $channelInstance = $this->find($appId, $channel)) {
91
            $class = $this->getChannelClassName($channel);
92
93
            $this->channels[$appId][$channel] = new $class($channel);
94
        }
95
96
        return $this->channels[$appId][$channel];
97
    }
98
99
    /**
100
     * Get the local connections, regardless of the channel
101
     * they are connected to.
102
     *
103
     * @return \React\Promise\PromiseInterface
104
     */
105
    public function getLocalConnections(): PromiseInterface
106
    {
107
        $connections = collect($this->channels)
108
            ->map(function ($channelsWithConnections, $appId) {
0 ignored issues
show
Unused Code introduced by
The parameter $appId is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
109
                return collect($channelsWithConnections)->values();
110
            })
111
            ->values()->collapse()
112
            ->map(function ($channel) {
113
                return collect($channel->getConnections());
114
            })
115
            ->values()->collapse()
116
            ->toArray();
117
118
        return Helpers::createFulfilledPromise($connections);
119
    }
120
121
    /**
122
     * Get all channels for a specific app
123
     * for the current instance.
124
     *
125
     * @param  string|int  $appId
126
     * @return \React\Promise\PromiseInterface[array]
0 ignored issues
show
Documentation introduced by
The doc-type \React\Promise\PromiseInterface[array] could not be parsed: Expected "]" at position 2, but found "array". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
127
     */
128
    public function getLocalChannels($appId): PromiseInterface
129
    {
130
        return Helpers::createFulfilledPromise(
131
            $this->channels[$appId] ?? []
132
        );
133
    }
134
135
    /**
136
     * Get all channels for a specific app
137
     * across multiple servers.
138
     *
139
     * @param  string|int  $appId
140
     * @return \React\Promise\PromiseInterface[array]
0 ignored issues
show
Documentation introduced by
The doc-type \React\Promise\PromiseInterface[array] could not be parsed: Expected "]" at position 2, but found "array". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
141
     */
142
    public function getGlobalChannels($appId): PromiseInterface
143
    {
144
        return $this->getLocalChannels($appId);
145
    }
146
147
    /**
148
     * Remove connection from all channels.
149
     *
150
     * @param  \Ratchet\ConnectionInterface  $connection
151
     * @return PromiseInterface[bool]
0 ignored issues
show
Documentation introduced by
The doc-type PromiseInterface[bool] could not be parsed: Expected "]" at position 2, but found "bool". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
152
     */
153
    public function unsubscribeFromAllChannels(ConnectionInterface $connection): PromiseInterface
154
    {
155
        if (! isset($connection->app)) {
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?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
156
            return new FuilfilledPromise(false);
157
        }
158
159
        $this->getLocalChannels($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?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
160
            ->then(function ($channels) use ($connection) {
161
                collect($channels)->each->unsubscribe($connection);
162
163
                collect($channels)
164
                    ->reject->hasConnections()
165
                    ->each(function (Channel $channel, string $channelName) use ($connection) {
166
                        unset($this->channels[$connection->app->id][$channelName]);
167
                    });
168
            });
169
170
        $this->getLocalChannels($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?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
171
            ->then(function ($channels) use ($connection) {
172
                if (count($channels) === 0) {
173
                    unset($this->channels[$connection->app->id]);
174
                }
175
            });
176
177
        return Helpers::createFulfilledPromise(true);
178
    }
179
180
    /**
181
     * Subscribe the connection to a specific channel.
182
     *
183
     * @param  \Ratchet\ConnectionInterface  $connection
184
     * @param  string  $channelName
185
     * @param  stdClass  $payload
186
     * @return PromiseInterface[bool]
0 ignored issues
show
Documentation introduced by
The doc-type PromiseInterface[bool] could not be parsed: Expected "]" at position 2, but found "bool". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
187
     */
188
    public function subscribeToChannel(ConnectionInterface $connection, string $channelName, stdClass $payload): PromiseInterface
189
    {
190
        $channel = $this->findOrCreate($connection->app->id, $channelName);
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?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
191
192
        return Helpers::createFulfilledPromise(
193
            $channel->subscribe($connection, $payload)
194
        );
195
    }
196
197
    /**
198
     * Unsubscribe the connection from the channel.
199
     *
200
     * @param  \Ratchet\ConnectionInterface  $connection
201
     * @param  string  $channelName
202
     * @param  stdClass  $payload
203
     * @return PromiseInterface[bool]
0 ignored issues
show
Documentation introduced by
The doc-type PromiseInterface[bool] could not be parsed: Expected "]" at position 2, but found "bool". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
204
     */
205
    public function unsubscribeFromChannel(ConnectionInterface $connection, string $channelName, stdClass $payload): PromiseInterface
206
    {
207
        $channel = $this->findOrCreate($connection->app->id, $channelName);
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?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
208
209
        return Helpers::createFulfilledPromise(
210
            $channel->unsubscribe($connection, $payload)
211
        );
212
    }
213
214
    /**
215
     * Subscribe the connection to a specific channel, returning
216
     * a promise containing the amount of connections.
217
     *
218
     * @param  string|int  $appId
219
     * @return PromiseInterface[int]
0 ignored issues
show
Documentation introduced by
The doc-type PromiseInterface[int] could not be parsed: Expected "]" at position 2, but found "int". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
220
     */
221
    public function subscribeToApp($appId): PromiseInterface
222
    {
223
        return Helpers::createFulfilledPromise(0);
224
    }
225
226
    /**
227
     * Unsubscribe the connection from the channel, returning
228
     * a promise containing the amount of connections after decrement.
229
     *
230
     * @param  string|int  $appId
231
     * @return PromiseInterface[int]
0 ignored issues
show
Documentation introduced by
The doc-type PromiseInterface[int] could not be parsed: Expected "]" at position 2, but found "int". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
232
     */
233
    public function unsubscribeFromApp($appId): PromiseInterface
234
    {
235
        return Helpers::createFulfilledPromise(0);
236
    }
237
238
    /**
239
     * Get the connections count on the app
240
     * for the current server instance.
241
     *
242
     * @param  string|int  $appId
243
     * @param  string|null  $channelName
244
     * @return PromiseInterface[int]
0 ignored issues
show
Documentation introduced by
The doc-type PromiseInterface[int] could not be parsed: Expected "]" at position 2, but found "int". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
245
     */
246
    public function getLocalConnectionsCount($appId, string $channelName = null): PromiseInterface
247
    {
248
        return $this->getLocalChannels($appId)
249
            ->then(function ($channels) use ($channelName) {
250
                return collect($channels)->when(! is_null($channelName), function ($collection) use ($channelName) {
251
                    return $collection->filter(function (Channel $channel) use ($channelName) {
252
                        return $channel->getName() === $channelName;
253
                    });
254
                })
255
                ->flatMap(function (Channel $channel) {
256
                    return collect($channel->getConnections())->pluck('socketId');
257
                })
258
                ->unique()->count();
259
            });
260
    }
261
262
    /**
263
     * Get the connections count
264
     * across multiple servers.
265
     *
266
     * @param  string|int  $appId
267
     * @param  string|null  $channelName
268
     * @return PromiseInterface[int]
0 ignored issues
show
Documentation introduced by
The doc-type PromiseInterface[int] could not be parsed: Expected "]" at position 2, but found "int". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
269
     */
270
    public function getGlobalConnectionsCount($appId, string $channelName = null): PromiseInterface
271
    {
272
        return $this->getLocalConnectionsCount($appId, $channelName);
273
    }
274
275
    /**
276
     * Broadcast the message across multiple servers.
277
     *
278
     * @param  string|int  $appId
279
     * @param  string|null  $socketId
280
     * @param  string  $channel
281
     * @param  stdClass  $payload
282
     * @param  string|null  $serverId
283
     * @return PromiseInterface[bool]
0 ignored issues
show
Documentation introduced by
The doc-type PromiseInterface[bool] could not be parsed: Expected "]" at position 2, but found "bool". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
284
     */
285
    public function broadcastAcrossServers($appId, ?string $socketId, string $channel, stdClass $payload, string $serverId = null): PromiseInterface
286
    {
287
        return Helpers::createFulfilledPromise(true);
288
    }
289
290
    /**
291
     * Handle the user when it joined a presence channel.
292
     *
293
     * @param  \Ratchet\ConnectionInterface  $connection
294
     * @param  stdClass  $user
295
     * @param  string  $channel
296
     * @param  stdClass  $payload
297
     * @return PromiseInterface[bool]
0 ignored issues
show
Documentation introduced by
The doc-type PromiseInterface[bool] could not be parsed: Expected "]" at position 2, but found "bool". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
298
     */
299
    public function userJoinedPresenceChannel(ConnectionInterface $connection, stdClass $user, string $channel, stdClass $payload): PromiseInterface
300
    {
301
        $this->users["{$connection->app->id}:{$channel}"][$connection->socketId] = json_encode($user);
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?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Bug introduced by
Accessing socketId on the interface Ratchet\ConnectionInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
302
        $this->userSockets["{$connection->app->id}:{$channel}:{$user->user_id}"][] = $connection->socketId;
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?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Bug introduced by
Accessing socketId on the interface Ratchet\ConnectionInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
303
304
        return Helpers::createFulfilledPromise(true);
305
    }
306
307
    /**
308
     * Handle the user when it left a presence channel.
309
     *
310
     * @param  \Ratchet\ConnectionInterface  $connection
311
     * @param  stdClass  $user
312
     * @param  string  $channel
313
     * @param  stdClass  $payload
0 ignored issues
show
Bug introduced by
There is no parameter named $payload. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
314
     * @return PromiseInterface[bool]
0 ignored issues
show
Documentation introduced by
The doc-type PromiseInterface[bool] could not be parsed: Expected "]" at position 2, but found "bool". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
315
     */
316
    public function userLeftPresenceChannel(ConnectionInterface $connection, stdClass $user, string $channel): PromiseInterface
317
    {
318
        unset($this->users["{$connection->app->id}:{$channel}"][$connection->socketId]);
319
320
        $deletableSocketKey = array_search(
321
            $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?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
322
            $this->userSockets["{$connection->app->id}:{$channel}:{$user->user_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?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
323
        );
324
325
        if ($deletableSocketKey !== false) {
326
            unset($this->userSockets["{$connection->app->id}:{$channel}:{$user->user_id}"][$deletableSocketKey]);
327
328
            if (count($this->userSockets["{$connection->app->id}:{$channel}:{$user->user_id}"]) === 0) {
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?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
329
                unset($this->userSockets["{$connection->app->id}:{$channel}:{$user->user_id}"]);
330
            }
331
        }
332
333
        return Helpers::createFulfilledPromise(true);
334
    }
335
336
    /**
337
     * Get the presence channel members.
338
     *
339
     * @param  string|int  $appId
340
     * @param  string  $channel
341
     * @return \React\Promise\PromiseInterface
342
     */
343
    public function getChannelMembers($appId, string $channel): PromiseInterface
344
    {
345
        $members = $this->users["{$appId}:{$channel}"] ?? [];
346
347
        $members = collect($members)->map(function ($user) {
348
            return json_decode($user);
349
        })->unique('user_id')->toArray();
350
351
        return Helpers::createFulfilledPromise($members);
352
    }
353
354
    /**
355
     * Get a member from a presence channel based on connection.
356
     *
357
     * @param  \Ratchet\ConnectionInterface  $connection
358
     * @param  string  $channel
359
     * @return \React\Promise\PromiseInterface
360
     */
361
    public function getChannelMember(ConnectionInterface $connection, string $channel): PromiseInterface
362
    {
363
        $member = $this->users["{$connection->app->id}:{$channel}"][$connection->socketId] ?? null;
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?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Bug introduced by
Accessing socketId on the interface Ratchet\ConnectionInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
364
365
        return Helpers::createFulfilledPromise($member);
366
    }
367
368
    /**
369
     * Get the presence channels total members count.
370
     *
371
     * @param  string|int  $appId
372
     * @param  array  $channelNames
373
     * @return \React\Promise\PromiseInterface
374
     */
375
    public function getChannelsMembersCount($appId, array $channelNames): PromiseInterface
376
    {
377
        $results = collect($channelNames)
378
            ->reduce(function ($results, $channel) use ($appId) {
379
                $results[$channel] = isset($this->users["{$appId}:{$channel}"])
380
                    ? count($this->users["{$appId}:{$channel}"])
381
                    : 0;
382
383
                return $results;
384
            }, []);
385
386
        return Helpers::createFulfilledPromise($results);
387
    }
388
389
    /**
390
     * Get the socket IDs for a presence channel member.
391
     *
392
     * @param  string|int  $userId
393
     * @param  string|int  $appId
394
     * @param  string  $channelName
395
     * @return \React\Promise\PromiseInterface
396
     */
397
    public function getMemberSockets($userId, $appId, $channelName): PromiseInterface
398
    {
399
        return Helpers::createFulfilledPromise(
400
            $this->userSockets["{$appId}:{$channelName}:{$userId}"] ?? []
401
        );
402
    }
403
404
    /**
405
     * Keep tracking the connections availability when they pong.
406
     *
407
     * @param  \Ratchet\ConnectionInterface  $connection
408
     * @return PromiseInterface[bool]
0 ignored issues
show
Documentation introduced by
The doc-type PromiseInterface[bool] could not be parsed: Expected "]" at position 2, but found "bool". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
409
     */
410
    public function connectionPonged(ConnectionInterface $connection): PromiseInterface
411
    {
412
        $connection->lastPongedAt = Carbon::now();
0 ignored issues
show
Bug introduced by
Accessing lastPongedAt on the interface Ratchet\ConnectionInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
413
414
        return $this->updateConnectionInChannels($connection);
415
    }
416
417
    /**
418
     * Remove the obsolete connections that didn't ponged in a while.
419
     *
420
     * @return PromiseInterface[bool]
0 ignored issues
show
Documentation introduced by
The doc-type PromiseInterface[bool] could not be parsed: Expected "]" at position 2, but found "bool". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
421
     */
422
    public function removeObsoleteConnections(): PromiseInterface
423
    {
424
        if (! $this->lock()->acquire()) {
425
            return Helpers::createFulfilledPromise(false);
426
        }
427
428
        $this->getLocalConnections()->then(function ($connections) {
429
            foreach ($connections as $connection) {
430
                $differenceInSeconds = $connection->lastPongedAt->diffInSeconds(Carbon::now());
431
432
                if ($differenceInSeconds > 120) {
433
                    $this->unsubscribeFromAllChannels($connection);
434
                }
435
            }
436
        });
437
438
        return Helpers::createFulfilledPromise(
439
            $this->lock()->release()
440
        );
441
    }
442
443
    /**
444
     * Update the connection in all channels.
445
     *
446
     * @param  ConnectionInterface  $connection
447
     * @return PromiseInterface[bool]
0 ignored issues
show
Documentation introduced by
The doc-type PromiseInterface[bool] could not be parsed: Expected "]" at position 2, but found "bool". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
448
     */
449
    public function updateConnectionInChannels($connection): PromiseInterface
450
    {
451
        return $this->getLocalChannels($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?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
452
            ->then(function ($channels) use ($connection) {
453
                foreach ($channels as $channel) {
454
                    if ($channel->hasConnection($connection)) {
455
                        $channel->saveConnection($connection);
456
                    }
457
                }
458
459
                return true;
460
            });
461
    }
462
463
    /**
464
     * Mark the current instance as unable to accept new connections.
465
     *
466
     * @return $this
467
     */
468
    public function declineNewConnections()
469
    {
470
        $this->acceptsNewConnections = false;
471
472
        return $this;
473
    }
474
475
    /**
476
     * Check if the current server instance
477
     * accepts new connections.
478
     *
479
     * @return bool
480
     */
481
    public function acceptsNewConnections(): bool
482
    {
483
        return $this->acceptsNewConnections;
484
    }
485
486
    /**
487
     * Get the channel class by the channel name.
488
     *
489
     * @param  string  $channelName
490
     * @return string
491
     */
492
    protected function getChannelClassName(string $channelName): string
493
    {
494
        if (Str::startsWith($channelName, 'private-')) {
495
            return PrivateChannel::class;
496
        }
497
498
        if (Str::startsWith($channelName, 'presence-')) {
499
            return PresenceChannel::class;
500
        }
501
502
        return Channel::class;
503
    }
504
505
    /**
506
     * Get a new ArrayLock instance to avoid race conditions.
507
     *
508
     * @return \Illuminate\Cache\CacheLock
509
     */
510
    protected function lock()
511
    {
512
        return new ArrayLock(new ArrayStore, static::$lockName, 0);
513
    }
514
}
515