Completed
Push — master ( 465cbd...e0d13e )
by Marco
06:44 queued 11s
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     MIT
34
 *
35
 * LICENSE:
36
 *
37
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
39
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
40
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
41
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
42
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
43
 * THE SOFTWARE.
44
 */
45
46
class Dispatcher extends AbstractModel {
47
48
    use TimingTrait;
49
    use CacheTrait;
50
    use RequestTrait;
51
    use ResponseTrait;
52
    use RouterTrait;
53
    use ExtraTrait;
54
    use EventsTrait;
55
56
    protected $route;
57
58
    /**
59
     * The main dispatcher constructor.
60
     *
61
     */
62 1
    public function __construct(
63
        array $configuration = [],
64
        EventsManager $events = null,
65
        SimpleCacheManager $cache = null,
66
        LoggerInterface $logger = null
67
    ) {
68
69
        // starting output buffer
70 1
        ob_start();
71
72
        // fix current timestamp
73 1
        $this->setTiming();
74
75
        // init core components
76
        // create new configuration object and merge configuration
77 1
        $configuration_object = new Configuration(DefaultConfiguration::get());
78 1
        $configuration_object->merge($configuration);
79
80 1
        $logger = is_null($logger) ? LogManager::createFromConfiguration($configuration_object)->getLogger() : $logger;
81
82 1
        parent::__construct($configuration_object, $logger);
83
84 1
        $this->logger->debug("--------------------------------------------------------");
85 1
        $this->logger->debug("Dispatcher run-cycle starts at ".$this->getTime()->format('c'));
86
87
        try {
88
89
            // init other components
90 1
            $this->setEvents(is_null($events) ? EventsManager::create($this->logger) : $events);
91 1
            $this->setCache(is_null($cache) ? SimpleCacheManager::createFromConfiguration($this->configuration, $this->logger) : $cache);
92
93
            // init models
94 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

94
            $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...
95 1
            $this->setRequest(new Request($this->configuration, $this->logger));
96 1
            $this->setRouter(new Router($this->configuration, $this->logger, $this->cache, $this->events, $this->extra));
97 1
            $this->setResponse(new Response($this->configuration, $this->logger));
98
99 1
        } catch (Exception $e) {
100
101
            $this->logger->critical($e->getMessage(), $e->getTrace());
102
103
            throw $e;
104
105 1
        }
106
107
        // we're ready!
108 1
        $this->logger->debug("Dispatcher ready");
109
110 1
    }
111
112 1
    public function dispatch() {
113
114 1
        $logger = $this->getLogger();
115 1
        $configuration = $this->getConfiguration();
116 1
        $events = $this->getEvents();
117
118 1
        $logger->debug("Starting to dispatch.");
119
120 1
        $logger->debug("Emitting global dispatcher event.");
121 1
        $events->emit(new DispatcherEvent($this));
122
123 1
        if ($configuration->get('enabled') === false) {
124
125
            $logger->debug("Dispatcher disabled, shutting down gracefully.");
126
127
            $status = $configuration->get('disabled-status');
128
            $content = $configuration->get('disabled-message');
129
130
            $this->getResponse()->getStatus()->set($status);
131
            $this->getResponse()->getContent()->set($content);
132
133
            return $this->shutdown();
134
135
        }
136
137 1
        $cache = new ServerCache($this->getCache());
138
139 1
        $events->emit($this->createServiceSpecializedEvents('dispatcher.request'));
140 1
        $events->emit($this->createServiceSpecializedEvents('dispatcher.request.'.$this->request->getMethod()->get()));
141 1
        $events->emit($this->createServiceSpecializedEvents('dispatcher.request.#'));
142
143 1
        if ($cache->read($this->request, $this->response)) {
144
            // we have a cache!
145
            // shutdown immediately
146
            return $this->shutdown();
147
        }
148
149 1
        $logger->debug("Starting router");
150
151
        try {
152
153 1
            $this->route = $this->getRouter()->route($this->request);
154
155 1
        } catch (DispatcherException $de) {
156
157 1
            $logger->debug("Route error (".$de->getStatus()."), shutting down dispatcher");
158 1
            $this->processDispatcherException($de);
159 1
            return $this->shutdown();
160
161
        }
162
163
        $route_type = $this->route->getType();
164
        $route_service = $this->route->getServiceName();
165
166
        $logger->debug("Route acquired, type $route_type");
167
168
        $events->emit($this->createServiceSpecializedEvents('dispatcher.route'));
169
        $events->emit($this->createServiceSpecializedEvents('dispatcher.route.'.$route_type));
170
171
        if ( $route_type == 'ROUTE' ) {
172
            $logger->debug("Route leads to service $route_service");
173
            $events->emit($this->createServiceSpecializedEvents('dispatcher.route.'.$route_service));
174
        }
175
176
        $events->emit($this->createServiceSpecializedEvents('dispatcher.route.#'));
177
178
        try {
179
180
            switch ($route_type) {
181
182
                case 'ROUTE':
183
184
                    $logger->debug("Running $route_service service");
185
186
                    // translate route to service
187
                    $this->router->compose($this->response);
188
189
                    $this->processConfigurationParameters($this->route);
190
191
                    $cache->dump($this->request, $this->response, $this->route);
192
193
                    break;
194
195
                case 'REDIRECT':
196
197
                    $logger->debug("Redirecting response...");
198
199
                    $this->processRedirect($this->route);
200
201
                    break;
202
203
                case 'ERROR':
204
205
                    $logger->debug("Sending error message...");
206
207
                    $this->processError($this->route);
208
209
                    break;
210
            }
211
212
        } catch (DispatcherException $de) {
213
214
            $logger->debug("Service exception (".$de->getStatus()."), shutting down dispatcher");
215
            $this->processDispatcherException($de);
216
217
        }
218
219
        return $this->shutdown();
220
221
    }
222
223
    private function processConfigurationParameters($route) {
224
225
        $params = $route->getParameter('headers');
226
227
        if ( !empty($params) && is_array($params) ) {
228
            foreach($params as $name => $value) {
229
                $this->getResponse()->getHeaders()->set($name, $value);
230
            }
231
        }
232
233
    }
234
235
    private function processRedirect(Route $route) {
236
237
        $status = $this->getResponse()->getStatus();
238
        $content = $this->getResponse()->getContent();
239
        $headers = $this->getResponse()->getHeaders();
240
241
        $code = $route->getRedirectCode();
242
243
        $location = $route->getRedirectLocation();
244
        $uri = empty($location) ? (string) $this->getRequest()->getUri() : $location;
245
246
        $message = $route->getRedirectMessage();
247
248
        if ( $route->getRedirectType() == Route::REDIRECT_REFRESH ) {
249
250
            $output = empty($message) ?
251
                "Please follow <a href=\"$location\">this link</a>"
252
                : $message;
253
254
            $status->set(200);
255
            $headers->set("Refresh", "0;url=$location");
256
            $content->set($output);
257
258
        } else {
259
260
            if ( !empty($code) ) {
261
                $status->set($code);
262
            } else if ( $this->getRequest()->getVersion() == 'HTTP/1.1' ) {
263
                $status->set( (string) $this->getRequest()->getMethod() !== 'GET' ? 303 : 307);
264
            } else {
265
                $status->set(302);
266
            }
267
268
            $content->set($message);
269
            $this->getResponse()->getLocation()->set($uri);
270
271
        }
272
273
    }
274
275
    private function processError(Route $route) {
276
277
        $code = $route->getErrorCode();
278
279
        $code = empty($code) ? 500 : $code;
280
281
        throw new DispatcherException($route->getErrorMessage(), 0, null, $code);
282
283
    }
284
285 1
    private function processDispatcherException(DispatcherException $de) {
286
287 1
        $status = $de->getStatus();
288 1
        $message = $de->getMessage();
289 1
        $headers = $de->getHeaders();
290
291 1
        $response = $this->getResponse();
292
293 1
        $response->getStatus()->set($status);
294 1
        $response->getContent()->set($message);
295 1
        $response->getHeaders()->merge($headers);
296
297 1
    }
298
299
    /**
300
     * @param string $name
301
     */
302 1
    private function createServiceSpecializedEvents($name) {
303
304 1
        $this->logger->debug("Emitting $name service-event");
305
306 1
        return new ServiceEvent(
307 1
            $name,
308 1
            $this->getConfiguration(),
309 1
            $this->getLogger(),
310 1
            $this->getCache(),
311 1
            $this->getEvents(),
312 1
            $this->getRequest(),
313 1
            $this->getRouter(),
314 1
            $this->getResponse(),
315 1
            $this->getExtra()
316 1
        );
317
318
    }
319
320 1
    private function shutdown() {
321
322 1
        $events = $this->getEvents();
323 1
        $request = $this->getRequest();
324 1
        $response = $this->getResponse();
325
326 1
        $response->consolidate($request, $this->route);
327
328 1
        $events->emit($this->createServiceSpecializedEvents('dispatcher.response'));
329 1
        $events->emit($this->createServiceSpecializedEvents('dispatcher.response.'.$response->getStatus()->get()));
330 1
        $events->emit($this->createServiceSpecializedEvents('dispatcher.response.#'));
331
332 1
        $this->logger->debug("Composing return value");
333
334 1
        $return = Processor::parse($this->getConfiguration(), $this->logger, $request, $response);
335
336 1
        $this->logger->debug("Dispatcher run-cycle ends");
337
338
        // This could cause WSOD with some PHP-FPM configurations
339
        // if ( function_exists('fastcgi_finish_request') ) fastcgi_finish_request();
340
        // else ob_end_clean();
341 1
        ob_end_clean();
342
343 1
        return $return;
344
345
    }
346
347
}
348