Completed
Push — master ( 9ad379...c29c0f )
by Biao
03:47
created

Server::__construct()   D

Complexity

Conditions 10
Paths 384

Size

Total Lines 35
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 24
nc 384
nop 1
dl 0
loc 35
rs 4.5333
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Hhxsv5\LaravelS\Swoole;
4
5
use Hhxsv5\LaravelS\Swoole\Socket\PortInterface;
6
use Hhxsv5\LaravelS\Swoole\Task\Event;
7
use Hhxsv5\LaravelS\Swoole\Task\Listener;
8
use Hhxsv5\LaravelS\Swoole\Task\Task;
9
use Hhxsv5\LaravelS\Swoole\Traits\LogTrait;
10
use Hhxsv5\LaravelS\Swoole\Traits\ProcessTitleTrait;
11
12
class Server
13
{
14
    use LogTrait;
15
    use ProcessTitleTrait;
16
17
    protected $conf;
18
19
    /**
20
     * @var \swoole_http_server|\swoole_websocket_server
21
     */
22
    protected $swoole;
23
24
    protected $enableWebSocket = false;
25
26
    protected $attachedSockets = [];
27
28
    protected function __construct(array $conf)
29
    {
30
        $this->conf = $conf;
31
        $this->enableWebSocket = !empty($this->conf['websocket']['enable']);
32
        $this->attachedSockets = empty($this->conf['sockets']) ? [] : $this->conf['sockets'];
33
34
        $ip = isset($conf['listen_ip']) ? $conf['listen_ip'] : '127.0.0.1';
35
        $port = isset($conf['listen_port']) ? $conf['listen_port'] : 5200;
36
        $socketType = isset($conf['socket_type']) ? $conf['socket_type'] : \SWOOLE_SOCK_TCP;
37
38
        if ($socketType === \SWOOLE_SOCK_UNIX_STREAM) {
39
            $socketDir = dirname($ip);
40
            if (!file_exists($socketDir)) {
41
                mkdir($socketDir);
42
            }
43
        }
44
45
        $settings = isset($conf['swoole']) ? $conf['swoole'] : [];
46
        $settings['enable_static_handler'] = !empty($conf['handle_static']);
47
48
        $serverClass = $this->enableWebSocket ? \swoole_websocket_server::class : \swoole_http_server::class;
49
        if (isset($settings['ssl_cert_file'], $settings['ssl_key_file'])) {
50
            $this->swoole = new $serverClass($ip, $port, \SWOOLE_PROCESS, $socketType | \SWOOLE_SSL);
51
        } else {
52
            $this->swoole = new $serverClass($ip, $port, \SWOOLE_PROCESS, $socketType);
53
        }
54
55
        $this->swoole->set($settings);
56
57
        $this->bindBaseEvent();
58
        $this->bindHttpEvent();
59
        $this->bindTaskEvent();
60
        $this->bindWebSocketEvent();
61
        $this->bindAttachedSockets();
62
        $this->bindSwooleTables();
63
    }
64
65
    protected function bindBaseEvent()
66
    {
67
        $this->swoole->on('Start', [$this, 'onStart']);
68
        $this->swoole->on('Shutdown', [$this, 'onShutdown']);
69
        $this->swoole->on('ManagerStart', [$this, 'onManagerStart']);
70
        $this->swoole->on('ManagerStop', [$this, 'onManagerStop']);
71
        $this->swoole->on('WorkerStart', [$this, 'onWorkerStart']);
72
        $this->swoole->on('WorkerStop', [$this, 'onWorkerStop']);
73
        $this->swoole->on('WorkerError', [$this, 'onWorkerError']);
74
        $this->swoole->on('PipeMessage', [$this, 'onPipeMessage']);
75
    }
76
77
    protected function bindHttpEvent()
78
    {
79
        $this->swoole->on('Request', [$this, 'onRequest']);
80
    }
81
82
    protected function bindTaskEvent()
83
    {
84
        if (!empty($this->conf['swoole']['task_worker_num'])) {
85
            $this->swoole->on('Task', [$this, 'onTask']);
86
            $this->swoole->on('Finish', [$this, 'onFinish']);
87
        }
88
    }
89
90
    protected function bindWebSocketEvent()
91
    {
92
        if ($this->enableWebSocket) {
93
            $eventHandler = function ($method, array $params) {
94
                try {
95
                    call_user_func_array([$this->getWebSocketHandler(), $method], $params);
96
                } catch (\Exception $e) {
97
                    $this->logException($e);
98
                }
99
            };
100
101
            $this->swoole->on('Open', function () use ($eventHandler) {
102
                $eventHandler('onOpen', func_get_args());
103
            });
104
105
            $this->swoole->on('Message', function () use ($eventHandler) {
106
                $eventHandler('onMessage', func_get_args());
107
            });
108
109
            $this->swoole->on('Close', function (\swoole_websocket_server $server, $fd, $reactorId) use ($eventHandler) {
110
                $clientInfo = $server->getClientInfo($fd);
111
                if (isset($clientInfo['websocket_status']) && $clientInfo['websocket_status'] === \WEBSOCKET_STATUS_FRAME) {
112
                    $eventHandler('onClose', func_get_args());
113
                }
114
                // else ignore the close event for http server
115
            });
116
        }
117
    }
118
119
    protected function bindAttachedSockets()
120
    {
121
        foreach ($this->attachedSockets as $socket) {
122
            $port = $this->swoole->addListener($socket['host'], $socket['port'], $socket['type']);
123
            if (!($port instanceof \swoole_server_port)) {
124
                $errno = method_exists($this->swoole, 'getLastError') ? $this->swoole->getLastError() : 'unknown';
125
                $errstr = sprintf('listen %s:%s failed: errno=%s', $socket['host'], $socket['port'], $errno);
126
                $this->log($errstr, 'ERROR');
127
                continue;
128
            }
129
130
            $port->set(empty($socket['settings']) ? [] : $socket['settings']);
131
132
            $handlerClass = $socket['handler'];
133
            $eventHandler = function ($method, array $params) use ($port, $handlerClass) {
134
                $handler = $this->getSocketHandler($port, $handlerClass);
135
                if (method_exists($handler, $method)) {
136
                    try {
137
                        call_user_func_array([$handler, $method], $params);
138
                    } catch (\Exception $e) {
139
                        $this->logException($e);
140
                    }
141
                }
142
            };
143
            static $events = [
144
                'Open',
145
                'Request',
146
                'Message',
147
                'Connect',
148
                'Close',
149
                'Receive',
150
                'Packet',
151
                'BufferFull',
152
                'BufferEmpty',
153
            ];
154
            foreach ($events as $event) {
155
                $port->on($event, function () use ($event, $eventHandler) {
156
                    $eventHandler('on' . $event, func_get_args());
157
                });
158
            }
159
        }
160
    }
161
162
    protected function getWebSocketHandler()
163
    {
164
        static $handler = null;
165
        if ($handler !== null) {
166
            return $handler;
167
        }
168
169
        $handlerClass = $this->conf['websocket']['handler'];
170
        $t = new $handlerClass();
171
        if (!($t instanceof WebSocketHandlerInterface)) {
172
            throw new \Exception(sprintf('%s must implement the interface %s', get_class($t), WebSocketHandlerInterface::class));
173
        }
174
        $handler = $t;
175
        return $handler;
176
    }
177
178
    protected function getSocketHandler(\swoole_server_port $port, $handlerClass)
179
    {
180
        static $handlers = [];
181
        $portHash = spl_object_hash($port);
182
        if (isset($handlers[$portHash])) {
183
            return $handlers[$portHash];
184
        }
185
        $t = new $handlerClass($port);
186
        if (!($t instanceof PortInterface)) {
187
            throw new \Exception(sprintf('%s must extend the abstract class TcpSocket/UdpSocket', get_class($t)));
188
        }
189
        $handlers[$portHash] = $t;
190
        return $handlers[$portHash];
191
    }
192
193
    protected function bindSwooleTables()
194
    {
195
        $tables = isset($this->conf['swoole_tables']) ? (array)$this->conf['swoole_tables'] : [];
196
        foreach ($tables as $name => $table) {
197
            $t = new \swoole_table($table['size']);
198
            foreach ($table['column'] as $column) {
199
                if (isset($column['size'])) {
200
                    $t->column($column['name'], $column['type'], $column['size']);
201
                } else {
202
                    $t->column($column['name'], $column['type']);
203
                }
204
            }
205
            $t->create();
206
            $name .= 'Table'; // Avoid naming conflicts
207
            $this->swoole->$name = $t;
208
        }
209
    }
210
211
    public function onStart(\swoole_http_server $server)
212
    {
213
        foreach (spl_autoload_functions() as $function) {
214
            spl_autoload_unregister($function);
215
        }
216
217
        $this->setProcessTitle(sprintf('%s laravels: master process', $this->conf['process_prefix']));
218
219
        if (version_compare(\swoole_version(), '1.9.5', '<')) {
220
            file_put_contents($this->conf['swoole']['pid_file'], $server->master_pid);
221
        }
222
    }
223
224
    public function onShutdown(\swoole_http_server $server)
225
    {
226
227
    }
228
229
    public function onManagerStart(\swoole_http_server $server)
230
    {
231
        $this->setProcessTitle(sprintf('%s laravels: manager process', $this->conf['process_prefix']));
232
    }
233
234
    public function onManagerStop(\swoole_http_server $server)
235
    {
236
237
    }
238
239
    public function onWorkerStart(\swoole_http_server $server, $workerId)
240
    {
241
        if ($workerId >= $server->setting['worker_num']) {
242
            $process = 'task worker';
243
        } else {
244
            $process = 'worker';
245
            if (!empty($this->conf['enable_coroutine'])) {
246
                \Swoole\Runtime::enableCoroutine();
0 ignored issues
show
Bug introduced by
The type Swoole\Runtime was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
247
            }
248
        }
249
        $this->setProcessTitle(sprintf('%s laravels: %s process %d', $this->conf['process_prefix'], $process, $workerId));
250
251
        if (function_exists('opcache_reset')) {
252
            opcache_reset();
253
        }
254
        if (function_exists('apc_clear_cache')) {
255
            apc_clear_cache();
256
        }
257
258
        clearstatcache();
259
    }
260
261
    public function onWorkerStop(\swoole_http_server $server, $workerId)
262
    {
263
264
    }
265
266
    public function onWorkerError(\swoole_http_server $server, $workerId, $workerPId, $exitCode, $signal)
267
    {
268
        $this->log(sprintf('worker[%d] error: exitCode=%s, signal=%s', $workerId, $exitCode, $signal), 'ERROR');
269
    }
270
271
    public function onPipeMessage(\swoole_http_server $server, $srcWorkerId, $message)
272
    {
273
        if ($message instanceof Task) {
274
            $this->onTask($server, uniqid('', true), $srcWorkerId, $message);
275
        }
276
    }
277
278
    public function onRequest(\swoole_http_request $request, \swoole_http_response $response)
279
    {
280
281
    }
282
283
    public function onTask(\swoole_http_server $server, $taskId, $srcWorkerId, $data)
284
    {
285
        if ($data instanceof Event) {
286
            $this->handleEvent($data);
287
        } elseif ($data instanceof Task) {
288
            if ($this->handleTask($data) && method_exists($data, 'finish')) {
289
                return $data;
290
            }
291
        }
292
    }
293
294
    public function onFinish(\swoole_http_server $server, $taskId, $data)
295
    {
296
        if ($data instanceof Task) {
297
            $data->finish();
0 ignored issues
show
Bug introduced by
The method finish() does not exist on Hhxsv5\LaravelS\Swoole\Task\Task. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

297
            $data->/** @scrutinizer ignore-call */ 
298
                   finish();

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...
298
        }
299
    }
300
301
    protected function handleEvent(Event $event)
302
    {
303
        $eventClass = get_class($event);
304
        if (!isset($this->conf['events'][$eventClass])) {
305
            return;
306
        }
307
308
        $listenerClasses = $this->conf['events'][$eventClass];
309
        if (!is_array($listenerClasses)) {
310
            $listenerClasses = (array)$listenerClasses;
311
        }
312
        foreach ($listenerClasses as $listenerClass) {
313
            /**
314
             * @var Listener $listener
315
             */
316
            $listener = new $listenerClass();
317
            if (!($listener instanceof Listener)) {
318
                throw new \Exception(sprintf('%s must extend the abstract class %s', $listenerClass, Listener::class));
319
            }
320
            try {
321
                $listener->handle($event);
322
            } catch (\Exception $e) {
323
                $this->logException($e);
324
            }
325
        }
326
    }
327
328
    protected function handleTask(Task $task)
329
    {
330
        try {
331
            $task->handle();
332
            return true;
333
        } catch (\Exception $e) {
334
            $this->logException($e);
335
            return false;
336
        }
337
    }
338
339
    public function run()
340
    {
341
        $this->swoole->start();
342
    }
343
}
344