Passed
Push — master ( 6ce97f...1fe2f9 )
by huang
05:13
created

ProcessManager   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 180
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 7

Test Coverage

Coverage 0%

Importance

Changes 2
Bugs 1 Features 0
Metric Value
dl 0
loc 180
rs 10
c 2
b 1
f 0
ccs 0
cts 102
cp 0
wmc 27
lcom 2
cbo 7

6 Methods

Rating   Name   Duplication   Size   Complexity  
A configure() 0 10 1
C execute() 0 77 13
A targetDirectory() 0 15 3
A showProcesses() 0 13 2
B getProcessInfo() 0 17 7
A finish() 0 4 1
1
<?php
2
/**
3
 * @author    jan huang <[email protected]>
4
 * @copyright 2017
5
 *
6
 * @see      https://www.github.com/janhuang
7
 * @see      https://fastdlabs.com
8
 */
9
10
namespace FastD\Process;
11
12
use FastD\Swoole\Process;
13
use Symfony\Component\Console\Command\Command;
14
use Symfony\Component\Console\Helper\Table;
15
use Symfony\Component\Console\Input\InputArgument;
16
use Symfony\Component\Console\Input\InputInterface;
17
use Symfony\Component\Console\Input\InputOption;
18
use Symfony\Component\Console\Output\OutputInterface;
19
20
/**
21
 * Class Process.
22
 */
23
class ProcessManager extends Command
24
{
25
    /**
26
     * @var string
27
     */
28
    protected $pidPath;
29
30
    /**
31
     * php bin/console process {name} {args} {options}.
32
     */
33
    protected function configure()
34
    {
35
        $this->setName('process');
36
        $this->addArgument('process', InputArgument::OPTIONAL, 'process name');
37
        $this->addArgument('action', InputArgument::OPTIONAL, 'process action, <comment>status|start|stop</comment>', 'status');
38
        $this->addOption('pid', '-p', InputOption::VALUE_OPTIONAL, 'set process pid path.');
39
        $this->addOption('name', null, InputOption::VALUE_OPTIONAL, 'set process name.', null);
40
        $this->addOption('daemon', '-d', InputOption::VALUE_NONE, 'set process daemonize.');
41
        $this->setDescription('Create new processor.');
42
    }
43
44
    /**
45
     * @param InputInterface  $input
46
     * @param OutputInterface $output
47
     *
48
     * @return int
49
     */
50
    protected function execute(InputInterface $input, OutputInterface $output)
51
    {
52
        $path = $this->targetDirectory($input);
53
54
        $processName = $input->getArgument('process');
55
56
        if ($input->hasParameterOption(['--list', '-l']) || empty($processName)) {
57
            return $this->showProcesses($input, $output);
58
        }
59
60
        $processes = config()->get('processes', []);
61
62
        if (!isset($processes[$processName])) {
63
            throw new \RuntimeException(sprintf('Process %s cannot found', $processName));
64
        }
65
66
        $config = $processes[$processName];
67
        if (!class_exists($config['process'])) {
68
            throw new \RuntimeException(sprintf('Process class "%s" is not found.', $processName));
69
        }
70
71
        $name = $input->getOption('name');
72
        if (empty($name)) {
73
            $name = $processName;
74
        }
75
        $process = $config['process'];
76
        $options = $config['options'];
77
        $process = new $process($name);
78
79
        if (!($process instanceof Process)) {
80
            throw new \RuntimeException('Process must be instance of \FastD\Swoole\Process');
81
        }
82
        if ($input->hasParameterOption(['--daemon', '-d'])) {
83
            $process->daemon();
84
        }
85
86
        $pidFile = $path.'/'.$processName.'.pid';
87
88
        switch ($input->getArgument('action')) {
89
            case 'start':
90
                $pid = $process->start();
91
                file_put_contents($pidFile, $pid);
92
                $output->writeln(sprintf('process <info>%s</info> pid: <info>%s</info>', $process->getName(), $pid));
93
                $output->writeln(sprintf('pid: <info>%s</info>', $pidFile));
94
95
                $process->wait(function ($ret) use ($name) {
96
                    return $this->finish($name, $ret['pid'], $ret['code'], $ret['signal']);
97
                });
98
99
                break;
100
            case 'stop':
101
                $pid = (int) file_get_contents($pidFile);
102
                if ($process->kill($pid, SIGTERM)) {
103
                    $output->writeln(sprintf('process %s pid %s is killed', $process->getName(), $pid));
104
                }
105
106
                break;
107
            case 'status':
108
            default:
109
                $table = new Table($output);
110
                $info = $this->getProcessInfo($name);
111
                $rows = [
112
                    ['name', $process->getName()],
113
                    ['class', get_class($process)],
114
                    ['status', $info[2]],
115
                    ['pid', $info[1]],
116
                ];
117
                foreach ($options as $key => $value) {
118
                    $rows[] = [$key, $value];
119
                }
120
                $table->setRows($rows);
121
                $table->setStyle('compact');
122
                $table->render();
123
        }
124
125
        return 0;
126
    }
127
128
    /**
129
     * @param InputInterface $input
130
     *
131
     * @return string
132
     */
133
    protected function targetDirectory(InputInterface $input)
134
    {
135
        $pid = $input->getParameterOption(['--path', '-p']);
136
137
        if (empty($pid)) {
138
            $path = app()->getPath().'/runtime/process';
139
        } else {
140
            $path = dirname($pid);
141
        }
142
        if (!file_exists($path)) {
143
            mkdir($path, 0755, true);
144
        }
145
146
        return $this->pidPath = $path;
147
    }
148
149
    /**
150
     * @param InputInterface  $input
151
     * @param OutputInterface $output
152
     *
153
     * @return int
154
     */
155
    protected function showProcesses(InputInterface $input, OutputInterface $output)
0 ignored issues
show
Unused Code introduced by
The parameter $input 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...
156
    {
157
        $table = new Table($output);
158
        $table->setHeaders(['Process', 'Pid', 'Status', 'Start At', 'Runtime']);
159
        $rows = [];
160
        foreach (config()->get('processes', []) as $name => $processor) {
161
            $rows[] = $this->getProcessInfo($name);
162
        }
163
        $table->setRows($rows);
164
        $table->render();
165
166
        return 0;
167
    }
168
169
    /**
170
     * @param $name
171
     *
172
     * @return array
173
     */
174
    protected function getProcessInfo($name)
175
    {
176
        $pidFile = $this->pidPath.'/'.$name.'.pid';
177
        $pid = file_exists($pidFile) ? (int) file_get_contents($pidFile) : '';
178
        $isRunning = false;
179
        if (is_numeric($pid)) {
180
            $isRunning = process_kill($pid, 0);
181
        }
182
183
        return [
184
            $name,
185
            $isRunning ? $pid : '',
186
            $isRunning ? 'running' : 'stopped',
187
            $isRunning ? date('Y-m-d H:i:s', filemtime($pidFile)) : '',
188
            $isRunning ? time() - filemtime($pidFile) : '',
189
        ];
190
    }
191
192
    /**
193
     * @param $name
194
     * @param $pid
195
     * @param int $code
196
     * @param int $signal
197
     */
198
    protected function finish($name, $pid, $code = 0, $signal = 0)
199
    {
200
        output()->writeln(sprintf('process: %s. pid: %s exit. code: %s. signal: %s', $name, $pid, $code, $signal));
201
    }
202
}
203