SwooleWebSocketWrapper   A
last analyzed

Complexity

Total Complexity 32

Size/Duplication

Total Lines 213
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 78.42%

Importance

Changes 10
Bugs 1 Features 0
Metric Value
c 10
b 1
f 0
dl 0
loc 213
ccs 109
cts 139
cp 0.7842
rs 9.6
wmc 32
lcom 1
cbo 3

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getParams() 0 7 1
A start() 0 16 1
A onFinish() 0 4 1
A registerCodec() 0 10 3
A onTask() 0 8 3
C onHandShake() 0 41 8
B onMessage() 0 37 6
B dispatch() 0 28 3
A endResponse() 0 20 3
A onClose() 0 12 2
1
<?php
2
namespace Laravoole\Wrapper;
3
4
use swoole_websocket_server;
5
use swoole_http_request;
6
use swoole_http_response;
7
use swoole_process;
8
9
use Laravoole\Request;
10
use Laravoole\Response;
11
use Laravoole\WebsocketCodec\Json;
12
use Laravoole\WebsocketCodec\JsonRpc;
13
14
use Symfony\Component\HttpFoundation\BinaryFileResponse;
15
16
class SwooleWebSocketWrapper extends SwooleHttpWrapper implements ServerInterface
17
{
18
    protected $defaultProtocol;
19
20
    protected $pushProcess;
21
22
    protected $connections = [];
23
24
    protected $unfinished = [];
25
26
    protected static $protocolCodecs = [
27
        'json' => Json::class,
28
        'jsonrpc' => JsonRpc::class,
29
    ];
30
31 4
    public function __construct($host, $port)
32
    {
33 4
        $this->server = new swoole_websocket_server($host, $port);
34 4
    }
35
36
    public static function getParams()
37
    {
38
        $params = parent::getParams();
39
        unset($params[array_search('task_worker_num', $params)]);
40
        $params['task_worker_num'] = 1;
41
        return $params;
42
    }
43
44 4
    public static function registerCodec($protocol, $class = null)
45
    {
46 4
        if (is_string($protocol)) {
47
            if (!class_exists($class)) {
48
                throw new Exception("class $class not found", 1);
49
            }
50
            $protocol = [$protocol => $class];
51
        }
52 4
        static::$protocolCodecs = array_merge(static::$protocolCodecs, $protocol);
53 4
    }
54
55 4
    public function start()
56
    {
57 4
        $this->defaultProtocol = $this->wrapper_config['websocket_default_protocol'];
58
59 4
        static::registerCodec($this->wrapper_config['websocket_protocols']);
60
61 4
        $this->callbacks = array_merge([
62 4
            'HandShake' => [$this, 'onHandShake'],
63 4
            'Message' => [$this, 'onMessage'],
64 4
            'Close' => [$this, 'onClose'],
65 4
            'Task' => [static::class, 'onTask'],
66 4
            'Finish' => [static::class, 'onFinish'],
67 4
        ], $this->callbacks);
68 4
        parent::start();
69
70
    }
71
72
    public static function onTask($server, $task_id, $from_id, $data)
0 ignored issues
show
Unused Code introduced by
The parameter $task_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $from_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
73
    {
74
        foreach ($data['fds'] as $fd) {
75
            try {
76
                $server->push($fd, $data['params']);
77
            } catch (\ErrorException $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
78
        }
79
    }
80
81
    public static function onFinish($server, $task_id, $data)
0 ignored issues
show
Unused Code introduced by
The parameter $server is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $task_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
82
    {
83
84
    }
85
86 4
    public function onHandShake(swoole_http_request $request, swoole_http_response $response)
87
    {
88 4
        $protocol = false;
89 4
        if (isset($request->header['sec-websocket-protocol'])) {
90 4
            $protocols = preg_split('~,\s*~', $request->header['sec-websocket-protocol']);
91 4
            foreach ($protocols as $protocol) {
92 4
                if (isset(static::$protocolCodecs[$protocol])) {
93 4
                    break;
94
                }
95 2
            }
96 4
            if ($protocol) {
97 4
                $response->header('Sec-WebSocket-Protocol', $protocol);
98 2
            }
99 2
        }
100
101 4
        if (!$protocol) {
102
            $protocol = $this->defaultProtocol;
103
        }
104
105 4
        $secKey = $request->header['sec-websocket-key'];
106 4
        $secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
107
108
        foreach ([
109 4
            "Upgrade" => "websocket",
110 4
            "Connection" => "Upgrade",
111 4
            "Sec-WebSocket-Version" => "13",
112 4
            "Sec-WebSocket-Accept" => $secAccept,
113 2
        ] as $k => $v) {
114 4
            $response->header($k, $v);
115 2
        }
116 4
        $response->status(101);
117
118 4
        $laravooleRequest = new Request($request->fd);
119 4
        foreach ($request as $k => $v) {
120 4
            $laravooleRequest->$k = $v;
121 2
        }
122 4
        $this->connections[$request->fd] = ['request' => $laravooleRequest, 'protocol' => static::$protocolCodecs[$protocol]];
123 4
        $this->unfinished[$request->fd] = '';
124 4
        return true;
125
126
    }
127
128 4
    public function onMessage(swoole_websocket_server $server, $frame)
129
    {
130 4
        if (!isset($this->unfinished[$frame->fd])) {
131
            return false;
132
        }
133 4
        if (isset($this->connections[$frame->fd]['request']->laravooleInfo->nextMessageRoute)) {
134 4
            $request = $this->connections[$frame->fd]['request'];
135 4
            $route = $request->laravooleInfo->nextMessageRoute;
136 4
            $data['method'] = $route['method'];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
137 4
            $data['params'] = $route['params'];
138 4
            $data['params']['_laravoole_raw'] = $frame->data;
139 4
            $data['params']['_laravoole_previous'] = $route['previous'];
140
141 4
            $data['echo'] = $request->echo;
142 4
            if ($frame->finish) {
143 4
                unset($request->laravooleInfo->nextMessageRoute);
144 2
            }
145 4
            return $this->dispatch($server, $frame->fd, $data);
146
147
        } else {
148 4
            $this->unfinished[$frame->fd] .= $frame->data;
149
        }
150
151 4
        if (!$frame->finish) {
152
            return;
153
        }
154 4
        $protocol = $this->connections[$frame->fd]['protocol'];
155 4
        $data = $protocol::decode($this->unfinished[$frame->fd]);
156 4
        if (is_null($data)) {
157
            return;
158
        }
159
160 4
        $this->unfinished[$frame->fd] = '';
161
162 4
        return $this->dispatch($server, $frame->fd, $data);
163
164
    }
165
166 4
    protected function dispatch($server, $fd, $data)
167
    {
168 4
        $request = $this->connections[$fd]['request'];
169
170 4
        $request->method = $request->server['request_uri'] = $data['method'];
171 4
        $request->get = (array) ($data['params']);
172 4
        $request->echo = isset($data['echo']) ? $data['echo'] : null;
173 4
        $request = $this->ucHeaders($request);
174
175 4
        $response = new Response($this, $request);
176 4
        $illuminateRequest = $this->convertRequest($request);
177
178 4
        if (isset($request->laravooleInfo)) {
179 4
            $illuminateRequest->setLaravooleInfo($request->laravooleInfo);
180 2
        } else {
181 4
            $illuminateRequest->setLaravooleInfo((object) [
182 4
                'fd' => $fd,
183 4
                'server' => $server,
184 4
                'codec' => $this->connections[$response->request->fd]['protocol'],
185 2
            ]);
186 2
        }
187 4
        $illuminateRequest->headers->set('X-Requested-With', 'XMLHttpRequest');
188 4
        $illuminateRequest->headers->set('Accept', 'Accept: text/html,application/xhtml+xml,application/xml,application/json');
189 4
        $illuminateResponse = parent::handleRequest($illuminateRequest);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (handleRequest() instead of dispatch()). Are you sure this is correct? If so, you might want to change this to $this->handleRequest().

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...
190 4
        $this->handleResponse($response, $illuminateResponse, '');
191
192 4
        $request->laravooleInfo = $illuminateRequest->getLaravooleInfo();
193 4
    }
194
195 4
    public function endResponse($response, $content)
196
    {
197 4
        if (isset($response->request)) {
198 4
            if (!is_string($content)) {
199
                $content = file_get_contents($content());
200
            }
201
            // This is a websocket request
202 4
            $protocol = $this->connections[$response->request->fd]['protocol'];
203 4
            $data = $protocol::encode(
204 4
                $response->http_status,
205 4
                $response->request->method,
206 2
                $content,
207 4
                $response->request->echo
208 2
            );
209 4
            $this->server->push($response->request->fd, $data);
0 ignored issues
show
Bug introduced by
The method push() does not seem to exist on object<Laravoole\Workerman\Worker>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
210 2
        } else {
211
            // This is a http request
212 4
            parent::endResponse($response, $content);
213
        }
214 4
    }
215
216 4
    public function onClose($server, $fd)
217
    {
218 4
        if (!isset($this->connections[$fd])) {
219 4
            return;
220
        }
221
        $request = $this->connections[$fd]['request'];
222
        $this->events->fire('laravoole.swoole.websocket.closing', [$request, $fd]);
0 ignored issues
show
Bug introduced by
The property events 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...
223
224
        unset($this->unfinished[$fd]);
225
        unset($this->connections[$fd]);
226
227
    }
228
}
229