Completed
Push — master ( 4a48b6...f69314 )
by Biao
03:40
created

Portal   B

Complexity

Total Complexity 48

Size/Duplication

Total Lines 287
Duplicated Lines 0 %

Importance

Changes 15
Bugs 3 Features 2
Metric Value
eloc 165
c 15
b 3
f 2
dl 0
loc 287
rs 8.5599
wmc 48

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
B start() 0 37 9
A configure() 0 9 1
B execute() 0 54 7
A kill() 0 6 2
A showInfo() 0 3 1
A runArtisanCommand() 0 4 1
A getConfig() 0 4 1
B stop() 0 45 9
A artisanCmd() 0 7 2
A restart() 0 7 2
A runCommand() 0 11 3
B reload() 0 54 9

How to fix   Complexity   

Complex Class

Complex classes like Portal 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 Portal, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Hhxsv5\LaravelS\Console;
4
5
use Hhxsv5\LaravelS\LaravelS;
6
use Hhxsv5\LaravelS\Swoole\Traits\LogTrait;
7
use Swoole\Process;
8
use Symfony\Component\Console\Command\Command;
9
use Symfony\Component\Console\Input\InputArgument;
10
use Symfony\Component\Console\Input\InputInterface;
11
use Symfony\Component\Console\Input\InputOption;
12
use Symfony\Component\Console\Output\OutputInterface;
13
use Symfony\Component\Console\Style\SymfonyStyle;
14
15
class Portal extends Command
16
{
17
    use LogTrait;
18
19
20
    /**@var string */
21
    protected $basePath;
22
23
    /**@var InputInterface */
24
    protected $input;
25
26
    /**@var OutputInterface */
27
    protected $output;
28
29
    public function __construct($basePath)
30
    {
31
        parent::__construct('laravels');
32
        $this->basePath = $basePath;
33
    }
34
35
    protected function configure()
36
    {
37
        $this->setDescription('LaravelS console tool');
38
        $this->setHelp('LaravelS console tool');
39
40
        $this->addArgument('action', InputArgument::OPTIONAL, 'start|stop|restart|reload|info|help', 'help');
41
        $this->addOption('env', 'e', InputOption::VALUE_OPTIONAL, 'The environment the command should run under, this feature requires Laravel 5.2+');
42
        $this->addOption('daemonize', 'd', InputOption::VALUE_NONE, 'Whether run as a daemon for "start & restart"');
43
        $this->addOption('ignore', 'i', InputOption::VALUE_NONE, 'Whether ignore checking process pid for "start & restart"');
44
    }
45
46
    protected function execute(InputInterface $input, OutputInterface $output)
47
    {
48
        $this->input = $input;
49
        $this->output = $output;
50
        LaravelS::setOutputStyle(new SymfonyStyle($this->input, $this->output));
51
52
        try {
53
            $action = $input->getArgument('action');
54
            switch ($action) {
55
                case 'start':
56
                    $this->start();
57
                    break;
58
                case 'stop':
59
                    $this->stop();
60
                    break;
61
                case 'restart':
62
                    $this->restart();
63
                    break;
64
                case 'reload':
65
                    $this->reload();
66
                    break;
67
                case 'info':
68
                    $this->showInfo();
69
                    break;
70
                default:
71
                    $help = <<<EOS
72
73
Usage: 
74
  [%s] ./bin/laravels [options] <action>
75
76
Arguments:
77
  action                start|stop|restart|reload|info|help
78
79
Options:
80
  -e, --env             The environment the command should run under, this feature requires Laravel 5.2+
81
  -d, --daemonize       Whether run as a daemon for "start & restart"
82
  -i, --ignore          Whether ignore checking process pid for "start & restart"
83
EOS;
84
85
                    $this->info(sprintf($help, PHP_BINARY));
86
                    break;
87
            }
88
        } catch (\Exception $e) {
89
            $error = sprintf(
90
                'Uncaught exception "%s"([%d]%s) at %s:%s, %s%s',
91
                get_class($e),
92
                $e->getCode(),
93
                $e->getMessage(),
94
                $e->getFile(),
95
                $e->getLine(),
96
                PHP_EOL,
97
                $e->getTraceAsString()
98
            );
99
            $this->error($error);
100
        }
101
    }
102
103
    public function start()
104
    {
105
        if (!extension_loaded('swoole')) {
106
            $this->error('LaravelS requires swoole extension, try to `pecl install swoole` and `php --ri swoole`.');
107
            return 1;
108
        }
109
110
        // Initialize configuration config/laravels.json
111
        $options = $this->input->getOptions();
112
        unset($options['env']);
113
        $options = array_filter($options);
114
        $optionStr = '';
115
        foreach ($options as $key => $value) {
116
            $optionStr .= sprintf('--%s%s ', $key, is_bool($value) ? '' : ('=' . $value));
117
        }
118
        $this->runArtisanCommand(trim('laravels config ' . $optionStr));
119
120
        // Here we go...
121
        $config = $this->getConfig();
122
123
        if (!$config['server']['ignore_check_pid'] && file_exists($config['server']['swoole']['pid_file'])) {
124
            $pid = (int)file_get_contents($config['server']['swoole']['pid_file']);
125
            if ($pid > 0 && self::kill($pid, 0)) {
126
                $this->warning(sprintf('Swoole[PID=%d] is already running.', $pid));
127
                return 1;
128
            }
129
        }
130
131
        if ($config['server']['swoole']['daemonize']) {
132
            $this->trace('Swoole is running in daemon mode, see "ps -ef|grep laravels".');
133
        } else {
134
            $this->trace('Swoole is running, press Ctrl+C to quit.');
135
        }
136
137
        (new LaravelS($config['server'], $config['laravel']))->run();
138
139
        return 0;
140
    }
141
142
    public function stop()
143
    {
144
        $config = $this->getConfig();
145
        $pidFile = $config['server']['swoole']['pid_file'];
146
        if (!file_exists($pidFile)) {
147
            $this->warning('It seems that Swoole is not running.');
148
            return 0;
149
        }
150
151
        $pid = file_get_contents($pidFile);
152
        if (self::kill($pid, 0)) {
153
            if (self::kill($pid, SIGTERM)) {
154
                // Make sure that master process quit
155
                $time = 1;
156
                $waitTime = isset($config['server']['swoole']['max_wait_time']) ? $config['server']['swoole']['max_wait_time'] : 60;
157
                $this->info("The max time of waiting to forcibly stop is {$waitTime}s.");
158
                while (self::kill($pid, 0)) {
159
                    if ($time > $waitTime) {
160
                        $this->warning("Swoole [PID={$pid}] cannot be stopped gracefully in {$waitTime}s, will be stopped forced right now.");
161
                        return 1;
162
                    }
163
                    $this->info("Waiting Swoole[PID={$pid}] to stop. [{$time}]");
164
                    sleep(1);
165
                    $time++;
166
                }
167
                $basePath = dirname($pidFile);
168
                $deleteFiles = [
169
                    $pidFile,
170
                    $basePath . '/laravels-custom-processes.pid',
171
                    $basePath . '/laravels-timer-process.pid',
172
                ];
173
                foreach ($deleteFiles as $deleteFile) {
174
                    if (file_exists($deleteFile)) {
175
                        unlink($deleteFile);
176
                    }
177
                }
178
                $this->info("Swoole [PID={$pid}] is stopped.");
179
                return 0;
180
            } else {
181
                $this->error("Swoole [PID={$pid}] is stopped failed.");
182
                return 1;
183
            }
184
        } else {
185
            $this->warning("Swoole [PID={$pid}] does not exist, or permission denied.");
186
            return 0;
187
        }
188
    }
189
190
    public function restart()
191
    {
192
        $code = $this->stop();
193
        if ($code !== 0) {
194
            return $code;
195
        }
196
        return $this->start();
197
    }
198
199
    public function reload()
200
    {
201
        $config = $this->getConfig();
202
        $pidFile = $config['server']['swoole']['pid_file'];
203
        if (!file_exists($pidFile)) {
204
            $this->error('It seems that Swoole is not running.');
205
            return;
206
        }
207
208
        // Reload worker process
209
        $pid = file_get_contents($pidFile);
210
        if (!$pid || !self::kill($pid, 0)) {
211
            $this->error("Swoole [PID={$pid}] does not exist, or permission denied.");
212
            return;
213
        }
214
215
        // Reload worker processes
216
        if (self::kill($pid, SIGUSR1)) {
217
            $this->info("Swoole [PID={$pid}] is reloaded.");
218
        } else {
219
            $this->error("Swoole [PID={$pid}] is reloaded failed.");
220
        }
221
222
        // TODO: next release: Reload custom processes
223
//        $pidFile = dirname($pidFile) . '/laravels-custom-processes.pid';
224
//        if (file_exists($pidFile)) {
225
//            $pids = (array)explode("\n", trim(file_get_contents($pidFile)));
226
//            foreach ($pids as $pid) {
227
//                if (!$pid || !self::kill($pid, 0)) {
228
//                    $this->error("Custom process[PID={$pid}] does not exist, or permission denied.");
229
//                    continue;
230
//                }
231
//
232
//                if (self::kill($pid, SIGUSR1)) {
233
//                    $this->info("Custom process[PID={$pid}] is reloaded.");
234
//                } else {
235
//                    $this->error("Custom process[PID={$pid}] is reloaded failed.");
236
//                }
237
//            }
238
//        }
239
240
        // Reload timer process
241
        if (!empty($config['server']['timer']['enable'])) {
242
            $pidFile = dirname($pidFile) . '/laravels-timer-process.pid';
243
            $pid = file_get_contents($pidFile);
244
            if (!$pid || !self::kill($pid, 0)) {
245
                $this->error("Timer process[PID={$pid}] does not exist, or permission denied.");
246
                return;
247
            }
248
249
            if (self::kill($pid, SIGUSR1)) {
250
                $this->info("Timer process[PID={$pid}] is reloaded.");
251
            } else {
252
                $this->error("Timer process[PID={$pid}] is reloaded failed.");
253
            }
254
        }
255
    }
256
257
    public function showInfo()
258
    {
259
        $this->runArtisanCommand('laravels info');
260
    }
261
262
    public function artisanCmd($subCmd)
263
    {
264
        $phpCmd = sprintf('%s -c "%s"', PHP_BINARY, php_ini_loaded_file());
265
        $env = $this->input->getOption('env');
266
        $envs = $env ? "APP_ENV={$env}" : '';
267
        $artisanCmd = trim(sprintf('%s %s %s/artisan %s', $envs, $phpCmd, $this->basePath, $subCmd));
268
        return $artisanCmd;
269
    }
270
271
    public function runArtisanCommand($cmd)
272
    {
273
        $cmd = $this->artisanCmd($cmd);
274
        self::runCommand($cmd);
275
    }
276
277
    public function getConfig()
278
    {
279
        $json = file_get_contents($this->basePath . '/storage/laravels.json');
280
        return (array)json_decode($json, true);
281
    }
282
283
    public static function runCommand($cmd, $input = null)
284
    {
285
        $fp = popen($cmd, 'w');
286
        if ($fp === false) {
287
            return false;
288
        }
289
        if ($input !== null) {
290
            fwrite($fp, $input);
291
        }
292
        pclose($fp);
293
        return true;
294
    }
295
296
    public static function kill($pid, $sig)
297
    {
298
        try {
299
            return Process::kill((int)$pid, $sig);
300
        } catch (\Exception $e) {
301
            return false;
302
        }
303
    }
304
}
305