Completed
Branch master (9dff9d)
by Albert
05:30
created

Sandbox::isLaravel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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

315
        Facade::setFacadeApplication(/** @scrutinizer ignore-type */ $app);
Loading history...
316
    }
317
318
    /**
319
     * Get current snapshot.
320
     */
321
    public function getSnapshot()
322
    {
323
        return Context::getApp();
324
    }
325
326
    /**
327
     * Remove current request.
328
     */
329
    protected function removeRequest()
330
    {
331
        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...
332
    }
333
334
    /**
335
     * Get current request.
336
     */
337
    public function getRequest()
338
    {
339
        return Context::getData('_request');
340
    }
341
}
342