Completed
Push — master ( 345654...ff532e )
by Gabriel
214:01 queued 148:58
created

HandleExceptions::createInternalErrorResponse()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 9
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 13
rs 9.9666
1
<?php
2
3
declare( strict_types = 1 );
4
5
namespace WMDE\Fundraising\Frontend\App\EventHandlers;
6
7
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
8
use Symfony\Component\HttpFoundation\JsonResponse;
9
use Symfony\Component\HttpFoundation\Response;
10
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
11
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
12
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
13
use Symfony\Component\HttpKernel\KernelEvents;
14
use WMDE\Fundraising\Frontend\App\AccessDeniedException;
15
use WMDE\Fundraising\Frontend\Factories\FunFunFactory;
16
17
/**
18
 * Generate different error responses for the different exceptions.
19
 *
20
 * @todo Replace this class with a custom error controller when we have switched to Symfony
21
 *       see https://phabricator.wikimedia.org/T263436
22
 *       see https://symfony.com/doc/current/controller/error_pages.html#overriding-the-default-errorcontroller
23
 */
24
class HandleExceptions implements EventSubscriberInterface {
25
26
	private const PRIORITY = -8;
27
28
	private FunFunFactory $presenterFactory;
29
30
	public function __construct( FunFunFactory $presenterFactory ) {
31
 $this->presenterFactory = $presenterFactory;
32
	}
33
34
	public static function getSubscribedEvents() {
35
		return [
36
			KernelEvents::EXCEPTION => [ 'onKernelException', self::PRIORITY ]
37
		];
38
	}
39
40
	public function onKernelException( GetResponseForExceptionEvent $event ): void {
41
		$exception = $event->getException();
42
		switch ( true ) {
43
			case $exception instanceof AccessDeniedException:
44
				$this->createAccessDeniedResponse( $event );
45
				break;
46
			case $exception instanceof NotFoundHttpException:
47
				$this->createNotFoundResponse( $event );
48
				break;
49
			default:
50
				$this->createInternalErrorResponse( $event );
51
		}
52
	}
53
54
	private function createAccessDeniedResponse( GetResponseForExceptionEvent $event ): void {
55
		$event->setResponse( new Response(
56
			$this->presenterFactory->newAccessDeniedHtmlPresenter()->present(
57
				$event->getException()->getMessage()
58
			),
59
			403,
60
			[ 'X-Status-Code' => 403 ]
61
		) );
62
	}
63
64
	private function createNotFoundResponse( GetResponseForExceptionEvent $event ): void {
65
		if ( $this->isJsonRequest( $event ) ) {
66
			$event->setResponse( JsonResponse::create(
67
				[ 'ERR' => $event->getException()->getMessage() ],
68
				404,
69
				[ 'X-Status-Code' => 404 ]
70
			) );
71
			return;
72
		}
73
74
		$event->setResponse( new Response(
75
			$this->presenterFactory->newPageNotFoundHtmlPresenter()->present(),
76
			404,
77
			[ 'X-Status-Code' => 404 ]
78
		) );
79
	}
80
81
	private function createInternalErrorResponse( GetResponseForExceptionEvent $event ) {
82
		$exception = $event->getException();
83
		if ( $this->isJsonRequest( $event ) ) {
84
			$event->setResponse( JsonResponse::create(
85
				[ 'ERR' => $exception->getMessage() ]
86
			) );
87
			return;
88
		}
89
90
		$code = $exception instanceof HttpExceptionInterface ? $exception->getStatusCode() : Response::HTTP_INTERNAL_SERVER_ERROR;
91
		$event->setResponse( new Response(
92
			$this->presenterFactory->getInternalErrorHtmlPresenter()->present( $exception ),
93
			$code
94
		) );
95
	}
96
97
	private function isJsonRequest( GetResponseForExceptionEvent $event ): bool {
98
		return $event->getRequest()
99
			->attributes
100
			->get( AddIndicatorAttributeForJsonRequests::REQUEST_IS_JSON_ATTRIBUTE, false );
101
	}
102
103
}
104