Passed
Branch refactor/kernels (b26d95)
by Atanas
01:39
created

HttpKernel::run()   A

Complexity

Conditions 2
Paths 5

Size

Total Lines 19
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 2

Importance

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