Passed
Pull Request — master (#470)
by
unknown
05:40
created

Sandbox::isSettledApp()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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

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