SwooleWebSocketWrapper::start()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 1.0005

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 11
nc 1
nop 0
dl 0
loc 16
ccs 11
cts 12
cp 0.9167
crap 1.0005
rs 9.4285
c 1
b 0
f 0
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