Passed
Push — master ( 33655b...f08be5 )
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 2
			$this->container[ WPEMERGE_APPLICATION_KEY ]
311
				->renderConfigExceptions( function () use ( $route, &$query_vars ) {
312 2
					$query_vars = $route->applyQueryFilter( $this->request, $query_vars );
313 2
				} );
314 2
			break;
315
		}
316
317 2
		return $query_vars;
318
	}
319
320
	/**
321
	 * Filter the main template file.
322
	 *
323
	 * @param  string $template
324
	 * @return string
325
	 */
326 4
	public function filterTemplateInclude( $template ) {
327
		/** @var WP_Query $wp_query */
328 4
		global $wp_query;
329
330 4
		$this->template = $template;
331
332 4
		$response = $this->handle( $this->request, [$template] );
333
334
		// A route has matched so we use its response.
335 4
		if ( $response instanceof ResponseInterface ) {
336 2
			if ( $response->getStatusCode() === 404 ) {
337 1
				$wp_query->set_404();
338
			}
339
340 2
			add_action( 'wpemerge.kernels.http_kernel.respond', [$this, 'respond'] );
341
342 2
			return WPEMERGE_DIR . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'view.php';
343
		}
344
345
		// No route has matched, but we still want to compose views.
346 2
		$composers = $this->view_service->getComposersForView( $template );
347
348 2
		if ( ! empty( $composers ) ) {
349 1
			add_action( 'wpemerge.kernels.http_kernel.respond', [$this, 'compose'] );
350
351 1
			return WPEMERGE_DIR . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'view.php';
352
		}
353
354 1
		return $template;
355
	}
356
357
	/**
358
	 * Register ajax action to hook into current one.
359
	 *
360
	 * @return void
361
	 */
362
	public function registerAjaxAction() {
363
		if ( ! wp_doing_ajax() ) {
364
			return;
365
		}
366
367
		$action = $this->request->body( 'action', $this->request->query( 'action' ) );
368
		$action = sanitize_text_field( $action );
369
370
		add_action( "wp_ajax_{$action}", [$this, 'actionAjax'] );
371
		add_action( "wp_ajax_nopriv_{$action}", [$this, 'actionAjax'] );
372
	}
373
374
	/**
375
	 * Act on ajax action.
376
	 *
377
	 * @return void
378
	 */
379
	public function actionAjax() {
380
		$response = $this->handle( $this->request, [''] );
381
382
		if ( ! $response instanceof ResponseInterface ) {
383
			return;
384
		}
385
386
		$this->response_service->respond( $response );
387
388
		wp_die( '', '', ['response' => null] );
389
	}
390
391
	/**
392
	 * Get page hook.
393
	 * Slightly modified version of code from wp-admin/admin.php.
394
	 *
395
	 * @return string
396
	 */
397
	protected function getAdminPageHook() {
398
		global $pagenow, $typenow, $plugin_page;
399
400
		$page_hook = '';
401
402
		if ( isset( $plugin_page ) ) {
403
			$the_parent = $pagenow;
404
405
			if ( ! empty( $typenow ) ) {
406
				$the_parent = $pagenow . '?post_type=' . $typenow;
407
			}
408
409
			$page_hook = get_plugin_page_hook( $plugin_page, $the_parent );
410
		}
411
412
		return $page_hook;
413
	}
414
415
	/**
416
	 * Get admin page hook.
417
	 * Slightly modified version of code from wp-admin/admin.php.
418
	 *
419
	 * @param  string $page_hook
420
	 * @return string
421
	 */
422
	protected function getAdminHook( $page_hook ) {
423
		global $pagenow, $plugin_page;
424
425
		if ( ! empty( $page_hook ) ) {
426
			return $page_hook;
427
		}
428
429
		if ( isset( $plugin_page ) ) {
430
			return $plugin_page;
431
		}
432
433
		if ( isset( $pagenow ) ) {
434
			return $pagenow;
435
		}
436
437
		return '';
438
	}
439
440
	/**
441
	 * Register admin action to hook into current one.
442
	 *
443
	 * @return void
444
	 */
445
	public function registerAdminAction() {
446
		$page_hook = $this->getAdminPageHook();
447
		$hook_suffix = $this->getAdminHook( $page_hook );
448
449
		add_action( "load-{$hook_suffix}", [$this, 'actionAdminLoad'] );
450
		add_action( $hook_suffix, [$this, 'actionAdmin'] );
451
	}
452
453
	/**
454
	 * Act on admin action load.
455
	 *
456
	 * @return void
457
	 */
458
	public function actionAdminLoad() {
459
		$response = $this->handle( $this->request, [''] );
460
461
		if ( ! $response instanceof ResponseInterface ) {
462
			return;
463
		}
464
465
		if ( ! headers_sent() ) {
466
			$this->response_service->sendHeaders( $response );
467
		}
468
	}
469
470
	/**
471
	 * Act on admin action.
472
	 *
473
	 * @return void
474
	 */
475
	public function actionAdmin() {
476
		$response = $this->getResponse();
477
478
		if ( ! $response instanceof ResponseInterface ) {
479
			return;
480
		}
481
482
		$this->response_service->sendBody( $response );
483
	}
484
}
485