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

RedisPusherBroadcaster   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 130
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 8

Importance

Changes 0
Metric Value
wmc 11
lcom 2
cbo 8
dl 0
loc 130
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A auth() 0 13 3
A validAuthenticationResponse() 0 18 2
A decodePusherResponse() 0 9 2
A broadcast() 0 17 3
1
<?php
2
3
namespace BeyondCode\LaravelWebSockets\PubSub\Broadcasters;
4
5
use Illuminate\Broadcasting\Broadcasters\Broadcaster;
6
use Illuminate\Broadcasting\Broadcasters\UsePusherChannelConventions;
7
use Illuminate\Contracts\Redis\Factory as Redis;
8
use Illuminate\Support\Arr;
9
use Illuminate\Support\Str;
10
use Pusher\Pusher;
11
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
12
13
class RedisPusherBroadcaster extends Broadcaster
14
{
15
    use UsePusherChannelConventions;
16
17
    /**
18
     * The Pusher SDK instance.
19
     *
20
     * @var \Pusher\Pusher
21
     */
22
    protected $pusher;
23
24
    /**
25
     * The Pusher app ID, to be passed in the payload.
26
     *
27
     * @var string
28
     */
29
    protected $appId;
30
31
    /**
32
     * The Redis instance.
33
     *
34
     * @var \Illuminate\Contracts\Redis\Factory
35
     */
36
    protected $redis;
37
38
    /**
39
     * Create a new broadcaster instance.
40
     *
41
     * @param  Pusher  $pusher
42
     * @param  mixed  $appId
43
     * @param  \Illuminate\Contracts\Redis\Factory  $redis
44
     */
45
    public function __construct(Pusher $pusher, $appId, Redis $redis)
46
    {
47
        $this->pusher = $pusher;
48
        $this->appId = $appId;
49
        $this->redis = $redis;
50
    }
51
52
    /**
53
     * Authenticate the incoming request for a given channel.
54
     *
55
     * @param  \Illuminate\Http\Request  $request
56
     * @return mixed
57
     * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
58
     */
59
    public function auth($request)
60
    {
61
        $channelName = $this->normalizeChannelName($request->channel_name);
62
63
        if ($this->isGuardedChannel($request->channel_name) &&
64
            ! $this->retrieveUser($request, $channelName)) {
65
            throw new AccessDeniedHttpException;
66
        }
67
68
        return parent::verifyUserCanAccessChannel(
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (verifyUserCanAccessChannel() instead of auth()). Are you sure this is correct? If so, you might want to change this to $this->verifyUserCanAccessChannel().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
69
            $request, $channelName
70
        );
71
    }
72
73
    /**
74
     * Return the valid authentication response.
75
     *
76
     * @param  \Illuminate\Http\Request  $request
77
     * @param  mixed  $result
78
     * @return mixed
79
     * @throws \Pusher\PusherException
80
     */
81
    public function validAuthenticationResponse($request, $result)
82
    {
83
        if (Str::startsWith($request->channel_name, 'private')) {
84
            return $this->decodePusherResponse(
85
                $request, $this->pusher->socket_auth($request->channel_name, $request->socket_id)
86
            );
87
        }
88
89
        $channelName = $this->normalizeChannelName($request->channel_name);
90
91
        return $this->decodePusherResponse(
92
            $request,
93
            $this->pusher->presence_auth(
94
                $request->channel_name, $request->socket_id,
95
                $this->retrieveUser($request, $channelName)->getAuthIdentifier(), $result
96
            )
97
        );
98
    }
99
100
    /**
101
     * Decode the given Pusher response.
102
     *
103
     * @param  \Illuminate\Http\Request  $request
104
     * @param  mixed  $response
105
     * @return array
106
     */
107
    protected function decodePusherResponse($request, $response)
108
    {
109
        if (! $request->input('callback', false)) {
110
            return json_decode($response, true);
111
        }
112
113
        return response()->json(json_decode($response, true))
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
114
            ->withCallback($request->callback);
115
    }
116
117
    /**
118
     * Broadcast the given event.
119
     *
120
     * @param  array  $channels
121
     * @param  string  $event
122
     * @param  array  $payload
123
     * @return void
124
     */
125
    public function broadcast(array $channels, $event, array $payload = [])
126
    {
127
        $connection = $this->redis->connection(
128
            config('websockets.replication.redis.connection') ?: 'default'
129
        );
130
131
        $payload = json_encode([
132
            'appId' => $this->appId,
133
            'event' => $event,
134
            'data' => $payload,
135
            'socket' => Arr::pull($payload, 'socket'),
136
        ]);
137
138
        foreach ($this->formatChannels($channels) as $channel) {
139
            $connection->publish("{$this->appId}:{$channel}", $payload);
140
        }
141
    }
142
}
143