Completed
Pull Request — master (#447)
by Alexandru
01:22
created

StartWebSocketServer::buildServer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.8666
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace BeyondCode\LaravelWebSockets\Console;
4
5
use BeyondCode\LaravelWebSockets\Facades\StatisticsLogger;
6
use BeyondCode\LaravelWebSockets\Facades\WebSocketsRouter;
7
use BeyondCode\LaravelWebSockets\PubSub\Drivers\LocalClient;
8
use BeyondCode\LaravelWebSockets\PubSub\Drivers\RedisClient;
9
use BeyondCode\LaravelWebSockets\PubSub\ReplicationInterface;
10
use BeyondCode\LaravelWebSockets\Server\Logger\ConnectionLogger;
11
use BeyondCode\LaravelWebSockets\Server\Logger\HttpLogger;
12
use BeyondCode\LaravelWebSockets\Server\Logger\WebsocketsLogger;
13
use BeyondCode\LaravelWebSockets\Server\WebSocketServerFactory;
14
use BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver;
15
use BeyondCode\LaravelWebSockets\Statistics\Logger\StatisticsLogger as StatisticsLoggerInterface;
16
use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager;
17
use Illuminate\Console\Command;
18
use Illuminate\Support\Facades\Cache;
19
use React\EventLoop\Factory as LoopFactory;
20
21
class StartWebSocketServer extends Command
22
{
23
    /**
24
     * The name and signature of the console command.
25
     *
26
     * @var string
27
     */
28
    protected $signature = 'websockets:serve
29
        {--host=0.0.0.0}
30
        {--port=6001}
31
        {--statistics-interval= : Overwrite the statistics interval set in the config.}
32
        {--debug : Forces the loggers to be enabled and thereby overriding the APP_DEBUG setting.}
33
        {--test : Prepare the server, but do not start it.}
34
    ';
35
36
    /**
37
     * The console command description.
38
     *
39
     * @var string|null
40
     */
41
    protected $description = 'Start the Laravel WebSocket Server';
42
43
    /**
44
     * Get the loop instance.
45
     *
46
     * @var \React\EventLoop\LoopInterface
47
     */
48
    protected $loop;
49
50
    /**
51
     * The Pusher server instance.
52
     *
53
     * @var \Ratchet\Server\IoServer
54
     */
55
    public $server;
56
57
    /**
58
     * Track the last restart.
59
     *
60
     * @var int
61
     */
62
    protected $lastRestart;
63
64
    /**
65
     * Initialize the command.
66
     *
67
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
68
     */
69
    public function __construct()
70
    {
71
        parent::__construct();
72
73
        $this->loop = LoopFactory::create();
74
    }
75
76
    /**
77
     * Run the command.
78
     *
79
     * @return void
80
     */
81
    public function handle()
82
    {
83
        $this
84
            ->configureStatisticsLogger()
85
            ->configureHttpLogger()
86
            ->configureMessageLogger()
87
            ->configureConnectionLogger()
88
            ->configureRestartTimer()
89
            ->configurePubSub()
90
            ->registerRoutes()
91
            ->startWebSocketServer();
92
    }
93
94
    /**
95
     * Configure the statistics logger class.
96
     *
97
     * @return $this
98
     */
99
    protected function configureStatisticsLogger()
100
    {
101
        $this->laravel->singleton(StatisticsLoggerInterface::class, function () {
102
            $class = config('websockets.statistics.logger', \BeyondCode\LaravelWebSockets\Statistics\Logger\MemoryStatisticsLogger::class);
103
104
            return new $class(
105
                $this->laravel->make(ChannelManager::class),
106
                $this->laravel->make(StatisticsDriver::class)
107
            );
108
        });
109
110
        $this->loop->addPeriodicTimer($this->option('statistics-interval') ?: config('websockets.statistics.interval_in_seconds'), function () {
111
            $this->line('Saving statistics...');
112
113
            StatisticsLogger::save();
114
        });
115
116
        return $this;
117
    }
118
119
    /**
120
     * Configure the HTTP logger class.
121
     *
122
     * @return $this
123
     */
124 View Code Duplication
    protected function configureHttpLogger()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
125
    {
126
        $this->laravel->singleton(HttpLogger::class, function () {
127
            return (new HttpLogger($this->output))
128
                ->enable($this->option('debug') ?: config('app.debug'))
129
                ->verbose($this->output->isVerbose());
130
        });
131
132
        return $this;
133
    }
134
135
    /**
136
     * Configure the logger for messages.
137
     *
138
     * @return $this
139
     */
140 View Code Duplication
    protected function configureMessageLogger()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
141
    {
142
        $this->laravel->singleton(WebsocketsLogger::class, function () {
143
            return (new WebsocketsLogger($this->output))
144
                ->enable($this->option('debug') ?: config('app.debug'))
145
                ->verbose($this->output->isVerbose());
146
        });
147
148
        return $this;
149
    }
150
151
    /**
152
     * Configure the connection logger.
153
     *
154
     * @return $this
155
     */
156 View Code Duplication
    protected function configureConnectionLogger()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
157
    {
158
        $this->laravel->bind(ConnectionLogger::class, function () {
159
            return (new ConnectionLogger($this->output))
160
                ->enable(config('app.debug'))
161
                ->verbose($this->output->isVerbose());
162
        });
163
164
        return $this;
165
    }
166
167
    /**
168
     * Configure the Redis PubSub handler.
169
     *
170
     * @return $this
171
     */
172
    public function configureRestartTimer()
173
    {
174
        $this->lastRestart = $this->getLastRestart();
175
176
        $this->loop->addPeriodicTimer(10, function () {
177
            if ($this->getLastRestart() !== $this->lastRestart) {
178
                $this->loop->stop();
179
            }
180
        });
181
182
        return $this;
183
    }
184
185
    /**
186
     * Configure the replicators.
187
     *
188
     * @return void
189
     */
190
    public function configurePubSub()
191
    {
192 View Code Duplication
        if (config('websockets.replication.driver', 'local') === 'local') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
193
            $this->laravel->singleton(ReplicationInterface::class, function () {
194
                return new LocalClient;
195
            });
196
        }
197
198 View Code Duplication
        if (config('websockets.replication.driver', 'local') === 'redis') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
199
            $this->laravel->singleton(ReplicationInterface::class, function () {
200
                return (new RedisClient)->boot($this->loop);
201
            });
202
        }
203
204
        $this->laravel
205
            ->get(ReplicationInterface::class)
206
            ->boot($this->loop);
207
208
        return $this;
209
    }
210
211
    /**
212
     * Register the routes.
213
     *
214
     * @return $this
215
     */
216
    protected function registerRoutes()
217
    {
218
        WebSocketsRouter::routes();
219
220
        return $this;
221
    }
222
223
    /**
224
     * Start the server.
225
     *
226
     * @return void
227
     */
228
    protected function startWebSocketServer()
229
    {
230
        $this->info("Starting the WebSocket server on port {$this->option('port')}...");
231
232
        $this->buildServer();
233
234
        // For testing, just boot up the server, run it
235
        // but exit after the next tick.
236
        if ($this->option('test')) {
237
            $this->loop->futureTick(function () {
238
                $this->loop->stop();
239
            });
240
        }
241
242
        /* 🛰 Start the server 🛰  */
243
        $this->server->run();
244
    }
245
246
    /**
247
     * Build the server instance.
248
     *
249
     * @return void
250
     */
251
    protected function buildServer()
252
    {
253
        $this->server = new WebSocketServerFactory(
0 ignored issues
show
Documentation Bug introduced by
It seems like new \BeyondCode\LaravelW... $this->option('port')) of type object<BeyondCode\Larave...WebSocketServerFactory> is incompatible with the declared type object<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..

Loading history...
254
            $this->option('host'), $this->option('port')
255
        );
256
257
        $this->server = $this->server
258
            ->setLoop($this->loop)
259
            ->useRoutes(WebSocketsRouter::getRoutes())
260
            ->setConsoleOutput($this->output)
261
            ->createServer();
262
    }
263
264
    /**
265
     * Get the last time the server restarted.
266
     *
267
     * @return int
268
     */
269
    protected function getLastRestart()
270
    {
271
        return Cache::get('beyondcode:websockets:restart', 0);
272
    }
273
}
274