| Total Complexity | 61 |
| Total Lines | 498 |
| Duplicated Lines | 0 % |
| Changes | 0 | ||
Complex classes like Manager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Manager, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 19 | class Manager |
||
| 20 | { |
||
| 21 | use CanWebsocket, CanSwooleTable, CanSwooleTask; |
||
|
|
|||
| 22 | |||
| 23 | const MAC_OSX = 'Darwin'; |
||
| 24 | |||
| 25 | /** |
||
| 26 | * @var \Swoole\Http\Server | \Swoole\Websocket\Server |
||
| 27 | */ |
||
| 28 | protected $server; |
||
| 29 | |||
| 30 | /** |
||
| 31 | * Container. |
||
| 32 | * |
||
| 33 | * @var \Illuminate\Contracts\Container\Container |
||
| 34 | */ |
||
| 35 | protected $container; |
||
| 36 | |||
| 37 | /** |
||
| 38 | * @var \SwooleTW\Http\Server\Application |
||
| 39 | */ |
||
| 40 | protected $application; |
||
| 41 | |||
| 42 | /** |
||
| 43 | * Laravel|Lumen Application. |
||
| 44 | * |
||
| 45 | * @var \Illuminate\Container\Container |
||
| 46 | */ |
||
| 47 | protected $app; |
||
| 48 | |||
| 49 | /** |
||
| 50 | * @var string |
||
| 51 | */ |
||
| 52 | protected $framework; |
||
| 53 | |||
| 54 | /** |
||
| 55 | * @var string |
||
| 56 | */ |
||
| 57 | protected $basePath; |
||
| 58 | |||
| 59 | /** |
||
| 60 | * @var \SwooleTW\Http\Server\Sandbox |
||
| 61 | */ |
||
| 62 | protected $sandbox; |
||
| 63 | |||
| 64 | /** |
||
| 65 | * Server events. |
||
| 66 | * |
||
| 67 | * @var array |
||
| 68 | */ |
||
| 69 | protected $events = [ |
||
| 70 | 'start', 'shutDown', 'workerStart', 'workerStop', 'packet', |
||
| 71 | 'bufferFull', 'bufferEmpty', 'task', 'finish', 'pipeMessage', |
||
| 72 | 'workerError', 'managerStart', 'managerStop', 'request', |
||
| 73 | ]; |
||
| 74 | |||
| 75 | /** |
||
| 76 | * HTTP server manager constructor. |
||
| 77 | * |
||
| 78 | * @param \Illuminate\Contracts\Container\Container $container |
||
| 79 | * @param string $framework |
||
| 80 | * @param string $basePath |
||
| 81 | */ |
||
| 82 | public function __construct(Container $container, $framework, $basePath = null) |
||
| 83 | { |
||
| 84 | $this->container = $container; |
||
| 85 | $this->framework = $framework; |
||
| 86 | $this->basePath = $basePath; |
||
| 87 | |||
| 88 | $this->initialize(); |
||
| 89 | } |
||
| 90 | |||
| 91 | /** |
||
| 92 | * Run swoole server. |
||
| 93 | */ |
||
| 94 | public function run() |
||
| 95 | { |
||
| 96 | $this->server->start(); |
||
| 97 | } |
||
| 98 | |||
| 99 | /** |
||
| 100 | * Stop swoole server. |
||
| 101 | */ |
||
| 102 | public function stop() |
||
| 103 | { |
||
| 104 | $this->server->shutdown(); |
||
| 105 | } |
||
| 106 | |||
| 107 | /** |
||
| 108 | * Initialize. |
||
| 109 | */ |
||
| 110 | protected function initialize() |
||
| 111 | { |
||
| 112 | $this->setProcessName('manager process'); |
||
| 113 | |||
| 114 | $this->createTables(); |
||
| 115 | |||
| 116 | $this->prepareWebsocket(); |
||
| 117 | $this->createSwooleServer(); |
||
| 118 | $this->configureSwooleServer(); |
||
| 119 | $this->setSwooleServerListeners(); |
||
| 120 | } |
||
| 121 | |||
| 122 | /** |
||
| 123 | * Prepare settings if websocket is enabled. |
||
| 124 | */ |
||
| 125 | protected function prepareWebsocket() |
||
| 126 | { |
||
| 127 | $isWebsocket = $this->container['config']->get('swoole_http.websocket.enabled'); |
||
| 128 | $parser = $this->container['config']->get('swoole_websocket.parser'); |
||
| 129 | |||
| 130 | if ($isWebsocket) { |
||
| 131 | array_push($this->events, ...$this->wsEvents); |
||
| 132 | $this->isWebsocket = true; |
||
| 133 | $this->setParser(new $parser); |
||
| 134 | $this->setWebsocketRoom(); |
||
| 135 | } |
||
| 136 | } |
||
| 137 | |||
| 138 | /** |
||
| 139 | * Create swoole server. |
||
| 140 | */ |
||
| 141 | protected function createSwooleServer() |
||
| 142 | { |
||
| 143 | $server = $this->isWebsocket ? WebsocketServer::class : HttpServer::class; |
||
| 144 | $host = $this->container['config']->get('swoole_http.server.host'); |
||
| 145 | $port = $this->container['config']->get('swoole_http.server.port'); |
||
| 146 | $hasCert = $this->container['config']->get('swoole_http.server.options.ssl_cert_file'); |
||
| 147 | $hasKey = $this->container['config']->get('swoole_http.server.options.ssl_key_file'); |
||
| 148 | $args = $hasCert && $hasKey ? [SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL] : []; |
||
| 149 | |||
| 150 | $this->server = new $server($host, $port, ...$args); |
||
| 151 | } |
||
| 152 | |||
| 153 | /** |
||
| 154 | * Set swoole server configurations. |
||
| 155 | */ |
||
| 156 | protected function configureSwooleServer() |
||
| 157 | { |
||
| 158 | $config = $this->container['config']->get('swoole_http.server.options'); |
||
| 159 | |||
| 160 | // only enable task worker in websocket mode |
||
| 161 | if (! $this->isWebsocket) { |
||
| 162 | unset($config['task_worker_num']); |
||
| 163 | } |
||
| 164 | |||
| 165 | $this->server->set($config); |
||
| 166 | } |
||
| 167 | |||
| 168 | /** |
||
| 169 | * Set swoole server listeners. |
||
| 170 | */ |
||
| 171 | protected function setSwooleServerListeners() |
||
| 172 | { |
||
| 173 | foreach ($this->events as $event) { |
||
| 174 | $listener = 'on' . ucfirst($event); |
||
| 175 | |||
| 176 | if (method_exists($this, $listener)) { |
||
| 177 | $this->server->on($event, [$this, $listener]); |
||
| 178 | } else { |
||
| 179 | $this->server->on($event, function () use ($event) { |
||
| 180 | $event = sprintf('swoole.%s', $event); |
||
| 181 | |||
| 182 | $this->container['events']->fire($event, func_get_args()); |
||
| 183 | }); |
||
| 184 | } |
||
| 185 | } |
||
| 186 | } |
||
| 187 | |||
| 188 | /** |
||
| 189 | * "onStart" listener. |
||
| 190 | */ |
||
| 191 | public function onStart() |
||
| 192 | { |
||
| 193 | $this->setProcessName('master process'); |
||
| 194 | $this->createPidFile(); |
||
| 195 | |||
| 196 | $this->container['events']->fire('swoole.start', func_get_args()); |
||
| 197 | } |
||
| 198 | |||
| 199 | /** |
||
| 200 | * "onWorkerStart" listener. |
||
| 201 | */ |
||
| 202 | public function onWorkerStart(HttpServer $server) |
||
| 203 | { |
||
| 204 | $this->clearCache(); |
||
| 205 | $this->setProcessName('worker process'); |
||
| 206 | |||
| 207 | $this->container['events']->fire('swoole.workerStart', func_get_args()); |
||
| 208 | |||
| 209 | // don't init laravel app in task workers |
||
| 210 | if ($server->taskworker) { |
||
| 211 | return; |
||
| 212 | } |
||
| 213 | |||
| 214 | // clear events instance in case of repeated listeners in worker process |
||
| 215 | Facade::clearResolvedInstance('events'); |
||
| 216 | |||
| 217 | // initialize laravel app |
||
| 218 | $this->createApplication(); |
||
| 219 | $this->setLaravelApp(); |
||
| 220 | |||
| 221 | // bind after setting laravel app |
||
| 222 | $this->bindToLaravelApp(); |
||
| 223 | |||
| 224 | // set application to sandbox environment |
||
| 225 | $this->sandbox = Sandbox::make($this->getApplication()); |
||
| 226 | |||
| 227 | // load websocket handlers after binding websocket to laravel app |
||
| 228 | if ($this->isWebsocket) { |
||
| 229 | $this->setWebsocketHandler(); |
||
| 230 | $this->loadWebsocketRoutes(); |
||
| 231 | } |
||
| 232 | } |
||
| 233 | |||
| 234 | /** |
||
| 235 | * "onRequest" listener. |
||
| 236 | * |
||
| 237 | * @param \Swoole\Http\Request $swooleRequest |
||
| 238 | * @param \Swoole\Http\Response $swooleResponse |
||
| 239 | */ |
||
| 240 | public function onRequest($swooleRequest, $swooleResponse) |
||
| 241 | { |
||
| 242 | $this->app['events']->fire('swoole.request'); |
||
| 243 | |||
| 244 | $this->resetOnRequest(); |
||
| 245 | |||
| 246 | $handleStatic = $this->container['config']->get('swoole_http.handle_static_files', true); |
||
| 247 | |||
| 248 | try { |
||
| 249 | // transform swoole request to illuminate request |
||
| 250 | $illuminateRequest = Request::make($swooleRequest)->toIlluminate(); |
||
| 251 | |||
| 252 | // handle static file request first |
||
| 253 | if ($handleStatic && $this->handleStaticRequest($illuminateRequest, $swooleResponse)) { |
||
| 254 | return; |
||
| 255 | } |
||
| 256 | |||
| 257 | // set current request to sandbox |
||
| 258 | $this->sandbox->setRequest($illuminateRequest); |
||
| 259 | |||
| 260 | // enable sandbox |
||
| 261 | $this->sandbox->enable(); |
||
| 262 | $application = $this->sandbox->getApplication(); |
||
| 263 | |||
| 264 | // handle request via laravel/lumen's dispatcher |
||
| 265 | $illuminateResponse = $application->run($illuminateRequest); |
||
| 266 | $response = Response::make($illuminateResponse, $swooleResponse); |
||
| 267 | $response->send(); |
||
| 268 | } catch (Exception $e) { |
||
| 269 | try { |
||
| 270 | $exceptionResponse = $this->app[ExceptionHandler::class]->render($illuminateRequest, $e); |
||
| 271 | $response = Response::make($exceptionResponse, $swooleResponse); |
||
| 272 | $response->send(); |
||
| 273 | } catch (Exception $e) { |
||
| 274 | $this->logServerError($e); |
||
| 275 | } |
||
| 276 | } finally { |
||
| 277 | // disable and recycle sandbox resource |
||
| 278 | $this->sandbox->disable(); |
||
| 279 | } |
||
| 280 | } |
||
| 281 | |||
| 282 | /** |
||
| 283 | * Handle static file request. |
||
| 284 | * |
||
| 285 | * @param \Illuminate\Http\Request $illuminateRequest |
||
| 286 | * @param \Swoole\Http\Response $swooleResponse |
||
| 287 | * @return boolean |
||
| 288 | */ |
||
| 289 | protected function handleStaticRequest($illuminateRequest, $swooleResponse) |
||
| 290 | { |
||
| 291 | $uri = $illuminateRequest->getRequestUri(); |
||
| 292 | $blackList = ['php', 'htaccess', 'config']; |
||
| 293 | $extension = substr(strrchr($uri, '.'), 1); |
||
| 294 | if ($extension && in_array($extension, $blackList)) { |
||
| 295 | return; |
||
| 296 | } |
||
| 297 | |||
| 298 | $publicPath = $this->container['config']->get('swoole_http.server.public_path', base_path('public')); |
||
| 299 | $filename = $publicPath . $uri; |
||
| 300 | |||
| 301 | if (! is_file($filename) || filesize($filename) === 0) { |
||
| 302 | return; |
||
| 303 | } |
||
| 304 | |||
| 305 | $swooleResponse->status(200); |
||
| 306 | $mime = mime_content_type($filename); |
||
| 307 | if ($extension === 'js') { |
||
| 308 | $mime = 'text/javascript'; |
||
| 309 | } elseif ($extension === 'css') { |
||
| 310 | $mime = 'text/css'; |
||
| 311 | } |
||
| 312 | $swooleResponse->header('Content-Type', $mime); |
||
| 313 | $swooleResponse->sendfile($filename); |
||
| 314 | |||
| 315 | return true; |
||
| 316 | } |
||
| 317 | |||
| 318 | /** |
||
| 319 | * Reset on every request. |
||
| 320 | */ |
||
| 321 | protected function resetOnRequest() |
||
| 322 | { |
||
| 323 | // Reset websocket data |
||
| 324 | if ($this->isWebsocket) { |
||
| 325 | $this->websocket->reset(true); |
||
| 326 | } |
||
| 327 | } |
||
| 328 | |||
| 329 | /** |
||
| 330 | * Set onTask listener. |
||
| 331 | */ |
||
| 332 | public function onTask(HttpServer $server, $taskId, $srcWorkerId, $data) |
||
| 333 | { |
||
| 334 | $this->container['events']->fire('swoole.task', func_get_args()); |
||
| 335 | |||
| 336 | try { |
||
| 337 | if ($this->isWebsocket) { |
||
| 338 | // push websocket message |
||
| 339 | if (is_array($data) |
||
| 340 | && array_key_exists('action', $data) |
||
| 341 | && $data['action'] === Websocket::PUSH_ACTION |
||
| 342 | ) { |
||
| 343 | $this->pushMessage($server, $data['data'] ?? []); |
||
| 344 | } else { |
||
| 345 | $unserializedData = str_contains($data, 'SuperClosure') ? (new Serializer)->unserialize($data) : unserialize($data); |
||
| 346 | |||
| 347 | $this->server->finish($unserializedData()); |
||
| 348 | } |
||
| 349 | } |
||
| 350 | } catch (Exception $e) { |
||
| 351 | $this->logServerError($e); |
||
| 352 | } |
||
| 353 | } |
||
| 354 | |||
| 355 | /** |
||
| 356 | * Set onFinish listener. |
||
| 357 | */ |
||
| 358 | public function onFinish(HttpServer $server, $taskId, $data) |
||
| 360 | // dump($taskId, $data); |
||
| 361 | } |
||
| 362 | |||
| 363 | /** |
||
| 364 | * Set onShutdown listener. |
||
| 365 | */ |
||
| 366 | public function onShutdown() |
||
| 367 | { |
||
| 368 | $this->removePidFile(); |
||
| 369 | } |
||
| 370 | |||
| 371 | /** |
||
| 372 | * Create application. |
||
| 373 | */ |
||
| 374 | protected function createApplication() |
||
| 377 | } |
||
| 378 | |||
| 379 | /** |
||
| 380 | * Get application. |
||
| 381 | * |
||
| 382 | * @return \SwooleTW\Http\Server\Application |
||
| 383 | */ |
||
| 384 | protected function getApplication() |
||
| 385 | { |
||
| 386 | if (! $this->application instanceof Application) { |
||
| 387 | $this->createApplication(); |
||
| 388 | } |
||
| 389 | |||
| 390 | return $this->application; |
||
| 391 | } |
||
| 392 | |||
| 393 | /** |
||
| 394 | * Set bindings to Laravel app. |
||
| 395 | */ |
||
| 396 | protected function bindToLaravelApp() |
||
| 397 | { |
||
| 398 | $this->bindSwooleServer(); |
||
| 399 | $this->bindSwooleTable(); |
||
| 400 | $this->bindSwooleTask(); |
||
| 401 | |||
| 402 | if ($this->isWebsocket) { |
||
| 403 | $this->bindRoom(); |
||
| 404 | $this->bindWebsocket(); |
||
| 405 | } |
||
| 406 | } |
||
| 407 | |||
| 408 | /** |
||
| 409 | * Set isSandbox config. |
||
| 410 | */ |
||
| 411 | protected function setIsSandbox() |
||
| 412 | { |
||
| 413 | $this->isSandbox = $this->container['config']->get('swoole_http.sandbox_mode', false); |
||
| 414 | } |
||
| 415 | |||
| 416 | /** |
||
| 417 | * Set Laravel app. |
||
| 418 | */ |
||
| 419 | protected function setLaravelApp() |
||
| 420 | { |
||
| 421 | $this->app = $this->getApplication()->getApplication(); |
||
| 422 | } |
||
| 423 | |||
| 424 | /** |
||
| 425 | * Bind swoole server to Laravel app container. |
||
| 426 | */ |
||
| 427 | protected function bindSwooleServer() |
||
| 428 | { |
||
| 429 | $this->app->singleton('swoole.server', function () { |
||
| 430 | return $this->server; |
||
| 431 | }); |
||
| 432 | } |
||
| 433 | |||
| 434 | /** |
||
| 435 | * Gets pid file path. |
||
| 436 | * |
||
| 437 | * @return string |
||
| 438 | */ |
||
| 439 | protected function getPidFile() |
||
| 440 | { |
||
| 441 | return $this->container['config']->get('swoole_http.server.options.pid_file'); |
||
| 442 | } |
||
| 443 | |||
| 444 | /** |
||
| 445 | * Create pid file. |
||
| 446 | */ |
||
| 447 | protected function createPidFile() |
||
| 448 | { |
||
| 449 | $pidFile = $this->getPidFile(); |
||
| 450 | $pid = $this->server->master_pid; |
||
| 451 | |||
| 452 | file_put_contents($pidFile, $pid); |
||
| 453 | } |
||
| 454 | |||
| 455 | /** |
||
| 456 | * Remove pid file. |
||
| 457 | */ |
||
| 458 | protected function removePidFile() |
||
| 459 | { |
||
| 460 | $pidFile = $this->getPidFile(); |
||
| 461 | |||
| 462 | if (file_exists($pidFile)) { |
||
| 463 | unlink($pidFile); |
||
| 464 | } |
||
| 465 | } |
||
| 466 | |||
| 467 | /** |
||
| 468 | * Clear APC or OPCache. |
||
| 469 | */ |
||
| 470 | protected function clearCache() |
||
| 471 | { |
||
| 472 | if (function_exists('apc_clear_cache')) { |
||
| 473 | apc_clear_cache(); |
||
| 474 | } |
||
| 475 | |||
| 476 | if (function_exists('opcache_reset')) { |
||
| 477 | opcache_reset(); |
||
| 478 | } |
||
| 479 | } |
||
| 480 | |||
| 481 | /** |
||
| 482 | * Set process name. |
||
| 483 | * |
||
| 484 | * @param $process |
||
| 485 | */ |
||
| 486 | protected function setProcessName($process) |
||
| 487 | { |
||
| 488 | if (PHP_OS === static::MAC_OSX) { |
||
| 489 | return; |
||
| 490 | } |
||
| 491 | $serverName = 'swoole_http_server'; |
||
| 492 | $appName = $this->container['config']->get('app.name', 'Laravel'); |
||
| 493 | |||
| 494 | $name = sprintf('%s: %s for %s', $serverName, $process, $appName); |
||
| 495 | |||
| 496 | swoole_set_process_name($name); |
||
| 497 | } |
||
| 498 | |||
| 499 | /** |
||
| 500 | * Log server error. |
||
| 501 | * |
||
| 502 | * @param Exception |
||
| 503 | */ |
||
| 504 | protected function logServerError(Exception $e) |
||
| 507 | } |
||
| 508 | |||
| 509 | /** |
||
| 510 | * Get swoole server instance. |
||
| 511 | * |
||
| 512 | * @return Swoole\Http\Server |
||
| 513 | */ |
||
| 514 | public function getServer() |
||
| 515 | { |
||
| 516 | return $this->server; |
||
| 517 | } |
||
| 518 | } |
||
| 519 |