Completed
Branch master (479eaa)
by Atanas
02:08
created

HttpKernel   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 330
Duplicated Lines 0 %

Test Coverage

Coverage 48.28%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 110
c 1
b 0
f 0
dl 0
loc 330
ccs 56
cts 116
cp 0.4828
rs 9.76
wmc 33

14 Methods

Rating   Name   Duplication   Size   Complexity  
A actionAjax() 0 10 2
A registerAdminAction() 0 6 1
A actionAdminLoad() 0 9 3
A getAdminHook() 0 16 4
A respond() 0 5 2
A __construct() 0 12 1
A actionAdmin() 0 8 2
A bootstrap() 0 10 1
A registerAjaxAction() 0 10 3
A run() 0 19 2
A handle() 0 32 2
A filterTemplateInclude() 0 17 3
A filterRequest() 0 18 4
A getAdminPageHook() 0 16 3
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 Psr\Http\Message\ResponseInterface;
14
use WPEmerge\Application\Application;
15
use WPEmerge\Exceptions\ErrorHandlerInterface;
16
use WPEmerge\Middleware\HasMiddlewareDefinitionsTrait;
17
use WPEmerge\Requests\RequestInterface;
18
use WPEmerge\Responses\ResponseService;
19
use WPEmerge\Routing\HasQueryFilterInterface;
20
use WPEmerge\Routing\Pipeline;
21
use WPEmerge\Routing\Router;
22
use WPEmerge\Routing\SortsMiddlewareTrait;
23
24
/**
25
 * Describes how a request is handled.
26
 */
27
class HttpKernel implements HttpKernelInterface {
28
	use HasMiddlewareDefinitionsTrait;
29
	use SortsMiddlewareTrait;
30
31
	/**
32
	 * Application.
33
	 *
34
	 * @var Application
35
	 */
36
	protected $app = null;
37
38
	/**
39
	 * Response service.
40
	 *
41
	 * @var ResponseService
42
	 */
43
	protected $response_service = null;
44
45
	/**
46
	 * Request.
47
	 *
48
	 * @var RequestInterface
49
	 */
50
	protected $request = null;
51
52
	/**
53
	 * Router.
54
	 *
55
	 * @var Router
56
	 */
57
	protected $router = null;
58
59
	/**
60
	 * Error handler.
61
	 *
62
	 * @var ErrorHandlerInterface
63
	 */
64
	protected $error_handler = null;
65
66
	/**
67
	 * Constructor.
68
	 *
69
	 * @codeCoverageIgnore
70
	 * @param Application           $app
71
	 * @param ResponseService       $response_service
72
	 * @param RequestInterface      $request
73
	 * @param Router                $router
74
	 * @param ErrorHandlerInterface $error_handler
75
	 */
76
	public function __construct(
77
		Application $app,
78
		ResponseService $response_service,
79
		RequestInterface $request,
80
		Router $router,
81
		ErrorHandlerInterface $error_handler
82
	) {
83
		$this->app = $app;
84
		$this->response_service = $response_service;
85
		$this->request = $request;
86
		$this->router = $router;
87
		$this->error_handler = $error_handler;
88
	}
89
90
	/**
91
	 * {@inheritDoc}
92
	 * @codeCoverageIgnore
93
	 */
94
	public function bootstrap() {
95
		// Web.
96
		add_action( 'request', [$this, 'filterRequest'], 1000 );
97
		add_action( 'template_include', [$this, 'filterTemplateInclude'], 1000 );
98
99
		// Ajax.
100
		add_action( 'admin_init', [$this, 'registerAjaxAction'] );
101
102
		// Admin.
103
		add_action( 'admin_init', [$this, 'registerAdminAction'] );
104
	}
105
106
	/**
107
	 * Respond with the current response.
108
	 *
109
	 * @return void
110
	 */
111
	public function respond() {
112
		$response = $this->app->resolve( WPEMERGE_RESPONSE_KEY );
113
114
		if ( $response instanceof ResponseInterface ) {
115
			$this->response_service->respond( $response );
116
		}
117
	}
118
119
	/**
120
	 * {@inheritDoc}
121
	 */
122 2
	public function handle( RequestInterface $request, $arguments = [] ) {
123 2
		$route = $this->router->execute( $request );
124
125 2
		if ( $route === null ) {
126 1
			return null;
127
		}
128
129 1
		$route_arguments = $route->getArguments( $request );
130
131
		$request = $request
132 1
			->withAttribute( 'route', $route )
133 1
			->withAttribute( 'routeArguments', $route_arguments );
134
135 1
		$handler = function ( $request ) use ( $route ) {
136 1
			return call_user_func( [$route, 'handle'], $request, func_get_args() );
137 1
		};
138
139 1
		$response = $this->run(
140 1
			$request,
141 1
			$route->getMiddleware(),
142 1
			$handler,
143 1
			array_merge(
144 1
				[$request],
145 1
				$arguments,
146
				$route_arguments
147 1
			)
148 1
		);
149
150 1
		$container = $this->app->getContainer();
151 1
		$container[ WPEMERGE_RESPONSE_KEY ] = $response;
152
153 1
		return $response;
154
	}
155
156
	/**
157
	 * {@inheritDoc}
158
	 */
159 2
	public function run( RequestInterface $request, $middleware, $handler, $arguments = [] ) {
160 2
		$this->error_handler->register();
161
162
		try {
163 2
			$middleware = $this->expandMiddleware( $middleware );
164 2
			$middleware = $this->uniqueMiddleware( $middleware );
165 2
			$middleware = $this->sortMiddleware( $middleware );
166
167 2
			$response = ( new Pipeline() )
168 2
				->pipe( $middleware )
169 2
				->to( $handler )
170 2
				->run( $request, $arguments );
171 2
		} catch ( Exception $exception ) {
172 1
			$response = $this->error_handler->getResponse( $request, $exception );
173
		}
174
175 1
		$this->error_handler->unregister();
176
177 1
		return $response;
178
	}
179
180
	/**
181
	 * Filter the main query vars.
182
	 *
183
	 * @param  array $query_vars
184
	 * @return array
185
	 */
186 2
	public function filterRequest( $query_vars ) {
187
		/** @var $routes \WPEmerge\Routing\RouteInterface[] */
188 2
		$routes = $this->router->getRoutes();
189
190 2
		foreach ( $routes as $route ) {
191 2
			if ( ! $route instanceof HasQueryFilterInterface ) {
192 2
				continue;
193
			}
194
195 2
			if ( ! $route->isSatisfied( $this->request ) ) {
196 1
				continue;
197
			}
198
199 2
			$query_vars = $route->applyQueryFilter( $this->request, $query_vars );
200 2
			break;
201 2
		}
202
203 2
		return $query_vars;
204
	}
205
206
	/**
207
	 * Filter the main template file.
208
	 *
209
	 * @param  string $view
210
	 * @return string
211
	 */
212 3
	public function filterTemplateInclude( $view ) {
213
		/** @var $wp_query \WP_Query */
214 3
		global $wp_query;
215
216 3
		$response = $this->handle( $this->request, [$view] );
217
218 3
		if ( $response instanceof ResponseInterface ) {
219 2
			if ( $response->getStatusCode() === 404 ) {
220 1
				$wp_query->set_404();
221 1
			}
222
223 2
			add_action( 'wpemerge.respond', [$this, 'respond'] );
224
225 2
			return WPEMERGE_DIR . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'view.php';
226
		}
227
228 1
		return $view;
229
	}
230
231
	/**
232
	 * Register ajax action to hook into current one.
233
	 *
234
	 * @return void
235
	 */
236
	public function registerAjaxAction() {
237
		if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) {
238
			return;
239
		}
240
241
		$action = $this->request->body( 'action', $this->request->query( 'action' ) );
242
		$action = sanitize_text_field( $action );
243
244
		add_action( "wp_ajax_{$action}", [$this, 'actionAjax'] );
245
		add_action( "wp_ajax_nopriv_{$action}", [$this, 'actionAjax'] );
246
	}
247
248
	/**
249
	 * Act on ajax action.
250
	 *
251
	 * @return void
252
	 */
253
	public function actionAjax() {
254
		$response = $this->handle( $this->request, [''] );
255
256
		if ( ! $response instanceof ResponseInterface ) {
257
			return;
258
		}
259
260
		$this->response_service->respond( $response );
261
262
		wp_die( '', '', ['response' => null] );
263
	}
264
265
	/**
266
	 * Get page hook.
267
	 * Slightly modified version of code from wp-admin/admin.php.
268
	 *
269
	 * @return string
270
	 */
271
	protected function getAdminPageHook() {
272
		global $pagenow, $typenow, $plugin_page;
273
274
		$page_hook = '';
275
276
		if ( isset( $plugin_page ) ) {
277
			$the_parent = $pagenow;
278
279
			if ( ! empty( $typenow ) ) {
280
				$the_parent = $pagenow . '?post_type=' . $typenow;
281
			}
282
283
			$page_hook = get_plugin_page_hook( $plugin_page, $the_parent );
284
		}
285
286
		return $page_hook;
287
	}
288
289
	/**
290
	 * Get admin page hook.
291
	 * Slightly modified version of code from wp-admin/admin.php.
292
	 *
293
	 * @param  string $page_hook
294
	 * @return string
295
	 */
296
	protected function getAdminHook( $page_hook ) {
297
		global $pagenow, $plugin_page;
298
299
		if ( ! empty( $page_hook ) ) {
300
			return $page_hook;
301
		}
302
303
		if ( isset( $plugin_page ) ) {
304
			return $plugin_page;
305
		}
306
307
		if ( isset( $pagenow ) ) {
308
			return $pagenow;
309
		}
310
311
		return '';
312
	}
313
314
	/**
315
	 * Register admin action to hook into current one.
316
	 *
317
	 * @return void
318
	 */
319
	public function registerAdminAction() {
320
		$page_hook = $this->getAdminPageHook();
321
		$hook_suffix = $this->getAdminHook( $page_hook );
322
323
		add_action( "load-{$hook_suffix}", [$this, 'actionAdminLoad'] );
324
		add_action( $hook_suffix, [$this, 'actionAdmin'] );
325
	}
326
327
	/**
328
	 * Act on admin action load.
329
	 *
330
	 * @return void
331
	 */
332
	public function actionAdminLoad() {
333
		$response = $this->handle( $this->request, [''] );
334
335
		if ( ! $response instanceof ResponseInterface ) {
336
			return;
337
		}
338
339
		if ( ! headers_sent() ) {
340
			$this->response_service->sendHeaders( $response );
341
		}
342
	}
343
344
	/**
345
	 * Act on admin action.
346
	 *
347
	 * @return void
348
	 */
349
	public function actionAdmin() {
350
		$response = $this->app->resolve( WPEMERGE_RESPONSE_KEY );
351
352
		if ( ! $response instanceof ResponseInterface ) {
353
			return;
354
		}
355
356
		$this->response_service->sendBody( $response );
357
	}
358
}
359