Router   D
last analyzed

Complexity

Total Complexity 87

Size/Duplication

Total Lines 890
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 0
loc 890
rs 4.4444
c 0
b 0
f 0
wmc 87
lcom 1
cbo 4

27 Methods

Rating   Name   Duplication   Size   Complexity  
A parseUrlRewrite() 0 53 3
A __construct() 0 7 1
A addRoutes() 0 6 2
A getRoutes() 0 4 1
A delRoute() 0 4 1
A dispatchToDefaultRoute() 0 15 1
A dispatchTo404() 0 5 1
A fixNoRewriteShorthands() 0 9 2
A setRequestURI() 0 4 1
A offsetSet() 0 4 1
A offsetExists() 0 4 2
A offsetUnset() 0 4 1
A prepareRequestURI() 0 18 3
B addRoute() 0 38 2
B processSegmentsRegExp() 0 45 4
A reset() 0 12 2
A generateURL() 0 16 2
C buildURL() 0 80 9
B route() 0 28 3
D match() 0 115 15
B parseUrlNoRewrite() 0 41 5
A isRewriteEngineOn() 0 17 3
B checkEnvForModRewrite() 0 25 5
A placeholdersToRegexp() 0 10 1
A reduceRoutesToSegmentCount() 0 16 3
C loadDefaultRoutes() 0 63 10
A offsetGet() 0 8 3

How to fix   Complexity   

Complex Class

Complex classes like Router often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Router, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Koch Framework
5
 * Jens-André Koch © 2005 - onwards.
6
 *
7
 * This file is part of "Koch Framework".
8
 *
9
 * License: GNU/GPL v2 or any later version, see LICENSE file.
10
 *
11
 * This program is free software; you can redistribute it and/or modify
12
 * it under the terms of the GNU General Public License as published by
13
 * the Free Software Foundation; either version 2 of the License, or
14
 * (at your option) any later version.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU General Public License
22
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23
 */
24
25
namespace Koch\Router;
26
27
use Koch\Cache\Cache;
28
use Koch\Http\HttpRequestInterface;
29
30
/**
31
 * Router.
32
 *
33
 * Router does URL Formatting and internal Rewriting.
34
 * The URL is segmented and restructured to fit the internal route to a controller.
35
 * The internal routes are described in a central routing configuration file.
36
 * This central config is updated on installation and deinstallation of modules and plugins.
37
 *
38
 * @see \Koch\Routes\Manager
39
 *
40
 * Normally all requests made map to a specific physical resource rather than a logical name.
41
 * With Routing you are able to map a logical name to a specific physical name.
42
 * Examples: map a logical URL (a mod_rewritten one) to a Controller/Method/Parameter
43
 * or map a FileRequest via logical URL (a mod_rewritten one) to a DownloadController/Method/Parameters.
44
 * Routes are a valuable concept because they separate your URLs from your data.
45
 *
46
 * There are two different URL Formatings allowed:
47
 * 1. Slashes as Segment Dividers-Style, like so: /mod/sub/action/id
48
 * 2. Fake HTML File Request or SMF-Style, like so: /mod.sub.action.id.html
49
 */
50
class Router implements RouterInterface, \ArrayAccess
51
{
52
    /**
53
     * @var object Koch\Config
54
     */
55
    private $config;
56
57
    /**
58
     * Whether to use caching for routes or not.
59
     *
60
     * @var bool
61
     */
62
    private static $useCache = false;
63
64
    /**
65
     * The Request URI (came in from the HttpRequest object).
66
     *
67
     * @var string
68
     */
69
    private $uri = '';
70
71
    /**
72
     * The Request URI as an array.
73
     *
74
     * @var array
75
     */
76
    public $uriSegments = [];
77
78
    /**
79
     * The "extension" on the URI
80
     * Would be "html" for the URI "/news/show/1.html".
81
     *
82
     * @var string
83
     */
84
    private static $extension = '';
85
86
    /**
87
     * Routes Mapping Table.
88
     * Is an array containing several route definitions.
89
     *
90
     * @var array Routes Array
91
     */
92
    private $routes = [];
93
94
    /**
95
     * Constructor.
96
     */
97
    public function __construct(HttpRequestInterface $request)
98
    {
99
        $this->request = $request;
0 ignored issues
show
Bug introduced by
The property request does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
100
101
        // get URI from request, clean it and set it as a class property
102
        $this->uri = self::prepareRequestURI($request->getRequestURI());
103
    }
104
105
    /**
106
     * Get and prepare the SERVER_URL/URI.
107
     *
108
     * Several fixes are applied to the $request_uri.
109
     *
110
     * When incomming via \Koch\Http\HttpRequest::getRequestURI()
111
     * the $request_rui is already
112
     * (1) lowercased and
113
     * (2) urldecoded.
114
     *
115
     * This function
116
     * (3) strips slashes from the beginning and the end,
117
     * (4) prepends a slash and
118
     * (5) strips PHP_SELF from the uri string.
119
     *
120
     * A multislash removal is not needed, because of the later usage of preg_split().
121
     *
122
     * @return string Request URL
123
     */
124
    public function prepareRequestURI($uri)
125
    {
126
        // remove xdebug_session_start parameter from uri
127
        if (function_exists('xdebug_break')) {
128
            $uri = str_replace('xdebug_session_start=netbeans-xdebug', '', $uri);
129
            // remove trailing '?' or '&'
130
            $uri = rtrim($uri, '?&');
131
        }
132
133
        // add slash in front + remove slash at the end
134
        if ($uri !== '/') {
135
            $uri = '/' . trim($uri, '/');
136
        }
137
138
        $this->uri = $uri;
139
140
        return $uri;
141
    }
142
143
    /**
144
     * Adds a route.
145
     *
146
     * @param string $url_pattern  A route string.
147
     * @param array  $requirements Routing options.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $requirements not be null|array? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
148
     */
149
    public function addRoute($url_pattern, array $requirements = null)
150
    {
151
        /*
152
         * 1) Preprocess the route
153
         */
154
155
        $url_pattern = ltrim($url_pattern, '/');
0 ignored issues
show
Coding Style introduced by
$url_pattern does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
156
157
        /*
158
         * Replace all static placeholders, like (:num) || (:id)
159
         * with their equivalent regular expression ([0-9]+).
160
         *
161
         * All static placeholders not having a regexp equivalent,
162
         * will remain on the route, like ":news".
163
         * They will be handled as "static named" routes and route directly to
164
         * a controller with the same name!
165
         */
166
        if (strpos($url_pattern, '(') !== false) {
0 ignored issues
show
Coding Style introduced by
$url_pattern does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
167
            $url_pattern = self::placeholdersToRegexp($url_pattern);
0 ignored issues
show
Coding Style introduced by
$url_pattern does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
168
        }
169
170
        // explode the uri pattern to get uri segments
171
        $segments = explode('/', $url_pattern);
0 ignored issues
show
Coding Style introduced by
$url_pattern does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
172
173
        // combines all regexp patterns of segements to one regexp pattern for the route
174
        $regexp = $this->processSegmentsRegExp($segments, $requirements);
175
176
        $options = [
177
            'regexp'             => $regexp,
178
            'number_of_segments' => count($segments),
179
            'requirements'       => $requirements,
180
        ];
181
182
        /*
183
         * 2) Finally add the now *preprocessed* Route.
184
         */
185
        $this->routes['/' . $url_pattern] = $options;
0 ignored issues
show
Coding Style introduced by
$url_pattern does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
186
    }
187
188
    /**
189
     * Returns a regexp pattern for the route
190
     * by combining the regexp patterns of all uri segments.
191
     *
192
     * It's basically string concatenation of regexp strings.
193
     *
194
     * @param array $segments     Array with URI segments.
195
     * @param array $requirements Array with
0 ignored issues
show
Documentation introduced by
Should the type for parameter $requirements not be null|array? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
196
     *
197
     * @return string Regular Expression for the route.
198
     */
199
    public function processSegmentsRegExp(array $segments, array $requirements = null)
200
    {
201
        // start regular expression
202
        $regexp = '#';
203
204
        // process all segments
205
        foreach ($segments as $segment) {
206
207
            /*
208
             * Process "Static Named Parameters".
209
             *
210
             * Static named parameters starts with a ":".
211
             * Example: ":contoller".
212
             */
213
            if (strpos($segment, ':') !== false) {
214
                $name = substr($segment, 1); // remove :
215
216
                // is there a requirement for this param? 'id' => '([0-9])'
217
                if (isset($requirements[$name])) {
218
                    // add it to the regex
219
                    $regexp .= '(?P<' . $name . '>' . $requirements[$name] . ')';
220
                    // and remove the now processed requirement
221
                    unset($requirements[$name]);
222
                } else { // no requirement
223
                    $regexp .= '(?P<' . $name . '>[a-z_-]+)';
224
                }
225
            } else {
226
                /*
227
                 * Process "Static Parameter".
228
                 *
229
                 * Static parameters starts with a "/".
230
                 * Example: "/index" or "/news".
231
                 */
232
                $regexp .= '\\/' . $segment;
233
            }
234
235
            // regexp between segments (regexp combiner)
236
            $regexp .= '\/?';
237
        }
238
239
        // finish regular expression
240
        $regexp .= '#';
241
242
        return $regexp;
243
    }
244
245
    /**
246
     * Add multiple route.
247
     *
248
     * @param array $routes Array with multiple routes.
249
     */
250
    public function addRoutes(array $routes)
251
    {
252
        foreach ($routes as $route => $options) {
253
            $this->addRoute((string) $route, (array) $options);
254
        }
255
    }
256
257
    /**
258
     * Method returns all loaded routes.
259
     *
260
     * @return array Returns array with all loaded Routes.
261
     */
262
    public function getRoutes()
263
    {
264
        return $this->routes;
265
    }
266
267
    /**
268
     * Delete a route by its url pattern.
269
     *
270
     * @param string $url_pattern
271
     */
272
    public function delRoute($url_pattern)
273
    {
274
        unset($this->routes[$url_pattern]);
0 ignored issues
show
Coding Style introduced by
$url_pattern does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
275
    }
276
277
    /**
278
     * Resets the routes array.
279
     *
280
     * @param bool Load the default routes. Defaults to false.
281
     *
282
     * @return Router \Koch\Router\Router
283
     */
284
    public function reset($loadDefaultRoutes = false)
285
    {
286
        $this->routes = [];
287
288
        TargetRoute::reset();
289
290
        if ($loadDefaultRoutes === true) {
291
            $this->loadDefaultRoutes();
292
        }
293
294
        return $this;
295
    }
296
297
    /**
298
     * Generates a URL by parameters.
299
     *
300
     * @param string $url_pattern The URL Pattern of the route
301
     * @param array  $params      An array of parameters
0 ignored issues
show
Documentation introduced by
Should the type for parameter $params not be null|array? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
302
     * @param bool   $absolute    Whether to generate an absolute URL
303
     *
304
     * @return string The generated (relative or absolute) URL.
305
     */
306
    public function generateURL($url_pattern, array $params = null, $absolute = false)
307
    {
308
        $url = '';
309
310
        // @todo merge with buildURL + routing rules + parameters
311
312
        $url_pattern = $url_pattern;
0 ignored issues
show
Coding Style introduced by
$url_pattern does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
Bug introduced by
Why assign $url_pattern to itself?

This checks looks for cases where a variable has been assigned to itself.

This assignement can be removed without consequences.

Loading history...
313
314
        $params = $params;
0 ignored issues
show
Bug introduced by
Why assign $params to itself?

This checks looks for cases where a variable has been assigned to itself.

This assignement can be removed without consequences.

Loading history...
315
316
        if ($absolute) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
317
        } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
318
        }
319
320
        return $url;
321
    }
322
323
    /**
324
     * Builds a url string.
325
     *
326
     * @param $url Array or String to build the url from (e.g. '/news/admin/show')
327
     * @param $encode bool True (default) encodes the "&" in the url (amp).
328
     */
329
    public static function buildURL($url, $encode = true)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
330
    {
331
        // if urlstring is array, then a relation (urlstring => parameter_order) is given
332
        if (is_array($url)) {
333
            $parameterOrder             = '';
334
            list($url, $parameterOrder) = each($url);
335
        }
336
337
        // return, if urlstring is already a qualified url (http://...)
338
        if (false !== strpos($url, WWW_ROOT . 'index.php?')) {
339
            return $url;
340
        }
341
342
        // only the http prefix is missing
343
        if (false !== strpos($url, 'index.php?')) {
344
            return 'http://' . $url;
345
        }
346
347
        // cleanup: remove all double slashes
348
        while (false !== strpos($url, '//')) {
349
            $url = str_replace('//', '/', $url);
350
        }
351
352
        // cleanup: remove space and slashes from begin and end of string
353
        $url = trim($url, ' /');
354
355
        /*
356
         * Mod_Rewrite is ON.
357
         *
358
         * The requested url style is:
359
         * ROOT/news/2
360
         */
361
        if (self::isRewriteEngineOn() === true) { /* self::checkEnvForModRewrite() */
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% 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...
362
363
            return WWW_ROOT . ltrim($url, '/');
364
        } else {
365
            /*
366
             * Mod_Rewrite is OFF.
367
             *
368
             * The requested url style is:
369
             * ROOT/index.php?mod=new&ctrl=admin&action=show&id=2
370
             */
371
372
            // get only the part after "index.php?"
373
            if (false !== strpos($url, 'index.php?')) {
374
                $url = strstr($url, 'index.php?');
375
            }
376
377
            // $urlstring contains something like "/news/show/2"
378
            // explode the string into an indexed array
379
            $urlParameters = explode('/', $url);
380
381
            // do we have a parameter_order given?
382
            if (isset($parameterOrder)) {
383
                // replace parameter names with shorthands used in the url
384
                $search         = ['module', 'controller', 'action'];
385
                $replace        = ['mod', 'ctrl', 'action'];
386
                $parameterOrder = str_replace($search, $replace, $parameterOrder);
387
388
                $urlKeys = explode('/', $parameterOrder);
389
            } else {
390
                // default static whitelist for url parameter keys
391
                $urlKeys = ['mod', 'ctrl', 'action', 'id', 'type'];
392
            }
393
394
            /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% 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...
395
             * This turns the indexed url parameters array into a named one.
396
             * [0]=> "news"  to  [mod]    => "news"
397
             * [1]=> "show"  to  [action] => "show"
398
             * [2]=> "2"     to  [id]     => "2"
399
             */
400
            $urlData = \Koch\Functions\Functions::arrayUnequalCombine($urlKeys, $urlParameters);
401
402
            // determine the separator. it defaults to "&amp;" for internal usage in html documents
403
            $argSeparator = ($encode === true) ? '&amp;' : '&';
404
405
            // Finally: build and return the url!
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% 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...
406
            return WWW_ROOT . 'index.php?' . http_build_query($urlData, '', $argSeparator);
407
        }
408
    }
409
410
    /**
411
     * Main method of \Koch\Router\Router.
412
     *
413
     * The routing workflow is
414
     * 1. firstly, check if ModRewrite is enabled,
415
     *    this decides upon which URL parser to use.
416
     * 2. URL parser splits the uri into uri segments.
417
     * 3. routes are initialized (the defaultRoute and all module routes)
418
     * 4. try to find a route/map matching with the uri_segments
419
     * 5. if no mapping applies, then set default values from config and fallback to a static routing
420
     * 6. always! -> found_route -> call!
421
     *
422
     * @return TargetRoute|null
423
     */
424
    public function route()
425
    {
426
        /*
427
         * If there are no uri segments, loading routes and matching is pointless.
428
         * Instead dispatch to the default route and return the according TargetRoute object.
429
         */
430
        if (empty($this->uri) or $this->uri === '/') {
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...
431
            return $this->dispatchToDefaultRoute();
432
        }
433
434
        // initialize Routes
435
        $this->loadDefaultRoutes();
436
437
        /*
438
         * Now match the URI against the Routes.
439
         * The result is either a "dispatchable target route object" or "No target route found.".
440
         */
441
        $targetRoute = $this->match();
442
443
       /*
444
         * Inject the target route object back to the request.
445
         * Thereby the request gains full knowledge about the URL mapping (external to internal).
446
         * We might ask the request object later, where the requests maps to.
447
         */
448
        $this->request->setRoute($targetRoute);
449
450
        return $targetRoute;
451
    }
452
453
    public function dispatchToDefaultRoute()
454
    {
455
        $targetRoute = TargetRoute::instantiate();
456
        // was the default route configured correctly
457
        // @todo this is only possible if set from config to target route
458
        //if ($targetRoute::dispatchable()) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
73% 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...
459
        // default route is dispatchable, set it to the request
460
        $this->request->setRoute($targetRoute);
461
462
        return $targetRoute;
463
        // } else {
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...
464
        // an undispatchable route was configured
465
        //    self::dispatchTo404();
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% 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...
466
        //}
467
    }
468
469
    public static function dispatchTo404()
470
    {
471
        TargetRoute::setController('error');
472
        TargetRoute::setAction('routenotfound');
473
    }
474
475
    /**
476
     * Renameds URL shorthands like "mod" to "module".
477
     * This is needed, because routing might be noRewrite.
478
     * So the uri segments array might contain something like "mod" => "news".
479
     * We need this to be "module" => "news" for setting it to the TargetRoute.
480
     *
481
     * @param $array
482
     *
483
     * @return $array
0 ignored issues
show
Documentation introduced by
The doc-type $array could not be parsed: Unknown type name "$array" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
484
     */
485
    public static function fixNoRewriteShorthands($array)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
486
    {
487
        if (isset($array['mod'])) {
488
            $array['module'] = $array['mod'];
489
            unset($array['mod']);
490
        }
491
492
        return $array;
493
    }
494
495
    /**
496
     * Setter Method for URI. Needed for testing.
497
     *
498
     * @param string $uri
499
     */
500
    public function setRequestURI($uri)
501
    {
502
        $this->uri = $uri;
503
    }
504
505
    /**
506
     * Matches the URI against the Routes Mapping Table.
507
     * Taking static, dynamic and regexp routings into account.
508
     * In other words, it "map matches the URI".
509
     *
510
     * @return TargetRoute|null
511
     */
512
    public function match()
0 ignored issues
show
Coding Style introduced by
match uses the super-global variable $_ENV 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
match 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...
513
    {
514
        // do we have some routes now?
515
        if (0 === count($this->routes)) {
516
            throw new \OutOfBoundsException('The routes lookup table is empty. Define some routes.');
517
        }
518
519
        /*
520
         * Detects if Mod_Rewrite engine is active and
521
         * calls the proper URL Parser/Segmentizer method for the extraction of uri segments.
522
         */
523
        if ($this->isRewriteEngineOn() or isset($_ENV['FORCE_MOD_REWRITE_ON']) 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...
524
                true === empty($_GET['mod']) and true === empty($_GET['ctrl'])) {
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...
525
            $this->uriSegments = $this->parseUrlRewrite($this->uri);
526
        } else {
527
            $this->uriSegments = $this->parseUrlNoRewrite($this->uri);
528
529
            $this->uriSegments = self::fixNoRewriteShorthands($this->uriSegments);
530
531
            $targetRoute = TargetRoute::setSegmentsToTargetRoute($this->uriSegments);
532
533
            if ($targetRoute::dispatchable()) {
534
                return $targetRoute;
535
            }
536
        }
537
538
        /*
539
         * Reduce the map lookup table, by dropping all routes
540
         * with more segments than the current requested uri.
541
         */
542
        if (count($this->routes) > 1 and count($this->uriSegments) >= 1) {
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...
543
            self::reduceRoutesToSegmentCount();
544
        }
545
546
        /*
547
         * Process:     Static Route
548
         *
549
         * Do we have a direct match ?
550
         * This matches "static routes". Without any preg_match overhead.
551
         *
552
         * Example:
553
         * The request URI "/news/index" relates 1:1 to $routes['/news/index'].
554
         * The request URI "/login"      relates 1:1 to $routes['/login']
555
         */
556
        if (isset($this->routes[$this->uri])) {
557
558
            // we have a direct match
559
            $found_route = $this->routes[$this->uri];
0 ignored issues
show
Coding Style introduced by
$found_route does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
560
561
            // return the TargetRoute object
562
            return TargetRoute::setSegmentsToTargetRoute($found_route);
0 ignored issues
show
Coding Style introduced by
$found_route does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
563
        } else {
564
565
            /*
566
             * No, there wasn't a 1:1 match.
567
             * Now we have to check the uri segments.
568
             *
569
             * Let's loop over the remaining routes and try to map match the uri_segments.
570
             */
571
            foreach ($this->routes as $route_pattern => $route_values) {
0 ignored issues
show
Coding Style introduced by
$route_pattern does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
572
                unset($route_pattern);
0 ignored issues
show
Coding Style introduced by
$route_pattern does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
573
574
                $matches = '';
575
576
                /**
577
                 * Process:     Dynamic Regular Expression Parameters.
578
                 *
579
                 * Example:
580
                 * URI: /news
581
                 * Rule /:controller
582
                 * Regexp: "#(?P<controller>[a-z0-9_-]+)\/?#"
583
                 * Matches: $matches['controller'] = 'news';
584
                 */
585
                if (1 === preg_match($route_values['regexp'], $this->uri, $matches)) {
0 ignored issues
show
Coding Style introduced by
$route_values does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
586
587
                    // matches[0] contains $this->uri
0 ignored issues
show
Unused Code Comprehensibility introduced by
46% 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...
588
                    unset($matches[0]);
589
590
                    // remove duplicate values
591
                    // e.g. [controller] = news
592
                    //      [1]          = news
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% 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...
593
                    $matches = array_unique($matches);
594
595
                    # @todo # fetch key and its position from $route_values['requirements']
596
                    if (count($route_values['requirements']) > 0) {
0 ignored issues
show
Coding Style introduced by
$route_values does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
597
                        foreach ($route_values['requirements'] as $array_position => $key_name) {
0 ignored issues
show
Coding Style introduced by
$route_values does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
598
599
                            // insert a new key
600
                            // with name from requirements array
601
                            // and value from matches array
602
                            // ([id] => 42)
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...
603
                            $pos                = $array_position + 1;
0 ignored issues
show
Coding Style introduced by
$array_position does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
604
                            $matches[$key_name] = $matches[$pos];
0 ignored issues
show
Coding Style introduced by
$key_name does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
605
606
                            // remove the old not-named key ([2] => 42)
607
                            unset($matches[$pos]);
608
                        }
609
                    }
610
611
                    // insert $matches[<controller>] etc
612
                    TargetRoute::setSegmentsToTargetRoute($matches);
613
                }
614
615
                if (TargetRoute::dispatchable()) {
616
                    // route found, stop foreach
617
                    break;
618
                } else {
619
                    TargetRoute::reset();
620
                    continue;
621
                }
622
            }
623
        }
624
625
        return TargetRoute::instantiate();
626
    }
627
628
    /**
629
     * Parses the URI and returns an array with URI segments.
630
     *
631
     * URL Parser for Apache Mod_Rewrite URL/URIs.
632
     * Think of it as a ModRewrite_Request_Resolver.
633
     *
634
     * This is based on htaccess rewriting with [QSA,L] (Query Append String).
635
     *
636
     * @param string $uri
637
     *
638
     * @return array Array with URI segments.
639
     */
640
    private static function parseUrlRewrite($uri)
0 ignored issues
show
Coding Style introduced by
parseUrlRewrite 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...
641
    {
642
        $uri = str_replace(strtolower($_SERVER['SCRIPT_NAME']), '', $uri);
643
644
        /*
645
         * The query string up to the question mark (?)
646
         *
647
         * Removes everything after a "?".
648
         * Note: with correct rewrite rules in htaccess, this conditon is not needed.
649
         */
650
        $pos = mb_strpos($uri, '?');
651
        if ($pos !== false) {
652
            $uri = mb_substr($uri, 0, $pos);
653
        }
654
655
        /*
656
         * The last dot (.)
657
         *
658
         * This detects the extension and removes it from the uri string.
659
         * The extension determines the output format. It is always the last piece of the URI.
660
         * Even if there are multiple points in the url string this processes only the last dot
661
         * and fetches everything after it as the extension.
662
         */
663
        $pos = mb_strpos($uri, '.');
664
        if ($pos !== false) {
665
            $uri_dot_array = [];
0 ignored issues
show
Coding Style introduced by
$uri_dot_array does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
Unused Code introduced by
$uri_dot_array 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...
666
            // Segmentize the url into an array
667
            $uri_dot_array = explode('.', $uri);
0 ignored issues
show
Coding Style introduced by
$uri_dot_array does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
668
            // chop off the last piece as the extension
669
            self::$extension = array_pop($uri_dot_array);
0 ignored issues
show
Coding Style introduced by
$uri_dot_array does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
670
            // there might be multiple dots in the url
671
            // thats why implode is used to reassemble the segmentized array to a string again
672
            // but note the different glue string: the dots are now replaced by slashes ,)
673
            // = ini_get('arg_separator.output')
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% 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...
674
            $uri = implode('/', $uri_dot_array);
0 ignored issues
show
Coding Style introduced by
$uri_dot_array does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
675
            unset($uri_dot_array);
0 ignored issues
show
Coding Style introduced by
$uri_dot_array does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
676
        }
677
        unset($pos);
678
679
        /*
680
         * The slashes (/) and empty segments (double slashes)
681
         *
682
         * This segmentizes the URI by splitting at slashes.
683
         */
684
        $uri_segments = preg_split('#/#', $uri, -1, PREG_SPLIT_NO_EMPTY);
0 ignored issues
show
Coding Style introduced by
$uri_segments does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
685
        unset($uri);
686
687
        /*
688
         * Finished!
689
         */
690
691
        return $uri_segments;
0 ignored issues
show
Coding Style introduced by
$uri_segments does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
692
    }
693
694
    /**
695
     * Parses the URI and returns an array with URI segments.
696
     *
697
     * URL Parser for NoRewrite URL/URIs.
698
     * This URLParser has to extract mod, sub, action, id/parameters from the URI.
699
     * Alternate name: Standard_Request_Resolver.
700
     *
701
     * @param string $uri
702
     *
703
     * @return array Array with URI segments.
704
     */
705
    private function parseUrlNoRewrite($uri)
706
    {
707
        if (false !== strpos('?', $uri)) {
708
            return [0 => $uri];
709
        }
710
711
        // use some parse_url magic to get the url_query part from the uri
712
        $uri_query_string = parse_url($uri, PHP_URL_QUERY);
0 ignored issues
show
Coding Style introduced by
$uri_query_string does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
713
        unset($uri);
714
715
        /*
716
         * The ampersand (&)
717
         *
718
         * Use ampersand as the split char for string to array conversion.
719
         */
720
        $uri_query_array = explode('&', $uri_query_string);
0 ignored issues
show
Coding Style introduced by
$uri_query_array does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
721
722
        /*
723
         * The equals sign (=)
724
         *
725
         * This addresses the pair relationship between parameter name and value, like "id=77".
726
         */
727
        $uri_segments = [];
0 ignored issues
show
Coding Style introduced by
$uri_segments does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
728
729
        if (count($uri_query_array) > 0) {
0 ignored issues
show
Coding Style introduced by
$uri_query_array does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
730
            $key        = '';
731
            $value      = '';
732
            $query_pair = '';
0 ignored issues
show
Coding Style introduced by
$query_pair does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
733
            foreach ($uri_query_array as $query_pair) {
0 ignored issues
show
Coding Style introduced by
$uri_query_array does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
734
                if (false !== strpos($query_pair, '=')) {
0 ignored issues
show
Coding Style introduced by
$query_pair does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
735
                    list($key, $value)  = explode('=', $query_pair);
0 ignored issues
show
Coding Style introduced by
$query_pair does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
736
                    $uri_segments[$key] = $value;
0 ignored issues
show
Coding Style introduced by
$uri_segments does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
737
                }
738
            }
739
            unset($query_pair, $key, $value);
0 ignored issues
show
Coding Style introduced by
$query_pair does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
740
        }
741
        unset($uri_query_string, $uri_query_array);
0 ignored issues
show
Coding Style introduced by
$uri_query_string does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
742
743
        // Finished!
744
        return $uri_segments;
0 ignored issues
show
Coding Style introduced by
$uri_segments does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
745
    }
746
747
    /**
748
     * Check if Apache "mod_rewrite" is activated in configuration.
749
     *
750
     * @return bool True, if "mod_rewrite" enabled. False otherwise.
751
     */
752
    public static function isRewriteEngineOn()
753
    {
754
        // via constant
755
        if (defined('REWRITE_ENGINE_ON') && REWRITE_ENGINE_ON === true) {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return defined('REWRITE_...ITE_ENGINE_ON === true;.
Loading history...
756
            return true;
757
        }
758
759
        // via config
760
        /*if (isset($this->config['router']['mod_rewrite'])) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% 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...
761
            $bool = (bool) $this->config['router']['mod_rewrite'];
762
            define('REWRITE_ENGINE_ON', $bool);
763
764
            return $bool;
765
        }*/
766
767
        return false; # $this->checkEnvForModRewrite();
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% 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...
768
    }
769
770
    /**
771
     * Checks if Apache Module "mod_rewrite" is loaded/enabled
772
     * and Rewrite Engine is enabled in .htaccess".
773
     *
774
     * @return bool True, if mod_rewrite on.
775
     */
776
    public static function checkEnvForModRewrite()
0 ignored issues
show
Coding Style introduced by
function checkEnvForModRewrite() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
777
    {
778
        // ensure apache has module mod_rewrite active
779
        if (true === function_exists('apache_get_modules') 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...
780
            true === in_array('mod_rewrite', apache_get_modules(), true)) {
781
            if (true === is_file(APPLICATION_PATH . '.htaccess')) {
782
                // load htaccess and check if RewriteEngine is enabled
783
                $htaccess_content = file_get_contents(APPLICATION_PATH . '.htaccess');
0 ignored issues
show
Coding Style introduced by
$htaccess_content does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
784
                $rewriteEngineOn  = preg_match('/.*[^#][\t ]+RewriteEngine[\t ]+On/i', $htaccess_content);
0 ignored issues
show
Coding Style introduced by
$htaccess_content does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
785
786
                if (true === (bool) $rewriteEngineOn) {
0 ignored issues
show
Coding Style introduced by
The if-else statement can be simplified to return true === (bool) $rewriteEngineOn;.
Loading history...
787
                    return true;
788
                } else {
789
                    // @todo Hint: Please enable mod_rewrite in htaccess.
790
                    return false;
791
                }
792
            } else {
793
                // @todo Hint: No htaccess file found. Create and enable mod_rewrite.
794
                return false;
795
            }
796
        } else {
797
            // @todo Hint: Please enable mod_rewrite module for Apache.
798
            return false;
799
        }
800
    }
801
802
    /**
803
     * Replaces the placeholders in a route, like alpha, num, word
804
     * with their regular expressions for later preg_matching.
805
     * This is used while adding a new Route.
806
     *
807
     * @param string $route_with_placeholders A Route with a placeholder like alpha or num.
808
     */
809
    public static function placeholdersToRegexp($route_with_placeholders)
0 ignored issues
show
Coding Style introduced by
$route_with_placeholders does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
810
    {
811
        $placeholders = ['(:id)', '(:num)', '(:alpha)', '(:alphanum)', '(:any)', '(:word)',
812
                              '(:year)', '(:month)', '(:day)', ];
813
814
        $replacements = ['([0-9]+)', '([0-9]+)', '([a-zA-Z]+)', '([a-zA-Z0-9]+)', '(.*)', '(\w+)',
815
                              '([12][0-9]{3})', '(0[1-9]|1[012])', '(0[1-9]|1[012])', ];
816
817
        return str_replace($placeholders, $replacements, $route_with_placeholders);
0 ignored issues
show
Coding Style introduced by
$route_with_placeholders does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
818
    }
819
820
    /**
821
     * This unsets all Routes of Routing Table ($this->routes)
822
     * which have more segments then the request uri.
823
     */
824
    public function reduceRoutesToSegmentCount()
825
    {
826
        $route_pattern           = '';
0 ignored issues
show
Coding Style introduced by
$route_pattern does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
827
        $route_values            = '';
0 ignored issues
show
Coding Style introduced by
$route_values does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
828
        $number_of_uri_segements = count($this->uriSegments);
0 ignored issues
show
Coding Style introduced by
$number_of_uri_segements does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
829
830
        foreach ($this->routes as $route_pattern => $route_values) {
0 ignored issues
show
Coding Style introduced by
$route_pattern does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
831
            if ($route_values['number_of_segments'] === $number_of_uri_segements) {
0 ignored issues
show
Coding Style introduced by
$route_values does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
832
                continue;
833
            } else {
834
                unset($this->routes[$route_pattern]);
0 ignored issues
show
Coding Style introduced by
$route_pattern does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
835
            }
836
        }
837
838
        unset($route_pattern, $route_values);
0 ignored issues
show
Coding Style introduced by
$route_pattern does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
839
    }
840
841
    /**
842
     * Register the default routes.
843
     */
844
    public function loadDefaultRoutes()
845
    {
846
        // Is Routes Caching is enabled in config?
847
        if (isset($this->config['router']['caching'])) {
848
            self::$useCache = ($this->config['router']['caching'] === true) ? true : false;
849
        }
850
851
        // Load Routes from Cache
852
        if (true === self::$useCache and true === empty($this->routes) 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...
853
            Cache::contains('clansuite.routes')) {
854
            $this->addRoutes(Cache::read('clansuite.routes'));
0 ignored issues
show
Bug introduced by
The method read() does not seem to exist on object<Koch\Cache\Cache>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
855
        }
856
857
        // Load Routes from Config "routes.config.php"
858
        if (true === empty($this->routes)) {
859
            $routes = Manager::loadRoutesFromConfig();
860
            if ($routes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $routes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
861
                $this->addRoutes($routes);
862
            }
863
864
            // and save these routes to cache
865
            if (true === self::$useCache) {
866
                Cache::store('clansuite.routes', $this->getRoutes());
0 ignored issues
show
Documentation introduced by
$this->getRoutes() is of type array, but the function expects a object<Koch\Cache\type>.

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...
867
            }
868
        }
869
870
        /*
871
         * Connect some default fallback Routes
872
         *
873
         * Example for Route definition with ArrayAccess: $r['/:controller'];
874
         */
875
        if (empty($this->routes)) {
876
            # one segment
877
            //// "/news" (list)
878
            $this->addRoute('/:module');
879
            # two segments
880
            // "/news/new" (new)
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% 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...
881
            $this->addRoute('/:module/:action');
882
            // "/news/news" (list)
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% 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...
883
            $this->addRoute('/:module/:controller');
884
            // "/news/31" (show/update/delete)
885
            $this->addRoute('/:controller/(:id)', [1 => 'id']);
886
            // "/news/news/31" (show/update/delete)
887
            $this->addRoute('/:module/(:id)', [1 => 'id']);
888
            # three segments
889
            // "/news/news/new" (new)
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% 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...
890
            $this->addRoute('/:module/:controller/:action');
891
            // "/news/edit/42" (edit)
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...
892
            $this->addRoute('/:controller/:action/(:id)', [2 => 'id']);
893
            // "/news/42/edit" (edit)
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...
894
            $this->addRoute('/:module/(:id)/:action', [1 => 'id']);
895
            // "/news/news/31" (show/update/delete)
896
            $this->addRoute('/:module/:controller/(:id)', [2 => 'id']);
897
            # four segments
898
            // "/news/news/31/edit" (edit)
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...
899
            $this->addRoute('/:module/:controller/(:id)/:action', [2 => 'id']);
900
            // "/news/news/edit/31" (edit)
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...
901
            $this->addRoute('/:module/:controller/:action/(:id)', [3 => 'id']);
902
            # five segments
903
            // "/news/news/edit/31.html" (edit)
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...
904
            $this->addRoute('/:module/:controller/:action/(:id)/:format', [4 => 'id']);
905
        }
906
    }
907
908
    /**
909
     * Implementation of SPL ArrayAccess.
910
     */
911
912
    /**
913
     * Instead of working with $router->addRoute(name,map);
914
     * you may now access the routing table as an array $router[$route] = $map;.
915
     */
916
    final public function offsetSet($route, $target)
917
    {
918
        $this->addRoute($route, $target);
919
    }
920
921
    final public function offsetGet($name)
922
    {
923
        if ((isset($this->routes[$name])) || (array_key_exists($name, $this->routes))) {
924
            return $this->routes[$name];
925
        } else {
926
            return;
927
        }
928
    }
929
930
    final public function offsetExists($name)
931
    {
932
        return isset($this->routes[$name]) === true || array_key_exists($name, $this->routes);
933
    }
934
935
    final public function offsetUnset($name)
936
    {
937
        unset($this->routes[$name]);
938
    }
939
}
940