Passed
Branch 4.0 (5a5e94)
by Marco
11:08
created

Dispatcher::processRedirect()   C

Complexity

Conditions 7
Paths 10

Size

Total Lines 35
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
dl 0
loc 35
c 0
b 0
f 0
ccs 0
cts 24
cp 0
rs 6.7272
cc 7
eloc 23
nc 10
nop 1
crap 56
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 Comodojo\Dispatcher\Extra\Model::__construct() has too many arguments starting with $this->logger. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

99
            $this->setExtra(/** @scrutinizer ignore-call */ new Extra($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. Please note the @ignore annotation hint above.

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':
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':
201
202
                    $logger->debug("Redirecting response...");
203
204
                    $this->processRedirect($this->route);
205
206
                    break;
207
208
                case 'ERROR':
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();
345
        // else ob_end_clean();
346 1
        ob_end_clean();
347
348 1
        return $return;
349
350
    }
351
352
}
353