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

Channel::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace BeyondCode\LaravelWebSockets\Channels;
4
5
use BeyondCode\LaravelWebSockets\Contracts\ChannelManager;
6
use BeyondCode\LaravelWebSockets\DashboardLogger;
7
use BeyondCode\LaravelWebSockets\Server\Exceptions\InvalidSignature;
8
use Illuminate\Support\Str;
9
use Ratchet\ConnectionInterface;
10
use stdClass;
11
12
class Channel
13
{
14
    /**
15
     * The channel name.
16
     *
17
     * @var string
18
     */
19
    protected $name;
20
21
    /**
22
     * The connections that got subscribed to this channel.
23
     *
24
     * @var array
25
     */
26
    protected $connections = [];
27
28
    /**
29
     * Create a new instance.
30
     *
31
     * @param  string  $name
32
     * @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...
33
     */
34
    public function __construct(string $name)
35
    {
36
        $this->name = $name;
37
        $this->channelManager = app(ChannelManager::class);
0 ignored issues
show
Bug introduced by
The property channelManager does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
38
    }
39
40
    /**
41
     * Get channel name.
42
     *
43
     * @return string
44
     */
45
    public function getName()
46
    {
47
        return $this->name;
48
    }
49
50
    /**
51
     * Get the list of subscribed connections.
52
     *
53
     * @return array
54
     */
55
    public function getConnections()
56
    {
57
        return $this->connections;
58
    }
59
60
    /**
61
     * Check if the channel has connections.
62
     *
63
     * @return bool
64
     */
65
    public function hasConnections(): bool
66
    {
67
        return count($this->getConnections()) > 0;
68
    }
69
70
    /**
71
     * Add a new connection to the channel.
72
     *
73
     * @see    https://pusher.com/docs/pusher_protocol#presence-channel-events
74
     * @param  \Ratchet\ConnectionInterface  $connection
75
     * @param  \stdClass  $payload
76
     * @return void
77
     */
78
    public function subscribe(ConnectionInterface $connection, stdClass $payload)
79
    {
80
        $this->saveConnection($connection);
81
82
        $connection->send(json_encode([
83
            'event' => 'pusher_internal:subscription_succeeded',
84
            'channel' => $this->getName(),
85
        ]));
86
87
        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...
88
            '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...
89
            'channel' => $this->getName(),
90
        ]);
91
    }
92
93
    /**
94
     * Unsubscribe connection from the channel.
95
     *
96
     * @param  \Ratchet\ConnectionInterface  $connection
97
     * @return void
98
     */
99
    public function unsubscribe(ConnectionInterface $connection)
100
    {
101
        if (! isset($this->connections[$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...
102
            return;
103
        }
104
105
        unset($this->connections[$connection->socketId]);
106
    }
107
108
    /**
109
     * Store the connection to the subscribers list.
110
     *
111
     * @param  \Ratchet\ConnectionInterface  $connection
112
     * @return void
113
     */
114
    protected function saveConnection(ConnectionInterface $connection)
115
    {
116
        $this->connections[$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...
117
    }
118
119
    /**
120
     * Broadcast a payload to the subscribed connections.
121
     *
122
     * @param  string|int  $appId
123
     * @param  \stdClass  $payload
124
     * @param  bool  $replicate
125
     * @return bool
126
     */
127
    public function broadcast($appId, stdClass $payload, bool $replicate = true): bool
128
    {
129
        collect($this->getConnections())
130
            ->each->send(json_encode($payload));
131
132
        if ($replicate) {
133
            $this->channelManager->broadcastAcrossServers($appId, null, $this->getName(), $payload);
134
        }
135
136
        return true;
137
    }
138
139
    /**
140
     * Broadcast a payload to the locally-subscribed connections.
141
     *
142
     * @param  string|int  $appId
143
     * @param  \stdClass  $payload
144
     * @return bool
145
     */
146
    public function broadcastLocally($appId, stdClass $payload): bool
147
    {
148
        return $this->broadcast($appId, $payload, false);
149
    }
150
151
    /**
152
     * Broadcast the payload, but exclude a specific socket id.
153
     *
154
     * @param  \stdClass  $payload
155
     * @param  string|null  $socketId
156
     * @param  string|int  $appId
157
     * @param  bool  $replicate
158
     * @return bool
159
     */
160
    public function broadcastToEveryoneExcept(stdClass $payload, ?string $socketId, $appId, bool $replicate = true)
161
    {
162
        if ($replicate) {
163
            $this->channelManager->broadcastAcrossServers($appId, $socketId, $this->getName(), $payload);
164
        }
165
166
        if (is_null($socketId)) {
167
            return $this->broadcast($appId, $payload, $replicate);
168
        }
169
170
        collect($this->getConnections())->each(function (ConnectionInterface $connection) use ($socketId, $payload) {
171
            if ($connection->socketId !== $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...
172
                $connection->send(json_encode($payload));
173
            }
174
        });
175
176
        return true;
177
    }
178
179
    /**
180
     * Broadcast the payload, but exclude a specific socket id.
181
     *
182
     * @param  \stdClass  $payload
183
     * @param  string|null  $socketId
184
     * @param  string|int  $appId
185
     * @return bool
186
     */
187
    public function broadcastLocallyToEveryoneExcept(stdClass $payload, ?string $socketId, $appId)
188
    {
189
        return $this->broadcastToEveryoneExcept(
190
            $payload, $socketId, $appId, false
191
        );
192
    }
193
194
    /**
195
     * Check if the signature for the payload is valid.
196
     *
197
     * @param  \Ratchet\ConnectionInterface  $connection
198
     * @param  \stdClass  $payload
199
     * @return void
200
     * @throws InvalidSignature
201
     */
202
    protected function verifySignature(ConnectionInterface $connection, stdClass $payload)
203
    {
204
        $signature = "{$connection->socketId}:{$this->getName()}";
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...
205
206
        if (isset($payload->channel_data)) {
207
            $signature .= ":{$payload->channel_data}";
208
        }
209
210
        if (! hash_equals(
211
            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...
212
            Str::after($payload->auth, ':'))
213
        ) {
214
            throw new InvalidSignature;
215
        }
216
    }
217
}
218