Completed
Push — master ( 53691c...15e6b6 )
by Marcel
01:44
created

src/WebSockets/Channels/Channel.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace BeyondCode\LaravelWebSockets\WebSockets\Channels;
4
5
use stdClass;
6
use Ratchet\ConnectionInterface;
7
use BeyondCode\LaravelWebSockets\Dashboard\DashboardLogger;
8
use BeyondCode\LaravelWebSockets\WebSockets\Exceptions\InvalidSignature;
9
10
class Channel
11
{
12
    /** @var string */
13
    protected $channelName;
14
15
    /** @var \Ratchet\ConnectionInterface[] */
16
    protected $subscribedConnections = [];
17
18
    public function __construct(string $channelName)
19
    {
20
        $this->channelName = $channelName;
21
    }
22
23
    public function hasConnections(): bool
24
    {
25
        return count($this->subscribedConnections) > 0;
26
    }
27
28
    public function getSubscribedConnections(): array
29
    {
30
        return $this->subscribedConnections;
31
    }
32
33
    protected function verifySignature(ConnectionInterface $connection, stdClass $payload)
34
    {
35
        $signature = "{$connection->socketId}:{$this->channelName}";
36
37
        if (isset($payload->channel_data)) {
38
            $signature .= ":{$payload->channel_data}";
39
        }
40
41
        if (str_after($payload->auth, ':') !== hash_hmac('sha256', $signature, $connection->app->secret)) {
42
            throw new InvalidSignature();
43
        }
44
    }
45
46
    /*
47
     * @link https://pusher.com/docs/pusher_protocol#presence-channel-events
48
     */
49
    public function subscribe(ConnectionInterface $connection, stdClass $payload)
50
    {
51
        $this->saveConnection($connection);
52
53
        $connection->send(json_encode([
54
            'event' => 'pusher_internal:subscription_succeeded',
55
            'channel' => $this->channelName,
56
        ]));
57
    }
58
59
    public function unsubscribe(ConnectionInterface $connection)
60
    {
61
        unset($this->subscribedConnections[$connection->socketId]);
62
63
        if (! $this->hasConnections()) {
64
            DashboardLogger::vacated($connection, $this->channelName);
65
        }
66
    }
67
68
    protected function saveConnection(ConnectionInterface $connection)
69
    {
70
        $hadConnectionsPreviously = $this->hasConnections();
71
72
        $this->subscribedConnections[$connection->socketId] = $connection;
73
74
        if (! $hadConnectionsPreviously) {
75
            DashboardLogger::occupied($connection, $this->channelName);
76
        }
77
78
        DashboardLogger::subscribed($connection, $this->channelName);
79
    }
80
81
    public function broadcast($payload)
82
    {
83
        foreach ($this->subscribedConnections as $connection) {
84
            $connection->send(json_encode($payload));
85
        }
86
    }
87
88
    public function broadcastToOthers(ConnectionInterface $connection, $payload)
89
    {
90
        $this->broadcastToEveryoneExcept($payload, $connection->socketId);
91
    }
92
93
    public function broadcastToEveryoneExcept($payload, ?string $socketId = null)
94
    {
95
        if (is_null($socketId)) {
96
            return $this->broadcast($payload);
97
        }
98
99
        foreach ($this->subscribedConnections as $connection) {
100
            if ($connection->socketId !== $socketId) {
0 ignored issues
show
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...
101
                $connection->send(json_encode($payload));
102
            }
103
        }
104
    }
105
106
    public function toArray(): array
107
    {
108
        return [
109
            'occupied' => count($this->subscribedConnections) > 0,
110
            'subscription_count' => count($this->subscribedConnections),
111
        ];
112
    }
113
}
114