Completed
Pull Request — master (#2)
by René
05:31 queued 03:25
created

Dispatcher::dispatch()   F

Complexity

Conditions 17
Paths 378

Size

Total Lines 135
Code Lines 61

Duplication

Lines 21
Ratio 15.56 %

Code Coverage

Tests 0
CRAP Score 306

Importance

Changes 15
Bugs 0 Features 0
Metric Value
c 15
b 0
f 0
dl 21
loc 135
ccs 0
cts 80
cp 0
rs 3.6909
cc 17
eloc 61
nc 378
nop 1
crap 306

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types = 1);
3
4
namespace Zortje\MVC\Routing;
5
6
use Monolog\Logger;
7
use Zortje\MVC\Configuration\Configuration;
8
use Zortje\MVC\Controller\ControllerFactory;
9
use Zortje\MVC\Controller\Exception\ControllerActionNonexistentException;
10
use Zortje\MVC\Controller\Exception\ControllerActionPrivateInsufficientAuthenticationException;
11
use Zortje\MVC\Controller\Exception\ControllerActionProtectedInsufficientAuthenticationException;
12
use Zortje\MVC\Controller\Exception\ControllerInvalidSuperclassException;
13
use Zortje\MVC\Controller\Exception\ControllerNonexistentException;
14
use Zortje\MVC\Controller\SignInsController;
15
use Zortje\MVC\Controller\NotFoundController;
16
use Zortje\MVC\Network\Request;
17
use Zortje\MVC\Network\Response;
18
use Zortje\MVC\Routing\Exception\RouteNonexistentException;
19
use Zortje\MVC\User\UserAuthenticator;
20
21
/**
22
 * Class Dispatcher
23
 *
24
 * @package Zortje\MVC\Routing
25
 */
26
class Dispatcher
27
{
28
29
    /**
30
     * @var \PDO PDO
31
     */
32
    protected $pdo;
33
34
    /**
35
     * @var Configuration Configuration
36
     */
37
    protected $configuration;
38
39
    /**
40
     * @var Logger
41
     */
42
    protected $logger;
43
44
    /**
45
     * Dispatcher constructor.
46
     *
47
     * @param \PDO          $pdo
48
     * @param Configuration $configuration
49
     */
50
    public function __construct(\PDO $pdo, Configuration $configuration)
51
    {
52
        $this->pdo           = $pdo;
53
        $this->configuration = $configuration;
54
    }
55
56
    /**
57
     * Set logger to be used for any logging that could occure in the dispatching process
58
     *
59
     * @param Logger $logger
60
     */
61
    public function setLogger(Logger $logger)
62
    {
63
        $this->logger = $logger;
64
    }
65
66
    /**
67
     * @param Request $request Request object
68
     *
69
     * @return Response Reponse object
70
     *
71
     * @throws \Exception If unexpected exception is thrown
72
     */
73
    public function dispatch(Request $request): Response
74
    {
75
        /**
76
         * Figure out what controller to use and what action to call
77
         */
78
        $controllerFactory = new ControllerFactory($this->pdo, $this->configuration, $request, $this->getUserFromRequest($request));
0 ignored issues
show
Bug introduced by
It seems like $this->getUserFromRequest($request) targeting Zortje\MVC\Routing\Dispa...r::getUserFromRequest() can also be of type object<Zortje\MVC\Model\Table\Entity\Entity>; however, Zortje\MVC\Controller\Co...rFactory::__construct() does only seem to accept null|object<Zortje\MVC\User\User>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
79
80
        try {
81
            /**
82
             * @var Router $router
83
             */
84
            $router = $this->configuration->get('Router');
85
86
            list($controllerName, $action) = array_values($router->route($request->getPath()));
87
88
            /**
89
             * Validate and initialize controller
90
             */
91
            try {
92
                $controller = $controllerFactory->create($controllerName);
93
            } catch (\Exception $e) {
94
                if ($e instanceof ControllerNonexistentException || $e instanceof ControllerInvalidSuperclassException) {
95
                    /**
96
                     * Log invalid superclass
97
                     */
98
                    if ($this->logger && $e instanceof ControllerInvalidSuperclassException) {
99
                        $this->logger->addCritical('Controller must be an subclass of Zortje\MVC\Controller', [
100
                            'path'       => $request->getPath(),
101
                            'controller' => $controllerName
102
                        ]);
103
                    }
104
105
                    /**
106
                     * Log nonexistent
107
                     */
108
                    if ($this->logger && $e instanceof ControllerNonexistentException) {
109
                        $this->logger->addCritical('Controller is nonexistent', [
110
                            'path'       => $request->getPath(),
111
                            'controller' => $controllerName
112
                        ]);
113
                    }
114
115
                    $controller = $controllerFactory->create(NotFoundController::class);
116
                    $action     = 'index';
117
                } else {
118
                    throw $e;
119
                }
120
            }
121
        } catch (RouteNonexistentException $e) {
122
            /**
123
             * Log nonexistent route (404)
124
             */
125
            if ($this->logger) {
126
                $this->logger->addWarning('Route not connected', [
127
                    'path' => $request->getPath()
128
                ]);
129
            }
130
131
            $controller = $controllerFactory->create(NotFoundController::class);
132
            $action     = 'index';
133
        }
134
135
        /**
136
         * Validate and set controller action
137
         */
138
        try {
139
            $controller->setAction($action);
140
        } catch (ControllerActionProtectedInsufficientAuthenticationException $e) {
141
            /**
142
             * Log unauthed protected controller action (403)
143
             */
144 View Code Duplication
            if ($this->logger) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
145
                $this->logger->addWarning('Unauthenticated attempt to access protected action', [
146
                    'path'       => $request->getPath(),
147
                    'controller' => $controller->getShortName(),
148
                    'action'     => $action
149
                ]);
150
            }
151
152
            /**
153
             * Save what controller and action was requested and then redirect to sign in form
154
             */
155
            $cookie = $request->getCookie();
156
157
            $cookie->set('SignIn.onSuccess.controller', $controller->getShortName());
158
            $cookie->set('SignIn.onSuccess.action', $action);
159
160
            $controller = $controllerFactory->create(SignInsController::class);
161
            $controller->setAction('form');
162
        } catch (ControllerActionPrivateInsufficientAuthenticationException $e) {
163
            /**
164
             * Log unauthed private controller action (403)
165
             */
166 View Code Duplication
            if ($this->logger) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
167
                $this->logger->addWarning('Unauthenticated attempt to access private action', [
168
                    'path'       => $request->getPath(),
169
                    'controller' => $controller->getShortName(),
170
                    'action'     => $action
171
                ]);
172
            }
173
174
            $controller = $controllerFactory->create(NotFoundController::class);
175
            $controller->setAction('index');
176
        } catch (ControllerActionNonexistentException $e) {
177
            /**
178
             * Log nonexistent controller action
179
             */
180 View Code Duplication
            if ($this->logger) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
181
                $this->logger->addCritical('Controller action is nonexistent', [
182
                    'path'       => $request->getPath(),
183
                    'controller' => $controller->getShortName(),
184
                    'action'     => $action
185
                ]);
186
            }
187
188
            $controller = $controllerFactory->create(NotFoundController::class);
189
            $controller->setAction('index');
190
        }
191
192
        /**
193
         * Create response from controller action headers and output
194
         */
195
        list($headers, $output) = array_values($controller->callAction());
196
197
        /**
198
         * Performance logging
199
         */
200
        if ($this->logger) {
201
            $time = number_format((microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000, 2);
202
203
            $this->logger->addDebug("Dispatched request in $time ms", ['path' => $request->getPath()]);
204
        }
205
206
        return new Response($headers, $request->getCookie(), $output);
207
    }
208
209
    protected function getUserFromRequest(Request $request)
210
    {
211
        /**
212
         * Authenticate user from cookie
213
         */
214
        $cookie = $request->getCookie();
215
216
        $userAuthenticator = new UserAuthenticator($this->pdo, $this->configuration);
217
218
        $user = $userAuthenticator->userFromCookie($cookie);
219
220
        return $user;
221
    }
222
}
223