GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Route::dispatch()   F
last analyzed

Complexity

Conditions 20
Paths 603

Size

Total Lines 119
Code Lines 70

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 420

Importance

Changes 6
Bugs 0 Features 0
Metric Value
cc 20
eloc 70
nc 603
nop 0
dl 0
loc 119
ccs 0
cts 67
cp 0
crap 420
rs 2.3199
c 6
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Kotori.php
4
 *
5
 * A Tiny Model-View-Controller PHP Framework
6
 *
7
 * This content is released under the Apache 2 License
8
 *
9
 * Copyright (c) 2015-2017 Kotori Technology. All rights reserved.
10
 *
11
 * Licensed under the Apache License, Version 2.0 (the "License");
12
 * you may not use this file except in compliance with the License.
13
 * You may obtain a copy of the License at
14
 *
15
 *     http://www.apache.org/licenses/LICENSE-2.0
16
 *
17
 * Unless required by applicable law or agreed to in writing, software
18
 * distributed under the License is distributed on an "AS IS" BASIS,
19
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
 * See the License for the specific language governing permissions and
21
 * limitations under the License.
22
 */
23
24
/**
25
 * Route class
26
 *
27
 * Parses URIs and determines routing
28
 *
29
 * @package     Kotori
30
 * @subpackage  Http
31
 * @author      Kokororin
32
 * @link        https://kotori.love
33
 */
34
namespace Kotori\Http;
35
36
use Kotori\Core\Container;
37
use Kotori\Core\Helper;
38
use Kotori\Core\Middleware;
39
use Kotori\Debug\Hook;
40
use Kotori\Exception\ConfigException;
41
use Kotori\Exception\NotFoundException;
42
use Kotori\Exception\RouteNotFoundException;
43
use ReflectionClass;
44
use Symfony\Component\Finder\Finder;
45
use zpt\anno\Annotations;
46
47
class Route
48
{
49
    /**
50
     * Controllers Array
51
     *
52
     * @var array
53
     */
54
    protected $controllers = [];
55
56
    /**
57
     * Current controller
58
     *
59
     * @var string
60
     */
61
    protected $controller;
62
63
    /**
64
     * Current action
65
     *
66
     * @var string
67
     */
68
    protected $action;
69
70
    /**
71
     * Current URI string
72
     *
73
     * @var mixed
74
     */
75
    protected $uri = '';
76
77
    /**
78
     * Parsed URI Array
79
     *
80
     * @var array
81
     */
82
    protected $uris = [];
83
84
    /**
85
     * Parsed params
86
     *
87
     * @var array
88
     */
89
    protected $params = [];
90
91
    /**
92
     * Parsed routes
93
     *
94
     * @var array
95
     */
96
    protected $routes = [];
97
98
    /**
99
     * Class constructor
100
     *
101
     * Initialize route class.
102
     */
103 1
    public function __construct()
0 ignored issues
show
Coding Style introduced by
__construct 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...
Coding Style introduced by
__construct uses the super-global variable $_SERVER which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
104
    {
105 1
        if (Container::get('request')->isCli()) {
106 1
            $this->uri = $this->parseArgv();
107
        } else {
108
            if (isset($_GET['_i'])) {
109
                $_SERVER['PATH_INFO'] = $_GET['_i'];
110
            }
111
112
            $_SERVER['PATH_INFO'] = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO']
113
            : (isset($_SERVER['ORIG_PATH_INFO']) ? $_SERVER['ORIG_PATH_INFO']
114
                : (isset($_SERVER['REDIRECT_PATH_INFO']) ? $_SERVER['REDIRECT_PATH_INFO'] : ''));
115
116
            $this->uri = $_SERVER['PATH_INFO'];
117
        }
118
119 1
        if (substr($this->uri, 0, 1) == '/') {
120
            $this->uri = ltrim($this->uri, '/');
121
        }
122
123 1
        if (trim($this->uri, '/') == '') {
124
            $this->uri = '/';
125
        }
126
127 1
        Hook::listen(__CLASS__);
128 1
    }
129
130
    /**
131
     * Map URL to controller and action
132
     *
133
     * @return void
134
     *
135
     * @throws \Kotori\Exception\RouteNotFoundException
136
     * @throws \Kotori\Exception\NotFoundException
137
     */
138
    public function dispatch()
0 ignored issues
show
Coding Style introduced by
dispatch uses the super-global variable $_SERVER which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
dispatch 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...
Coding Style introduced by
dispatch uses the super-global variable $_REQUEST 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
dispatch uses the super-global variable $_POST which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
dispatch uses the super-global variable $_COOKIE 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...
139
    {
140
        if (strtolower(Container::get('config')->get('url_mode')) == 'query_string') {
141
            $this->uri = explode('?', $this->uri, 2);
142
            $_SERVER['QUERY_STRING'] = isset($this->uri[1]) ? $this->uri[1] : '';
143
            $this->uri = $this->uri[0];
144
            parse_str($_SERVER['QUERY_STRING'], $_GET);
145
        }
146
147
        if ($this->uri == 'favicon.ico') {
148
            return Container::get('response')->setStatus(404);
149
        }
150
151
        Middleware::register('before_route');
152
        $parsedRoute = $this->parseRoutes($this->uri);
153
        Middleware::register('after_route');
154
155
        if ($parsedRoute) {
156
            $this->uri = $parsedRoute;
157
        } else {
158
            if (Container::get('request')->isOptions()) {
159
                Container::get('response')->setStatus(204);
160
                exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method dispatch() 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...
161
            }
162
163
            throw new RouteNotFoundException('Request URI ' . $this->uri . ' is not Matched by any route.');
164
        }
165
166
        $this->uris = ($this->uri != '') ? explode('/', trim($this->uri, '/')) : [];
167
168
        // Clean uris
169
        foreach ($this->uris as $key => $value) {
170
            if ($value == '') {
171
                unset($this->uris[$key]);
172
            }
173
        }
174
175
        $this->uris = array_merge($this->uris);
176
177
        $this->controller = $this->getController();
178
        $this->action = $this->getAction();
179
180
        // If is already initialized
181
        $prefix = Container::get('config')->get('namespace_prefix');
182
183
        $controllerClassName = $prefix . 'controllers\\' . $this->controller;
184
185
        Middleware::register('before_controller');
186
187
        if (isset($this->controllers[$this->controller])) {
188
            $class = $this->controllers[$this->controller];
189
        } else {
190
            $constructInstances = $this->getMethodInstances($controllerClassName);
191
            if (!$constructInstances) {
192
                $class = new $controllerClassName();
193
            } else {
194
                $reflect = new ReflectionClass($controllerClassName);
195
                $class = $reflect->newInstanceArgs($constructInstances);
196
            }
197
198
            $this->controllers[$this->controller] = $class;
199
        }
200
201
        Middleware::register('after_controller');
202
203
        if (!class_exists($controllerClassName)) {
204
            throw new NotFoundException('Request Controller ' . $this->controller . ' is not Found.');
205
        }
206
207
        if (!method_exists($class, $this->action)) {
208
            throw new NotFoundException('Request Action ' . $this->action . ' is not Found.');
209
        }
210
211
        $callback = [$class, $this->action];
212
        if (!is_callable($callback)) {
213
            throw new NotFoundException($controllerClassName . '::' . $this->action . '() is not callable');
214
        }
215
216
        // Parse params from uri
217
        $this->params = $this->getParams();
218
219
        // Do some final cleaning of the params
220
        $_GET = array_merge($this->params, $_GET);
221
        $_REQUEST = array_merge($_POST, $_GET, $_COOKIE);
222
223
        if (Container::get('config')->get('app_debug')) {
224
            Container::get('response')->setHeader('X-Kotori-Hash', call_user_func(function () {
225
                $lockFile = Helper::getComposerVendorPath() . '/../composer.lock';
226
                if (!Helper::isFile($lockFile)) {
227
                    return 'unknown';
228
                } else {
229
                    $lockData = file_get_contents($lockFile);
230
                    $lockData = json_decode($lockData, true);
231
                    foreach ($lockData['packages'] as $package) {
232
                        if ($package['name'] == 'kokororin/kotori-php') {
233
                            return substr($package['source']['reference'], 0, 6);
234
                        }
235
                    }
236
                }
237
238
                return 'unknown';
239
            }));
240
        }
241
242
        Middleware::register('before_action');
243
        // Call the requested method
244
245
        $methodInstances = $this->getMethodInstances($callback[0], $callback[1]);
246
        if (!$methodInstances) {
247
            $output = call_user_func_array($callback, $this->params);
248
        } else {
249
            $output = call_user_func_array($callback, $methodInstances);
250
        }
251
252
        Middleware::register('after_action');
253
        if (is_array($output)) {
254
            Container::get('response')->throwJSON($output);
255
        }
256
    }
257
258
    /**
259
     * Returns the controller name
260
     *
261
     * @return string
262
     *
263
     * @throws \Kotori\Exception\NotFoundException
264
     */
265 View Code Duplication
    public function getController()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
266
    {
267
        if (isset($this->uris[0]) && '' !== $this->uris[0]) {
268
            $_controller = $this->uris[0];
269
        } else {
270
            throw new NotFoundException('Cannot dispatch controller name.');
271
        }
272
273
        return strip_tags($_controller);
274
    }
275
276
    /**
277
     * Returns the action name
278
     *
279
     * @return string
280
     *
281
     * @throws \Kotori\Exception\NotFoundException
282
     */
283 View Code Duplication
    public function getAction()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
284
    {
285
        if (isset($this->uris[1])) {
286
            $_action = $this->uris[1];
287
        } else {
288
            throw new NotFoundException('Cannot dispatch action name.');
289
        }
290
291
        return strip_tags($_action);
292
    }
293
294
    /**
295
     * Returns the request params
296
     *
297
     * @return array
298
     */
299
    public function getParams()
300
    {
301
        $params = $this->uris;
302
        unset($params[0], $params[1]);
303
        return array_merge($params);
304
    }
305
306
    /**
307
     * Returns the request params instances
308
     *
309
     * @param  mixed  $class
310
     * @param  string $methodName
311
     * @return mixed
312
     *
313
     * @throws \Kotori\Exception\NotFoundException
314
     */
315
    private function getMethodInstances($class, $methodName = '__construct')
316
    {
317
        if (is_object($class)) {
318
            $reflectClass = new ReflectionClass(get_class($class));
319
        } else {
320
            $reflectClass = new ReflectionClass($class);
321
        }
322
323
        $instances = [];
324
        $hasDI = false;
325
326
        if ($reflectClass->hasMethod($methodName)) {
327
            $reflectMethod = $reflectClass->getMethod($methodName);
328
329
            $params = $reflectMethod->getParameters();
330
331
            if (count($params) > 0) {
332
                foreach ($params as $param) {
333
                    $paramClass = $param->getClass();
334
                    if ($paramClass) {
335
                        $paramClassName = $paramClass->getName();
336
                        array_push($instances, Container::getByClassName($paramClassName));
337
                        $hasDI = true;
338
                    } elseif ($hasDI) {
339
                        throw new NotFoundException('Dependency Injection cannot work with normal params');
340
                    }
341
                }
342
            }
343
        }
344
345
        if ($hasDI) {
346
            return $instances;
347
        }
348
349
        return false;
350
    }
351
352
    /**
353
     * Returns the URI
354
     *
355
     * @return string
356
     */
357
    public function getUri()
358
    {
359
        return $this->uri;
360
    }
361
362
    /**
363
     * Returns routes
364
     *
365
     * @return array
366
     */
367
    public function getRoutes()
368
    {
369
        return $this->routes;
370
    }
371
372
    /**
373
     * Parse Routes
374
     *
375
     * Matches any routes that may exist in URL_ROUTE array
376
     * against the URI to determine if the class/method need to be remapped.
377
     *
378
     * @param  string $uri
379
     * @return string
380
     */
381
    protected function parseRoutes($uri)
0 ignored issues
show
Coding Style introduced by
parseRoutes 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...
382
    {
383
        if (Container::get('config')->get('url_route_annotation')) {
384
            $finder = new Finder();
385
            $finder
386
                ->in(Container::get('config')->get('app_full_path') . '/controllers')
387
                ->name('*.php');
388
            $controllerNamespaces = [];
389
            foreach ($finder as $file) {
390
                array_push($controllerNamespaces, '\\' . Container::get('config')->get('namespace_prefix') . 'controllers\\' . $file->getBasename('.' . $file->getExtension()));
391
            }
392
393
            foreach ($controllerNamespaces as $namespace) {
394
                $classReflector = new ReflectionClass($namespace);
395
                $classAnnotations = new Annotations($classReflector);
0 ignored issues
show
Unused Code introduced by
$classAnnotations 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...
396
397
                foreach ($classReflector->getMethods() as $methodReflector) {
398
                    $methodAnnotations = new Annotations($methodReflector);
399
                    if (!isset($methodAnnotations['route'])) {
400
                        continue;
401
                    } else {
402
                        $routeAnnotations = $methodAnnotations['route'];
403
                        if (!isset($routeAnnotations['uri'])) {
404
                            throw new ConfigException('Route annotations error');
405
                        }
406
407
                        $controllerName = $classReflector->getShortName();
408
                        $actionName = $methodReflector->getName();
0 ignored issues
show
Bug introduced by
Consider using $methodReflector->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
409
                        $route = $controllerName . '/' . $actionName;
410
                        if (isset($routeAnnotations['params'])) {
411
                            $route .= '/' . $routeAnnotations['params'];
412
                        }
413
414
                        if (!isset($routeAnnotations['method'])) {
415
                            $this->routes[$routeAnnotations['uri']] = $route;
416
                        } else {
417
                            $this->routes[$routeAnnotations['uri']][$routeAnnotations['method']] = $route;
418
                        }
419
420
                        unset($route);
421
                    }
422
                }
423
            }
424
        } else {
425
            $this->routes = Container::get('config')->get('url_route');
426
        }
427
428
        $hostName = Container::get('request')->getHostName();
429
430
        if (isset($this->routes[$hostName])) {
431
            $this->routes = $this->routes[$hostName];
432
        }
433
434
        // Get HTTP verb
435
        $httpVerb = isset($_SERVER['REQUEST_METHOD']) ? strtolower($_SERVER['REQUEST_METHOD']) : 'cli';
436
437
        if (null != $this->routes) {
438
            foreach ($this->routes as $key => $val) {
439
                // Check if route format is using HTTP verbs
440
                if (is_array($val)) {
441
                    $val = array_change_key_case($val, CASE_LOWER);
442
                    if (isset($val[$httpVerb])) {
443
                        $val = $val[$httpVerb];
444
                    } else {
445
                        continue;
446
                    }
447
                }
448
449
                // Does the RegEx match?
450
                if (preg_match('#^' . $key . '$#', $uri, $matches)) {
451
                    // Are we using callbacks to process back-references?
452
                    if (!is_string($val) && is_callable($val)) {
453
                        // Remove the original string from the matches array.
454
                        array_shift($matches);
455
456
                        // Execute the callback using the values in matches as its parameters.
457
                        $val = call_user_func_array($val, $matches);
458
                    } // Are we using the default routing method for back-references?
459
                    elseif (strpos($val, '$') !== false && strpos($key, '(') !== false) {
460
                        $val = preg_replace('#^' . $key . '$#', $val, $uri);
461
                    }
462
463
                    return $val;
464
                }
465
            }
466
        }
467
    }
468
469
    /**
470
     * Parse CLI arguments
471
     *
472
     * Take each command line argument and assume it is a URI segment.
473
     *
474
     * @return  string
475
     */
476 1
    protected function parseArgv()
0 ignored issues
show
Coding Style introduced by
parseArgv 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...
477
    {
478 1
        $args = array_slice($_SERVER['argv'], 1);
479 1
        return $args ? implode('/', $args) : '';
480
    }
481
482
    /**
483
     * Build Full URL
484
     *
485
     * @param  string $uri
486
     * @param  string $module
487
     * @return string
488
     *
489
     * @throws \Kotori\Exception\ConfigException
490
     */
491
    public function url($uri = '', $module = null)
492
    {
493
        if ($module != null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $module of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
494
            $appNames = Container::get('config')->get('app_name');
495
            if (is_array($appNames)) {
496
                foreach ($appNames as &$appName) {
497
                    $appName = str_replace('./', '', $appName);
498
                }
499
500
                $appNames = array_flip($appNames);
501
                $baseUrl = $appNames[$module];
502
                $baseUrl = '//' . $baseUrl . '/';
503
            }
504
        } else {
505
            $baseUrl = Container::get('request')->getBaseUrl();
506
        }
507
508
        $uri = is_array($uri) ? implode('/', $uri) : trim($uri, '/');
509
        $prefix = $baseUrl . 'index.php?_i=';
0 ignored issues
show
Bug introduced by
The variable $baseUrl does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
510
511
        switch (strtolower(Container::get('config')->get('url_mode'))) {
512
            case 'path_info':
513
                return $uri == '' ? rtrim($baseUrl, '/') : $baseUrl . $uri;
514
            case 'query_string':
515
                return $uri == '' ? rtrim($baseUrl, '/') : $prefix . $uri;
516
            default:
517
                throw new ConfigException('`url_mode` Config ERROR');
518
        }
519
    }
520
}
521