Passed
Push — master ( f7f7f6...4e1c41 )
by Atanas
02:10
created

HttpKernel::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 8
c 0
b 0
f 0
dl 0
loc 18
ccs 0
cts 0
cp 0
rs 10
cc 1
nc 1
nop 8
crap 2

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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