Issues (1019)

src/LaravelS.php (1 issue)

1
<?php
2
3
namespace Hhxsv5\LaravelS;
4
5
use Hhxsv5\LaravelS\Illuminate\Laravel;
6
use Hhxsv5\LaravelS\Illuminate\LaravelTrait;
7
use Hhxsv5\LaravelS\Illuminate\LogTrait;
8
use Hhxsv5\LaravelS\Swoole\DynamicResponse;
9
use Hhxsv5\LaravelS\Swoole\Events\ServerStartInterface;
10
use Hhxsv5\LaravelS\Swoole\Events\ServerStopInterface;
11
use Hhxsv5\LaravelS\Swoole\Events\WorkerErrorInterface;
12
use Hhxsv5\LaravelS\Swoole\Events\WorkerStartInterface;
13
use Hhxsv5\LaravelS\Swoole\Events\WorkerStopInterface;
14
use Hhxsv5\LaravelS\Swoole\InotifyTrait;
15
use Hhxsv5\LaravelS\Swoole\Process\CustomProcessTrait;
16
use Hhxsv5\LaravelS\Swoole\Process\ProcessTitleTrait;
17
use Hhxsv5\LaravelS\Swoole\Request;
18
use Hhxsv5\LaravelS\Swoole\Server;
19
use Hhxsv5\LaravelS\Swoole\StaticResponse;
20
use Hhxsv5\LaravelS\Swoole\Timer\TimerTrait;
21
use Illuminate\Http\Request as IlluminateRequest;
22
use Swoole\Http\Request as SwooleRequest;
23
use Swoole\Http\Response as SwooleResponse;
24
use Swoole\Http\Server as HttpServer;
25
use Swoole\Process;
26
use Swoole\Server\Port;
27
use Symfony\Component\Console\Style\OutputStyle;
28
use Symfony\Component\HttpFoundation\BinaryFileResponse;
29
30
31
/**
32
 * Swoole Request => Laravel Request
33
 * Laravel Request => Laravel handle => Laravel Response
34
 * Laravel Response => Swoole Response
35
 */
36
class LaravelS extends Server
37
{
38
    /**
39
     * Fix conflicts of traits
40
     */
41
    use InotifyTrait, LaravelTrait, LogTrait, ProcessTitleTrait, TimerTrait, CustomProcessTrait;
42
43
    /**@var array */
44
    protected array $laravelConf;
45
46
    /**@var Laravel */
47
    protected Laravel $laravel;
48
49
    /**@var Process[] */
50
    protected static array $customProcesses = [];
51
52
    public function __construct(array $svrConf, array $laravelConf)
53
    {
54
        parent::__construct($svrConf);
55
        $this->laravelConf = $laravelConf;
56
57
        $timerConf = $this->conf['timer'] ?? [];
58
        $timerConf['process_prefix'] = $svrConf['process_prefix'];
59
        $this->addTimerProcess($this->swoole, $timerConf, $this->laravelConf);
60
61
        $inotifyConf = $this->conf['inotify_reload'] ?? [];
62
        if (!isset($inotifyConf['watch_path'])) {
63
            $inotifyConf['watch_path'] = $this->laravelConf['root_path'];
64
        }
65
        $inotifyConf['process_prefix'] = $svrConf['process_prefix'];
66
        $this->addInotifyProcess($this->swoole, $inotifyConf, $this->laravelConf);
67
68
        $processes = $this->conf['processes'] ?? [];
69
        static::$customProcesses = $this->addCustomProcesses($this->swoole, $svrConf['process_prefix'], $processes, $this->laravelConf);
70
71
        // Fire ServerStart event
72
        if (isset($this->conf['event_handlers']['ServerStart'])) {
73
            Laravel::autoload($this->laravelConf['root_path']);
74
            $this->fireEvent('ServerStart', ServerStartInterface::class, [$this->swoole]);
75
        }
76
    }
77
78
    public static function getCustomProcesses(): array
79
    {
80
        return static::$customProcesses;
81
    }
82
83
    protected function beforeWebSocketHandShake(SwooleRequest $request)
84
    {
85
        // Start Laravel's lifetime, then support session ...middleware.
86
        $laravelRequest = $this->convertRequest($this->laravel, $request);
87
        $this->laravel->bindRequest($laravelRequest);
88
        $this->laravel->fireEvent('laravels.received_request', [$laravelRequest]);
89
        $this->laravel->cleanProviders();
90
        $laravelResponse = $this->laravel->handleDynamic($laravelRequest);
91
        $this->laravel->fireEvent('laravels.generated_response', [$laravelRequest, $laravelResponse]);
92
    }
93
94
    protected function afterWebSocketOpen(SwooleRequest $request)
95
    {
96
        // End Laravel's lifetime.
97
        $this->laravel->saveSession();
98
        $this->laravel->clean();
99
    }
100
101
    protected function triggerWebSocketEvent($event, array $params)
102
    {
103
        if ($event === 'onHandShake') {
104
            $this->beforeWebSocketHandShake($params[0]);
105
            if (!empty($this->conf['server'])) {
106
                $params[1]->header('Server', $this->conf['server']);
107
            }
108
        }
109
110
        parent::triggerWebSocketEvent($event, $params);
111
112
        switch ($event) {
113
            case 'onHandShake':
114
                if (isset($params[1]->header['Sec-Websocket-Accept'])) {
115
                    // Successful handshake
116
                    parent::triggerWebSocketEvent('onOpen', [$this->swoole, $params[0]]);
117
                }
118
                $this->afterWebSocketOpen($params[0]);
119
                break;
120
            case 'onOpen':
121
                $this->afterWebSocketOpen($params[1]);
122
                break;
123
        }
124
    }
125
126
    protected function triggerPortEvent(Port $port, $handlerClass, $event, array $params)
127
    {
128
        switch ($event) {
129
            case 'onHandShake':
130
                $this->beforeWebSocketHandShake($params[0]);
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
131
            case 'onRequest':
132
                if (!empty($this->conf['server'])) {
133
                    $params[1]->header('Server', $this->conf['server']);
134
                }
135
                break;
136
        }
137
138
        parent::triggerPortEvent($port, $handlerClass, $event, $params);
139
140
        switch ($event) {
141
            case 'onHandShake':
142
                if (isset($params[1]->header['Sec-Websocket-Accept'])) {
143
                    // Successful handshake
144
                    parent::triggerPortEvent($port, $handlerClass, 'onOpen', [$this->swoole, $params[0]]);
145
                }
146
                $this->afterWebSocketOpen($params[0]);
147
                break;
148
            case 'onOpen':
149
                $this->afterWebSocketOpen($params[1]);
150
                break;
151
        }
152
    }
153
154
    public function onShutdown(HttpServer $server)
155
    {
156
        parent::onShutdown($server);
157
158
        // Fire ServerStop event
159
        if (isset($this->conf['event_handlers']['ServerStop'])) {
160
            $this->laravel = $this->initLaravel($this->laravelConf, $this->swoole);
161
            $this->fireEvent('ServerStop', ServerStopInterface::class, [$server]);
162
        }
163
    }
164
165
    public function onWorkerStart(HttpServer $server, $workerId)
166
    {
167
        parent::onWorkerStart($server, $workerId);
168
169
        // To implement gracefully reload
170
        // Delay to create Laravel
171
        // Delay to include Laravel's autoload.php
172
        $this->laravel = $this->initLaravel($this->laravelConf, $this->swoole);
173
174
        // Fire WorkerStart event
175
        $this->fireEvent('WorkerStart', WorkerStartInterface::class, func_get_args());
176
    }
177
178
    public function onWorkerStop(HttpServer $server, $workerId)
179
    {
180
        parent::onWorkerStop($server, $workerId);
181
182
        // Fire WorkerStop event
183
        $this->fireEvent('WorkerStop', WorkerStopInterface::class, func_get_args());
184
    }
185
186
    public function onWorkerError(HttpServer $server, $workerId, $workerPId, $exitCode, $signal)
187
    {
188
        parent::onWorkerError($server, $workerId, $workerPId, $exitCode, $signal);
189
190
        Laravel::autoload($this->laravelConf['root_path']);
191
192
        // Fire WorkerError event
193
        $this->fireEvent('WorkerError', WorkerErrorInterface::class, func_get_args());
194
    }
195
196
    protected function convertRequest(Laravel $laravel, SwooleRequest $request)
197
    {
198
        $rawGlobals = $laravel->getRawGlobals();
199
        return (new Request($request))->toIlluminateRequest($rawGlobals['_SERVER'], $rawGlobals['_ENV']);
200
    }
201
202
    public function onRequest(SwooleRequest $swooleRequest, SwooleResponse $swooleResponse)
203
    {
204
        try {
205
            $laravelRequest = $this->convertRequest($this->laravel, $swooleRequest);
206
            $this->laravel->bindRequest($laravelRequest);
207
            $this->laravel->fireEvent('laravels.received_request', [$laravelRequest]);
208
            $handleStaticSuccess = false;
209
            if ($this->conf['handle_static']) {
210
                // For Swoole < 1.9.17
211
                $handleStaticSuccess = $this->handleStaticResource($this->laravel, $laravelRequest, $swooleResponse);
212
            }
213
            if (!$handleStaticSuccess) {
214
                $this->handleDynamicResource($this->laravel, $laravelRequest, $swooleResponse);
215
            }
216
        } catch (\Exception $e) {
217
            $this->handleException($e, $swooleResponse);
218
        }
219
    }
220
221
    /**
222
     * @param \Exception $e
223
     * @param SwooleResponse $response
224
     */
225
    protected function handleException($e, SwooleResponse $response)
226
    {
227
        $error = sprintf(
228
            'onRequest: Uncaught exception "%s"([%d]%s) at %s:%s, %s%s',
229
            get_class($e),
230
            $e->getCode(),
231
            $e->getMessage(),
232
            $e->getFile(),
233
            $e->getLine(),
234
            PHP_EOL,
235
            $e->getTraceAsString()
236
        );
237
        $this->error($error);
238
        try {
239
            $response->status(500);
240
            $response->end('Oops! An unexpected error occurred');
241
        } catch (\Exception $e) {
242
            $this->logException($e);
243
        }
244
    }
245
246
    protected function handleStaticResource(Laravel $laravel, IlluminateRequest $laravelRequest, SwooleResponse $swooleResponse)
247
    {
248
        $laravelResponse = $laravel->handleStatic($laravelRequest);
249
        if ($laravelResponse === false) {
250
            return false;
251
        }
252
        if (!empty($this->conf['server'])) {
253
            $laravelResponse->headers->set('Server', $this->conf['server'], true);
254
        }
255
        $laravel->fireEvent('laravels.generated_response', [$laravelRequest, $laravelResponse]);
256
        $response = new StaticResponse($swooleResponse, $laravelResponse);
257
        $response->setChunkLimit($this->conf['swoole']['buffer_output_size']);
258
        $response->send($this->conf['enable_gzip']);
259
        return true;
260
    }
261
262
    protected function handleDynamicResource(Laravel $laravel, IlluminateRequest $laravelRequest, SwooleResponse $swooleResponse)
263
    {
264
        $laravel->cleanProviders();
265
        $laravelResponse = $laravel->handleDynamic($laravelRequest);
266
        if (!empty($this->conf['server'])) {
267
            $laravelResponse->headers->set('Server', $this->conf['server'], true);
268
        }
269
        $laravel->fireEvent('laravels.generated_response', [$laravelRequest, $laravelResponse]);
270
        if ($laravelResponse instanceof BinaryFileResponse) {
271
            $response = new StaticResponse($swooleResponse, $laravelResponse);
272
        } else {
273
            $response = new DynamicResponse($swooleResponse, $laravelResponse);
274
        }
275
        $response->setChunkLimit($this->conf['swoole']['buffer_output_size']);
276
        $response->send($this->conf['enable_gzip']);
277
        $laravel->clean();
278
        return true;
279
    }
280
281
    /**@var OutputStyle */
282
    protected static $outputStyle;
283
284
    public static function setOutputStyle(OutputStyle $outputStyle)
285
    {
286
        static::$outputStyle = $outputStyle;
287
    }
288
289
    public static function getOutputStyle()
290
    {
291
        return static::$outputStyle;
292
    }
293
}
294