Completed
Push — 4.0 ( 89799b...3c6496 )
by Marco
02:53
created

Dispatcher::dumpCache()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 29
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 29
rs 8.439
cc 6
eloc 13
nc 6
nop 1
1
<?php namespace Comodojo\Dispatcher;
2
3
use \Psr\Log\LoggerInterface;
4
use \Comodojo\Dispatcher\Components\Configuration;
5
use \Comodojo\Dispatcher\Components\DefaultConfiguration;
6
use \Comodojo\Dispatcher\Components\EventsManager;
7
use \Comodojo\Dispatcher\Components\Timestamp as TimestampTrait;
8
use \Comodojo\Dispatcher\Components\CacheManager as DispatcherCache;
9
use \Comodojo\Dispatcher\Components\LogManager;
10
use \Comodojo\Dispatcher\Request\Model as Request;
11
use \Comodojo\Dispatcher\Router\Model as Router;
12
use \Comodojo\Dispatcher\Response\Model as Response;
13
use \Comodojo\Dispatcher\Extra\Model as Extra;
14
use \Comodojo\Dispatcher\Output\Processor;
15
use \Comodojo\Dispatcher\Events\DispatcherEvent;
16
use \Comodojo\Dispatcher\Events\ServiceEvent;
17
use \Comodojo\Cache\CacheManager;
18
use \Comodojo\Exception\DispatcherException;
19
use \Exception;
20
21
/**
22
 * @package     Comodojo Dispatcher
23
 * @author      Marco Giovinazzi <[email protected]>
24
 * @author      Marco Castiello <[email protected]>
25
 * @license     GPL-3.0+
26
 *
27
 * LICENSE:
28
 *
29
 * This program is free software: you can redistribute it and/or modify
30
 * it under the terms of the GNU Affero General Public License as
31
 * published by the Free Software Foundation, either version 3 of the
32
 * License, or (at your option) any later version.
33
 *
34
 * This program is distributed in the hope that it will be useful,
35
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
36
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37
 * GNU Affero General Public License for more details.
38
 *
39
 * You should have received a copy of the GNU Affero General Public License
40
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
41
 */
42
43
class Dispatcher {
44
45
    use TimestampTrait;
46
47
    private $configuration;
48
49
    private $request;
50
51
    private $router;
52
53
    private $response;
54
55
    private $extra;
56
57
    private $logger;
58
59
    private $cache;
60
61
    private $events;
62
63
    public function __construct(
64
        $configuration = array(),
65
        EventsManager $events = null,
66
        CacheManager $cache = null,
67
        LoggerInterface $logger = null
68
    ) {
69
70
        // starting output buffer
71
        ob_start();
72
73
        // fix current timestamp
74
        $this->setTimestamp();
75
76
        // parsing configuration
77
        $this->configuration = new Configuration( DefaultConfiguration::get() );
78
79
        $this->configuration()->merge($configuration);
80
81
        // init core components
82
        $this->logger = is_null($logger) ? LogManager::create($this->configuration()) : $logger;
83
84
        $this->events = is_null($events) ? new EventsManager($this->logger()) : $events;
85
86
        $this->cache = is_null($cache) ? DispatcherCache::create($this->configuration(), $this->logger()) : $cache;
87
88
        // init models
89
        $this->extra = 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...
90
        
91
        $this->request = new Request($this->configuration(), $this->logger());
92
93
        $this->router = new Router($this->configuration(), $this->logger(), $this->cache(), $this->extra());
94
95
        $this->response = new Response($this->configuration(), $this->logger());
96
97
        // we're ready!
98
        $this->logger()->debug("Dispatcher ready, current date ".date('c', $this->getTimestamp()));
99
100
    }
101
102
    public function configuration() {
103
104
        return $this->configuration;
105
106
    }
107
108
    public function events() {
109
110
        return $this->events;
111
112
    }
113
114
    public function cache() {
115
116
        return $this->cache;
117
118
    }
119
120
    public function request() {
121
122
        return $this->request;
123
124
    }
125
126
    public function router() {
127
128
        return $this->router;
129
130
    }
131
132
    public function response() {
133
134
        return $this->response;
135
136
    }
137
138
    public function extra() {
139
140
        return $this->extra;
141
142
    }
143
144
    public function logger() {
145
146
        return $this->logger;
147
148
    }
149
150
    public function dispatch() {
151
152
        $this->logger()->debug("Starting to dispatch.");
153
154
        $this->logger()->debug("Emitting global dispatcher event.");
155
156
        $this->events()->emit( new DispatcherEvent($this) );
157
158
        if ( $this->configuration()->get('enabled') === false ) {
159
160
            $this->logger()->debug("Dispatcher disabled, shutting down gracefully.");
161
162
            $status = $this->configuration()->get('disabled-status');
163
164
            $content = $this->configuration()->get('disabled-message');
165
166
            $this->response()->status()->set($status);
167
168
            $this->response()->content()->set($content);
169
170
            return $this->shutdown();
171
172
        }
173
174
        $this->events()->emit( $this->emitServiceSpecializedEvents('dispatcher.request') );
175
176
        $this->events()->emit( $this->emitServiceSpecializedEvents('dispatcher.request.'.$this->request()->method()->get()) );
177
178
        $this->events()->emit( $this->emitServiceSpecializedEvents('dispatcher.request.#') );
179
180
        if ( $this->readCache() ) {
181
            
182
            return $this->shutdown();
183
            
184
        }
185
186
        $this->logger()->debug("Starting router.");
187
188
        try {
189
190
            $route = $this->router()->route($this->request());
191
192
        } catch (DispatcherException $de) {
193
194
            $this->logger()->debug("Route error (".$de->getStatus()."), shutting down dispatcher.");
195
196
            $this->processDispatcherException($de);
197
198
            return $this->shutdown();
199
200
        }
201
202
        $route_type = $route->getType();
203
204
        $route_service = $route->getServiceName();
205
206
        $this->logger()->debug("Route acquired, type $route_type directed to $route_service.");
207
208
        $this->events()->emit( $this->emitServiceSpecializedEvents('dispatcher.route') );
209
210
        $this->events()->emit( $this->emitServiceSpecializedEvents('dispatcher.route.'.$route_type) );
211
212
        $this->events()->emit( $this->emitServiceSpecializedEvents('dispatcher.route.'.$route_service) );
213
214
        $this->events()->emit( $this->emitServiceSpecializedEvents('dispatcher.route.#') );
215
216
        // translate route to service
217
218
        $this->logger()->debug("Running $route_service service.");
219
220
        try {
221
222
            $this->router()->compose($this->response());
223
224
        } catch (DispatcherException $de) {
225
226
            $this->logger()->debug("Service exception (".$de->getStatus()."), shutting down dispatcher.");
227
228
            $this->processDispatcherException($de);
229
230
        }
231
        
232
        $this->processConfigurationParameters($route);
233
        
234
        $this->dumpCache($route);
235
236
        return $this->shutdown();
237
238
    }
239
240
    private function readCache() {
241
        
242
        $name = (string) $this->request()->uri();
243
        
244
        $cache = $this->cache()->setNamespace('dispatcher-service')->get($name);
245
        
246
        if ( is_null($cache) ) return false;
247
        
248
        $this->response = $cache;
249
        
250
        return true;
251
        
252
    }
253
    
254
    private function dumpCache($route) {
255
        
256
        $cache = strtoupper($route->getParameter('cache'));
257
        $ttl = $route->getParameter('ttl');
258
        $name = (string) $this->request()->uri();
259
        
260
        if ( $cache == 'CLIENT' || $cache == 'BOTH' ) {
261
            
262
            if ( $ttl > 0 ) {
263
                
264
                $this->response()->headers()->set("Cache-Control","max-age=".$ttl.", must-revalidate");
265
                $this->response()->headers()->set("Expires",gmdate("D, d M Y H:i:s", (int)$this->request()->getTimestamp() + $ttl)." GMT");
266
                
267
            } else {
268
                
269
                $this->response()->headers()->set("Cache-Control","no-cache, must-revalidate");
270
                $this->response()->headers()->set("Expires","Mon, 26 Jul 1997 05:00:00 GMT");
271
                
272
            }
273
            
274
        }
275
        
276
        if ( $cache == 'SERVER' || $cache == 'BOTH' ) {
277
            
278
            $this->cache()->setNamespace('dispatcher-service')->set($name, $this->response(), $ttl);
279
            
280
        }
281
       
282
    }
283
    
284
    private function processConfigurationParameters($route) {
285
        
286
        $params = $route->getParameter('headers');
287
        
288
        if ( !empty($params) && is_array($params) ) {
289
            
290
            foreach($params as $name=>$value) $this->response()->headers()->set($name, $value);
291
        }
292
        
293
    }
294
295
    private function emitServiceSpecializedEvents($name) {
296
297
        $this->logger()->debug("Emitting $name service-event.");
298
299
        return new ServiceEvent(
300
            $name,
301
            $this->logger(),
0 ignored issues
show
Compatibility introduced by
$this->logger() of type object<Psr\Log\LoggerInterface> is not a sub-type of object<Monolog\Logger>. It seems like you assume a concrete implementation of the interface Psr\Log\LoggerInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
302
            $this->request(),
303
            $this->router(),
304
            $this->response(),
305
            $this->extra()
306
        );
307
308
    }
309
310
    private function processDispatcherException(DispatcherException $de) {
311
312
        $status = $de->getStatus();
313
314
        $message = $de->getMessage();
315
316
        //$headers = $de->getHeaders();
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% 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...
317
        $headers = array();
318
319
        $this->response()->status()->set($status);
320
321
        $this->response()->content()->set($message);
322
323
        $this->response()->headers()->merge($headers);
324
325
    }
326
327
    private function shutdown() {
328
329
        $this->events()->emit( $this->emitServiceSpecializedEvents('dispatcher.response') );
330
331
        $this->events()->emit( $this->emitServiceSpecializedEvents('dispatcher.response.'.$this->response()->status()->get()) );
332
333
        $this->events()->emit( $this->emitServiceSpecializedEvents('dispatcher.response.#') );
334
335
        $this->logger()->debug("Composing return value.");
336
337
        $return = Processor::parse($this->configuration(), $this->logger(), $this->response());
0 ignored issues
show
Documentation introduced by
$this->logger() is of type object<Psr\Log\LoggerInterface>, but the function expects a object<Comodojo\Dispatcher\Output\Logger>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
338
339
        $this->logger()->debug("Dispatcher run-cycle ends.");
340
341
        ob_end_clean();
342
343
        return $return;
344
345
    }
346
347
}
348