Completed
Push — 4.0 ( b44693...cafdfc )
by Marco
02:39
created

Dispatcher::processDispatcherException()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 0
cts 9
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 8
nc 1
nop 1
crap 2
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\Response\Model as Response;
9
use \Comodojo\Dispatcher\Extra\Model as Extra;
10
use \Comodojo\Dispatcher\Output\Processor;
11
use \Comodojo\Dispatcher\Events\DispatcherEvent;
12
use \Comodojo\Dispatcher\Events\ServiceEvent;
13
use \Comodojo\Dispatcher\Traits\CacheTrait;
14
use \Comodojo\Dispatcher\Traits\RequestTrait;
15
use \Comodojo\Dispatcher\Traits\ResponseTrait;
16
use \Comodojo\Dispatcher\Traits\RouterTrait;
17
use \Comodojo\Dispatcher\Traits\ExtraTrait;
18
use \Comodojo\Dispatcher\Traits\EventsTrait;
19
use \Comodojo\Foundation\Base\Configuration;
20
use \Comodojo\Foundation\Timing\TimingTrait;
21
use \Comodojo\Foundation\Events\Manager as EventsManager;
22
use \Comodojo\Foundation\Logging\Manager as LogManager;
23
use \Comodojo\SimpleCache\Manager as SimpleCacheManager;
24
use \Psr\Log\LoggerInterface;
25
use \Comodojo\Exception\DispatcherException;
26
use \Exception;
27
28
/**
29
 * @package     Comodojo Dispatcher
30
 * @author      Marco Giovinazzi <[email protected]>
31
 * @author      Marco Castiello <[email protected]>
32
 * @license     GPL-3.0+
33
 *
34
 * LICENSE:
35
 *
36
 * This program is free software: you can redistribute it and/or modify
37
 * it under the terms of the GNU Affero General Public License as
38
 * published by the Free Software Foundation, either version 3 of the
39
 * License, or (at your option) any later version.
40
 *
41
 * This program is distributed in the hope that it will be useful,
42
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
43
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
44
 * GNU Affero General Public License for more details.
45
 *
46
 * You should have received a copy of the GNU Affero General Public License
47
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
48
 */
49
50
class Dispatcher extends AbstractModel {
51
52
    use TimingTrait;
53
    use CacheTrait;
54
    use RequestTrait;
55
    use ResponseTrait;
56
    use RouterTrait;
57
    use ExtraTrait;
58
    use EventsTrait;
59
60
    protected $route;
61
62
    /**
63
     * The main dispatcher constructor.
64
     *
65
     */
66
    public function __construct(
67
        array $configuration = [],
68
        EventsManager $events = null,
69
        SimpleCacheManager $cache = null,
70
        LoggerInterface $logger = null
71
    ) {
72
73
        // starting output buffer
74
        ob_start();
75
76
        // fix current timestamp
77
        $this->setTiming();
78
79
        // init core components
80
        // create new configuration object and merge configuration
81
        $configuration_object = new Configuration( DefaultConfiguration::get() );
82
        $configuration_object->merge($configuration);
83
84
        $logger = is_null($logger) ? LogManager::createFromConfiguration($configuration_object)->getLogger() : $logger;
85
86
        parent::__construct($configuration_object, $logger);
87
88
        $this->logger->debug("--------------------------------------------------------");
89
        $this->logger->debug("Dispatcher run-cycle starts at ".$this->getTime()->format('c'));
90
91
        try {
92
93
            // init other components
94
            $this->setEvents(is_null($events) ? EventsManager::create($this->logger) : $events);
95
            $this->setCache(is_null($cache) ? SimpleCacheManager::createFromConfiguration($this->configuration, $this->logger) : $cache);
96
97
            // init models
98
            $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...
99
            $this->setRequest(new Request($this->configuration, $this->logger));
100
            $this->setRouter(new Router($this->configuration, $this->logger, $this->cache, $this->events, $this->extra));
101
            $this->setResponse(new Response($this->configuration, $this->logger));
102
103
        } catch (Exception $e) {
104
105
            $this->logger->critical($e->getMessage(),$e->getTrace());
106
107
            throw $e;
108
109
        }
110
111
        // we're ready!
112
        $this->logger->debug("Dispatcher ready");
113
114
    }
115
116
    public function dispatch() {
117
118
        $logger = $this->getLogger();
119
        $configuration = $this->getConfiguration();
120
        $events = $this->getEvents();
121
122
        $logger->debug("Starting to dispatch.");
123
124
        $logger->debug("Emitting global dispatcher event.");
125
        $events->emit( new DispatcherEvent($this) );
126
127
        if ( $configuration->get('enabled') === false ) {
128
129
            $logger->debug("Dispatcher disabled, shutting down gracefully.");
130
131
            $status = $configuration->get('disabled-status');
132
            $content = $configuration->get('disabled-message');
133
134
            $this->getResponse()->getStatus()->set($status);
135
            $this->getResponse()->getContent()->set($content);
136
137
            return $this->shutdown();
138
139
        }
140
141
        $cache = new ServerCache($this->getCache());
142
143
        $events->emit( $this->createServiceSpecializedEvents('dispatcher.request') );
144
        $events->emit( $this->createServiceSpecializedEvents('dispatcher.request.'.$this->request->method->get()) );
0 ignored issues
show
Bug introduced by
The property method cannot be accessed from this context as it is declared protected in class Comodojo\Dispatcher\Request\Model.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
145
        $events->emit( $this->createServiceSpecializedEvents('dispatcher.request.#') );
146
147
        if ( $cache->read($this->request, $this->response) ) {
148
            // we have a cache!
149
            // shutdown immediately
150
            return $this->shutdown();
151
        }
152
153
        $logger->debug("Starting router");
154
155
        try {
156
157
            $this->route = $this->getRouter()->route($this->request);
158
159
        } catch (DispatcherException $de) {
160
161
            $logger->debug("Route error (".$de->getStatus()."), shutting down dispatcher");
162
            $this->processDispatcherException($de);
163
            return $this->shutdown();
164
165
        }
166
167
        $route_type = $this->route->getType();
168
169
        $route_service = $this->route->getServiceName();
170
171
        $logger->debug("Route acquired, type $route_type directed to $route_service");
172
173
        $events->emit( $this->createServiceSpecializedEvents('dispatcher.route') );
174
        $events->emit( $this->createServiceSpecializedEvents('dispatcher.route.'.$route_type) );
175
        $events->emit( $this->createServiceSpecializedEvents('dispatcher.route.'.$route_service) );
176
        $events->emit( $this->createServiceSpecializedEvents('dispatcher.route.#') );
177
178
        // translate route to service
179
        $logger->debug("Running $route_service service");
180
181
        try {
182
183
            $this->router->compose($this->response);
184
185
        } catch (DispatcherException $de) {
186
187
            $logger->debug("Service exception (".$de->getStatus()."), shutting down dispatcher");
188
            $this->processDispatcherException($de);
189
190
        }
191
192
        $this->processConfigurationParameters($this->route);
193
194
        $cache->dump($this->request, $this->response, $this->route);
195
196
        return $this->shutdown();
197
198
    }
199
200
    private function processConfigurationParameters($route) {
201
202
        $params = $route->getParameter('headers');
203
204
        if ( !empty($params) && is_array($params) ) {
205
            foreach($params as $name => $value) $this->getResponse()->getHeaders()->set($name, $value);
206
        }
207
208
    }
209
210
    private function createServiceSpecializedEvents($name) {
211
212
        $this->logger->debug("Emitting $name service-event");
213
214
        return new ServiceEvent(
215
            $name,
216
            $this->getConfiguration(),
217
            $this->getLogger(),
218
            $this->getCache(),
219
            $this->getEvents(),
220
            $this->getRequest(),
221
            $this->getRouter(),
222
            $this->getResponse(),
223
            $this->getExtra()
224
        );
225
226
    }
227
228
    private function processDispatcherException(DispatcherException $de) {
229
230
        $status = $de->getStatus();
231
        $message = $de->getMessage();
232
        $headers = $de->getHeaders();
233
234
        $response = $this->getResponse();
235
236
        $response->getStatus()->set($status);
237
        $response->getContent()->set($message);
238
        $response->getHeaders()->merge($headers);
239
240
    }
241
242
    private function shutdown() {
243
244
        $events = $this->getEvents();
245
        $request = $this->getRequest();
246
        $response = $this->getResponse();
247
248
        $response->consolidate($request, $this->route);
249
250
        $events->emit( $this->createServiceSpecializedEvents('dispatcher.response') );
251
        $events->emit( $this->createServiceSpecializedEvents('dispatcher.response.' . $response->getStatus()->get()) );
252
        $events->emit( $this->createServiceSpecializedEvents('dispatcher.response.#') );
253
254
        $this->logger->debug("Composing return value");
255
256
        $return = Processor::parse($this->getConfiguration(), $this->logger, $request, $response);
257
258
        $this->logger->debug("Dispatcher run-cycle ends");
259
260
        // This could cause WSOD with some PHP-FPM configurations
261
        // 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...
262
        // 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...
263
        ob_end_clean();
264
265
        return $return;
266
267
    }
268
269
}
270