Completed
Push — master ( 2c1559...dd108d )
by Marco
13:03
created

Dispatcher::end()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 13
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 13
rs 9.4286
cc 3
eloc 5
nc 2
nop 1
1
<?php namespace Comodojo\Dispatcher;
2
3
use \Comodojo\Dispatcher\Debug;
4
use \Comodojo\Exception\DispatcherException;
5
use \Comodojo\Exception\IOException;
6
use \Exception;
7
use \Comodojo\Dispatcher\ObjectRequest\ObjectRequest;
8
use \Comodojo\Dispatcher\ObjectRoutingTable\ObjectRoutingTable;
9
use \Comodojo\Dispatcher\ObjectRoute\ObjectRoute;
10
use \Comodojo\Dispatcher\ObjectResult\ObjectResultInterface;
11
use \Comodojo\Dispatcher\ObjectResult\ObjectSuccess;
12
use \Comodojo\Dispatcher\ObjectResult\ObjectError;
13
use \Comodojo\Dispatcher\ObjectResult\ObjectRedirect;
14
15
/**
16
 * THE comodojo dispatcher
17
 *
18
 * @package     Comodojo dispatcher
19
 * @author      Marco Giovinazzi <[email protected]>
20
 * @license     GPL-3.0+
21
 *
22
 * LICENSE:
23
 *
24
 * This program is free software: you can redistribute it and/or modify
25
 * it under the terms of the GNU Affero General Public License as
26
 * published by the Free Software Foundation, either version 3 of the
27
 * License, or (at your option) any later version.
28
 *
29
 * This program is distributed in the hope that it will be useful,
30
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32
 * GNU Affero General Public License for more details.
33
 *
34
 * You should have received a copy of the GNU Affero General Public License
35
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
36
 */
37
38
class Dispatcher {
39
40
    /**
41
     * Is dispatcher enabled?
42
     *
43
     * @var bool
44
     */
45
    private $enabled = DISPATCHER_ENABLED;
46
47
    /**
48
     * Time at the time of request
49
     *
50
     * @var float
51
     */
52
    private $current_time = null;
53
54
    /**
55
     * Working mode (rewrite/standard)
56
     *
57
     * @var string
58
     */
59
    private $working_mode = 'STANDARD';
60
61
    /**
62
     * Service URI
63
     *
64
     * @var string
65
     */
66
    private $service_uri = null;
67
68
    /**
69
     * Service URL
70
     *
71
     * @var string
72
     */
73
    private $service_url = null;
74
75
    /**
76
     * Request method (HTTP)
77
     *
78
     * @var string
79
     */
80
    private $request_method = null;
81
82
    /**
83
     * Cache switcher: if true, cacher will not re-cache current response
84
     *
85
     * @var bool
86
     */
87
    private $result_comes_from_cache = false;
88
89
    /**
90
     * Container for ObjectRequest
91
     *
92
     * @var Object|null
93
     */
94
    private $request = null;
95
96
    /**
97
     * Container for ObjectRoutingTable
98
     *
99
     * @var Object|null
100
     */
101
    private $routingtable = null;
102
103
    /**
104
     * Container for ObjectRoute
105
     *
106
     * @var Object|null
107
     */
108
    private $serviceroute = null;
109
110
    // ###### Helpers ###### //
111
112
    /**
113
     * Do server-side caching
114
     *
115
     * @var Object|null
116
     */
117
    private $cacher = null;
118
119
    /**
120
     * Logging facilities
121
     *
122
     * @var Object|null
123
     */
124
    private $logger = null;
125
126
    /**
127
     * Do headers manipulations
128
     *
129
     * @var Object|null
130
     */
131
    private $header = null;
132
133
    /**
134
     * Dispatch events
135
     *
136
     * @var Object|null
137
     */
138
    private $events = null;
139
140
    /**
141
     * Constructor method
142
     *
143
     * It reads request and transforms it in a modeled ObjectRequest.
144
     *
145
     * @return null
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
146
     */
147
    final public function __construct() {
0 ignored issues
show
Coding Style introduced by
__construct uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
148
149
        ob_start();
150
151
        // Now start to build dispatcher instance
152
153
        $this->current_time = microtime(true);
154
155
        $this->working_mode = $this->getWorkingMode();
156
157
        $this->service_uri = $this->urlGetUri();
158
159
        $this->service_url = $this->urlGetUrl();
160
161
        $this->request_method = $_SERVER['REQUEST_METHOD'];
162
163
        // Init logger
164
        $this->logger = new Debug();
165
166
        $this->logger->info('Dispatcher online, request time: '.$this->current_time);
167
168
        $this->logger->debug('Working mode: '.$this->working_mode);
169
170
        $this->logger->debug('Request URI: '.$this->service_uri);
171
172
        // Init routing table
173
        $this->routingtable = new ObjectRoutingTable();
174
175
        // Init components
176
        $this->cacher = new Cache($this->current_time, $this->logger);
177
178
        $this->header = new Header($this->current_time);
179
180
        $this->events = new Events($this->logger);
181
182
        // Starts composing request object (ObjectRequest)
183
184
        list($request_service,$request_attributes) = $this->urlInterpreter($this->working_mode);
185
186
        list($request_parameters, $request_raw_parameters) = $this->deserializeParameters($this->request_method);
187
188
        $this->logger->debug('Provided attributes',$request_attributes);
189
190
        $this->logger->debug('Provided parameters',$request_parameters);
191
192
        $request_headers = $this->header->getRequestHeaders();
193
194
        // Before composing the object request, remember to define the current (absolute) dispatcher baseurl
195
        // (if not specified in dispatcher-config)
196
        if ( !defined("DISPATCHER_BASEURL") ) define("DISPATCHER_BASEURL",$this->urlGetAbsolute($request_service));
197
198
        // Now let's compose request object
199
200
        $this->request = new ObjectRequest();
201
202
        $this->request
203
            ->setCurrentTime($this->current_time)
204
            ->setService($request_service)
205
            ->setMethod($this->request_method)
206
            ->setAttributes($request_attributes)
207
            ->setParameters($request_parameters)
208
            ->setRawParameters($request_raw_parameters)
209
            ->setHeaders($request_headers);
210
211
        $this->logger->info('Requested service: '.$request_service);
212
        $this->logger->info('Request HTTP method: '.$this->request_method);
213
214
    }
215
216
    /**
217
     * Set punctual service route
218
     *
219
     * @param   string  $service    Service name (src)
220
     * @param   string  $type       Route type (ROUTE, REDIRECT or ERROR)
221
     * @param   string  $target     Service target (dst)
222
     * @param   array   $parameters (optional) Service options (cache, ...)
223
     * @param   bool    $relative   (optional) If true, target will be assumed in default service directory
224
     */
225
    final public function setRoute($service, $type, $target, $parameters=array(), $relative=true) {
226
227
        try {
228
229
            if ( strtoupper($type) == "ROUTE" ) {
230
231 View Code Duplication
                if ( $relative ) $this->routingtable->setRoute($service, $type, DISPATCHER_SERVICES_FOLDER.$target, $parameters);
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
232
233
                else $this->routingtable->setRoute($service, $type, $target, $parameters);
234
235
            }
236
237
            else if ( strtoupper($type) == "REDIRECT" ) {
238
239 View Code Duplication
                if ( $relative ) $this->routingtable->setRoute($service, $type, DISPATCHER_BASEURL.$target, $parameters);
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
240
241
                else $this->routingtable->setRoute($service, $type, $target, $parameters);
242
243
            }
244
245
            else $this->routingtable->setRoute($service, $type, $target, $parameters);
246
247
        } catch (Exception $e) {
248
249
            //debug error but do not stop dispatcher
250
            $this->logger->warning( 'Unable to set route', array('SERVIVE' => $service) );
251
252
        }
253
254
    }
255
256
    /**
257
     * Unset punctual service route
258
     *
259
     * @param   string  $service    Service name (src)
260
     *
261
     * @return null
262
     */
263
    final public function unsetRoute($service) {
264
265
        try {
266
267
            $this->routingtable->unsetRoute($service);
268
269
        } catch (Exception $e) {
270
271
            //debug error but do not stop dispatcher
272
            $this->logger->warning( 'Unable to unset route', array('SERVIVE' => $service) );
273
274
        }
275
276
    }
277
278
    /**
279
     * Add hook for an event
280
     *
281
     * @param   string  $event      The event name
282
     * @param   string  $callback   The callback (or class if $method is specified)
283
     * @param   string  $method     (optional) Method for $callback
284
     */
285 View Code Duplication
    final public function addHook($event, $callback, $method=null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
286
287
        try {
288
289
            $this->events->add($event, $callback, $method);
290
291
        } catch (Exception $e) {
292
293
            //debug error but do not stop dispatcher
294
            $this->logger->warning( 'Unable to add hook', array(
295
                'CALLBACK' => $callback,
296
                'METHOD' => $method,
297
                'EVENT' => $event
298
            ) );
299
300
        }
301
302
    }
303
304
    /**
305
     * Remove an hook
306
     *
307
     * @param   string  $event      The event name
308
     * @param   string  $callback   The callback (or class if $method is specified)
309
     */
310 View Code Duplication
    final public function removeHook($event, $callback=null) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
311
312
        try {
313
314
            $this->events->remove($event, $callback);
315
316
        } catch (Exception $e) {
317
318
            //debug error but do not stop dispatcher
319
            $this->logger->warning( 'Unable to remove hook', array(
320
                'CALLBACK' => $callback,
321
                'EVENT' => $event
322
            ) );
323
324
        }
325
326
    }
327
328
    /**
329
     * Include a plugin
330
     *
331
     * @param   string  $plugin     The plugin name
332
     * @param   string  $folder     (optional) plugin folder (if omitted, dispatcher will use default one)
333
     */
334
    final public function loadPlugin($plugin, $folder=DISPATCHER_PLUGINS_FOLDER) {
335
336
        include $folder.$plugin.".php";
337
338
    }
339
340
    /**
341
     * Get current time (time at the time of the request)
342
     *
343
     * @return  float   time in microsec
344
     */
345
    final public function getCurrentTime() {
346
347
        return $this->current_time;
348
349
    }
350
351
    /**
352
     * Get dispatcher logger
353
     *
354
     * @return  Object
355
     */
356
    final public function getLogger() {
357
358
        return $this->logger;
359
360
    }
361
362
    /**
363
     * Crear cache for a single request or entirely
364
     *
365
     * @param   mixed    $all    True = purge entire cache, false/null = purge current request cache
366
     *
367
     * @return  Object
368
     */
369
    final public function clearCache($all = null) {
370
371
        return $this->cacher->purge( $all == true ? null : $this->service_url );
372
373
    }
374
375
    /**
376
     * Dispatcher working method
377
     *
378
     * @return  mixed
379
     */
380
    final public function dispatch() {
0 ignored issues
show
Coding Style introduced by
dispatch uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
381
382
        // Before building dispatcher instance, fire THE level1 event "dispatcher"
383
        // This is the only way (out of dispatcher-config) to disable dispatcher
384
385
        $fork = $this->events->fire("dispatcher", "VOID", $this);
0 ignored issues
show
Unused Code introduced by
$fork is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
386
387
        // if ( is_bool($fork)  ) $this->enabled = $fork;
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
388
389
        // After building dispatcher instance, fire THE level2 event "dispatcher.request"
390
        // This default hook will expose current request (ObjectRequest) to callbacks
391
392
        $fork = $this->events->fire("dispatcher.request", "REQUEST", $this->request);
393
394
        if ( $fork instanceof \Comodojo\Dispatcher\ObjectRequest\ObjectRequest ) $this->request = $fork;
395
396
        // Fire level3 event "dispatcher.request.[method]"
397
398
        $fork = $this->events->fire("dispatcher.request.".$this->request_method, "REQUEST", $this->request);
399
400
        if ( $fork instanceof \Comodojo\Dispatcher\ObjectRequest\ObjectRequest ) $this->request = $fork;
401
402
        // Fire level3 event "dispatcher.request.[service]"
403
404
        $fork = $this->events->fire("dispatcher.request.".$this->request->getService(), "REQUEST", $this->request);
405
406
        if ( $fork instanceof \Comodojo\Dispatcher\ObjectRequest\ObjectRequest ) $this->request = $fork;
407
408
        // Fire special event, it will not modify request
409
410
        $this->events->fire("dispatcher.request.#", "VOID", $this->request);
411
412
        // Check if dispatcher is enabled
413
414
        if ( $this->enabled == false ) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
415
416
            $route = new ObjectError();
417
            $route->setStatusCode(503);
418
419
            $return = $this->route($route);
420
421
            $this->logger->info('Shutting down dispatcher (administratively disabled)');
422
423
            ob_end_clean();
424
425
            return $return;
426
427
        }
428
429
        // Before calculating service route, expose the routing table via level2 event "dispatcher.routingtable"
430
431
        $fork = $this->events->fire("dispatcher.routingtable", "TABLE", $this->routingtable);
432
433
        if ( $fork instanceof \Comodojo\Dispatcher\ObjectRoutingTable\ObjectRoutingTable ) $this->routingtable = $fork;
434
435
        // Retrieve current route from routing table
436
437
        $service = $this->request->getService();
438
439
        $this->logger->debug('Querying routingtable for service route', array(
440
            'SERVICE' => $service
441
        ));
442
443
        $preroute = $this->routingtable->getRoute($service);
444
445
        $this->serviceroute = new ObjectRoute();
446
        $this->serviceroute->setService($service)
447
            ->setType($preroute["type"])
448
            ->setTarget($preroute["target"]);
449
450
        if ( isset($preroute["parameters"]["class"]) ) {
451
            $this->serviceroute->setClass($preroute["parameters"]["class"]);
452
        } else {
453
            $t = pathinfo($preroute["target"]);
454
            $this->serviceroute->setClass(preg_replace('/\\.[^.\\s]{3,4}$/', '', $t["filename"]));
455
        }
456
457 View Code Duplication
        if ( isset($preroute["parameters"]["redirectCode"]) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
458
            $this->serviceroute->setRedirectCode($preroute["parameters"]["redirectCode"]);
459
            unset($preroute["parameters"]["redirectCode"]);
460
        }
461 View Code Duplication
        if ( isset($preroute["parameters"]["errorCode"]) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
462
            $this->serviceroute->setErrorCode($preroute["parameters"]["errorCode"]);
463
            unset($preroute["parameters"]["errorCode"]);
464
        }
465 View Code Duplication
        if ( isset($preroute["parameters"]["cache"]) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
466
            $this->serviceroute->setCache($preroute["parameters"]["cache"]);
467
            unset($preroute["parameters"]["cache"]);
468
        }
469 View Code Duplication
        if ( isset($preroute["parameters"]["ttl"]) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
470
            $this->serviceroute->setTtl($preroute["parameters"]["ttl"]);
471
            unset($preroute["parameters"]["ttl"]);
472
        }
473
        if ( isset($preroute["parameters"]["headers"]) ) {
474
            if ( is_array($preroute["parameters"]["headers"]) ) foreach ($preroute["parameters"]["headers"] as $header => $value) $this->serviceroute->setHeader($header, $value);
475
            unset($preroute["parameters"]["headers"]);
476
        }
477 View Code Duplication
        if ( isset($preroute["parameters"]["accessControl"]) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
478
            $this->serviceroute->setRedirectCode($preroute["parameters"]["accessControl"]);
479
            unset($preroute["parameters"]["accessControl"]);
480
        }
481
482
        foreach ($preroute["parameters"] as $parameter => $value) {
483
            $this->serviceroute->setParameter($parameter, $value);
484
        }
485
486
        // Now that we have a route, fire the level2 event "dispatcher.serviceroute"
487
        // and level3 events:
488
        // - "dispatcher.serviceroute.[type]"
489
        // - "dispatcher.serviceroute.[service]"
490
491
        $fork = $this->events->fire("dispatcher.serviceroute", "ROUTE", $this->serviceroute);
492
493
        if ( $fork instanceof \Comodojo\Dispatcher\ObjectRoute\ObjectRoute ) $this->serviceroute = $fork;
494
495
        $fork = $this->events->fire("dispatcher.serviceroute.".$this->serviceroute->getType(), "ROUTE", $this->serviceroute);
496
497
        if ( $fork instanceof \Comodojo\Dispatcher\ObjectRoute\ObjectRoute ) $this->serviceroute = $fork;
498
499
        $fork = $this->events->fire("dispatcher.serviceroute.".$this->serviceroute->getService(), "ROUTE", $this->serviceroute);
500
501
        if ( $fork instanceof \Comodojo\Dispatcher\ObjectRoute\ObjectRoute ) $this->serviceroute = $fork;
502
503
        // Fire special event, it will not modify route
504
505
        $this->events->fire("dispatcher.serviceroute.#", "VOID", $this->serviceroute);
506
507
        // Before using route to handle request, check if access control should block instance
508
509
        $accesscontrol = preg_replace('/\s+/', '', $this->serviceroute->getAccessControl());
510
511
        if ( $accesscontrol != null AND $accesscontrol != "*" ) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
512
513
            $origins = explode(",", $accesscontrol);
514
515
            if ( !in_array(@$_SERVER['HTTP_ORIGIN'], $origins) ) {
516
517
                $route = new ObjectError();
518
                $route->setStatusCode(403)->setContent("Origin not allowed");
519
520
                $return = $this->route($route);
521
522
                ob_end_clean();
523
524
                return $return;
525
526
            }
527
528
        }
529
530
        $this->logger->debug('Service route acquired', array(
531
            'SERVICE'       => $this->serviceroute->getService(),
532
            'TYPE'          => $this->serviceroute->getType(),
533
            'CLASS'         => $this->serviceroute->getClass(),
534
            'REDIRECTCODE'  => $this->serviceroute->getRedirectCode(),
535
            'ERRORCODE'     => $this->serviceroute->getErrorCode(),
536
            'CACHE'         => $this->serviceroute->getCache(),
537
            'TTL'           => $this->serviceroute->getTtl(),
538
            'HEADERS'       => $this->serviceroute->getHeaders(),
539
            'REDIRECTCODE'  => $this->serviceroute->getRedirectCode()
540
        ));
541
542
        switch($this->serviceroute->getType()) {
543
544 View Code Duplication
            case "ERROR":
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
545
546
                $route = new ObjectError();
547
                $route->setService($this->serviceroute->getService())
548
                    ->setStatusCode($this->serviceroute->getErrorCode())
549
                    ->setContent($this->serviceroute->getTarget())
550
                    ->setHeaders($this->serviceroute->getHeaders());
551
552
                break;
553
554 View Code Duplication
            case "REDIRECT":
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
555
556
                $route = new ObjectRedirect();
557
                $route->setService($this->serviceroute->getService())
558
                    ->setStatusCode($this->serviceroute->getRedirectCode())
559
                    ->setLocation($this->serviceroute->getTarget())
560
                    ->setHeaders($this->serviceroute->getHeaders());
561
562
                break;
563
564
            case "ROUTE":
565
566
                try {
567
568
                    $route = $this->runService($this->request, $this->serviceroute);
0 ignored issues
show
Documentation introduced by
$this->request is of type object|null, but the function expects a object<Comodojo\Dispatch...tRequest\ObjectRequest>.

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...
569
570
                } catch (DispatcherException $de) {
571
572
                    $this->logger->error('Service returns a DispatcherException', array(
573
                        'SERVICE' => $this->serviceroute->getService(),
574
                        'CODE'    => $de->getCode(),
575
                        'MESSAGE' => $de->getMessage()
576
                    ));
577
578
                    $route = new ObjectError();
579
                    $route->setService($this->serviceroute->getService())
580
                        ->setStatusCode($de->getCode())
581
                        ->setContent($de->getMessage());
582
583
                } catch (Exception $e) {
584
585
                    $this->logger->error('Error processing service', array(
586
                        'SERVICE' => $this->serviceroute->getService(),
587
                        'CODE'    => $e->getCode(),
588
                        'MESSAGE' => $e->getMessage()
589
                    ));
590
591
                    $route = new ObjectError();
592
                    $route->setService($this->serviceroute->getService())
593
                        ->setStatusCode(500)
594
                        ->setContent($e->getMessage());
595
596
                }
597
598
                break;
599
600
        }
601
602
        $return = $this->route($route);
0 ignored issues
show
Bug introduced by
The variable $route does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
603
604
        ob_end_clean();
605
606
        return $return;
607
608
    }
609
610
    /**
611
     * Url interpreter
612
     *
613
     * Starting from $workingMode (REWRITE|STANDARD) will acquire service route from request.
614
     *
615
     * In other words, will separate service and attributes and populate class parameters
616
     * service_requested and service_attributes
617
     *
618
     * @param   string  $workingMode    (REWRITE|STANDARD)
619
     */
620
    private function urlInterpreter($workingMode) {
0 ignored issues
show
Coding Style introduced by
urlInterpreter uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
urlInterpreter uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
621
622
        if ($workingMode == "REWRITE") {
623
624
            $uri = explode('/', $_SERVER['REQUEST_URI']);
625
            $scr = explode('/', $_SERVER['SCRIPT_NAME']);
626
627
            for($i= 0;$i < sizeof($scr);$i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function sizeof() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
628
                if ($uri[$i] == $scr[$i]) unset($uri[$i]);
629
            }
630
631
            $service_matrix = array_values($uri);
632
633
            if (isset($service_matrix[0])) {
634
635
                $service_requested = $service_matrix[0];
636
637
                $last = $service_matrix[sizeof($service_matrix)-1];
638
639
                $service_attributes = empty($last) ? array_slice($service_matrix, 1, -1) : array_slice($service_matrix, 1);
640
641
            }
642
            else {
643
644
                $service_requested = "default";
645
                $service_attributes = array();
646
647
            }
648
649
        }
650
        else {
651
652
            $service_matrix = $_GET;
653
654
            if (isset($service_matrix["service"])) {
655
656
                $service_requested = $service_matrix["service"];
657
                unset($service_matrix["service"]);
658
                $service_attributes = $service_matrix;
659
660
            }
661
            else {
662
663
                $service_requested = "";
664
                $service_attributes = array();
665
666
            }
667
668
        }
669
670
        return array($service_requested, $service_attributes);
671
672
    }
673
674
    /**
675
     * Return current request uri
676
     *
677
     * @return string  The request uri
678
     */
679
    private function urlGetUri() {
0 ignored issues
show
Coding Style introduced by
urlGetUri uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
680
681
        return $_SERVER['REQUEST_URI'];
682
683
    }
684
685
    /**
686
     * Return current request url
687
     *
688
     * @return string  The request uri
689
     */
690
    private function urlGetUrl() {
0 ignored issues
show
Coding Style introduced by
urlGetUrl uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
691
692
        return $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
693
694
    }
695
696
    /**
697
     * Return dispatcher baseurl, no matter the request
698
     *
699
     * @return uri  The baseurl
700
     */
701
    private function urlGetAbsolute($service=null) {
0 ignored issues
show
Coding Style introduced by
urlGetAbsolute uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
702
703
        $http = 'http' . ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 's' : '') . '://';
704
705
        if ( is_null($service) ) $uri = "";
706
707
        else {
708
709
            $self = $_SERVER['PHP_SELF'];
710
711
            $uri = preg_replace("/\/index.php(.*?)$/i","",$self);
712
713
        }
714
715
        return ( $http . $_SERVER['HTTP_HOST'] . $uri . "/" );
716
717
    }
718
719
    /**
720
     * Get current working mode
721
     *
722
     * @return  string  "REWRITE" or "STANDARD"
723
     */
724
    private function getWorkingMode() {
725
726
        return DISPATCHER_USE_REWRITE ? "REWRITE" : "STANDARD";
727
728
    }
729
730
    /**
731
     * Retrieve parameters from input
732
     *
733
     * @return  array
734
     */
735
    private function deserializeParameters($method) {
0 ignored issues
show
Coding Style introduced by
deserializeParameters uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
736
737
        $parameters = array();
738
739
        switch($method) {
740
741
            case 'POST':
742
743
            $parameters = $_POST;
744
745
            break;
746
747
            case 'PUT':
748
            case 'DELETE':
749
750
            parse_str(file_get_contents('php://input'), $parameters);
751
752
            break;
753
754
        }
755
756
        return array($parameters, file_get_contents('php://input'));
757
758
    }
759
760
    private function attributesMatch($provided, $expected, $liked) {
761
762
        if ( $this->working_mode == "STANDARD" ) return $this->parametersMatch($provided, $expected, $liked);
763
764
        $attributes = array();
0 ignored issues
show
Unused Code introduced by
$attributes is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
765
766
        $psize = sizeof($provided);
767
        $esize = sizeof($expected);
768
        $lsize = sizeof($liked);
769
770
        if ( $psize < $esize ) throw new DispatcherException("Conversation error", 400);
771
772
        else if ( $psize == $esize ) {
773
774
            $attributes = $psize == 0 ? array() : array_combine($expected, $provided);
775
776
        }
777
778
        else {
779
780
            if ( $esize == 0 ) {
781
782
                $e_attributes = array();
783
784
                $lvalues = $provided;
785
786
            } else {
787
788
                $pvalues = array_slice($provided, 0, $esize);
789
790
                $e_attributes = array_combine($expected, $pvalues);
791
792
                $lvalues = array_slice($provided, $esize);
793
794
            }
795
796
            $lvaluessize = sizeof($lvalues);
797
798
            $l_attributes = array();
0 ignored issues
show
Unused Code introduced by
$l_attributes is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
799
800
            if ( $lvaluessize < $lsize ) {
801
802
                $l_attributes = array_combine(array_slice($liked, 0, $lvaluessize), $lvalues);
803
804
            }
805
            else if ( $lvaluessize == $lsize ) {
806
807
                $l_attributes = $lvaluessize == 0 ? array() : array_combine($liked, $lvalues);
808
809
            }
810
            else {
811
812
                if ( $lsize == 0 ) {
813
814
                    $l_attributes = $lvalues;
815
816
                } else {
817
818
                    $r_attributes = array_combine($liked, array_slice($lvalues, 0, $lsize));
819
820
                    $remaining_parameters = array_slice($lvalues, $lsize);
821
822
                    $l_attributes = array_merge($r_attributes, $remaining_parameters);
823
824
                }
825
826
            }
827
828
            $attributes = array_merge($e_attributes, $l_attributes);
829
830
        }
831
832
        return $attributes;
833
834
    }
835
836
    private function parametersMatch($provided, $expected, $liked) {
0 ignored issues
show
Unused Code introduced by
The parameter $liked is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
837
838
        foreach ($expected as $parameter) {
839
840
            if ( !isset($provided[$parameter]) ) throw new DispatcherException("Conversation error", 400);
841
842
        }
843
844
        return $provided;
845
846
    }
847
848
    private function runService(ObjectRequest $request, ObjectRoute $route) {
849
850
        $method = $request->getMethod();
851
        $service = $route->getService();
852
        $cache = $route->getCache();
853
        $ttl = $route->getTtl();
854
        $target = $route->getTarget();
855
856
        // First of all, check cache (in case of GET request)
857
858
        if ( $method == "GET" AND ( $cache == "SERVER" OR $cache == "BOTH" ) ) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
859
860
            $from_cache = $this->cacher->get($this->service_url, $ttl);
861
862
            if ( is_array($from_cache) ) {
863
864
                $maxage = $from_cache["maxage"];
0 ignored issues
show
Unused Code introduced by
$maxage is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
865
                $bestbefore = $from_cache["bestbefore"];
0 ignored issues
show
Unused Code introduced by
$bestbefore is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
866
                $result = $from_cache["object"];
867
868
                // Publish that result comes from cache (so will not be re-cached)
869
870
                $this->result_comes_from_cache = true;
871
872
                return $result;
873
874
            }
875
876
        }
877
878
        // If there's no cache for this request, use routing information to find service
879
880
        if ( (include($target)) === false ) throw new DispatcherException("Cannot run service", 500);
881
882
        // Find a service implementation and try to init it
883
884
        $service_class = $route->getClass();
885
886
        if ( empty($service_class) ) throw new DispatcherException("Cannot run service", 500);
887
888
        $service_class = "\\Comodojo\\Dispatcher\\Service\\".$service_class;
889
890
        // Setup service
891
892
        try {
893
894
            $theservice = new $service_class($this->logger, $this->cacher);
895
896
            $theservice->setup();
897
898
        } catch (Exception $e) {
899
900
            throw $e;
901
902
        }
903
904
        // Check if service supports current HTTP method
905
906
        if ( !in_array($method, explode(",", $theservice->getSupportedMethods())) ) {
907
908
            throw new DispatcherException("Allow: ".$theservice->getSupportedMethods(), 405);
909
910
        }
911
912
        // Check if service implements current HTTP method
913
914
        if ( !in_array($method, $theservice->getImplementedMethods()) ) {
915
916
            throw new DispatcherException("Allow: ".implode(",",$theservice->getImplementedMethods()), 501);
917
918
        }
919
920
        // Match attributes and parameters
921
922
        list($expected_attributes, $expected_parameters) = $theservice->getExpected($method);
923
924
        list($liked_attributes, $liked_parameters) = $theservice->getLiked($method);
925
926
        try {
927
928
            $validated_attributes = $this->attributesMatch($request->getAttributes(), $expected_attributes, $liked_attributes);
929
930
            $validated_parameters = $this->parametersMatch($request->getParameters(), $expected_parameters, $liked_parameters);
931
932
        } catch (DispatcherException $de) {
933
934
            throw $de;
935
936
        }
937
938
        // Fill service with dispatcher pieces
939
940
        $theservice->setAttributes($validated_attributes);
941
        $theservice->setParameters($validated_parameters);
942
        $theservice->setRawParameters($request->getRawParameters());
943
        $theservice->setRequestHeaders($request->getHeaders());
944
945
        // Requesto to service the callable method (just to handle any method)
946
947
        $current_method = $theservice->getCallableMethod($method);
948
949
        // Finally run service method and catch exceptions
950
951
        try {
952
953
            $result = $theservice->$current_method();
954
955
            $return = new ObjectSuccess();
956
            $return->setService($service)
957
                ->setStatusCode($theservice->getStatusCode())
958
                ->setContent($result)
959
                ->setHeaders( array_merge($theservice->getHeaders(), $route->getHeaders()) )
960
                ->setContentType($theservice->getContentType())
961
                ->setCharset($theservice->getCharset());
962
963
        } catch (DispatcherException $de) {
964
965
            throw $de;
966
967
        } catch (Exception $e) {
968
969
            throw $e;
970
971
        }
972
973
        return $return;
974
975
    }
976
977
    /**
978
     * Route request handling ObjectResult hooks
979
     *
980
     * @param   ObjectResult    $route  An implementation of ObjectResultInterface
981
     * @return  string                  Content (stuff that will go on screen)
982
     */
983
    private function route(ObjectResultInterface $route) {
984
985
        // Starting from the routing instance, select the relative level2 hook
986
        // This means event engine will fire a dispatcher.[routetype] event
987
        // In case of wrong instance, create an ObjectError (500, null) instance
988
989
        if ( $route instanceof ObjectSuccess ) {
990
991
            $hook = "dispatcher.route";
992
993
        } else if ( $route instanceof ObjectError ) {
994
995
            $hook = "dispatcher.error";
996
997
        } else if ( $route instanceof ObjectRedirect ) {
998
999
            $hook = "dispatcher.redirect";
1000
1001
        } else {
1002
1003
            $hook = "dispatcher.error";
1004
1005
            $route = new ObjectError();
1006
1007
        }
1008
1009
        // Fire first hook, a generic "dispatcher.result", Object Type independent
1010
1011
        $fork = $this->events->fire("dispatcher.result", "RESULT", $route);
1012
1013
        if ( $fork instanceof \Comodojo\Dispatcher\ObjectResult\ObjectResultInterface ) $route = $fork;
1014
1015
        // Fire second hook (level2), as specified above
1016
1017
        $fork = $this->events->fire($hook, "RESULT", $route);
1018
1019
        if ( $fork instanceof \Comodojo\Dispatcher\ObjectResult\ObjectResultInterface ) $route = $fork;
1020
1021
        // Now select and fire last hook (level3)
1022
        // This means that event engine will fire something like "dispatcher.route.200"
1023
        // or "dispatcher.error.500"
1024
1025
        $fork = $this->events->fire($hook.".".$route->getStatusCode(), "RESULT", $route);
1026
1027
        if ( $fork instanceof \Comodojo\Dispatcher\ObjectResult\ObjectResultInterface ) $route = $fork;
1028
1029
        // Fire special event, it may modify result
1030
1031
        $fork = $this->events->fire("dispatcher.result.#", "RESULT", $route);
1032
1033
        if ( $fork instanceof \Comodojo\Dispatcher\ObjectResult\ObjectResultInterface ) $route = $fork;
1034
1035
        // After hooks:
1036
        // - store cache
1037
        // - start composing header
1038
        // - return result
1039
1040
        $cache = $route instanceof \Comodojo\Dispatcher\ObjectResult\ObjectSuccess ? $this->serviceroute->getCache() : null;
1041
1042
        if ( $this->request_method == "GET" AND
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
1043
            ( $cache == "SERVER" OR $cache == "BOTH" ) AND
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
1044
            $this->result_comes_from_cache == false AND
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
1045
            $route instanceof \Comodojo\Dispatcher\ObjectResult\ObjectSuccess )
1046
        {
1047
1048
            $this->cacher->set($this->service_url, $route);
1049
1050
        }
1051
1052
1053
        $this->header->free();
1054
1055
        if ( $cache == "CLIENT" OR $cache == "BOTH" ) $this->header->setClientCache($this->serviceroute->getTtl());
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
1056
1057
        $this->header->setContentType($route->getContentType(), $route->getCharset());
1058
1059
        foreach ($route->getHeaders() as $header => $value) {
1060
1061
            $this->header->set($header, $value);
1062
1063
        }
1064
1065
        $message = $route->getContent();
1066
1067
        $this->header->compose($route->getStatusCode(), strlen($message), $route->getLocation()); # ><!/\!°>
1068
1069
        // Return the content (stuff that will go on screen)
1070
1071
        return $message;
1072
1073
    }
1074
1075
}
1076