Completed
Push — master ( 97f6b5...9ef69c )
by Matze
08:25
created

AppKernel   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 153
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 94.23%

Importance

Changes 0
Metric Value
wmc 14
lcom 1
cbo 9
dl 0
loc 153
ccs 49
cts 52
cp 0.9423
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 13 1
B handle() 0 26 2
A handleRequest() 0 23 3
A prepareResponse() 0 12 3
A applyResponseMiddleware() 0 8 2
A applyExceptionMiddleware() 0 11 3
1
<?php
2
3
namespace BrainExe\Core\Application;
4
5
use BrainExe\Core\Annotations\Service;
6
use BrainExe\Core\EventDispatcher\DelayedCallable;
7
use BrainExe\Core\EventDispatcher\EventDispatcher;
8
use BrainExe\Core\Middleware\MiddlewareInterface;
9
use Iterator;
10
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
11
use Throwable;
12
use Symfony\Component\HttpFoundation\JsonResponse;
13
use Symfony\Component\HttpFoundation\Request;
14
use Symfony\Component\HttpFoundation\Response;
15
use Symfony\Component\HttpKernel\HttpKernelInterface;
16
17
/**
18
 * @Service(public=true)
19
 */
20
class AppKernel implements HttpKernelInterface
21
{
22
23
    /**
24
     * @var ControllerResolver
25
     */
26
    private $resolver;
27
28
    /**
29
     * @var SerializedRouteCollection
30
     */
31
    private $routes;
32
33
    /**
34
     * @var UrlMatcher
35
     */
36
    private $urlMatcher;
37
38
    /**
39
     * @var MiddlewareInterface[]
40
     */
41
    private $middlewares;
42
    /**
43
     * @var EventDispatcher
44
     */
45
    private $dispatcher;
46
47
    /**
48
     * @param ControllerResolver $resolver
49
     * @param SerializedRouteCollection $routes
50
     * @param UrlMatcher $urlMatcher
51
     * @param EventDispatcher $dispatcher
52
     * @param MiddlewareInterface[] $middlewares
53
     */
54 3
    public function __construct(
55
        ControllerResolver $resolver,
56
        SerializedRouteCollection $routes,
57
        UrlMatcher $urlMatcher,
58
        EventDispatcher $dispatcher,
59
        array $middlewares
60
    ) {
61 3
        $this->resolver    = $resolver;
62 3
        $this->routes      = $routes;
63 3
        $this->urlMatcher  = $urlMatcher;
64 3
        $this->middlewares = $middlewares;
65 3
        $this->dispatcher  = $dispatcher;
66 3
    }
67
68
    /**
69
     * {@inheritdoc}
70
     */
71 3
    public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true)
72
    {
73 3
        $response = null;
74
        try {
75 3
            $response = $this->handleRequest($request);
76 1
        } catch (Throwable $exception) {
77 1
            $response = $this->applyExceptionMiddleware($request, $exception);
78
        }
79
80 3
        $response = $this->prepareResponse($response);
81
82 3
        $this->applyResponseMiddleware($request, $response);
83
84 3
        $response->send();
85
86 3
        $this->dispatcher->dispatch(
87 3
            DelayedCallable::FINISHED_EVENT,
88 3
            new FinishRequestEvent(
89
                $this,
90
                $request,
91 3
                HttpKernelInterface::MASTER_REQUEST
92
            )
93
        );
94
95 3
        return $response;
96
    }
97
98
    /**
99
     * @param Request $request
100
     * @return Response|mixed
101
     */
102 3
    private function handleRequest(Request $request)
103
    {
104
        // match route and set attributes in request object
105 3
        $attributes = $this->urlMatcher->match($request);
106 2
        $request->attributes->add($attributes);
107
108 2
        $routeName = $attributes['_route'];
109 2
        $route     = $this->routes->get($routeName);
110
111 2
        foreach ($this->middlewares as $middleware) {
112 2
            $response = $middleware->processRequest($request, $route);
113 2
            if ($response) {
114
                // e.g. RedirectResponse or rendered error page
115 1
                return $response;
116
            }
117
        }
118
119
        /** @var callable $callable */
120 1
        $callable  = $this->resolver->getController($request);
121 1
        $arguments = $this->resolver->getArguments($request, $callable);
122
123 1
        return $callable(...$arguments);
124
    }
125
126
    /**
127
     * @param Response|mixed $response
128
     * @return Response
129
     */
130 3
    private function prepareResponse($response) : Response
131
    {
132 3
        if ($response instanceof Iterator) {
133 1
            $response = iterator_to_array($response);
134
        }
135
136 3
        if (!$response instanceof Response) {
137 1
            return new JsonResponse($response);
138
        }
139
140 2
        return $response;
141
    }
142
143
    /**
144
     * @param Request $request
145
     * @param Response $response
146
     */
147 3
    protected function applyResponseMiddleware(Request $request, Response $response) : void
148
    {
149 3
        $middlewareIdx = count($this->middlewares) - 1;
150 3
        for ($i = $middlewareIdx; $i >= 0; $i--) {
151 3
            $middleware = $this->middlewares[$i];
152 3
            $middleware->processResponse($request, $response);
153
        }
154 3
    }
155
156
    /**
157
     * @param Request $request
158
     * @param Throwable $exception
159
     * @return Response|null
160
     */
161 1
    protected function applyExceptionMiddleware(Request $request, Throwable $exception) : ?Response
162
    {
163 1
        foreach ($this->middlewares as $middleware) {
164 1
            $response = $middleware->processException($request, $exception);
165 1
            if ($response !== null) {
166 1
                return $response;
167
            }
168
        }
169
170
        return null;
171
    }
172
}
173