Passed
Branch feature/coroutine_feature (16a200)
by Albert
02:06
created

Sandbox::clearInstances()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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

370
        Facade::setFacadeApplication(/** @scrutinizer ignore-type */ $app);
Loading history...
371
    }
372
373
    /**
374
     * Get current snapshot.
375
     */
376
    public function getSnapshot()
377
    {
378
        return Context::getApp();
379
    }
380
381
    /**
382
     * Remove current request.
383
     */
384
    protected function removeRequest()
385
    {
386
        return Context::removeData('_request');
0 ignored issues
show
Bug introduced by
Are you sure the usage of SwooleTW\Http\Coroutine\...:removeData('_request') targeting SwooleTW\Http\Coroutine\Context::removeData() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
387
    }
388
389
    /**
390
     * Get current request.
391
     */
392
    public function getRequest()
393
    {
394
        return Context::getData('_request');
395
    }
396
}
397