Completed
Push — 4.0 ( b89114...5a5e94 )
by Marco
02:16
created

Dispatcher::dispatch()   D

Complexity

Conditions 9
Paths 27

Size

Total Lines 110
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 31.0501

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 110
ccs 19
cts 54
cp 0.3519
rs 4.8196
cc 9
eloc 56
nc 27
nop 0
crap 31.0501

How to fix   Long Method   

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 namespace Comodojo\Dispatcher;
2
3
use \Comodojo\Dispatcher\Components\AbstractModel;
4
use \Comodojo\Dispatcher\Components\DefaultConfiguration;
5
use \Comodojo\Dispatcher\Cache\ServerCache;
6
use \Comodojo\Dispatcher\Request\Model as Request;
7
use \Comodojo\Dispatcher\Router\Model as Router;
8
use \Comodojo\Dispatcher\Router\Route;
9
use \Comodojo\Dispatcher\Response\Model as Response;
10
use \Comodojo\Dispatcher\Extra\Model as Extra;
11
use \Comodojo\Dispatcher\Output\Processor;
12
use \Comodojo\Dispatcher\Events\DispatcherEvent;
13
use \Comodojo\Dispatcher\Events\ServiceEvent;
14
use \Comodojo\Dispatcher\Traits\CacheTrait;
15
use \Comodojo\Dispatcher\Traits\RequestTrait;
16
use \Comodojo\Dispatcher\Traits\ResponseTrait;
17
use \Comodojo\Dispatcher\Traits\RouterTrait;
18
use \Comodojo\Dispatcher\Traits\ExtraTrait;
19
use \Comodojo\Foundation\Events\EventsTrait;
20
use \Comodojo\Foundation\Base\Configuration;
21
use \Comodojo\Foundation\Timing\TimingTrait;
22
use \Comodojo\Foundation\Events\Manager as EventsManager;
23
use \Comodojo\Foundation\Logging\Manager as LogManager;
24
use \Comodojo\SimpleCache\Manager as SimpleCacheManager;
25
use \Psr\Log\LoggerInterface;
26
use \Comodojo\Exception\DispatcherException;
27
use \Exception;
28
29
/**
30
 * @package     Comodojo Dispatcher
31
 * @author      Marco Giovinazzi <[email protected]>
32
 * @author      Marco Castiello <[email protected]>
33
 * @license     GPL-3.0+
34
 *
35
 * LICENSE:
36
 *
37
 * This program is free software: you can redistribute it and/or modify
38
 * it under the terms of the GNU Affero General Public License as
39
 * published by the Free Software Foundation, either version 3 of the
40
 * License, or (at your option) any later version.
41
 *
42
 * This program is distributed in the hope that it will be useful,
43
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
44
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
45
 * GNU Affero General Public License for more details.
46
 *
47
 * You should have received a copy of the GNU Affero General Public License
48
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
49
 */
50
51
class Dispatcher extends AbstractModel {
52
53
    use TimingTrait;
54
    use CacheTrait;
55
    use RequestTrait;
56
    use ResponseTrait;
57
    use RouterTrait;
58
    use ExtraTrait;
59
    use EventsTrait;
60
61
    protected $route;
62
63
    /**
64
     * The main dispatcher constructor.
65
     *
66
     */
67 1
    public function __construct(
68
        array $configuration = [],
69
        EventsManager $events = null,
70
        SimpleCacheManager $cache = null,
71
        LoggerInterface $logger = null
72
    ) {
73
74
        // starting output buffer
75 1
        ob_start();
76
77
        // fix current timestamp
78 1
        $this->setTiming();
79
80
        // init core components
81
        // create new configuration object and merge configuration
82 1
        $configuration_object = new Configuration(DefaultConfiguration::get());
83 1
        $configuration_object->merge($configuration);
84
85 1
        $logger = is_null($logger) ? LogManager::createFromConfiguration($configuration_object)->getLogger() : $logger;
86
87 1
        parent::__construct($configuration_object, $logger);
88
89 1
        $this->logger->debug("--------------------------------------------------------");
90 1
        $this->logger->debug("Dispatcher run-cycle starts at ".$this->getTime()->format('c'));
91
92
        try {
93
94
            // init other components
95 1
            $this->setEvents(is_null($events) ? EventsManager::create($this->logger) : $events);
96 1
            $this->setCache(is_null($cache) ? SimpleCacheManager::createFromConfiguration($this->configuration, $this->logger) : $cache);
97
98
            // init models
99 1
            $this->setExtra(new Extra($this->logger));
0 ignored issues
show
Unused Code introduced by
The call to Model::__construct() has too many arguments starting with $this->logger.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
100 1
            $this->setRequest(new Request($this->configuration, $this->logger));
101 1
            $this->setRouter(new Router($this->configuration, $this->logger, $this->cache, $this->events, $this->extra));
102 1
            $this->setResponse(new Response($this->configuration, $this->logger));
103
104 1
        } catch (Exception $e) {
105
106
            $this->logger->critical($e->getMessage(), $e->getTrace());
107
108
            throw $e;
109
110 1
        }
111
112
        // we're ready!
113 1
        $this->logger->debug("Dispatcher ready");
114
115 1
    }
116
117 1
    public function dispatch() {
118
119 1
        $logger = $this->getLogger();
120 1
        $configuration = $this->getConfiguration();
121 1
        $events = $this->getEvents();
122
123 1
        $logger->debug("Starting to dispatch.");
124
125 1
        $logger->debug("Emitting global dispatcher event.");
126 1
        $events->emit(new DispatcherEvent($this));
127
128 1
        if ($configuration->get('enabled') === false) {
129
130
            $logger->debug("Dispatcher disabled, shutting down gracefully.");
131
132
            $status = $configuration->get('disabled-status');
133
            $content = $configuration->get('disabled-message');
134
135
            $this->getResponse()->getStatus()->set($status);
136
            $this->getResponse()->getContent()->set($content);
137
138
            return $this->shutdown();
139
140
        }
141
142 1
        $cache = new ServerCache($this->getCache());
143
144 1
        $events->emit($this->createServiceSpecializedEvents('dispatcher.request'));
145 1
        $events->emit($this->createServiceSpecializedEvents('dispatcher.request.'.$this->request->getMethod()->get()));
146 1
        $events->emit($this->createServiceSpecializedEvents('dispatcher.request.#'));
147
148 1
        if ($cache->read($this->request, $this->response)) {
149
            // we have a cache!
150
            // shutdown immediately
151
            return $this->shutdown();
152
        }
153
154 1
        $logger->debug("Starting router");
155
156
        try {
157
158 1
            $this->route = $this->getRouter()->route($this->request);
159
160 1
        } catch (DispatcherException $de) {
161
162 1
            $logger->debug("Route error (".$de->getStatus()."), shutting down dispatcher");
163 1
            $this->processDispatcherException($de);
164 1
            return $this->shutdown();
165
166
        }
167
168
        $route_type = $this->route->getType();
169
        $route_service = $this->route->getServiceName();
170
171
        $logger->debug("Route acquired, type $route_type");
172
173
        $events->emit($this->createServiceSpecializedEvents('dispatcher.route'));
174
        $events->emit($this->createServiceSpecializedEvents('dispatcher.route.'.$route_type));
175
176
        if ( $route_type == 'ROUTE' ) {
177
            $logger->debug("Route leads to service $route_service");
178
            $events->emit($this->createServiceSpecializedEvents('dispatcher.route.'.$route_service));
179
        }
180
181
        $events->emit($this->createServiceSpecializedEvents('dispatcher.route.#'));
182
183
        try {
184
185
            switch ($route_type) {
186
187
                case 'ROUTE':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
188
189
                    $logger->debug("Running $route_service service");
190
191
                    // translate route to service
192
                    $this->router->compose($this->response);
193
194
                    $this->processConfigurationParameters($this->route);
195
196
                    $cache->dump($this->request, $this->response, $this->route);
197
198
                    break;
199
200
                case 'REDIRECT':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
201
202
                    $logger->debug("Redirecting response...");
203
204
                    $this->processRedirect($this->route);
205
206
                    break;
207
208
                case 'ERROR':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
209
210
                    $logger->debug("Sending error message...");
211
212
                    $this->processError($this->route);
213
214
                    break;
215
            }
216
217
        } catch (DispatcherException $de) {
218
219
            $logger->debug("Service exception (".$de->getStatus()."), shutting down dispatcher");
220
            $this->processDispatcherException($de);
221
222
        }
223
224
        return $this->shutdown();
225
226
    }
227
228
    private function processConfigurationParameters($route) {
229
230
        $params = $route->getParameter('headers');
231
232
        if ( !empty($params) && is_array($params) ) {
233
            foreach($params as $name => $value) {
234
                $this->getResponse()->getHeaders()->set($name, $value);
235
            }
236
        }
237
238
    }
239
240
    private function processRedirect(Route $route) {
241
242
        $status = $this->getResponse()->getStatus();
243
        $content = $this->getResponse()->getContent();
244
        $headers = $this->getResponse()->getHeaders();
245
246
        $code = $route->getRedirectCode();
247
248
        $location = $route->getRedirectLocation();
249
        $uri = empty($location) ? (string) $this->getRequest()->getUri() : $location;
250
251
        $message = $route->getRedirectMessage();
252
253
        if ( $route->getRedirectType() == Route::REDIRECT_REFRESH ) {
254
255
            $output = empty($message) ?
256
                "Please follow <a href=\"$location\">this link</a>"
257
                : $message;
258
259
            $status->set(200);
260
            $headers->set("Refresh", "0;url=$location");
261
            $content->set($output);
262
263
        } else {
264
265
            if ( !empty($code) ) {
266
                $status->set($code);
267
            } else if ( $this->getRequest()->getVersion() == 'HTTP/1.1' ) {
268
                $status->set( (string) $this->getRequest()->getMethod() !== 'GET' ? 303 : 307);
269
            } else {
270
                $status->set(302);
271
            }
272
273
            $content->set($message);
274
            $this->getResponse()->getLocation()->set($uri);
275
276
        }
277
278
    }
279
280
    private function processError(Route $route) {
281
282
        $code = $route->getErrorCode();
283
284
        $code = empty($code) ? 500 : $code;
285
286
        throw new DispatcherException($route->getErrorMessage(), 0, null, $code);
287
288
    }
289
290 1
    private function processDispatcherException(DispatcherException $de) {
291
292 1
        $status = $de->getStatus();
293 1
        $message = $de->getMessage();
294 1
        $headers = $de->getHeaders();
295
296 1
        $response = $this->getResponse();
297
298 1
        $response->getStatus()->set($status);
299 1
        $response->getContent()->set($message);
300 1
        $response->getHeaders()->merge($headers);
301
302 1
    }
303
304
    /**
305
     * @param string $name
306
     */
307 1
    private function createServiceSpecializedEvents($name) {
308
309 1
        $this->logger->debug("Emitting $name service-event");
310
311 1
        return new ServiceEvent(
312 1
            $name,
313 1
            $this->getConfiguration(),
314 1
            $this->getLogger(),
315 1
            $this->getCache(),
316 1
            $this->getEvents(),
317 1
            $this->getRequest(),
318 1
            $this->getRouter(),
319 1
            $this->getResponse(),
320 1
            $this->getExtra()
321 1
        );
322
323
    }
324
325 1
    private function shutdown() {
326
327 1
        $events = $this->getEvents();
328 1
        $request = $this->getRequest();
329 1
        $response = $this->getResponse();
330
331 1
        $response->consolidate($request, $this->route);
332
333 1
        $events->emit($this->createServiceSpecializedEvents('dispatcher.response'));
334 1
        $events->emit($this->createServiceSpecializedEvents('dispatcher.response.'.$response->getStatus()->get()));
335 1
        $events->emit($this->createServiceSpecializedEvents('dispatcher.response.#'));
336
337 1
        $this->logger->debug("Composing return value");
338
339 1
        $return = Processor::parse($this->getConfiguration(), $this->logger, $request, $response);
340
341 1
        $this->logger->debug("Dispatcher run-cycle ends");
342
343
        // This could cause WSOD with some PHP-FPM configurations
344
        // if ( function_exists('fastcgi_finish_request') ) fastcgi_finish_request();
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
345
        // else ob_end_clean();
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
346 1
        ob_end_clean();
347
348 1
        return $return;
349
350
    }
351
352
}
353