htmlburger /
wpemerge
| 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'], array_values( $arguments ) ); |
|||
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||
| 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 ) . ').' |
|||
|
0 ignored issues
–
show
The function
gettype was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 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() ) { |
||||
|
0 ignored issues
–
show
The function
headers_sent was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 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 |