Completed
Push — master ( 0a2195...0a340e )
by Biao
04:38
created

Portal   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 286
Duplicated Lines 0 %

Importance

Changes 17
Bugs 5 Features 2
Metric Value
eloc 177
c 17
b 5
f 2
dl 0
loc 286
rs 6.96
wmc 53

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
B stop() 0 45 9
A restart() 0 7 2
A kill() 0 6 2
A showInfo() 0 3 1
A runArtisanCommand() 0 4 1
A getConfig() 0 4 1
A artisanCmd() 0 7 2
A runCommand() 0 11 3
C reload() 0 53 14

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\Illuminate\LogTrait;
6
use Hhxsv5\LaravelS\LaravelS;
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 processes
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
        if (self::kill($pid, SIGUSR1)) {
215
            $this->info("Swoole [PID={$pid}] is reloaded.");
216
        } else {
217
            $this->error("Swoole [PID={$pid}] is reloaded failed.");
218
        }
219
220
        // Reload custom processes
221
        $pidFile = dirname($pidFile) . '/laravels-custom-processes.pid';
222
        if (file_exists($pidFile)) {
223
            $pids = (array)explode("\n", trim(file_get_contents($pidFile)));
224
            unlink($pidFile);
225
            foreach ($pids as $pid) {
226
                if (!$pid || !self::kill($pid, 0)) {
227
                    $this->error("Custom process[PID={$pid}] does not exist, or permission denied.");
228
                    continue;
229
                }
230
231
                if (self::kill($pid, SIGUSR1)) {
232
                    $this->info("Custom process[PID={$pid}] is reloaded.");
233
                } else {
234
                    $this->error("Custom process[PID={$pid}] is reloaded failed.");
235
                }
236
            }
237
        }
238
239
        // Reload timer process
240
        if (!empty($config['server']['timer']['enable'])) {
241
            $pidFile = dirname($pidFile) . '/laravels-timer-process.pid';
242
            $pid = file_get_contents($pidFile);
243
            if (!$pid || !self::kill($pid, 0)) {
244
                $this->error("Timer process[PID={$pid}] does not exist, or permission denied.");
245
                return;
246
            }
247
248
            if (self::kill($pid, SIGUSR1)) {
249
                $this->info("Timer process[PID={$pid}] is reloaded.");
250
            } else {
251
                $this->error("Timer process[PID={$pid}] is reloaded failed.");
252
            }
253
        }
254
    }
255
256
    public function showInfo()
257
    {
258
        $this->runArtisanCommand('laravels info');
259
    }
260
261
    public function artisanCmd($subCmd)
262
    {
263
        $phpCmd = sprintf('%s -c "%s"', PHP_BINARY, php_ini_loaded_file());
264
        $env = $this->input->getOption('env');
265
        $envs = $env ? "APP_ENV={$env}" : '';
266
        $artisanCmd = trim(sprintf('%s %s %s/artisan %s', $envs, $phpCmd, $this->basePath, $subCmd));
267
        return $artisanCmd;
268
    }
269
270
    public function runArtisanCommand($cmd)
271
    {
272
        $cmd = $this->artisanCmd($cmd);
273
        self::runCommand($cmd);
274
    }
275
276
    public function getConfig()
277
    {
278
        $json = file_get_contents($this->basePath . '/storage/laravels.json');
279
        return (array)json_decode($json, true);
280
    }
281
282
    public static function runCommand($cmd, $input = null)
283
    {
284
        $fp = popen($cmd, 'w');
285
        if ($fp === false) {
286
            return false;
287
        }
288
        if ($input !== null) {
289
            fwrite($fp, $input);
290
        }
291
        pclose($fp);
292
        return true;
293
    }
294
295
    public static function kill($pid, $sig)
296
    {
297
        try {
298
            return Process::kill((int)$pid, $sig);
299
        } catch (\Exception $e) {
300
            return false;
301
        }
302
    }
303
}
304