Failed Conditions
Push — master ( 63b660...353e51 )
by Atanas
02:58
created

HttpKernel   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 300
Duplicated Lines 0 %

Test Coverage

Coverage 50%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 106
c 1
b 0
f 0
dl 0
loc 300
ccs 52
cts 104
cp 0.5
rs 9.92
wmc 31

13 Methods

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