Completed
Pull Request — master (#519)
by Alexandru
14:48
created

Channel   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 179
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 17
lcom 1
cbo 5
dl 0
loc 179
rs 10
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A getName() 0 4 1
A getConnections() 0 4 1
A hasConnections() 0 4 1
A subscribe() 0 14 1
A unsubscribe() 0 8 2
A saveConnection() 0 4 1
A broadcast() 0 11 2
A broadcastToEveryoneExcept() 0 18 4
A verifySignature() 0 15 3
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, $this->getName(), $payload);
134
        }
135
136
        return true;
137
    }
138
139
    /**
140
     * Broadcast the payload, but exclude a specific socket id.
141
     *
142
     * @param  \stdClass  $payload
143
     * @param  string|null  $socketId
144
     * @param  string|int  $appId
145
     * @param  bool  $replicate
146
     * @return bool
147
     */
148
    public function broadcastToEveryoneExcept(stdClass $payload, ?string $socketId, $appId, bool $replicate = true)
149
    {
150
        if ($replicate) {
151
            $this->channelManager->broadcastAcrossServers($appId, $this->getName(), $payload);
152
        }
153
154
        if (is_null($socketId)) {
155
            return $this->broadcast($appId, $payload, $replicate);
156
        }
157
158
        collect($this->getConnections())->each(function (ConnectionInterface $connection) use ($socketId, $payload) {
159
            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...
160
                $connection->send(json_encode($payload));
161
            }
162
        });
163
164
        return true;
165
    }
166
167
    /**
168
     * Check if the signature for the payload is valid.
169
     *
170
     * @param  \Ratchet\ConnectionInterface  $connection
171
     * @param  \stdClass  $payload
172
     * @return void
173
     * @throws InvalidSignature
174
     */
175
    protected function verifySignature(ConnectionInterface $connection, stdClass $payload)
176
    {
177
        $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...
178
179
        if (isset($payload->channel_data)) {
180
            $signature .= ":{$payload->channel_data}";
181
        }
182
183
        if (! hash_equals(
184
            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...
185
            Str::after($payload->auth, ':'))
186
        ) {
187
            throw new InvalidSignature;
188
        }
189
    }
190
}
191