Completed
Push — master ( 8347cd...ba182b )
by Song
01:03
created

src/Command.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Encore\LumenSwoole;
4
5
use Error;
6
use ErrorException;
7
use Laravel\Lumen\Exceptions\Handler;
8
use Symfony\Component\Console\Output\ConsoleOutput;
9
use Symfony\Component\Debug\Exception\FatalErrorException;
10
use Symfony\Component\Debug\Exception\FatalThrowableError;
11
12
class Command
13
{
14
    protected $pidFile;
15
16
    protected $options = [];
17
18
    protected $host = 'localhost';
19
20
    protected $port = 8083;
21
22
    protected $bootstrap = 'bootstrap/app.php';
23
24
    protected $serverOptions = [];
25
26
    public function __construct()
27
    {
28
        $this->registerErrorHandling();
29
    }
30
31
    public static function main($argv)
32
    {
33
        $command = new static();
34
35
        return $command->run($argv);
36
    }
37
38
    public function run($argv)
39
    {
40
        $this->handleAction($argv);
41
42
        $this->handleArguments();
43
44
        $server = new Server($this->host, $this->port);
45
        $server->setApplication(require $this->bootstrap);
46
47
        $server->options($this->serverOptions)->run();
48
    }
49
50
    /**
51
     * @param array $argv
52
     *
53
     * @return void
54
     */
55
    public function handleAction($argv)
56
    {
57
        if (count($argv) < 2) {
58
            return;
59
        }
60
61
        if (in_array($argv[1], ['stop', 'reload', 'restart'])) {
62
            call_user_func([$this, $argv[1]]);
63
64
            exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method handleAction() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
65
        }
66
    }
67
68
    public function handleArguments()
69
    {
70
        $serverOptions = array_map(function ($option) {
71
            return "$option:";
72
        }, Server::$validServerOptions);
73
74
        $longOptions = array_merge(['host:', 'port:', 'help'], $serverOptions);
75
76
        $options = getopt('dp:h::s:', $longOptions);
77
78
        foreach ($options as $option => $value) {
79
            switch ($option) {
80
                case 'h':
81
                case 'host':
82
                    if ($value) {
83
                        $this->host = $value;
84
                    } else {
85
                        $this->usage();
86
                    }
87
                    break;
88
89
                case 'help':
90
                    $this->usage();
91
                    break;
92
93
                case 'p':
94
                case 'port':
95
                    if ($value) {
96
                        $this->port = (int) $value;
97
                    }
98
                    break;
99
100
                case 's':
101
                    if ($value) {
102
                        $this->bootstrap = $value;
103
                    }
104
                    break;
105
106
                case 'd':
107
                    $this->serverOptions['daemonize'] = true;
108
                    break;
109
110
                default:
111
                    if (in_array($option, Server::$validServerOptions) && $value) {
112
                        $this->serverOptions[$option] = $value;
113
                    }
114
                    break;
115
            }
116
        }
117
118
        return $options;
119
    }
120
121
    /**
122
     * Show usage.
123
     */
124
    public function usage()
125
    {
126
        exit("Usage: ./vendor/bin/lumen-swoole {stop|restart|reload}\r\n");
0 ignored issues
show
Coding Style Compatibility introduced by
The method usage() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
127
    }
128
129
    /**
130
     * Stop the server.
131
     *
132
     * @throws \Exception
133
     *
134
     * @return void
135
     */
136
    public function stop()
137
    {
138
        $pid = $this->getPid();
139
140
        echo "Server is stopping...\r\n";
141
142
        posix_kill($pid, SIGTERM);
143
144
        usleep(500);
145
146
        posix_kill($pid, SIGKILL);
147
148
        unlink($this->pidFile);
149
    }
150
151
    /**
152
     * Reload the server.
153
     *
154
     * @throws \Exception
155
     *
156
     * @return void
157
     */
158
    public function reload()
159
    {
160
        posix_kill($this->getPid(), SIGUSR1);
161
    }
162
163
    /**
164
     * Restart the server.
165
     *
166
     * @return void
167
     */
168
    public function restart()
169
    {
170
        $cmd = exec('ps -eo args | grep lumen-swoole | grep -v grep | head -n 1');
171
172
        $this->stop();
173
174
        usleep(2000);
175
176
        echo "Server is starting...\r\n";
177
178
        exec($cmd);
179
    }
180
181
    /**
182
     * Get process identifier of this server.
183
     *
184
     * @throws \Exception
185
     *
186
     * @return bool|string
187
     */
188
    protected function getPid()
189
    {
190
        $this->pidFile = __DIR__.'/../../../../storage/lumen-swoole.pid';
191
192
        if (!file_exists($this->pidFile)) {
193
            throw new \Exception('The Server is not running.');
194
        }
195
196
        $pid = file_get_contents($this->pidFile);
197
198
        if (posix_getpgid($pid)) {
199
            return $pid;
200
        }
201
202
        unlink($this->pidFile);
203
204
        return false;
205
    }
206
207
    /**
208
     * Set the error handling for the application.
209
     *
210
     * @return void
211
     */
212
    protected function registerErrorHandling()
213
    {
214
        error_reporting(-1);
215
216
        set_error_handler(function ($level, $message, $file = '', $line = 0) {
217
            if (error_reporting() & $level) {
218
                throw new ErrorException($message, 0, $level, $file, $line);
219
            }
220
        });
221
222
        set_exception_handler(function ($e) {
223
            $this->handleUncaughtException($e);
224
        });
225
226
        register_shutdown_function(function () {
227
            $this->handleShutdown();
228
        });
229
    }
230
231
    /**
232
     * Handle an uncaught exception instance.
233
     *
234
     * @param \Throwable $e
235
     *
236
     * @return void
237
     */
238
    protected function handleUncaughtException($e)
239
    {
240
        if ($e instanceof Error) {
241
            $e = new FatalThrowableError($e);
242
        }
243
244
        (new Handler())->renderForConsole(new ConsoleOutput(), $e);
245
    }
246
247
    /**
248
     * Handle the application shutdown routine.
249
     *
250
     * @return void
251
     */
252
    protected function handleShutdown()
253
    {
254
        if (!is_null($error = error_get_last()) && $this->isFatalError($error['type'])) {
255
            $this->handleUncaughtException(new FatalErrorException(
0 ignored issues
show
new \Symfony\Component\D...file'], $error['line']) is of type object<Symfony\Component...on\FatalErrorException>, but the function expects a object<Throwable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
256
                $error['message'],
257
                $error['type'],
258
                0,
259
                $error['file'],
260
                $error['line']
261
            ));
262
        }
263
    }
264
265
    /**
266
     * Determine if the error type is fatal.
267
     *
268
     * @param int $type
269
     *
270
     * @return bool
271
     */
272
    protected function isFatalError($type)
273
    {
274
        $errorCodes = [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE];
275
276
        if (defined('FATAL_ERROR')) {
277
            $errorCodes[] = FATAL_ERROR;
278
        }
279
280
        return in_array($type, $errorCodes);
281
    }
282
}
283