Completed
Pull Request — master (#447)
by Alexandru
01:39
created

Channelable::broadcast()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
3
namespace BeyondCode\LaravelWebSockets\Concerns;
4
5
use BeyondCode\LaravelWebSockets\Dashboard\DashboardLogger;
6
use BeyondCode\LaravelWebSockets\PubSub\ReplicationInterface;
7
use BeyondCode\LaravelWebSockets\WebSockets\Exceptions\InvalidSignature;
8
use Illuminate\Support\Str;
9
use Ratchet\ConnectionInterface;
10
use stdClass;
11
12
trait Channelable
13
{
14
    /**
15
     * The channel name.
16
     *
17
     * @var string
18
     */
19
    protected $channelName;
20
21
    /**
22
     * The replicator client.
23
     *
24
     * @var ReplicationInterface
25
     */
26
    protected $replicator;
27
28
    /**
29
     * The connections that got subscribed.
30
     *
31
     * @var array
32
     */
33
    protected $subscribedConnections = [];
34
35
    /**
36
     * Create a new instance.
37
     *
38
     * @param  string  $channelName
39
     * @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...
40
     */
41
    public function __construct(string $channelName)
42
    {
43
        $this->channelName = $channelName;
44
        $this->replicator = app(ReplicationInterface::class);
45
    }
46
47
    /**
48
     * Get the channel name.
49
     *
50
     * @return string
51
     */
52
    public function getChannelName(): string
53
    {
54
        return $this->channelName;
55
    }
56
57
    /**
58
     * Check if the channel has connections.
59
     *
60
     * @return bool
61
     */
62
    public function hasConnections(): bool
63
    {
64
        return count($this->subscribedConnections) > 0;
65
    }
66
67
    /**
68
     * Get all subscribed connections.
69
     *
70
     * @return array
71
     */
72
    public function getSubscribedConnections(): array
73
    {
74
        return $this->subscribedConnections;
75
    }
76
77
    /**
78
     * Check if the signature for the payload is valid.
79
     *
80
     * @param  \Ratchet\ConnectionInterface  $connection
81
     * @param  \stdClass  $payload
82
     * @return void
83
     * @throws InvalidSignature
84
     */
85
    protected function verifySignature(ConnectionInterface $connection, stdClass $payload)
86
    {
87
        $signature = "{$connection->socketId}:{$this->channelName}";
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...
88
89
        if (isset($payload->channel_data)) {
90
            $signature .= ":{$payload->channel_data}";
91
        }
92
93
        if (! hash_equals(
94
            hash_hmac('sha256', $signature, $connection->app->secret),
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...
95
            Str::after($payload->auth, ':'))
96
        ) {
97
            throw new InvalidSignature();
98
        }
99
    }
100
101
    /**
102
     * Subscribe to the channel.
103
     *
104
     * @see    https://pusher.com/docs/pusher_protocol#presence-channel-events
105
     * @param  \Ratchet\ConnectionInterface  $connection
106
     * @param  \stdClass  $payload
107
     * @return void
108
     */
109
    public function subscribe(ConnectionInterface $connection, stdClass $payload)
0 ignored issues
show
Unused Code introduced by
The parameter $payload 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...
110
    {
111
        $this->saveConnection($connection);
112
113
        $connection->send(json_encode([
114
            'event' => 'pusher_internal:subscription_succeeded',
115
            'channel' => $this->channelName,
116
        ]));
117
118
        $this->replicator->subscribe($connection->app->id, $this->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...
119
    }
120
121
    /**
122
     * Unsubscribe connection from the channel.
123
     *
124
     * @param  \Ratchet\ConnectionInterface  $connection
125
     * @return void
126
     */
127
    public function unsubscribe(ConnectionInterface $connection)
128
    {
129
        unset($this->subscribedConnections[$connection->socketId]);
130
131
        $this->replicator->unsubscribe($connection->app->id, $this->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...
132
133
        if (! $this->hasConnections()) {
134
            DashboardLogger::log($connection->app->id, DashboardLogger::TYPE_VACATED, [
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...
135
                'socketId' => $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...
136
                'channel' => $this->channelName,
137
            ]);
138
        }
139
    }
140
141
    /**
142
     * Store the connection to the subscribers list.
143
     *
144
     * @param  \Ratchet\ConnectionInterface  $connection
145
     * @return void
146
     */
147
    protected function saveConnection(ConnectionInterface $connection)
148
    {
149
        $hadConnectionsPreviously = $this->hasConnections();
150
151
        $this->subscribedConnections[$connection->socketId] = $connection;
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...
152
153
        if (! $hadConnectionsPreviously) {
154
            DashboardLogger::log($connection->app->id, DashboardLogger::TYPE_OCCUPIED, [
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...
155
                'channel' => $this->channelName,
156
            ]);
157
        }
158
159
        DashboardLogger::log($connection->app->id, DashboardLogger::TYPE_SUBSCRIBED, [
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
            'socketId' => $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...
161
            'channel' => $this->channelName,
162
        ]);
163
    }
164
165
    /**
166
     * Broadcast a payload to the subscribed connections.
167
     *
168
     * @param  \stdClass  $payload
169
     * @return void
170
     */
171
    public function broadcast($payload)
172
    {
173
        foreach ($this->subscribedConnections as $connection) {
174
            $connection->send(json_encode($payload));
175
        }
176
    }
177
178
    /**
179
     * Broadcast the payload, but exclude the current connection.
180
     *
181
     * @param  \Ratchet\ConnectionInterface  $connection
182
     * @param  \stdClass  $payload
183
     * @return void
184
     */
185
    public function broadcastToOthers(ConnectionInterface $connection, stdClass $payload)
186
    {
187
        $this->broadcastToEveryoneExcept(
188
            $payload, $connection->socketId, $connection->app->id
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...
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...
189
        );
190
    }
191
192
    /**
193
     * Broadcast the payload, but exclude a specific socket id.
194
     *
195
     * @param  \stdClass  $payload
196
     * @param  string|null  $socketId
197
     * @param  mixed  $appId
198
     * @param  bool  $publish
199
     * @return void
200
     */
201
    public function broadcastToEveryoneExcept(stdClass $payload, ?string $socketId, $appId, bool $publish = true)
202
    {
203
        // Also broadcast via the other websocket server instances.
204
        // This is set false in the Redis client because we don't want to cause a loop
205
        // in this case. If this came from TriggerEventController, then we still want
206
        // to publish to get the message out to other server instances.
207
        if ($publish) {
208
            $this->replicator->publish($appId, $this->channelName, $payload);
209
        }
210
211
        // Performance optimization, if we don't have a socket ID,
212
        // then we avoid running the if condition in the foreach loop below
213
        // by calling broadcast() instead.
214
        if (is_null($socketId)) {
215
            $this->broadcast($payload);
216
217
            return;
218
        }
219
220
        foreach ($this->subscribedConnections as $connection) {
221
            if ($connection->socketId !== $socketId) {
222
                $connection->send(json_encode($payload));
223
            }
224
        }
225
    }
226
227
    /**
228
     * Convert the channel to array.
229
     *
230
     * @param  mixed  $appId
231
     * @return array
232
     */
233
    public function toArray($appId = null)
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...
234
    {
235
        return [
236
            'occupied' => count($this->subscribedConnections) > 0,
237
            'subscription_count' => count($this->subscribedConnections),
238
        ];
239
    }
240
}
241