Completed
Push — master ( e1a197...682608 )
by Anderson
02:01
created

Route::trigger404()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Route class
5
 *
6
 * @author    Anderson Salas <[email protected]>
7
 * @copyright 2017
8
 * @license   GNU-3.0
9
 *
10
 */
11
12
namespace Luthier\Core;
13
14
class Route
15
{
16
17
    /**
18
     * Supported HTTP Verbs for this class
19
     *
20
     * @var static array $http_verbs Array of supported HTTP Verbs
21
     *
22
     * @access protected
23
     */
24
    protected static $http_verbs = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'TRACE', 'CONNECT', 'HEAD'];
25
26
27
    /**
28
     * All improved routes parsed
29
     *
30
     * @var static array $routes Array of routes with meta data
31
     *
32
     * @access protected
33
     */
34
    protected static $routes = array();
35
36
37
    /**
38
     * CodeIgniter 'default_controller' index of the $route variable in config/routes.php
39
     *
40
     * @var static $defaultController
41
     *
42
     * @access protected
43
     */
44
    protected static $defaultController;
45
46
47
    /**
48
     * CodeIgniter '404_override' index of the $route variable in config/routes.php
49
     *
50
     * @var static $_404page
51
     *
52
     * @access protected
53
     */
54
    protected static $_404page = NULL;
55
56
57
    /**
58
     * CodeIgniter 'translate_uri_dashes' index of the $route variable in config/routes.php
59
     *
60
     * @var static $translateDashes
61
     *
62
     * @access protected
63
     */
64
    protected static $translateDashes = FALSE;
65
66
67
    /**
68
     * Array of hidden routes, it will parsed as an route with a show_404() callback into a clousure
69
     *
70
     * @var static $hiddenRoutes
71
     *
72
     * @access protected
73
     */
74
    protected static $hiddenRoutes = array();
75
76
77
    /**
78
     * Route group '$hideOriginal' directive
79
     *
80
     * @var static $hideOriginals
81
     *
82
     * @access protected
83
     */
84
    protected static $hideOriginals = [];
85
86
87
    /**
88
     * Route group prefix
89
     *
90
     * @var static $prefix
91
     *
92
     * @access protected
93
     */
94
    protected static $prefix = [];
95
96
97
    /**
98
     * Route group namespace
99
     *
100
     * @var static $namespace
101
     *
102
     * @access protected
103
     */
104
    protected static $namespace = [];
105
106
107
    /**
108
     * Route group middleware
109
     *
110
     * @var static $middleware [add description]
111
     *
112
     * @access protected
113
     */
114
    protected static $middleware = [];
115
116
117
    /**
118
     * Generic method to add a improved route
119
     *
120
     * @param  mixed $verb String or array of string of valid HTTP Verbs that will be accepted in this route
121
     * @param  mixed $url String or array of strings that will trigger this route
0 ignored issues
show
Bug introduced by
There is no parameter named $url. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
122
     * @param  array $attr Associative array of route attributes
123
     * @param  bool $hideOriginal (Optional) map the original $url as a route with a show_404() callback inside
124
     *
125
     * @return void
126
     *
127
     * @access public
128
     * @static
129
     */
130
    public static function add($verb, $path, $attr, $hideOriginal = TRUE, $return = FALSE)
131
    {
132
        if(!is_array($attr))
133
        {
134
            show_error('You must specify the route attributes as an array',500,'Route error: bad attributes');
135
        }
136
137
        if(!isset($attr['uses']))
138
        {
139
            show_error('Route requires a \'controller@method\' to be pointed and it\'s not defined in the route attributes', 500, 'Route error: missing controller');
140
        }
141
142
        if(!preg_match('/^([a-zA-Z1-9-_]+)@([a-zA-Z1-9-_]+)$/', $attr['uses'], $parsedController) !== FALSE)
143
        {
144
            show_error('Route controller must be in format controller@method', 500, 'Route error: bad controller format');
145
        }
146
147
        $controller = $parsedController[1];
148
        $method     = $parsedController[2];
149
150
        if(!is_string($path))
151
            show_error('Route path must be a string ', 500, 'Route error: bad route path');
152
153
        if(!is_string($verb))
154
            show_error('Route HTTP Verb must be a string', 500, 'Route error: bad verb type');
155
156
        $verb = strtoupper($verb);
157
158
        if(!in_array($verb, self::$http_verbs,TRUE))
159
        {
160
            $errorMsg = 'Verb "'.$verb.'" is not a valid HTTP Verb, allowed verbs:<ul>';
161
            foreach( self::$http_verbs as $validVerb )
0 ignored issues
show
Bug introduced by
The expression self::$http_verbs of type object<Luthier\Core\Route> is not traversable.
Loading history...
162
            {
163
                $errorMsg .= '<li>'.$validVerb.'</li>';
164
            }
165
            $errorMsg .= '</ul>';
166
            show_error($errorMsg,500,'Route error: unknow method');
167
        }
168
169
        $route['verb'] = $verb;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$route was never initialized. Although not strictly required by PHP, it is generally a good practice to add $route = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
170
171
        $path = trim($path,'/');
172
173
        $route['path']       = $path;
174
        $route['controller'] = $controller;
175
        $route['method']     = $method;
176
177
        if(isset($attr['as']))
178
        {
179
            $route['name'] = $attr['as'];
180
        }
181
        else
182
        {
183
            $route['name'] = NULL;
184
        }
185
186
        // Setting up the prefix
187
188
        $route['prefix'] = NULL;
189
        $group_prefix = implode('/', self::$prefix);
190
191
        if($group_prefix)
192
            $route['prefix'] = $group_prefix.'/';
193
194
        if(isset($attr['prefix']))
195
            $route['prefix'] .= $attr['prefix'];
196
197
        // Setting up the namespace
198
199
        $route['namespace'] = NULL;
200
        $group_namespace = implode('/', self::$namespace);
201
202
        if(!is_null($group_namespace))
203
            $route['namespace'] = $group_namespace.'/';
204
        if(isset($attr['namespace']))
205
            $route['namespace'] .= $attr['namespace'];
206
207
        $route['prefix']    = trim($route['prefix'], '/');
208
        $route['namespace'] = trim($route['namespace'],'/');
209
210
        if(empty($route['prefix']))
211
            $route['prefix'] = NULL;
212
213
        if(empty($route['namespace']))
214
            $route['namespace'] = NULL;
215
216
        // Route middleware
217
        $route['middleware'] = [];
218
        $route['middleware'] = array_merge($route['middleware'], self::$middleware);
219
220
        if(isset($attr['middleware']))
221
        {
222
            if(is_array($attr['middleware']))
223
            {
224
                foreach($attr['middleware'] as $middleware)
225
                    $route['middleware'][] = $middleware; # Group
226
            }
227
            elseif( is_string($attr['middleware']))
228
            {
229
                $route['middleware'][] = $attr['middleware']; # Group
230
            }
231
            else
232
            {
233
                show_error('Route middleware must be a string or an array',500,'Route error: bad middleware format');
234
            }
235
        }
236
237
        $compiledRoute = self::compileRoute((object) $route);
238
239
        $route['compiled'] =
240
            [
241
                $compiledRoute->path => $compiledRoute->route
242
            ];
243
244
        $groupHideOriginals = end(self::$hideOriginals);
245
246
        if($hideOriginal || $groupHideOriginals || ($compiledRoute->path != '' && $compiledRoute->path != '/' ) )
247
        {
248
            $hiddenRoutePath      = $controller.'/'.$method;
249
            $hiddenRouteNamespace = '';
250
251
            if(!is_null($route['namespace']))
252
            {
253
                $hiddenRouteNamespace = $route['namespace'].'/';
254
            }
255
256
            $hiddenRoutePath = $hiddenRouteNamespace.$hiddenRoutePath;
257
258
            if($method == 'index')
259
            {
260
                self::$hiddenRoutes[] = [ $hiddenRouteNamespace.$controller  => function(){ self::trigger404(); }];
261
            }
262
263
            self::$hiddenRoutes[] = [$hiddenRoutePath => function(){ self::trigger404(); }];
264
        }
265
266
        if(!$return)
267
        {
268
            self::$routes[] = (object) $route;
269
        }
270
        else
271
        {
272
            return (object) $route;
273
        }
274
    }
275
276
277
    /**
278
     * Adds a GET route, alias of Route::add('GET',$url,$attr,$hideOriginal)
279
     *
280
     * @param  mixed $url String or array of strings that will trigger this route
281
     * @param  array $attr Associative array of route attributes
282
     * @param  bool $hideOriginal (Optional) map the original $url as a route with a show_404() callback inside
283
     *
284
     * @return void
285
     *
286
     * @access public
287
     * @static
288
     */
289
    public static function get($url, $attr, $hideOriginal = TRUE)
290
    {
291
        self::add('GET', $url,$attr, $hideOriginal);
292
    }
293
294
295
    /**
296
     * Adds a POST route, alias of Route::add('POST',$url,$attr,$hideOriginal)
297
     *
298
     * @param  mixed $url String or array of strings that will trigger this route
299
     * @param  array $attr Associative array of route attributes
300
     * @param  bool $hideOriginal (Optional) map the original $url as a route with a show_404() callback inside
301
     *
302
     * @return void
303
     *
304
     * @access public
305
     * @static
306
     */
307
    public static function post($url, $attr, $hideOriginal = TRUE)
308
    {
309
        self::add('POST', $url,$attr, $hideOriginal);
310
    }
311
312
313
    /**
314
     * Adds a PUT route, alias of Route::add('PUT',$url,$attr,$hideOriginal)
315
     *
316
     * @param  mixed $url String or array of strings that will trigger this route
317
     * @param  array $attr Associative array of route attributes
318
     * @param  bool $hideOriginal (Optional) map the original $url as a route with a show_404() callback inside
319
     *
320
     * @return void
321
     *
322
     * @access public
323
     * @static
324
     */
325
    public static function put($url, $attr, $hideOriginal = TRUE)
326
    {
327
        self::add('PUT', $url,$attr, $hideOriginal);
328
    }
329
330
331
    /**
332
     * Adds a PATCH route, alias of Route::add('PATCH',$url,$attr,$hideOriginal)
333
     *
334
     * @param  mixed $url String or array of strings that will trigger this route
335
     * @param  array $attr Associative array of route attributes
336
     * @param  bool $hideOriginal (Optional) map the original $url as a route with a show_404() callback inside
337
     *
338
     * @return void
339
     *
340
     * @access public
341
     * @static
342
     */
343
    public static function patch($url, $attr, $hideOriginal = TRUE)
344
    {
345
        self::add('PATCH', $url,$attr, $hideOriginal);
346
    }
347
348
349
    /**
350
     * Adds a DELETE route, alias of Route::add('DELETE',$url,$attr,$hideOriginal)
351
     *
352
     * @param  mixed $url String or array of strings that will trigger this route
353
     * @param  array $attr Associative array of route attributes
354
     * @param  bool $hideOriginal (Optional) map the original $url as a route with a show_404() callback inside
355
     *
356
     * @return void
357
     *
358
     * @access public
359
     * @static
360
     */
361
    public static function delete($url, $attr, $hideOriginal = TRUE)
362
    {
363
        self::add('DELETE', $url,$attr, $hideOriginal);
364
    }
365
366
367
    /**
368
     * Adds a route with ALL accepted verbs on Route::$http_verbs
369
     *
370
     * @param  mixed $url String or array of strings that will trigger this route
371
     * @param  array $attr Associative array of route attributes
372
     * @param  bool $hideOriginal (Optional) map the original $url as a route with a show_404() callback inside
373
     *
374
     * @return void
375
     *
376
     * @access public
377
     * @static
378
     */
379
    public static function any($url, $attr, $hideOriginal = TRUE)
380
    {
381
        foreach(self::$http_verbs as $verb)
0 ignored issues
show
Bug introduced by
The expression self::$http_verbs of type object<Luthier\Core\Route> is not traversable.
Loading history...
382
        {
383
            $verb = strtolower($verb);
384
            self::add($verb, $url, $attr, $hideOriginal);
385
        }
386
    }
387
388
389
    /**
390
     * Adds a list of routes with the verbs contained in $verbs, alias of Route::add($verbs,$url,$attr,$hideOriginal)
391
     *
392
     * @param  mixed $verb String or array of string of valid HTTP Verbs that will be accepted in this route
0 ignored issues
show
Bug introduced by
There is no parameter named $verb. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
393
     * @param  mixed $url String or array of strings that will trigger this route
394
     * @param  array $attr Associative array of route attributes
395
     * @param  bool $hideOriginal (Optional) map the original $url as a route with a show_404() callback inside
396
     *
397
     * @return void
398
     *
399
     * @access public
400
     * @static
401
     */
402
    public static function matches($verbs, $url, $attr, $hideOriginal = FALSE)
403
    {
404
        if(!is_array($verbs))
405
            show_error('Route::matches() first argument must be an array of valid HTTP Verbs', 500, 'Route error: bad Route::matches() verb list');
406
407
        foreach($verbs as $verb)
408
        {
409
            self::add($verb, $url, $attr, $hideOriginal);
410
        }
411
    }
412
413
414
    /**
415
     * Adds a RESTFul route wich contains methods for create, read, update, view an specific resource
416
     *
417
     *
418
     * @param  string $name
419
     * @param  string $controller
420
     * @param  array $attr
421
     *
422
     * @return void
423
     *
424
     * @access public
425
     * @static
426
     */
427
    public static function resource($name, $controller, $attr = NULL)
428
    {
429
        $base_attr = [];
430
431
        $hideOriginal = FALSE;
432
433
        if(isset($attr['namespace']))
434
            $base_attr['namespace']  = $attr['namespace'];
435
436
        if(isset($attr['middleware']))
437
            $base_attr['middleware'] = $attr['middleware'];
438
439
        if(isset($attr['hideOriginal']))
440
            $hideOriginal = (bool) $attr['hideOriginal'];
441
442
        $base_attr['prefix'] = strtolower($name);
443
444
        if(isset($attr['prefix']))
445
            $base_attr['prefix']  = $attr['prefix'];
446
447
        $only = [];
448
449
        $controller = strtolower($controller);
450
451
        if(isset($attr['only']) && (is_array($attr['only']) || is_string($attr['only'])))
452
        {
453
            if(is_array($attr['only']))
454
            {
455
                $only  = $attr['only'];
456
            }
457
            else
458
            {
459
                $only[] = $attr['only'];
460
            }
461
        }
462
463 View Code Duplication
        if(empty($only) || in_array('index', $only))
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...
464
        {
465
            $route_attr = array_merge($base_attr, ['uses' => $controller.'@index',   'as' => $name.'.index']);
466
            self::get('/', $route_attr, $hideOriginal);
467
        }
468
469 View Code Duplication
        if(empty($only) || in_array('create', $only))
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
        {
471
            $route_attr = array_merge($base_attr, ['uses' => $controller.'@create', 'as' => $name.'.create']);
472
            self::get('create', $route_attr, $hideOriginal);
473
        }
474
475 View Code Duplication
        if(empty($only) || in_array('store', $only))
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...
476
        {
477
            $route_attr = array_merge($base_attr, ['uses' => $controller.'@store', 'as' => $name.'.store']);
478
            self::post('/', $route_attr, $hideOriginal);
479
        }
480
481 View Code Duplication
        if(empty($only) || in_array('show', $only))
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...
482
        {
483
            $route_attr = array_merge($base_attr, ['uses' => $controller.'@show', 'as' => $name.'.show']);
484
            self::get('{slug}', $route_attr, $hideOriginal);
485
        }
486
487 View Code Duplication
        if(empty($only) || in_array('edit', $only))
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...
488
        {
489
            $route_attr = array_merge($base_attr, ['uses' => $controller.'@edit', 'as' => $name.'.edit']);
490
            self::get('{slug}/edit', $route_attr, $hideOriginal);
491
        }
492
493 View Code Duplication
        if(empty($only) || in_array('update', $only))
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...
494
        {
495
            $route_attr = array_merge($base_attr, ['uses' => $controller.'@update', 'as' => $name.'.update']);
496
            self::matches(['PUT', 'PATCH'], '{slug}/update', $route_attr, $hideOriginal);
497
        }
498
499 View Code Duplication
        if(empty($only) || in_array('destroy', $only))
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...
500
        {
501
            $route_attr = array_merge($base_attr, ['uses' => $controller.'@destroy', 'as' => $name.'.destroy']);
502
            self::delete('{slug}', $route_attr, $hideOriginal);
503
        }
504
    }
505
506
507
    /**
508
     * Compiles an improved route to a valid CodeIgniter route
509
     *
510
     * @param  array $route an improved route
511
     *
512
     * @return array
513
     *
514
     * @access public
515
     * @static
516
     */
517
    public static function compileRoute($route)
518
    {
519
        $prefix    = NULL;
520
        $namespace = NULL;
521
522
        if(!is_null($route->prefix))
523
        {
524
            $prefix = $route->prefix;
525
        }
526
527
        if(!is_null($route->namespace))
528
        {
529
            $namespace = $route->namespace;
530
        }
531
532
        $path = $route->path;
533
534
        if(!is_null($prefix))
535
            $path = $prefix.'/'.$path;
536
537
        $controller = $route->controller.'/'.$route->method;
538
539
        if(!is_null($namespace))
540
            $controller = $namespace.'/'.$controller;
541
542
        $path       = trim($path,'/');
543
        $controller = trim($controller,'/');
544
        $baseController = $controller;
545
546
        $replaces =
547
            [
548
                '{\((.*)\):[a-zA-Z0-9-_]*(\?}|})' => '($1)',   # Custom regular expression
549
                '{num:[a-zA-Z0-9-_]*(\?}|})'      => '(:num)', # (:num) route
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...
550
                '{any:[a-zA-Z0-9-_]*(\?}|})'      => '(:any)', # (:any) route
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...
551
                '{[a-zA-Z0-9-_]*(\?}|})'          => '(:any)', # Everything else
552
            ];
553
554
        $foundedArgs = [];
555
        $basePath    = '';
556
557
558
        foreach(explode('/', $path) as $path_segment)
559
        {
560
            if(!preg_match('/^\{(.*)\}$/', $path_segment))
561
            {
562
                $basePath .= $path_segment . '/' ;
563
            }
564
        }
565
566
        $basePath = trim($basePath,'/');
567
568
        foreach($replaces as $regex => $replace)
569
        {
570
            $matches = [];
571
572
            //if(preg_match_all('/'.$regex.'/', $path, $matches ))
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...
573
            if(preg_match_all('/\{(.*)\}/', $path, $matches ))
574
            {
575
                //$foundedArgs = $matches[0];
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...
576
                $foundedArgs = explode('/', $matches[0][0]);
577
                //var_dump($path, $foundedArgs);
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% 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...
578
            }
579
580
            $path = preg_replace('/'.$regex.'/', $replace, $path);
581
        }
582
583
        $argConstraint = FALSE;
584
585
        $args = [];
586
        $args['required'] = [];
587
        $args['optional'] = [];
588
589
        foreach($foundedArgs as $arg)
590
        {
591
            if(substr($arg,-2) == '?}')
592
            {
593
                $args['optional'][] = $arg;
594
                $argConstraint = TRUE;
595
            }
596
            else
597
            {
598
                if($argConstraint)
599
                    show_error('Optional route path argument not valid at this position', 500, 'Route error');
600
                $args['required'][] = $arg;
601
            }
602
        }
603
604
        if(count($foundedArgs) > 0)
605
        {
606
            for($i = 0; $i < count($foundedArgs); $i++)
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() 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...
607
            {
608
                $controller .= '/$'.($i + 1);
609
            }
610
        }
611
612
        return (object) [
613
            'path'      => $path,
614
            'route'     => $controller,
615
            'args'      => $args,
616
            'baseRoute' => $baseController,
617
            'basePath'  => $basePath
618
        ];
619
    }
620
621
622
    /**
623
     * Compile ALL improved routes into a valid CodeIgniter's associative array of routes
624
     *
625
     * @return array
626
     *
627
     * @access public
628
     * @static
629
     */
630
    public static function register()
631
    {
632
        $routes = array();
633
634
        foreach(self::$routes as $index => $route)
0 ignored issues
show
Bug introduced by
The expression self::$routes of type object<Luthier\Core\Route> is not traversable.
Loading history...
635
        {
636
            $compiled = self::compileRoute($route);
637
638
            $backtrackingPath  = '';
0 ignored issues
show
Unused Code introduced by
$backtrackingPath 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...
639
            $backtrackingRoute = '';
0 ignored issues
show
Unused Code introduced by
$backtrackingRoute 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...
640
641
            if( count($compiled->args['optional']) > 0 )
642
            {
643
                $e_path  = explode('/', $compiled->path);
644
                $e_route = explode('/', $compiled->route);
0 ignored issues
show
Unused Code introduced by
$e_route 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...
645
646
                $basePath = $compiled->basePath;
647
                $baseRoute = $compiled->baseRoute;
648
649
                $a = count(explode('/',$basePath));
650
651
                for($r = 0; $r < count($compiled->args['required']); $r++)
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() 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...
652
                {
653
                    $basePath .= '/' . $e_path[ $a + $r ];
654
                    $baseRoute .= '/' . '$' . ($r + 1);
655
                }
656
657
                $a = count(explode('/',$basePath));
658
                $b = ($r + 1);
659
660
                $backtracking = [];
661
662
                for($o = 0; $o <= count($compiled->args['optional']); $o++)
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() 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...
663
                {
664
                    $backtrackingPath  = $basePath;
665
                    $backtrackingRoute = $baseRoute;
666
667
                    for($c = 0; $c < $o  ; $c++)
668
                    {
669
                        $backtrackingPath .= '/' . $e_path[$a + $c - 1];
670
                        $backtrackingRoute .= '/' . '$' . ($b + $c);
671
                    }
672
673
                    $backtracking[$o] = [ 'path' => $backtrackingPath, 'route' => $backtrackingRoute ];
674
                }
675
676
                foreach($backtracking as $b_route)
677
                {
678
                    $b_compiled   = self::compileRoute($route);
679
                    $b_args       = array_merge($b_compiled->args['required'], $b_compiled->args['optional']);
680
                    $b_route_path = $b_route['path'];
681
682
                    foreach($b_args as $arg)
683
                    {
684
                        $b_route_path = preg_replace('/\((.*?)\)/', $arg, $b_route_path, 1);
685
                    }
686
687
                    self::add($route->verb, $b_route_path, ['uses' => $route->controller.'@'.$route->method]);
688
689
                    if( !isset($routes[$b_route['path']]) || $route->verb == 'GET' )
690
                    {
691
                        $routes[$b_route['path']] = $b_route['route'];
692
                    }
693
                }
694
            }
695
696
            if( !isset($routes[$compiled->path]) || $route->verb == 'GET' )
697
            {
698
                $routes[$compiled->path] = $compiled->route;
699
            }
700
701
            self::$routes[$index] = (object) $route;
702
        }
703
704
        foreach(self::$hiddenRoutes as $route)
0 ignored issues
show
Bug introduced by
The expression self::$hiddenRoutes of type object<Luthier\Core\Route> is not traversable.
Loading history...
705
        {
706
            $path = key($route);
707
            $_404 = $route[$path];
708
709
            if(!isset($routes[$path]))
710
                $routes[$path] = $_404;
711
        }
712
713
        if(is_null(self::$defaultController))
714
            show_error('You must specify a home route: Route::home() as default controller!', 500, 'Route error: missing default controller');
715
716
        $defaultController = self::$defaultController->compiled;
0 ignored issues
show
Bug introduced by
The property compiled 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...
717
        $defaultController = $defaultController[key($defaultController)];
718
719
        $routes['default_controller'] = $defaultController;
720
721
        if(is_null(self::$_404page))
722
        {
723
            $routes['404_override'] = '';
724
        }
725
        else
726
        {
727
            $routes['404_override'] = self::$_404page->controller;
0 ignored issues
show
Bug introduced by
The property controller does not seem to exist. Did you mean defaultController?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
728
        }
729
730
        $routes['translate_uri_dashes'] = self::$translateDashes;
731
732
        return $routes;
733
    }
734
735
736
    /**
737
     * Creates a group of routes with common attributes
738
     *
739
     * @param  array $attr set of global attributes
740
     * @param  callback $routes wich contains a set of Route methods
741
     *
742
     * @return void
743
     *
744
     * @access public
745
     * @static
746
     */
747
    public static function group($attr, $routes)
748
    {
749
        if(!is_array($attr))
750
            show_error('Group attribute must be a valid array');
751
752
        if(!isset($attr['prefix']))
753
            show_error('You must specify an prefix!');
754
755
        self::$prefix[] = $attr['prefix'];
756
757
        if(isset($attr['namespace']))
758
        {
759
            self::$namespace[] = $attr['namespace'];
760
        }
761
762
        if(isset($attr['hideOriginals']) && $attr['hideOriginals'] === TRUE)
763
        {
764
            self::$hideOriginals[] = TRUE;
765
        }
766
        else
767
        {
768
            self::$hideOriginals[] = FALSE;
769
        }
770
771
        if(isset($attr['middleware']))
772
        {
773
            if(is_array($attr['middleware']) || is_string($attr['middleware']))
774
            {
775
                if(is_array($attr['middleware']) && !empty($attr['middleware']))
776
                {
777
                    foreach($attr['middleware'] as $middleware)
778
                        self::$middleware[] = $middleware;
779
                }
780
                else
781
                {
782
                    self::$middleware[] = $attr['middleware'];
783
                }
784
            }
785
            else
786
            {
787
                show_error('Group middleware must be an array o a string', 500, 'Route error');
788
            }
789
        }
790
791
        $res = $routes->__invoke();
0 ignored issues
show
Bug introduced by
The method __invoke cannot be called on $routes (of type callable).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
Unused Code introduced by
$res 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...
792
793
        array_pop(self::$prefix);
794
        array_pop(self::$namespace);
795
        array_pop(self::$middleware);
796
        array_pop(self::$hideOriginals);
797
    }
798
799
800
    /**
801
     * Creates the 'default_controller' key in CodeIgniter's route array
802
     *
803
     * @param  string $route controller/method name
0 ignored issues
show
Bug introduced by
There is no parameter named $route. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
804
     * @param  string $alias (Optional) alias of the default controller
0 ignored issues
show
Bug introduced by
There is no parameter named $alias. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
805
     *
806
     * Due a CodeIgniter limitations, this route MAY NOT be a directory.
807
     *
808
     * @return void
809
     *
810
     * @access public
811
     * @static
812
     */
813
    public static function home($controller, $as = 'home', $attr = NULL)
814
    {
815
        $routeAttr =
816
            [
817
                'uses' => $controller,
818
                'as'   => $as
819
            ];
820
821
        if(!is_null($attr) && !is_array($attr))
822
            show_error('Default controller attributes must be an array',500,'Route error: bad attribute type');
823
824
        if(!is_null($attr))
825
            $routeAttr = array_merge($routeAttr,$attr);
0 ignored issues
show
Unused Code introduced by
$routeAttr 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...
826
827
        if(isset($attr['prefix']))
828
            show_error('Default controller may not have a prefix!',500,'Route error: prefix not allowed');
829
830
        self::$defaultController = self::$routes[] = self::add('GET', '/', ['uses' => $controller, 'as' => $as],TRUE, TRUE);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to self::$routes[] is correct as self::add('GET', '/', ar...s' => $as), TRUE, TRUE) (which targets Luthier\Core\Route::add()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
831
    }
832
833
834
    /**
835
     * Get all the improved routes defined
836
     *
837
     * @return array List of all defined routes
838
     *
839
     * @access public
840
     * @static
841
     */
842
    public static function getRoutes($verb = NULL)
843
    {
844
        if(is_null($verb))
845
        {
846
            return self::$routes;
847
        }
848
        else
849
        {
850
            $routes = [];
851
            foreach(self::$routes as $route)
0 ignored issues
show
Bug introduced by
The expression self::$routes of type object<Luthier\Core\Route> is not traversable.
Loading history...
852
            {
853
                if($route->verb == $verb)
854
                    $routes[] = $route;
855
            }
856
            return $routes;
857
        }
858
    }
859
860
861
    /**
862
     * Get all hidden routes
863
     *
864
     * @return array
865
     *
866
     * @access public
867
     * @static
868
     */
869
    public static function getHiddenRoutes()
870
    {
871
        return self::$hiddenRoutes;
872
    }
873
874
875
    /**
876
     * Retrieve a route wich is called $search (if exists)
877
     *
878
     * @param  string $search The route name to search
879
     * @param  $args (Optional) The route arguments that will be parsed
880
     *
881
     * @return mixed Founded route in case of success, and error in case of no matches.
882
     *
883
     * @access public
884
     * @static
885
     */
886
    public static function getRouteByName($search)
887
    {
888
        $founded = NULL;
889
890
        $args = func_get_args();
891
        unset($args[0]);
892
893
        foreach(self::$routes as $route)
0 ignored issues
show
Bug introduced by
The expression self::$routes of type object<Luthier\Core\Route> is not traversable.
Loading history...
894
        {
895
            if($route->name == $search)
896
            {
897
                $founded = $route;
898
            }
899
        }
900
901
        if(!is_null($founded))
902
        {
903
            $routeArgs        = self::compileRoute($founded)->args;
904
            $routeArgCount    = count($routeArgs['required']) + count($routeArgs['optional']);
905
            $routeReqArgCount = count($routeArgs['required']);
906
907
            if(count($args) < $routeReqArgCount)
908
            {
909
                $missingArgs = $routeReqArgCount - count($args);
910
                throw new \Exception('Missing '.$missingArgs.' required argument'.($missingArgs != 1 ? 's' : '').' for route "'.$founded->name.'"');
911
            }
912
            if(count($args) > $routeArgCount)
913
            {
914
                throw new \Exception('The route "'.$founded->name.'" expects maximum '.$routeArgCount.' argument'.($routeArgCount != 1 ? 's' : '').', '.count($args).' provided');
915
            }
916
917
            $path = self::compileRoute($founded)->path;
918
919
            foreach($args as $replacement)
920
            {
921
                $path = preg_replace('/\((.*?)\)/', $replacement, $path, 1);
922
            }
923
924
            $argsLeft =  $routeArgCount - count($args);
925
926
            for($i = $argsLeft; $i >= 0; $i--)
927
            {
928
                $path = preg_replace('/\((.*?)\)/', '', $path, 1);
929
            }
930
931
            return base_url(trim($path,'/'));
932
        }
933
934
        throw new \Exception('The route "'.$search.'" is not defined');
935
    }
936
937
938
    /**
939
     *  Heuristic testing of current uri_string in compiled routes
940
     *
941
     *  This is the 'reverse' process of the improved routing, it'll take the current
942
     *  uri string and attempts to find a CodeIgniter route that matches with his pattern
943
     *
944
     * @param  string $search
0 ignored issues
show
Bug introduced by
There is no parameter named $search. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
945
     *
946
     * @return mixed
947
     */
948
    public static function getRouteByPath($path, $requestMethod = NULL)
0 ignored issues
show
Coding Style introduced by
getRouteByPath 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...
949
    {
950
        if(is_null($requestMethod))
951
            $requestMethod = $_SERVER['REQUEST_METHOD'];
952
953
        $routes = self::getRoutes($requestMethod);
954
955
        if(empty($routes))
956
            return FALSE;
957
958
        $path = trim($path);
959
960
        if($path == '')
961
            return self::$defaultController;
962
963
        $wildcards =
964
            [
965
                '/\(:any\)/',
966
                '/\(:num\)/',
967
                '/\((.*?)\)/',
968
            ];
969
970
        $replaces =
971
            [
972
                '[^/]+',
973
                '[0-9]+',
974
                '(.*)'
975
            ];
976
977
        foreach( ['exact' , 'regex'] as $mode)
978
        {
979
            foreach( [ $path, $path.'/index' ] as $findPath )
980
            {
981
                foreach($routes as $route)
0 ignored issues
show
Bug introduced by
The expression $routes of type object<Luthier\Core\Route>|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
982
                {
983
                    $compiledPath = key($route->compiled);
984
985
                    if($mode == 'exact')
986
                    {
987
                        if($findPath == $compiledPath)
988
                            return $route;
989
                    }
990
                    else
991
                    {
992
                        $e_findPath     = explode('/', $findPath);
993
                        $e_compiledPath = explode('/', $compiledPath);
994
995
                        if( count($e_findPath) == count($e_compiledPath))
996
                        {
997
                            //var_dump($findPath, $compiledPath);
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% 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...
998
999
                            $valid = TRUE;
1000
                            $seachUntil = NULL;
1001
1002
                            for($i = 0; $i < count($e_findPath); $i++)
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() 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...
1003
                            {
1004
                                $reg = preg_replace($wildcards, $replaces, $e_compiledPath[$i]);
1005
1006
                                $valid = (bool) preg_match('#^'.$reg.'$#', $e_findPath[$i]);
1007
1008
                                if($valid && is_null($seachUntil))
1009
                                    $seachUntil = $i;
1010
                            }
1011
1012
                            if($valid)
1013
                            {
1014
                                for($i = 0; $i < $seachUntil; $i++)
1015
                                    $valid = $e_findPath[$i] == $e_compiledPath[$i];
1016
                            }
1017
1018
                            if($valid)
1019
                                return $route;
1020
                        }
1021
                    }
1022
                }
1023
            }
1024
        }
1025
1026
        return FALSE;
1027
    }
1028
1029
1030
    /**
1031
     * Parse improved route arguments by a provided path
1032
     *
1033
     * @param  object  $route
1034
     * @param  string  $path
1035
     *
1036
     * @return bool | object
1037
     *
1038
     * @access public
1039
     * @static
1040
     */
1041
    public static function getRouteArgs($route, $path)
1042
    {
1043
        $compiled = self::compileRoute($route);
0 ignored issues
show
Documentation introduced by
$route is of type object, but the function expects a array.

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...
1044
1045
        $r_seg = explode('/', $compiled->path);
1046
        $p_seg = explode('/', $path);
1047
1048
        $args   = [];
1049
        $n_args = 1;
1050
1051
        for($s = 0; $s < count($r_seg); $s++)
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() 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...
1052
        {
1053
            if(!isset($p_seg[$s]))
1054
                continue;
1055
1056
            if($r_seg[$s] != $p_seg[$s])
1057
            {
1058
                $args['$'.$n_args] = $p_seg[$s];
1059
                $n_args++;
1060
            }
1061
        }
1062
1063
        return $args;
1064
    }
1065
1066
1067
    /**
1068
     * Returns an array with the valid HTTP Verbs used in routes
1069
     *
1070
     * @return array
1071
     *
1072
     * @access public
1073
     * @static
1074
     */
1075
    public static function getHTTPVerbs()
1076
    {
1077
        return self::$http_verbs;
1078
    }
1079
1080
1081
    /**
1082
     * Set the 404 error controller ($route['404_override'])
1083
     *
1084
     * @param  string  $controller
1085
     * @param  string  $namespace (Optional)
0 ignored issues
show
Bug introduced by
There is no parameter named $namespace. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1086
     *
1087
     * @return void
1088
     *
1089
     * @access public
1090
     * @static
1091
     */
1092
    public static function set404($controller, $path = '404')
1093
    {
1094
        self::$_404page = (object)
1095
        [
1096
            'controller' => $controller,
1097
            'path'       => $path
1098
        ];
1099
    }
1100
1101
1102
    /**
1103
     * Get the 404 route
1104
     *
1105
     * @return array $_404page
1106
     *
1107
     * @return object | null
1108
     *
1109
     * @access public
1110
     * @static
1111
     */
1112
    public static function get404()
1113
    {
1114
        return self::$_404page;
1115
    }
1116
1117
1118
    /**
1119
     * Set the 'translate_uri_dashes' value ($route['translate_uri_dashes'])
1120
     *
1121
     * @param  $value
1122
     *
1123
     * @return void
1124
     *
1125
     * @access public
1126
     * @static
1127
     */
1128
    public static function setTrasnlateUriDashes($value)
1129
    {
1130
        self::$translateDashes = (bool) $value;
0 ignored issues
show
Documentation Bug introduced by
It seems like (bool) $value of type boolean is incompatible with the declared type object<Luthier\Core\Route> of property $translateDashes.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1131
    }
1132
1133
1134
    /**
1135
     * Attempts to trigger a nice 404 view (if a custom 404 controller is defined)
1136
     *
1137
     * @return void
1138
     *
1139
     * @access public
1140
     * @static
1141
     */
1142
    public static function trigger404()
1143
    {
1144
        if( !is_null(self::$_404page) )
1145
        {
1146
            header( 'Location: ' . config_item('base_url') . self::$_404page->path );
0 ignored issues
show
Bug introduced by
The property path 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...
1147
            die;
0 ignored issues
show
Coding Style Compatibility introduced by
The method trigger404() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1148
        }
1149
1150
        show_404();
1151
    }
1152
}