FrontController   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 174
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 174
rs 10
c 0
b 0
f 0
wmc 18

6 Methods

Rating   Name   Duplication   Size   Complexity  
A processController() 0 11 2
A __construct() 0 10 1
A processRoute() 0 7 1
A processError() 0 7 2
B processResult() 0 18 7
B dispatch() 0 23 5
1
<?php
2
3
namespace WebComplete\mvc\front;
4
5
use WebComplete\core\utils\container\ContainerInterface;
6
use WebComplete\core\utils\event\Observable;
7
use WebComplete\core\utils\traits\TraitObservable;
8
use WebComplete\mvc\controller\AbstractController;
9
use WebComplete\mvc\router\Route;
10
use WebComplete\mvc\router\Router;
11
use WebComplete\mvc\router\exception\NotAllowedException;
12
use WebComplete\mvc\router\exception\NotFoundException;
13
use Symfony\Component\HttpFoundation\Request;
14
use Symfony\Component\HttpFoundation\Response;
15
use Symfony\Component\HttpFoundation\RedirectResponse;
16
17
class FrontController implements Observable
18
{
19
    use TraitObservable;
20
21
    const ERROR_CONTROLLER_KEY  = 'errorController';
22
    const EVENT_DISPATCH_BEFORE = 'fc_dispatch_before';
23
    const EVENT_DISPATCH_AFTER  = 'fc_dispatch_after';
24
25
    public static $errorActions = [
26
        403 => 'action403',
27
        404 => 'action404',
28
        500 => 'action500',
29
    ];
30
31
    /**
32
     * @var Request
33
     */
34
    protected $request;
35
    /**
36
     * @var Response
37
     */
38
    protected $response;
39
    /**
40
     * @var Router
41
     */
42
    protected $router;
43
    /**
44
     * @var ContainerInterface
45
     */
46
    protected $controllerContainer;
47
48
    /**
49
     * @param Router $router
50
     * @param Request $request
51
     * @param Response $response
52
     * @param ContainerInterface $controllerResolver
53
     */
54
    public function __construct(
55
        Router $router,
56
        Request $request,
57
        Response $response,
58
        ContainerInterface $controllerResolver
59
    ) {
60
        $this->router = $router;
61
        $this->request = $request;
62
        $this->response = $response;
63
        $this->controllerContainer = $controllerResolver;
64
    }
65
66
    /**
67
     * @param string|null $method
68
     * @param string|null $uri
69
     *
70
     * @return Response
71
     * @throws \WebComplete\mvc\router\exception\NotAllowedException
72
     * @throws \UnexpectedValueException
73
     * @throws \InvalidArgumentException
74
     * @throws \Psr\Container\NotFoundExceptionInterface
75
     * @throws \Psr\Container\ContainerExceptionInterface
76
     * @throws \Exception
77
     */
78
    public function dispatch($method = null, $uri = null): Response
79
    {
80
        $method = $method ?? $this->request->getMethod();
81
        $uri = $uri ?? \parse_url($this->request->getRequestUri(), \PHP_URL_PATH);
82
        $eventData = ['method' => $method, 'url' => $this->request->getRequestUri()];
83
        $this->trigger(self::EVENT_DISPATCH_BEFORE, $eventData);
84
85
        try {
86
            $route = $this->router->dispatch($method, $uri);
87
            $this->processRoute($route);
88
        } catch (NotFoundException $e) {
89
            $this->processError($e, 404);
90
        } catch (NotAllowedException $e) {
91
            $this->processError($e, 403);
92
        } catch (\Exception $e) {
93
            if (\ENV === 'dev') {
0 ignored issues
show
Bug introduced by
The constant ENV was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
94
                throw $e;
95
            }
96
            $this->processError($e, 500);
97
        }
98
        $this->response->prepare($this->request);
99
        $this->trigger(self::EVENT_DISPATCH_AFTER, $eventData);
100
        return $this->response;
101
    }
102
103
    /**
104
     * @param Route $route
105
     *
106
     * @throws \InvalidArgumentException
107
     * @throws \UnexpectedValueException
108
     * @throws \Psr\Container\NotFoundExceptionInterface
109
     * @throws \Psr\Container\ContainerExceptionInterface
110
     * @throws \WebComplete\mvc\router\exception\NotAllowedException
111
     */
112
    public function processRoute(Route $route)
113
    {
114
        $controllerClass = $route->getClass();
115
        $actionMethod = $route->getMethod();
116
        /** @var AbstractController $controller */
117
        $controller = $this->controllerContainer->get($controllerClass);
118
        $this->processController($controller, $actionMethod, $route->getParams());
119
    }
120
121
    /**
122
     * @param AbstractController $controller
123
     * @param string $actionMethod
124
     * @param array $params
125
     *
126
     * @throws \Psr\Container\NotFoundExceptionInterface
127
     * @throws \Psr\Container\ContainerExceptionInterface
128
     * @throws \InvalidArgumentException
129
     * @throws \UnexpectedValueException
130
     * @throws \WebComplete\mvc\router\exception\NotAllowedException
131
     */
132
    public function processController(
133
        AbstractController $controller,
134
        string $actionMethod,
135
        array $params = []
136
    ) {
137
        $result = $controller->beforeAction();
138
        if ($result === true) {
0 ignored issues
show
introduced by
The condition $result === true can never be false.
Loading history...
139
            $result = \call_user_func_array([$controller, $actionMethod], $params);
140
            $result = $controller->afterAction($result);
141
        }
142
        $this->processResult($result);
143
    }
144
145
    /**
146
     * @param \Exception|null $exception
147
     * @param int $code
148
     *
149
     * @throws \Psr\Container\NotFoundExceptionInterface
150
     * @throws \Psr\Container\ContainerExceptionInterface
151
     * @throws \UnexpectedValueException
152
     * @throws \InvalidArgumentException
153
     * @throws \WebComplete\mvc\router\exception\NotAllowedException
154
     */
155
    public function processError(\Exception $exception = null, int $code)
156
    {
157
        $this->response->setContent('Page not found');
158
        if ($controller = $this->controllerContainer->get(self::ERROR_CONTROLLER_KEY)) {
159
            $this->processController($controller, self::$errorActions[$code], [$exception]);
160
        }
161
        $this->response->setStatusCode($code);
162
    }
163
164
    /**
165
     * @param $result
166
     *
167
     * @throws \InvalidArgumentException
168
     * @throws \UnexpectedValueException
169
     * @throws \Psr\Container\ContainerExceptionInterface
170
     * @throws \Psr\Container\NotFoundExceptionInterface
171
     * @throws \WebComplete\mvc\router\exception\NotAllowedException
172
     */
173
    protected function processResult($result)
174
    {
175
        if (\is_string($result)) {
176
            $this->response->setStatusCode(200);
177
            $this->response->headers->set('content-type', 'text/html');
178
            $this->response->setContent($result);
179
        } elseif (\is_array($result)) {
180
            $this->response->setStatusCode(200);
181
            $this->response->headers->set('content-type', 'application/json');
182
            $this->response->setContent(\json_encode($result));
183
        } elseif ($result instanceof Response) {
184
            if ($result instanceof RedirectResponse) {
185
                $this->response = $result;
186
            } elseif ($result->getStatusCode() !== 200) {
187
                $this->processError(null, $result->getStatusCode());
188
            }
189
        } elseif ($result === false) {
190
            throw new NotAllowedException('Action is not allowed');
191
        }
192
    }
193
}
194