Passed
Push — master ( 77d7ab...4b706d )
by Atanas
02:14
created

HttpKernel   A

Complexity

Total Complexity 40

Size/Duplication

Total Lines 406
Duplicated Lines 0 %

Test Coverage

Coverage 55.26%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 40
eloc 125
c 2
b 0
f 0
dl 0
loc 406
ccs 63
cts 114
cp 0.5526
rs 9.2

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 16 1
A actionAjax() 0 10 2
A executeHandler() 0 12 2
A handle() 0 27 2
A registerAdminAction() 0 6 1
A actionAdminLoad() 0 9 3
A getAdminHook() 0 16 4
A respond() 0 8 2
A filterTemplateInclude() 0 17 3
A getResponseService() 0 2 1
A getResponse() 0 2 2
A filterRequest() 0 18 4
A makeMiddleware() 0 2 1
A actionAdmin() 0 8 2
A getAdminPageHook() 0 16 3
A bootstrap() 0 12 1
A registerAjaxAction() 0 10 3
A run() 0 21 3

How to fix   Complexity   

Complex Class

Complex classes like HttpKernel often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use HttpKernel, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @package   WPEmerge
4
 * @author    Atanas Angelov <[email protected]>
5
 * @copyright 2018 Atanas Angelov
6
 * @license   https://www.gnu.org/licenses/gpl-2.0.html GPL-2.0
7
 * @link      https://wpemerge.com/
8
 */
9
10
namespace WPEmerge\Kernels;
11
12
use Exception;
13
use Pimple\Container;
14
use Psr\Http\Message\ResponseInterface;
15
use WPEmerge\Application\GenericFactory;
16
use WPEmerge\Exceptions\ConfigurationException;
17
use WPEmerge\Exceptions\ErrorHandlerInterface;
18
use WPEmerge\Helpers\Handler;
19
use WPEmerge\Helpers\HandlerFactory;
20
use WPEmerge\Middleware\ExecutesMiddlewareTrait;
21
use WPEmerge\Middleware\HasMiddlewareDefinitionsTrait;
22
use WPEmerge\Middleware\ReadsHandlerMiddlewareTrait;
23
use WPEmerge\Requests\RequestInterface;
24
use WPEmerge\Responses\ConvertsToResponseTrait;
25
use WPEmerge\Responses\ResponseService;
26
use WPEmerge\Routing\HasQueryFilterInterface;
27
use WPEmerge\Routing\Router;
28
use WPEmerge\Routing\SortsMiddlewareTrait;
29
30
/**
31
 * Describes how a request is handled.
32
 */
33
class HttpKernel implements HttpKernelInterface {
34
	use HasMiddlewareDefinitionsTrait;
35
	use SortsMiddlewareTrait;
36
	use ConvertsToResponseTrait;
37
	use ReadsHandlerMiddlewareTrait;
38
	use ExecutesMiddlewareTrait;
39
40
	/**
41
	 * Container.
42
	 *
43
	 * @var Container
44
	 */
45
	protected $container = null;
46
47
	/**
48
	 * Injection factory.
49
	 *
50
	 * @var GenericFactory
51
	 */
52
	protected $factory = null;
53
54
	/**
55
	 * Handler factory.
56
	 *
57
	 * @var HandlerFactory
58
	 */
59
	protected $handler_factory = null;
60
61
	/**
62
	 * Response service.
63
	 *
64
	 * @var ResponseService
65
	 */
66
	protected $response_service = null;
67
68
	/**
69
	 * Request.
70
	 *
71
	 * @var RequestInterface
72
	 */
73
	protected $request = null;
74
75
	/**
76
	 * Router.
77
	 *
78
	 * @var Router
79
	 */
80
	protected $router = null;
81
82
	/**
83
	 * Error handler.
84
	 *
85
	 * @var ErrorHandlerInterface
86
	 */
87
	protected $error_handler = null;
88
89
	/**
90
	 * Constructor.
91
	 *
92
	 * @codeCoverageIgnore
93
	 * @param Container             $container
94
	 * @param GenericFactory        $factory
95
	 * @param HandlerFactory        $handler_factory
96
	 * @param ResponseService       $response_service
97
	 * @param RequestInterface      $request
98
	 * @param Router                $router
99
	 * @param ErrorHandlerInterface $error_handler
100
	 */
101
	public function __construct(
102
		Container $container,
103
		GenericFactory $factory,
104
		HandlerFactory $handler_factory,
105
		ResponseService $response_service,
106
		RequestInterface $request,
107
		Router $router,
108
		ErrorHandlerInterface $error_handler
109
	) {
110
		$this->container = $container;
111
		$this->factory = $factory;
112
		$this->handler_factory = $handler_factory;
113
		$this->response_service = $response_service;
114
		$this->request = $request;
115
		$this->router = $router;
116
		$this->error_handler = $error_handler;
117
	}
118
119
	/**
120
	 * Get the current response.
121
	 *
122
	 * @codeCoverageIgnore
123
	 * @return ResponseInterface|null
124
	 */
125
	protected function getResponse() {
126
		return isset( $this->container[ WPEMERGE_RESPONSE_KEY ] ) ? $this->container[ WPEMERGE_RESPONSE_KEY ] : null;
127
	}
128
129
	/**
130
	 * Get a Response Service instance.
131
	 *
132
	 * @codeCoverageIgnore
133
	 * @return ResponseService
134
	 */
135
	protected function getResponseService() {
136
		return $this->response_service;
137
	}
138
139
	/**
140
	 * Make a middleware class instance.
141
	 *
142
	 * @codeCoverageIgnore
143
	 * @param  string $class
144
	 * @return object
145
	 */
146
	protected function makeMiddleware( $class ) {
147
		return $this->factory->make( $class );
148
	}
149
150
	/**
151
	 * Execute a handler.
152
	 *
153
	 * @param  Handler           $handler
154
	 * @param  array             $arguments
155
	 * @return ResponseInterface
156
	 */
157 2
	protected function executeHandler( Handler $handler, $arguments = [] ) {
158 2
		$response = call_user_func_array( [$handler, 'execute'], $arguments );
159 2
		$response = $this->toResponse( $response );
160
161 2
		if ( ! $response instanceof ResponseInterface ) {
162 1
			throw new ConfigurationException(
163
				'Response returned by controller is not valid ' .
164 1
				'(expected ' . ResponseInterface::class . '; received ' . gettype( $response ) . ').'
165
			);
166
		}
167
168 1
		return $response;
169
	}
170
171
	/**
172
	 * {@inheritDoc}
173
	 */
174 2
	public function run( RequestInterface $request, $middleware, $handler, $arguments = [] ) {
175 2
		$this->error_handler->register();
176
177
		try {
178 2
			$handler = $handler instanceof Handler ? $handler : $this->handler_factory->make( $handler );
179
180 2
			$middleware = array_merge( $middleware, $this->getHandlerMiddleware( $handler ) );
181 2
			$middleware = $this->expandMiddleware( $middleware );
182 2
			$middleware = $this->uniqueMiddleware( $middleware );
183 2
			$middleware = $this->sortMiddleware( $middleware );
184
185 2
			$response = $this->executeMiddleware( $middleware, $request, function () use ( $handler, $arguments ) {
186 2
				return $this->executeHandler( $handler, $arguments );
187 2
			} );
188 1
		} catch ( Exception $exception ) {
189 1
			$response = $this->error_handler->getResponse( $request, $exception );
190
		}
191
192 1
		$this->error_handler->unregister();
193
194 1
		return $response;
195
	}
196
197
	/**
198
	 * {@inheritDoc}
199
	 */
200 2
	public function handle( RequestInterface $request, $arguments = [] ) {
201 2
		$route = $this->router->execute( $request );
202
203 2
		if ( $route === null ) {
204 1
			return null;
205
		}
206
207 1
		$route_arguments = $route->getArguments( $request );
208
209
		$request = $request
210 1
			->withAttribute( 'route', $route )
211 1
			->withAttribute( 'route_arguments', $route_arguments );
212
213 1
		$response = $this->run(
214 1
			$request,
215 1
			$route->getMiddleware(),
216 1
			$route->getHandler(),
217 1
			array_merge(
218 1
				[$request],
219 1
				$arguments,
220 1
				$route_arguments
221
			)
222
		);
223
224 1
		$this->container[ WPEMERGE_RESPONSE_KEY ] = $response;
225
226 1
		return $response;
227
	}
228
229
	/**
230
	 * Respond with the current response.
231
	 *
232
	 * @return void
233
	 */
234 2
	public function respond() {
235 2
		$response = $this->getResponse();
236
237 2
		if ( ! $response instanceof ResponseInterface ) {
238 1
			return;
239
		}
240
241 1
		$this->response_service->respond( $response );
242 1
	}
243
244
	/**
245
	 * {@inheritDoc}
246
	 * @codeCoverageIgnore
247
	 */
248
	public function bootstrap() {
249
		// Web. Use 3100 so it's high enough and has uncommonly used numbers
250
		// before and after. For example, 1000 is too common and it would have 999 before it
251
		// which is too common as well.).
252
		add_action( 'request', [$this, 'filterRequest'], 3100 );
253
		add_action( 'template_include', [$this, 'filterTemplateInclude'], 3100 );
254
255
		// Ajax.
256
		add_action( 'admin_init', [$this, 'registerAjaxAction'] );
257
258
		// Admin.
259
		add_action( 'admin_init', [$this, 'registerAdminAction'] );
260
	}
261
262
	/**
263
	 * Filter the main query vars.
264
	 *
265
	 * @param  array $query_vars
266
	 * @return array
267
	 */
268 2
	public function filterRequest( $query_vars ) {
269
		/** @var $routes \WPEmerge\Routing\RouteInterface[] */
270 2
		$routes = $this->router->getRoutes();
271
272 2
		foreach ( $routes as $route ) {
273 2
			if ( ! $route instanceof HasQueryFilterInterface ) {
274 2
				continue;
275
			}
276
277 2
			if ( ! $route->isSatisfied( $this->request ) ) {
278 1
				continue;
279
			}
280
281 2
			$query_vars = $route->applyQueryFilter( $this->request, $query_vars );
282 2
			break;
283
		}
284
285 2
		return $query_vars;
286
	}
287
288
	/**
289
	 * Filter the main template file.
290
	 *
291
	 * @param  string $view
292
	 * @return string
293
	 */
294 3
	public function filterTemplateInclude( $view ) {
295
		/** @var $wp_query \WP_Query */
296 3
		global $wp_query;
297
298 3
		$response = $this->handle( $this->request, [$view] );
299
300 3
		if ( $response instanceof ResponseInterface ) {
301 2
			if ( $response->getStatusCode() === 404 ) {
302 1
				$wp_query->set_404();
303
			}
304
305 2
			add_action( 'wpemerge.respond', [$this, 'respond'] );
306
307 2
			return WPEMERGE_DIR . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'view.php';
308
		}
309
310 1
		return $view;
311
	}
312
313
	/**
314
	 * Register ajax action to hook into current one.
315
	 *
316
	 * @return void
317
	 */
318
	public function registerAjaxAction() {
319
		if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) {
320
			return;
321
		}
322
323
		$action = $this->request->body( 'action', $this->request->query( 'action' ) );
324
		$action = sanitize_text_field( $action );
325
326
		add_action( "wp_ajax_{$action}", [$this, 'actionAjax'] );
327
		add_action( "wp_ajax_nopriv_{$action}", [$this, 'actionAjax'] );
328
	}
329
330
	/**
331
	 * Act on ajax action.
332
	 *
333
	 * @return void
334
	 */
335
	public function actionAjax() {
336
		$response = $this->handle( $this->request, [''] );
337
338
		if ( ! $response instanceof ResponseInterface ) {
339
			return;
340
		}
341
342
		$this->response_service->respond( $response );
343
344
		wp_die( '', '', ['response' => null] );
345
	}
346
347
	/**
348
	 * Get page hook.
349
	 * Slightly modified version of code from wp-admin/admin.php.
350
	 *
351
	 * @return string
352
	 */
353
	protected function getAdminPageHook() {
354
		global $pagenow, $typenow, $plugin_page;
355
356
		$page_hook = '';
357
358
		if ( isset( $plugin_page ) ) {
359
			$the_parent = $pagenow;
360
361
			if ( ! empty( $typenow ) ) {
362
				$the_parent = $pagenow . '?post_type=' . $typenow;
363
			}
364
365
			$page_hook = get_plugin_page_hook( $plugin_page, $the_parent );
366
		}
367
368
		return $page_hook;
369
	}
370
371
	/**
372
	 * Get admin page hook.
373
	 * Slightly modified version of code from wp-admin/admin.php.
374
	 *
375
	 * @param  string $page_hook
376
	 * @return string
377
	 */
378
	protected function getAdminHook( $page_hook ) {
379
		global $pagenow, $plugin_page;
380
381
		if ( ! empty( $page_hook ) ) {
382
			return $page_hook;
383
		}
384
385
		if ( isset( $plugin_page ) ) {
386
			return $plugin_page;
387
		}
388
389
		if ( isset( $pagenow ) ) {
390
			return $pagenow;
391
		}
392
393
		return '';
394
	}
395
396
	/**
397
	 * Register admin action to hook into current one.
398
	 *
399
	 * @return void
400
	 */
401
	public function registerAdminAction() {
402
		$page_hook = $this->getAdminPageHook();
403
		$hook_suffix = $this->getAdminHook( $page_hook );
404
405
		add_action( "load-{$hook_suffix}", [$this, 'actionAdminLoad'] );
406
		add_action( $hook_suffix, [$this, 'actionAdmin'] );
407
	}
408
409
	/**
410
	 * Act on admin action load.
411
	 *
412
	 * @return void
413
	 */
414
	public function actionAdminLoad() {
415
		$response = $this->handle( $this->request, [''] );
416
417
		if ( ! $response instanceof ResponseInterface ) {
418
			return;
419
		}
420
421
		if ( ! headers_sent() ) {
422
			$this->response_service->sendHeaders( $response );
423
		}
424
	}
425
426
	/**
427
	 * Act on admin action.
428
	 *
429
	 * @return void
430
	 */
431
	public function actionAdmin() {
432
		$response = $this->getResponse();
433
434
		if ( ! $response instanceof ResponseInterface ) {
435
			return;
436
		}
437
438
		$this->response_service->sendBody( $response );
439
	}
440
}
441