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; |
|
|
|
|
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"); |
|
|
|
|
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
|
|
|
* @return void |
236
|
|
|
*/ |
237
|
|
|
protected function handleUncaughtException($e) |
238
|
|
|
{ |
239
|
|
|
if ($e instanceof Error) { |
240
|
|
|
$e = new FatalThrowableError($e); |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
(new Handler())->renderForConsole(new ConsoleOutput, $e); |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
/** |
247
|
|
|
* Handle the application shutdown routine. |
248
|
|
|
* |
249
|
|
|
* @return void |
250
|
|
|
*/ |
251
|
|
|
protected function handleShutdown() |
252
|
|
|
{ |
253
|
|
|
if (! is_null($error = error_get_last()) && $this->isFatalError($error['type'])) { |
254
|
|
|
$this->handleUncaughtException(new FatalErrorException( |
|
|
|
|
255
|
|
|
$error['message'], |
256
|
|
|
$error['type'], |
257
|
|
|
0, |
258
|
|
|
$error['file'], |
259
|
|
|
$error['line'] |
260
|
|
|
)); |
261
|
|
|
} |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
/** |
265
|
|
|
* Determine if the error type is fatal. |
266
|
|
|
* |
267
|
|
|
* @param int $type |
268
|
|
|
* @return bool |
269
|
|
|
*/ |
270
|
|
|
protected function isFatalError($type) |
271
|
|
|
{ |
272
|
|
|
$errorCodes = [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE]; |
273
|
|
|
|
274
|
|
|
if (defined('FATAL_ERROR')) { |
275
|
|
|
$errorCodes[] = FATAL_ERROR; |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
return in_array($type, $errorCodes); |
279
|
|
|
} |
280
|
|
|
} |
281
|
|
|
|
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.