Sandbox::isLaravel()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 2
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
namespace SwooleTW\Http\Server;
4
5
use Illuminate\Http\Request;
6
use Illuminate\Container\Container;
7
use SwooleTW\Http\Coroutine\Context;
8
use Illuminate\Contracts\Http\Kernel;
9
use Illuminate\Support\Facades\Facade;
10
use SwooleTW\Http\Concerns\ResetApplication;
11
use SwooleTW\Http\Exceptions\SandboxException;
12
use Laravel\Lumen\Application as LumenApplication;
13
use Symfony\Component\HttpFoundation\StreamedResponse;
14
use Symfony\Component\HttpFoundation\BinaryFileResponse;
15
use Illuminate\Contracts\Config\Repository as ConfigContract;
16
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
17
18
class Sandbox
19
{
20
    use ResetApplication;
21
22
    /**
23
     * @var \Illuminate\Container\Container
24
     */
25
    protected $app;
26
27
    /**
28
     * @var string
29
     */
30
    protected $framework = 'laravel';
31
32
    /**
33
     * Constructor
34
     *
35
     * @param null $app
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $app is correct as it would always require null to be passed?
Loading history...
36
     * @param null $framework
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $framework is correct as it would always require null to be passed?
Loading history...
37
     *
38
     * @throws \SwooleTW\Http\Exceptions\SandboxException
39
     */
40
    public function __construct($app = null, $framework = null)
41
    {
42
        if (! $app instanceof Container) {
43
            return;
44
        }
45
46
        $this->setBaseApp($app);
47
        $this->setFramework($framework ?: $this->framework);
0 ignored issues
show
introduced by
$framework is of type null, thus it always evaluated to false.
Loading history...
48
        $this->initialize();
49
    }
50
51
    /**
52
     * Set framework type.
53
     *
54
     * @param string $framework
55
     *
56
     * @return \SwooleTW\Http\Server\Sandbox
57
     */
58
    public function setFramework(string $framework)
59
    {
60
        $this->framework = $framework;
61
62
        return $this;
63
    }
64
65
    /**
66
     * Get framework type.
67
     */
68
    public function getFramework()
69
    {
70
        return $this->framework;
71
    }
72
73
    /**
74
     * Set a base application.
75
     *
76
     * @param \Illuminate\Container\Container
77
     *
78
     * @return \SwooleTW\Http\Server\Sandbox
79
     */
80
    public function setBaseApp(Container $app)
81
    {
82
        $this->app = $app;
83
84
        return $this;
85
    }
86
87
    /**
88
     * Set current request.
89
     *
90
     * @param \Illuminate\Http\Request
91
     *
92
     * @return \SwooleTW\Http\Server\Sandbox
93
     */
94
    public function setRequest(Request $request)
95
    {
96
        Context::setData('_request', $request);
97
98
        return $this;
99
    }
100
101
    /**
102
     * Set current snapshot.
103
     *
104
     * @param \Illuminate\Container\Container
105
     *
106
     * @return \SwooleTW\Http\Server\Sandbox
107
     */
108
    public function setSnapshot(Container $snapshot)
109
    {
110
        Context::setApp($snapshot);
111
112
        return $this;
113
    }
114
115
    /**
116
     * Initialize based on base app.
117
     *
118
     * @throws \SwooleTW\Http\Exceptions\SandboxException
119
     */
120
    public function initialize()
121
    {
122
        if (! $this->app instanceof Container) {
0 ignored issues
show
introduced by
$this->app is always a sub-type of Illuminate\Container\Container.
Loading history...
123
            throw new SandboxException('A base app has not been set.');
124
        }
125
126
        $this->setInitialConfig();
127
        $this->setInitialProviders();
128
        $this->setInitialResetters();
129
130
        return $this;
131
    }
132
133
    /**
134
     * Get base application.
135
     *
136
     * @return \Illuminate\Container\Container
137
     */
138
    public function getBaseApp()
139
    {
140
        return $this->app;
141
    }
142
143
    /**
144
     * Get an application snapshot
145
     *
146
     * @return \Illuminate\Container\Container
147
     */
148
    public function getApplication()
149
    {
150
        $snapshot = $this->getSnapshot();
151
        if ($snapshot instanceOf Container) {
152
            return $snapshot;
153
        }
154
155
        $snapshot = clone $this->getBaseApp();
156
        $this->setSnapshot($snapshot);
157
158
        return $snapshot;
159
    }
160
161
    /**
162
     * Run framework.
163
     *
164
     * @param \Illuminate\Http\Request $request
165
     *
166
     * @return \Illuminate\Http\Response
167
     * @throws \SwooleTW\Http\Exceptions\SandboxException
168
     * @throws \ReflectionException
169
     */
170
    public function run(Request $request)
171
    {
172
        if (! $this->getSnapshot() instanceof Container) {
173
            throw new SandboxException('Sandbox is not enabled.');
174
        }
175
176
        $shouldUseOb = $this->config->get('swoole_http.ob_output', true);
177
178
        if ($shouldUseOb) {
179
            return $this->prepareObResponse($request);
180
        }
181
182
        return $this->prepareResponse($request);
183
    }
184
185
    /**
186
     * Handle request for non-ob case.
187
     *
188
     * @param \Illuminate\Http\Request $request
189
     *
190
     * @return \Illuminate\Http\Response
191
     * @throws \ReflectionException
192
     */
193
    protected function prepareResponse(Request $request)
194
    {
195
        // handle request with laravel or lumen
196
        $response = $this->handleRequest($request);
197
198
        // process terminating logics
199
        $this->terminate($request, $response);
200
201
        return $response;
202
    }
203
204
    /**
205
     * Handle request for ob output.
206
     *
207
     * @param \Illuminate\Http\Request $request
208
     *
209
     * @return \Illuminate\Http\Response
210
     * @throws \ReflectionException
211
     */
212
    protected function prepareObResponse(Request $request)
213
    {
214
        ob_start();
215
216
        // handle request with laravel or lumen
217
        $response = $this->handleRequest($request);
218
219
        // prepare content for ob
220
        $content = '';
221
        $isFile = false;
222
        if ($isStream = $response instanceof StreamedResponse) {
223
            $response->sendContent();
224
        } elseif ($response instanceof SymfonyResponse) {
0 ignored issues
show
introduced by
$response is always a sub-type of Symfony\Component\HttpFoundation\Response.
Loading history...
225
            $content = $response->getContent();
226
        } elseif (! $isFile = $response instanceof BinaryFileResponse) {
227
            $content = (string) $response;
228
        }
229
230
        // process terminating logics
231
        $this->terminate($request, $response);
232
233
        // append ob content to response
234
        if (! $isFile && ob_get_length() > 0) {
0 ignored issues
show
introduced by
The condition $isFile is always false.
Loading history...
235
            if ($isStream) {
236
                $response->output = ob_get_contents();
0 ignored issues
show
Bug introduced by
The property output does not seem to exist on Illuminate\Http\Response.
Loading history...
237
            } else {
238
                $response->setContent(ob_get_contents() . $content);
239
            }
240
        }
241
242
        ob_end_clean();
243
244
        return $response;
245
    }
246
247
    /**
248
     * Handle request through Laravel or Lumen.
249
     *
250
     * @param \Illuminate\Http\Request $request
251
     *
252
     * @return \Illuminate\Http\Response
253
     */
254
    protected function handleRequest(Request $request)
255
    {
256
        if ($this->isLaravel()) {
257
            return $this->getKernel()->handle($request);
258
        }
259
260
        return $this->getApplication()->dispatch($request);
261
    }
262
263
    /**
264
     * Get Laravel kernel.
265
     */
266
    protected function getKernel()
267
    {
268
        return $this->getApplication()->make(Kernel::class);
269
    }
270
271
    /**
272
     * Return if it's Laravel app.
273
     */
274
    public function isLaravel()
275
    {
276
        return $this->framework === 'laravel';
277
    }
278
279
    /**
280
     * @param \Illuminate\Http\Request $request
281
     * @param \Illuminate\Http\Response $response
282
     *
283
     * @throws \ReflectionException
284
     */
285
    public function terminate(Request $request, $response)
286
    {
287
        if ($this->isLaravel()) {
288
            $this->getKernel()->terminate($request, $response);
289
290
            return;
291
        }
292
293
        $app = $this->getApplication();
294
        $reflection = new \ReflectionObject($app);
295
296
        $middleware = $reflection->getProperty('middleware');
297
        $middleware->setAccessible(true);
298
299
        $callTerminableMiddleware = $reflection->getMethod('callTerminableMiddleware');
300
        $callTerminableMiddleware->setAccessible(true);
301
302
        if (count($middleware->getValue($app)) > 0) {
303
            $callTerminableMiddleware->invoke($app, $response);
304
        }
305
    }
306
307
    /**
308
     * Set laravel snapshot to container and facade.
309
     *
310
     * @throws \SwooleTW\Http\Exceptions\SandboxException
311
     */
312
    public function enable()
313
    {
314
        if (! $this->config instanceof ConfigContract) {
0 ignored issues
show
introduced by
$this->config is always a sub-type of Illuminate\Contracts\Config\Repository.
Loading history...
315
            throw new SandboxException('Please initialize after setting base app.');
316
        }
317
318
        $this->setInstance($app = $this->getApplication());
319
        $this->resetApp($app);
320
    }
321
322
    /**
323
     * Set original laravel app to container and facade.
324
     */
325
    public function disable()
326
    {
327
        $this->setInstance($this->getBaseApp());
328
        Context::clear();
329
    }
330
331
    /**
332
     * Replace app's self bindings.
333
     *
334
     * @param \Illuminate\Container\Container $app
335
     */
336
    public function setInstance(Container $app)
337
    {
338
        $app->instance('app', $app);
339
        $app->instance(Container::class, $app);
340
341
        if ($this->framework === 'lumen') {
342
            $app->instance(LumenApplication::class, $app);
343
        }
344
345
        Container::setInstance($app);
346
        Context::setApp($app);
347
        Facade::clearResolvedInstances();
348
        Facade::setFacadeApplication($app);
0 ignored issues
show
Bug introduced by
$app of type Illuminate\Container\Container is incompatible with the type Illuminate\Contracts\Foundation\Application expected by parameter $app of Illuminate\Support\Facad...:setFacadeApplication(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

348
        Facade::setFacadeApplication(/** @scrutinizer ignore-type */ $app);
Loading history...
349
    }
350
351
    /**
352
     * Get current snapshot.
353
     */
354
    public function getSnapshot()
355
    {
356
        return Context::getApp();
357
    }
358
359
    /**
360
     * Get current request.
361
     */
362
    public function getRequest()
363
    {
364
        return Context::getData('_request');
365
    }
366
}
367