1 | <?php |
||||||
2 | |||||||
3 | namespace BeyondCode\LaravelWebSockets\Console\Commands; |
||||||
4 | |||||||
5 | use BeyondCode\LaravelWebSockets\Contracts\ChannelManager; |
||||||
6 | use BeyondCode\LaravelWebSockets\Facades\StatisticsCollector as StatisticsCollectorFacade; |
||||||
7 | use BeyondCode\LaravelWebSockets\Facades\WebSocketRouter; |
||||||
8 | use BeyondCode\LaravelWebSockets\Server\Loggers\ConnectionLogger; |
||||||
9 | use BeyondCode\LaravelWebSockets\Server\Loggers\HttpLogger; |
||||||
10 | use BeyondCode\LaravelWebSockets\Server\Loggers\WebSocketsLogger; |
||||||
11 | use BeyondCode\LaravelWebSockets\ServerFactory; |
||||||
12 | use Illuminate\Console\Command; |
||||||
13 | use Illuminate\Support\Facades\Cache; |
||||||
14 | use React\EventLoop\Factory as LoopFactory; |
||||||
15 | |||||||
16 | class StartServer extends Command |
||||||
17 | { |
||||||
18 | /** |
||||||
19 | * The name and signature of the console command. |
||||||
20 | * |
||||||
21 | * @var string |
||||||
22 | */ |
||||||
23 | protected $signature = 'websockets:serve |
||||||
24 | {--host=0.0.0.0} |
||||||
25 | {--port=6001} |
||||||
26 | {--disable-statistics : Disable the statistics tracking.} |
||||||
27 | {--statistics-interval= : The amount of seconds to tick between statistics saving.} |
||||||
28 | {--debug : Forces the loggers to be enabled and thereby overriding the APP_DEBUG setting.} |
||||||
29 | {--loop : Programatically inject the loop.} |
||||||
30 | '; |
||||||
31 | |||||||
32 | /** |
||||||
33 | * The console command description. |
||||||
34 | * |
||||||
35 | * @var string|null |
||||||
36 | */ |
||||||
37 | protected $description = 'Start the LaravelWebSockets server.'; |
||||||
38 | |||||||
39 | /** |
||||||
40 | * Get the loop instance. |
||||||
41 | * |
||||||
42 | * @var \React\EventLoop\LoopInterface |
||||||
43 | */ |
||||||
44 | protected $loop; |
||||||
45 | |||||||
46 | /** |
||||||
47 | * The Pusher server instance. |
||||||
48 | * |
||||||
49 | * @var \Ratchet\Server\IoServer |
||||||
50 | */ |
||||||
51 | public $server; |
||||||
52 | |||||||
53 | /** |
||||||
54 | * Initialize the command. |
||||||
55 | * |
||||||
56 | * @return void |
||||||
57 | */ |
||||||
58 | public function __construct() |
||||||
59 | { |
||||||
60 | parent::__construct(); |
||||||
61 | |||||||
62 | $this->loop = LoopFactory::create(); |
||||||
0 ignored issues
–
show
|
|||||||
63 | } |
||||||
64 | |||||||
65 | /** |
||||||
66 | * Run the command. |
||||||
67 | * |
||||||
68 | * @return void |
||||||
69 | */ |
||||||
70 | public function handle() |
||||||
71 | { |
||||||
72 | $this->configureLoggers(); |
||||||
73 | |||||||
74 | $this->configureManagers(); |
||||||
75 | |||||||
76 | $this->configureStatistics(); |
||||||
77 | |||||||
78 | $this->configureRestartTimer(); |
||||||
79 | |||||||
80 | $this->configureRoutes(); |
||||||
81 | |||||||
82 | $this->configurePcntlSignal(); |
||||||
83 | |||||||
84 | $this->configurePongTracker(); |
||||||
85 | |||||||
86 | $this->startServer(); |
||||||
87 | } |
||||||
88 | |||||||
89 | /** |
||||||
90 | * Configure the loggers used for the console. |
||||||
91 | * |
||||||
92 | * @return void |
||||||
93 | */ |
||||||
94 | protected function configureLoggers() |
||||||
95 | { |
||||||
96 | $this->configureHttpLogger(); |
||||||
97 | $this->configureMessageLogger(); |
||||||
98 | $this->configureConnectionLogger(); |
||||||
99 | } |
||||||
100 | |||||||
101 | /** |
||||||
102 | * Register the managers that are not resolved |
||||||
103 | * in the package service provider. |
||||||
104 | * |
||||||
105 | * @return void |
||||||
106 | */ |
||||||
107 | protected function configureManagers() |
||||||
108 | { |
||||||
109 | $this->laravel->singleton(ChannelManager::class, function ($app) { |
||||||
110 | $config = $app['config']['websockets']; |
||||||
111 | $mode = $config['replication']['mode'] ?? 'local'; |
||||||
112 | |||||||
113 | $class = $config['replication']['modes'][$mode]['channel_manager']; |
||||||
114 | |||||||
115 | return new $class($this->loop); |
||||||
116 | }); |
||||||
117 | } |
||||||
118 | |||||||
119 | /** |
||||||
120 | * Register the Statistics Collectors that |
||||||
121 | * are not resolved in the package service provider. |
||||||
122 | * |
||||||
123 | * @return void |
||||||
124 | */ |
||||||
125 | protected function configureStatistics() |
||||||
126 | { |
||||||
127 | if (! $this->option('disable-statistics')) { |
||||||
128 | $intervalInSeconds = $this->option('statistics-interval') ?: config('websockets.statistics.interval_in_seconds', 3600); |
||||||
129 | |||||||
130 | $this->loop->addPeriodicTimer($intervalInSeconds, function () { |
||||||
0 ignored issues
–
show
It seems like
$intervalInSeconds can also be of type string ; however, parameter $interval of React\EventLoop\LoopInterface::addPeriodicTimer() does only seem to accept double|integer , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
131 | $this->line('Saving statistics...'); |
||||||
132 | |||||||
133 | StatisticsCollectorFacade::save(); |
||||||
134 | }); |
||||||
135 | } |
||||||
136 | } |
||||||
137 | |||||||
138 | /** |
||||||
139 | * Configure the restart timer. |
||||||
140 | * |
||||||
141 | * @return void |
||||||
142 | */ |
||||||
143 | public function configureRestartTimer() |
||||||
144 | { |
||||||
145 | $this->lastRestart = $this->getLastRestart(); |
||||||
0 ignored issues
–
show
|
|||||||
146 | |||||||
147 | $this->loop->addPeriodicTimer(10, function () { |
||||||
148 | if ($this->getLastRestart() !== $this->lastRestart) { |
||||||
149 | $this->triggerSoftShutdown(); |
||||||
150 | } |
||||||
151 | }); |
||||||
152 | } |
||||||
153 | |||||||
154 | /** |
||||||
155 | * Register the routes for the server. |
||||||
156 | * |
||||||
157 | * @return void |
||||||
158 | */ |
||||||
159 | protected function configureRoutes() |
||||||
160 | { |
||||||
161 | WebSocketRouter::registerRoutes(); |
||||||
0 ignored issues
–
show
The method
registerRoutes() does not exist on BeyondCode\LaravelWebSoc...Facades\WebSocketRouter . Since you implemented __callStatic , consider adding a @method annotation.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
162 | } |
||||||
163 | |||||||
164 | /** |
||||||
165 | * Configure the PCNTL signals for soft shutdown. |
||||||
166 | * |
||||||
167 | * @return void |
||||||
168 | */ |
||||||
169 | protected function configurePcntlSignal() |
||||||
170 | { |
||||||
171 | // When the process receives a SIGTERM or a SIGINT |
||||||
172 | // signal, it should mark the server as unavailable |
||||||
173 | // to receive new connections, close the current connections, |
||||||
174 | // then stopping the loop. |
||||||
175 | |||||||
176 | if (! extension_loaded('pcntl')) { |
||||||
177 | return; |
||||||
178 | } |
||||||
179 | |||||||
180 | $this->loop->addSignal(SIGTERM, function () { |
||||||
181 | $this->line('Closing existing connections...'); |
||||||
182 | |||||||
183 | $this->triggerSoftShutdown(); |
||||||
184 | }); |
||||||
185 | |||||||
186 | $this->loop->addSignal(SIGINT, function () { |
||||||
187 | $this->line('Closing existing connections...'); |
||||||
188 | |||||||
189 | $this->triggerSoftShutdown(); |
||||||
190 | }); |
||||||
191 | } |
||||||
192 | |||||||
193 | /** |
||||||
194 | * Configure the tracker that will delete |
||||||
195 | * from the store the connections that. |
||||||
196 | * |
||||||
197 | * @return void |
||||||
198 | */ |
||||||
199 | protected function configurePongTracker() |
||||||
200 | { |
||||||
201 | $this->loop->addPeriodicTimer(10, function () { |
||||||
202 | $this->laravel |
||||||
203 | ->make(ChannelManager::class) |
||||||
204 | ->removeObsoleteConnections(); |
||||||
205 | }); |
||||||
206 | } |
||||||
207 | |||||||
208 | /** |
||||||
209 | * Configure the HTTP logger class. |
||||||
210 | * |
||||||
211 | * @return void |
||||||
212 | */ |
||||||
213 | protected function configureHttpLogger() |
||||||
214 | { |
||||||
215 | $this->laravel->singleton(HttpLogger::class, function ($app) { |
||||||
216 | return (new HttpLogger($this->output)) |
||||||
217 | ->enable($this->option('debug') ?: ($app['config']['app']['debug'] ?? false)) |
||||||
0 ignored issues
–
show
It seems like
$this->option('debug') ?...app']['debug'] ?? false can also be of type string ; however, parameter $enabled of BeyondCode\LaravelWebSoc...oggers\Logger::enable() does only seem to accept boolean , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
218 | ->verbose($this->output->isVerbose()); |
||||||
219 | }); |
||||||
220 | } |
||||||
221 | |||||||
222 | /** |
||||||
223 | * Configure the logger for messages. |
||||||
224 | * |
||||||
225 | * @return void |
||||||
226 | */ |
||||||
227 | protected function configureMessageLogger() |
||||||
228 | { |
||||||
229 | $this->laravel->singleton(WebSocketsLogger::class, function ($app) { |
||||||
230 | return (new WebSocketsLogger($this->output)) |
||||||
231 | ->enable($this->option('debug') ?: ($app['config']['app']['debug'] ?? false)) |
||||||
0 ignored issues
–
show
It seems like
$this->option('debug') ?...app']['debug'] ?? false can also be of type string ; however, parameter $enabled of BeyondCode\LaravelWebSoc...oggers\Logger::enable() does only seem to accept boolean , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
232 | ->verbose($this->output->isVerbose()); |
||||||
233 | }); |
||||||
234 | } |
||||||
235 | |||||||
236 | /** |
||||||
237 | * Configure the connection logger. |
||||||
238 | * |
||||||
239 | * @return void |
||||||
240 | */ |
||||||
241 | protected function configureConnectionLogger() |
||||||
242 | { |
||||||
243 | $this->laravel->bind(ConnectionLogger::class, function ($app) { |
||||||
244 | return (new ConnectionLogger($this->output)) |
||||||
245 | ->enable($app['config']['app']['debug'] ?? false) |
||||||
246 | ->verbose($this->output->isVerbose()); |
||||||
247 | }); |
||||||
248 | } |
||||||
249 | |||||||
250 | /** |
||||||
251 | * Start the server. |
||||||
252 | * |
||||||
253 | * @return void |
||||||
254 | */ |
||||||
255 | protected function startServer() |
||||||
256 | { |
||||||
257 | $this->info("Starting the WebSocket server on port {$this->option('port')}..."); |
||||||
258 | |||||||
259 | $this->buildServer(); |
||||||
260 | |||||||
261 | $this->server->run(); |
||||||
262 | } |
||||||
263 | |||||||
264 | /** |
||||||
265 | * Build the server instance. |
||||||
266 | * |
||||||
267 | * @return void |
||||||
268 | */ |
||||||
269 | protected function buildServer() |
||||||
270 | { |
||||||
271 | $this->server = new ServerFactory( |
||||||
0 ignored issues
–
show
It seems like
new BeyondCode\LaravelWe... $this->option('port')) of type BeyondCode\LaravelWebSockets\ServerFactory is incompatible with the declared type Ratchet\Server\IoServer of property $server .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. ![]() |
|||||||
272 | $this->option('host'), $this->option('port') |
||||||
273 | ); |
||||||
274 | |||||||
275 | if ($loop = $this->option('loop')) { |
||||||
276 | $this->loop = $loop; |
||||||
0 ignored issues
–
show
It seems like
$loop of type string is incompatible with the declared type React\EventLoop\LoopInterface of property $loop .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. ![]() |
|||||||
277 | } |
||||||
278 | |||||||
279 | $this->server = $this->server |
||||||
280 | ->setLoop($this->loop) |
||||||
281 | ->withRoutes(WebSocketRouter::getRoutes()) |
||||||
0 ignored issues
–
show
The method
getRoutes() does not exist on BeyondCode\LaravelWebSoc...Facades\WebSocketRouter . Since you implemented __callStatic , consider adding a @method annotation.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
282 | ->setConsoleOutput($this->output) |
||||||
283 | ->createServer(); |
||||||
284 | } |
||||||
285 | |||||||
286 | /** |
||||||
287 | * Get the last time the server restarted. |
||||||
288 | * |
||||||
289 | * @return int |
||||||
290 | */ |
||||||
291 | protected function getLastRestart() |
||||||
292 | { |
||||||
293 | return Cache::get( |
||||||
294 | 'beyondcode:websockets:restart', 0 |
||||||
295 | ); |
||||||
296 | } |
||||||
297 | |||||||
298 | /** |
||||||
299 | * Trigger a soft shutdown for the process. |
||||||
300 | * |
||||||
301 | * @return void |
||||||
302 | */ |
||||||
303 | protected function triggerSoftShutdown() |
||||||
304 | { |
||||||
305 | $channelManager = $this->laravel->make(ChannelManager::class); |
||||||
306 | |||||||
307 | // Close the new connections allowance on this server. |
||||||
308 | $channelManager->declineNewConnections(); |
||||||
0 ignored issues
–
show
The method
declineNewConnections() does not exist on BeyondCode\LaravelWebSoc...ontracts\ChannelManager . Since it exists in all sub-types, consider adding an abstract or default implementation to BeyondCode\LaravelWebSoc...ontracts\ChannelManager .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
309 | |||||||
310 | // Get all local connections and close them. They will |
||||||
311 | // be automatically be unsubscribed from all channels. |
||||||
312 | $channelManager->getLocalConnections() |
||||||
313 | ->then(function ($connections) { |
||||||
314 | foreach ($connections as $connection) { |
||||||
315 | $connection->close(); |
||||||
316 | } |
||||||
317 | }) |
||||||
318 | ->then(function () { |
||||||
319 | $this->loop->stop(); |
||||||
320 | }); |
||||||
321 | } |
||||||
322 | } |
||||||
323 |
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.