Completed
Pull Request — master (#447)
by Marcel
02:52 queued 01:28
created

RedisPusherBroadcaster::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 4
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
     * The Redis connection to use for broadcasting.
40
     *
41
     * @var string|null
42
     */
43
    protected $connection;
44
45
    /**
46
     * Create a new broadcaster instance.
47
     *
48
     * @param  Pusher $pusher
49
     * @param  string $appId
50
     * @param  \Illuminate\Contracts\Redis\Factory $redis
51
     * @param  string|null $connection
52
     */
53
    public function __construct(Pusher $pusher, string $appId, Redis $redis, $connection = null)
54
    {
55
        $this->pusher = $pusher;
56
        $this->appId = $appId;
57
        $this->redis = $redis;
58
        $this->connection = $connection;
59
    }
60
61
    /**
62
     * Authenticate the incoming request for a given channel.
63
     *
64
     * @param  \Illuminate\Http\Request  $request
65
     * @return mixed
66
     *
67
     * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
68
     */
69
    public function auth($request)
70
    {
71
        $channelName = $this->normalizeChannelName($request->channel_name);
72
73
        if ($this->isGuardedChannel($request->channel_name) &&
74
            ! $this->retrieveUser($request, $channelName)) {
75
            throw new AccessDeniedHttpException;
76
        }
77
78
        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...
79
            $request, $channelName
80
        );
81
    }
82
83
    /**
84
     * Return the valid authentication response.
85
     *
86
     * @param  \Illuminate\Http\Request $request
87
     * @param  mixed $result
88
     * @return mixed
89
     * @throws \Pusher\PusherException
90
     */
91
    public function validAuthenticationResponse($request, $result)
92
    {
93
        if (Str::startsWith($request->channel_name, 'private')) {
94
            return $this->decodePusherResponse(
95
                $request, $this->pusher->socket_auth($request->channel_name, $request->socket_id)
96
            );
97
        }
98
99
        $channelName = $this->normalizeChannelName($request->channel_name);
100
101
        return $this->decodePusherResponse(
102
            $request,
103
            $this->pusher->presence_auth(
104
                $request->channel_name, $request->socket_id,
105
                $this->retrieveUser($request, $channelName)->getAuthIdentifier(), $result
106
            )
107
        );
108
    }
109
110
    /**
111
     * Decode the given Pusher response.
112
     *
113
     * @param  \Illuminate\Http\Request  $request
114
     * @param  mixed  $response
115
     * @return array
116
     */
117
    protected function decodePusherResponse($request, $response)
118
    {
119
        if (! $request->input('callback', false)) {
120
            return json_decode($response, true);
121
        }
122
123
        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...
124
            ->withCallback($request->callback);
125
    }
126
127
    /**
128
     * Broadcast the given event.
129
     *
130
     * @param  array  $channels
131
     * @param  string  $event
132
     * @param  array  $payload
133
     * @return void
134
     */
135
    public function broadcast(array $channels, $event, array $payload = [])
136
    {
137
        $connection = $this->redis->connection($this->connection);
138
139
        $payload = json_encode([
140
            'appId' => $this->appId,
141
            'event' => $event,
142
            'data' => $payload,
143
            'socket' => Arr::pull($payload, 'socket'),
144
        ]);
145
146
        foreach ($this->formatChannels($channels) as $channel) {
147
            $connection->publish("{$this->appId}:$channel", $payload);
148
        }
149
    }
150
}
151