Completed
Pull Request — master (#2)
by René
06:36 queued 04:32
created

Dispatcher::setLogger()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 2
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
         * Authenticate user from cookie
77
         */
78
        $cookie = $request->getCookie();
79
80
        $userAuthenticator = new UserAuthenticator($this->pdo, $this->configuration);
81
82
        $user = $userAuthenticator->userFromCookie($cookie);
83
84
        /**
85
         * Figure out what controller to use and what action to call
86
         */
87
        $controllerFactory = new ControllerFactory($this->pdo, $this->configuration, $request, $user);
88
89
        try {
90
            /**
91
             * @var Router $router
92
             */
93
            $router = $this->configuration->get('Router');
94
95
            list($controllerName, $action) = array_values($router->route($request->getPath()));
96
97
            /**
98
             * Validate and initialize controller
99
             */
100
            try {
101
                $controller = $controllerFactory->create($controllerName);
102
            } catch (\Exception $e) {
103
                if ($e instanceof ControllerNonexistentException || $e instanceof ControllerInvalidSuperclassException) {
104
                    /**
105
                     * Log invalid superclass
106
                     */
107
                    if ($this->logger && $e instanceof ControllerInvalidSuperclassException) {
108
                        $this->logger->addCritical('Controller must be an subclass of Zortje\MVC\Controller', [
109
                            'path'       => $request->getPath(),
110
                            'controller' => $controllerName
111
                        ]);
112
                    }
113
114
                    /**
115
                     * Log nonexistent
116
                     */
117
                    if ($this->logger && $e instanceof ControllerNonexistentException) {
118
                        $this->logger->addCritical('Controller is nonexistent', [
119
                            'path'       => $request->getPath(),
120
                            'controller' => $controllerName
121
                        ]);
122
                    }
123
124
                    $controller = $controllerFactory->create(NotFoundController::class);
125
                    $action     = 'index';
126
                } else {
127
                    throw $e;
128
                }
129
            }
130
        } catch (RouteNonexistentException $e) {
131
            /**
132
             * Log nonexistent route (404)
133
             */
134
            if ($this->logger) {
135
                $this->logger->addWarning('Route not connected', [
136
                    'path' => $request->getPath()
137
                ]);
138
            }
139
140
            $controller = $controllerFactory->create(NotFoundController::class);
141
            $action     = 'index';
142
        }
143
144
        /**
145
         * Validate and set controller action
146
         */
147
        try {
148
            $controller->setAction($action);
149
        } catch (ControllerActionProtectedInsufficientAuthenticationException $e) {
150
            /**
151
             * Log unauthed protected controller action (403)
152
             */
153 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...
154
                $this->logger->addWarning('Unauthenticated attempt to access protected action', [
155
                    'path'       => $request->getPath(),
156
                    'controller' => $controller->getShortName(),
157
                    'action'     => $action
158
                ]);
159
            }
160
161
            /**
162
             * Save what controller and action was requested and then redirect to sign in form
163
             */
164
            $cookie->set('SignIn.onSuccess.controller', $controller->getShortName());
165
            $cookie->set('SignIn.onSuccess.action', $action);
166
167
            $controller = $controllerFactory->create(SignInsController::class);
168
            $controller->setAction('form');
169
        } catch (ControllerActionPrivateInsufficientAuthenticationException $e) {
170
            /**
171
             * Log unauthed private controller action (403)
172
             */
173 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...
174
                $this->logger->addWarning('Unauthenticated attempt to access private action', [
175
                    'path'       => $request->getPath(),
176
                    'controller' => $controller->getShortName(),
177
                    'action'     => $action
178
                ]);
179
            }
180
181
            $controller = $controllerFactory->create(NotFoundController::class);
182
            $controller->setAction('index');
183
        } catch (ControllerActionNonexistentException $e) {
184
            /**
185
             * Log nonexistent controller action
186
             */
187 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...
188
                $this->logger->addCritical('Controller action is nonexistent', [
189
                    'path'       => $request->getPath(),
190
                    'controller' => $controller->getShortName(),
191
                    'action'     => $action
192
                ]);
193
            }
194
195
            $controller = $controllerFactory->create(NotFoundController::class);
196
            $controller->setAction('index');
197
        }
198
199
        /**
200
         * Create response from controller action headers and output
201
         */
202
        list($headers, $output) = array_values($controller->callAction());
203
204
        /**
205
         * Performance logging
206
         */
207
        if ($this->logger) {
208
            $time = number_format((microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000, 2);
209
210
            $this->logger->addDebug("Dispatched request in $time ms", ['path' => $request->getPath()]);
211
        }
212
213
        return new Response($headers, $output);
214
    }
215
}
216