Passed
Push — 0.7.0 ( e442b6...679d5f )
by Alexander
11:21 queued 12s
created

Lenevor::shutdownMiddleware()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 10
c 1
b 0
f 0
nc 8
nop 2
dl 0
loc 18
rs 9.6111
1
<?php
2
3
/**
4
 * Lenevor Framework
5
 *
6
 * LICENSE
7
 *
8
 * This source file is subject to the new BSD license that is bundled
9
 * with this package in the file license.md.
10
 * It is also available through the world-wide-web at this URL:
11
 * https://lenevor.com/license
12
 * If you did not receive a copy of the license and are unable to
13
 * obtain it through the world-wide-web, please send an email
14
 * to [email protected] so we can send you a copy immediately.
15
 *
16
 * @package     Lenevor
17
 * @subpackage  Base
18
 * @link        https://lenevor.com
19
 * @copyright   Copyright (c) 2019 - 2021 Alexander Campo <[email protected]>
20
 * @license     https://opensource.org/licenses/BSD-3-Clause New BSD license or see https://lenevor.com/license or see /license.md
21
 */
22
23
namespace Syscodes\Core\Http;
24
25
use Closure;
26
use Throwable; 
27
use Syscodes\Routing\Route;
28
use Syscodes\Routing\Router;
29
use Syscodes\Routing\Pipeline;
30
use Syscodes\Support\Facades\Facade;
31
use Syscodes\Contracts\Core\Application;
32
use Syscodes\Contracts\Debug\ExceptionHandler;
33
use Syscodes\Contracts\Http\Lenevor as LenevorContract;
34
35
/**
36
 * The Lenevor class is the heart of the system framework.
37
 * 
38
 * @author Alexander Campo <[email protected]>
39
 */
40
class Lenevor implements LenevorContract
41
{
42
	/**
43
	 * The application implementation.
44
	 * 
45
	 * @var \Syscodes\Contracts\Core\Application $app
46
	 */
47
	protected $app;
48
	
49
	/**
50
	 * The bootstrap classes for the application.
51
	 * 
52
	 * @var array $bootstrappers
53
	 */
54
	protected $bootstrappers = [
55
		\Syscodes\Core\Bootstrap\BootDetectEnvironment::class,
56
		\Syscodes\Core\Bootstrap\BootConfiguration::class,
57
		\Syscodes\Core\Bootstrap\BootHandleExceptions::class,
58
		\Syscodes\Core\Bootstrap\BootRegisterFacades::class,
59
		\Syscodes\Core\Bootstrap\BootRegisterProviders::class,
60
		\Syscodes\Core\Bootstrap\BootProviders::class,
61
	];
62
63
	/**
64
	 * Get the application's middleware.
65
	 * 
66
	 * @var array $middleware
67
	 */
68
	protected $middleware = [];
69
70
	/**
71
	 * Get the application's middleware groups.
72
	 * 
73
	 * @var array $middlewareGroups
74
	 */
75
	protected $middlewareGroups = [];
76
77
	/**
78
	 * The router instance.
79
	 * 
80
	 * @var \Syscodes\Routing\Router $router
81
	 */
82
	protected $router;
83
84
	/**
85
	 * Get the application's route middleware.
86
	 * 
87
	 * @var array $routeMiddleware
88
	 */
89
	protected $routeMiddleware = [];
90
91
	/**
92
	 * Total app execution time.
93
	 * 
94
	 * @var float $totalTime
95
	 */
96
	protected $totalTime;
97
98
	/**
99
	 * Constructor. Lenevor class instance.
100
	 * 
101
	 * @param  \Syscodes\Contracts\Core\Application  $app
102
	 * @param  \Syscodes\Routing\Router  $router
103
	 * 
104
	 * @return void
105
	 */
106
	public function __construct(Application $app, Router $router)
107
	{
108
		$this->app    = $app;
109
		$this->router = $router;
110
111
		$this->syncMiddlewareRoute();
112
	}
113
	 
114
	/**
115
	 * Initializes the framework, this can only be called once.
116
	 * Launch the application.
117
	 * 
118
	 * @param  \Syscodes\http\Request  $request
119
	 *
120
	 * @return \Syscodes\Http\Response
121
	 */
122
	public function handle($request)
123
	{
124
		try {
125
			$response = $this->sendRequestThroughRouter($request);
126
		} catch (Throwable $e) {
127
			$this->reportException($e);
128
129
			$response = $this->renderException($request, $e);
130
		}		
131
132
		return $response;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response returns the type Syscodes\Http\Response which is incompatible with the return type mandated by Syscodes\Contracts\Http\Lenevor::handle() of void.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
133
	}
134
135
	/**
136
	 * Send the given request through the router.
137
	 * 
138
	 * @param  \Syscodes\Http\Request  $request
139
	 * 
140
	 * @return \Syscodes\Http\Response
141
	 */
142
	protected function sendRequestThroughRouter($request)
143
	{
144
		$this->app->instance('request', $request);  
145
146
		Facade::clearResolvedInstance('request');
147
148
		// Load configuration system
149
		$this->bootstrap();
150
151
		return (new Pipeline($this->app))
152
				->send($request)
153
				->through($this->app->skipGoingMiddleware() ? [] : $this->middleware)
154
				->then($this->dispatchToRouter());
155
	}
156
157
	/**
158
	 * Bootstrap the application for HTTP requests.
159
	 * 
160
	 * @return void
161
	 */
162
	protected function bootstrap()
163
	{		
164
		if ( ! $this->app->hasBeenBootstrapped()) {
165
			$this->app->bootstrapWith($this->bootstrappers());
166
		}
167
	}
168
169
	/**
170
	 * Get the bootstrap classes for the application.
171
	 * 
172
	 * @return array
173
	 */
174
	protected function bootstrappers()
175
	{
176
		return $this->bootstrappers;
177
	}
178
179
	/**
180
	 * Sync the current state of the middleware to the router.
181
	 * 
182
	 * @return void
183
	 */
184
	protected function syncMiddlewareRoute()
185
	{
186
		foreach ($this->middlewareGroups as $key => $middleware) {
187
			$this->router->middlewareGroup($key, $middleware);
188
		}
189
190
		foreach ($this->routeMiddleware as $key => $middleware) {
191
			$this->router->aliasMiddleware($key, $middleware);
192
		}
193
	}
194
195
	/**
196
	 * Get the dispatcher of routes.
197
	 * 	  
198
	 * @return \Closure
199
 	 */
200
	protected function dispatchToRouter()
201
	{
202
		return function ($request) {
203
			$this->app->instance('request', $request);
204
205
			return $this->router->dispatch($request);
206
		};
207
	}
208
209
	/**
210
	 * Call the shutdown method on any terminable middleware.
211
	 * 
212
	 * @param  \Syscodes\Http\Request  $request
213
	 * @param  \Syscodes\Http\Response  $response
214
	 * 
215
	 * @return void
216
	 */
217
	public function shutdown($request, $response)
218
	{
219
		$this->shutdownMiddleware($request, $response);
220
	}
221
222
	/**
223
	 * Call the terminate method on any terminable middleware.
224
	 * 
225
	 * @param  \Syscodes\Http\Request  $request
226
	 * @param  \Syscodes\Http\Response  $response
227
	 * 
228
	 * @return void
229
	 */
230
	protected function shutdownMiddleware($request, $response)
231
	{
232
		$middlewares = $this->app->skipGoingMiddleware() ? [] : array_merge(
233
			$this->gatherRouteMiddleware($request),
234
			$this->middleware
235
		);
236
237
		foreach ($middlewares as $middleware) {
238
			if (! is_string($middleware)) {
239
				continue;
240
			}
241
			
242
			[$name] = $this->parseMiddleware($middleware);
243
			
244
			$instance = $this->app->make($name);
245
			
246
			if (method_exists($instance, 'shutdown')) {
247
				$instance->shutdown($request, $response);
248
			}
249
		}
250
	}
251
252
	/**
253
	 * Gather the route middleware for the given request.
254
	 * 
255
	 * @param  \Syscodes\Http\Request  $request
256
	 * 
257
	 * @return array
258
	 */
259
	protected function gatherRouteMiddleware($request)
260
	{
261
		if ( ! is_null($route = $request->route())) {
262
			return $this->router->gatherRouteMiddleware($route);
0 ignored issues
show
Bug introduced by
It seems like $route can also be of type string; however, parameter $route of Syscodes\Routing\Router::gatherRouteMiddleware() does only seem to accept Syscodes\Routing\Route, maybe add an additional type check? ( Ignorable by Annotation )

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

262
			return $this->router->gatherRouteMiddleware(/** @scrutinizer ignore-type */ $route);
Loading history...
263
		}
264
265
		return [];
266
	}
267
	
268
	/**
269
	 * Parse a middleware string to get the name and parameters.
270
	 * 
271
	 * @param  string  $middleware
272
	 * 
273
	 * @return array
274
	 */
275
	protected function parseMiddleware($middleware)
276
	{
277
		[$name, $parameters] = array_pad(explode(':', $middleware, 2), 2, []);
278
		
279
		if (is_string($parameters)) {
280
			$parameters = explode(',', $parameters);
281
		}
282
		
283
		return [$name, $parameters];
284
    }
285
286
	/**
287
	 * Report the exception to the exception handler.
288
	 * 
289
	 * @param  \Throwable  $e
290
	 * 
291
	 * @return void
292
	 */
293
	protected function reportException(Throwable $e)
294
	{
295
		return $this->app[ExceptionHandler::class]->report($e);
296
	}
297
	
298
	/**
299
	 * Render the exception to a response.
300
	 * 
301
	 * @param  \Syscodes\Http\Request  $request
302
	 * @param  \Throwable  $e
303
	 * 
304
	 * @return \Syscodes\Http\Response
305
	 */
306
	protected function renderException($request, Throwable $e)
307
	{
308
		return $this->app[ExceptionHandler::class]->render($request, $e);
309
	}
310
 }