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
![]() |
|||||
36 | * @param null $framework |
||||
0 ignored issues
–
show
|
|||||
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
|
|||||
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
|
|||||
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
|
|||||
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
|
|||||
235 | if ($isStream) { |
||||
236 | $response->output = ob_get_contents(); |
||||
0 ignored issues
–
show
|
|||||
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
|
|||||
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
$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
![]() |
|||||
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 |