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.
Completed
Push — master ( f8be1c...07c7d5 )
by やかみ
03:38
created

Route::parseRoutes()   D

Complexity

Conditions 20
Paths 89

Size

Total Lines 90
Code Lines 52

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 420

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 20
eloc 52
c 2
b 0
f 0
nc 89
nop 1
dl 0
loc 90
ccs 0
cts 48
cp 0
crap 420
rs 4.7294

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
     * Class constructor
93
     *
94
     * Initialize route class.
95
     *
96
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

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

Please refer to the PHP core documentation on constructors.

Loading history...
97
     */
98 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...
99
    {
100 1
        if (Container::get('request')->isCli()) {
101 1
            $this->uri = $this->parseArgv();
102
        } else {
103
            if (isset($_GET['_i'])) {
104
                $_SERVER['PATH_INFO'] = $_GET['_i'];
105
            }
106
107
            $_SERVER['PATH_INFO'] = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO']
108
            : (isset($_SERVER['ORIG_PATH_INFO']) ? $_SERVER['ORIG_PATH_INFO']
109
                : (isset($_SERVER['REDIRECT_PATH_INFO']) ? $_SERVER['REDIRECT_PATH_INFO'] : ''));
110
111
            $this->uri = $_SERVER['PATH_INFO'];
112
        }
113
114 1
        if (substr($this->uri, 0, 1) == '/') {
115
            $this->uri = ltrim($this->uri, '/');
116
        }
117
118 1
        if (trim($this->uri, '/') == '') {
119
            $this->uri = '/';
120
        }
121
122 1
        Hook::listen(__CLASS__);
123 1
    }
124
125
    /**
126
     * Map URL to controller and action
127
     *
128
     * @return void
129
     *
130
     * @throws \Kotori\Exception\RouteNotFoundException
131
     * @throws \Kotori\Exception\NotFoundException
132
     */
133
    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...
134
    {
135
        if (strtolower(Container::get('config')->get('url_mode')) == 'query_string') {
136
            $this->uri = explode('?', $this->uri, 2);
137
            $_SERVER['QUERY_STRING'] = isset($this->uri[1]) ? $this->uri[1] : '';
138
            $this->uri = $this->uri[0];
139
            parse_str($_SERVER['QUERY_STRING'], $_GET);
140
        }
141
142
        if ($this->uri == 'favicon.ico') {
143
            return Container::get('response')->setStatus(404);
144
        }
145
146
        Middleware::register('before_route');
147
        $parsedRoute = $this->parseRoutes($this->uri);
148
        Middleware::register('after_route');
149
150
        if ($parsedRoute) {
151
            $this->uri = $parsedRoute;
152
        } else {
153
            if (Container::get('request')->isOptions()) {
154
                Container::get('response')->setStatus(204);
155
                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...
156
            }
157
158
            throw new RouteNotFoundException('Request URI ' . $this->uri . ' is not Matched by any route.');
159
        }
160
161
        $this->uris = ($this->uri != '') ? explode('/', trim($this->uri, '/')) : [];
162
163
        // Clean uris
164
        foreach ($this->uris as $key => $value) {
165
            if ($value == '') {
166
                unset($this->uris[$key]);
167
            }
168
        }
169
170
        $this->uris = array_merge($this->uris);
171
172
        $this->controller = $this->getController();
173
        $this->action = $this->getAction();
174
175
        // If is already initialized
176
        $prefix = Container::get('config')->get('namespace_prefix');
177
178
        $controllerClassName = $prefix . 'controllers\\' . $this->controller;
179
180
        Middleware::register('before_controller');
181
182
        if (isset($this->controllers[$this->controller])) {
183
            $class = $this->controllers[$this->controller];
184
        } else {
185
            $class = new $controllerClassName();
186
            $this->controllers[$this->controller] = $class;
187
        }
188
189
        Middleware::register('after_controller');
190
191
        if (!class_exists($controllerClassName)) {
192
            throw new NotFoundException('Request Controller ' . $this->controller . ' is not Found.');
193
        }
194
195
        if (!method_exists($class, $this->action)) {
196
            throw new NotFoundException('Request Action ' . $this->action . ' is not Found.');
197
        }
198
199
        $callback = [$class, $this->action];
200
        if (!is_callable($callback)) {
201
            throw new NotFoundException($controllerClassName . '::' . $this->action . '() is not callable');
202
        }
203
204
        // Parse params from uri
205
        $this->params = $this->getParams();
206
207
        // Do some final cleaning of the params
208
        $_GET = array_merge($this->params, $_GET);
209
        $_REQUEST = array_merge($_POST, $_GET, $_COOKIE);
210
211
        if (Container::get('config')->get('app_debug')) {
212
            Container::get('response')->setHeader('X-Kotori-Hash', call_user_func(function () {
213
                $lockFile = Helper::getComposerVendorPath() . '/../composer.lock';
214
                if (!Helper::isFile($lockFile)) {
215
                    return 'unknown';
216
                } else {
217
                    $lockData = file_get_contents($lockFile);
218
                    $lockData = json_decode($lockData, true);
219
                    foreach ($lockData['packages'] as $package) {
220
                        if ($package['name'] == 'kokororin/kotori-php') {
221
                            return substr($package['source']['reference'], 0, 6);
222
                        }
223
                    }
224
                }
225
226
                return 'unknown';
227
            }));
228
        }
229
230
        Middleware::register('before_action');
231
        // Call the requested method
232
        call_user_func_array($callback, $this->params);
233
        Middleware::register('after_action');
234
    }
235
236
    /**
237
     * Returns the controller name
238
     *
239
     * @return      string
240
     *
241
     * @throws \Kotori\Exception\NotFoundException
242
     */
243
    public function getController()
244
    {
245
        if (isset($this->uris[0]) && '' !== $this->uris[0]) {
246
            $_controller = $this->uris[0];
247
        } else {
248
            throw new NotFoundException('Cannot dispatch controller name.');
249
        }
250
251
        return strip_tags($_controller);
252
    }
253
254
    /**
255
     * Returns the action name
256
     *
257
     * @return      string
258
     *
259
     * @throws \Kotori\Exception\NotFoundException
260
     */
261
    public function getAction()
262
    {
263
        if (isset($this->uris[1])) {
264
            $_action = $this->uris[1];
265
        } else {
266
            throw new NotFoundException('Cannot dispatch action name.');
267
        }
268
269
        return strip_tags($_action);
270
    }
271
272
    /**
273
     * Returns the request params
274
     *
275
     * @return array
276
     */
277
    public function getParams()
278
    {
279
        $params = $this->uris;
280
        unset($params[0], $params[1]);
281
        return array_merge($params);
282
    }
283
284
    /**
285
     * Returns the URI
286
     *
287
     * @return string
288
     */
289
    public function getUri()
290
    {
291
        return $this->uri;
292
    }
293
294
    /**
295
     * Parse Routes
296
     *
297
     * Matches any routes that may exist in URL_ROUTE array
298
     * against the URI to determine if the class/method need to be remapped.
299
     *
300
     * @param  string $uri
301
     * @return string
302
     */
303
    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...
304
    {
305
        if (Container::get('config')->get('url_route_annotation')) {
306
            $finder = new Finder();
307
            $finder
308
                ->in(Container::get('config')->get('app_full_path') . '/controllers')
309
                ->name('*.php');
310
            $controllerNamespaces = [];
311
            $routes = [];
312
            foreach ($finder as $file) {
313
                array_push($controllerNamespaces, '\\' . Container::get('config')->get('namespace_prefix') . 'controllers\\' . $file->getBasename('.' . $file->getExtension()));
314
            }
315
316
            foreach ($controllerNamespaces as $namespace) {
317
                $classReflector = new ReflectionClass($namespace);
318
                $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...
319
320
                foreach ($classReflector->getMethods() as $methodReflector) {
321
                    $methodAnnotations = new Annotations($methodReflector);
322
                    if (!isset($methodAnnotations['route'])) {
323
                        continue;
324
                    } else {
325
                        $routeAnnotations = $methodAnnotations['route'];
326
                        if (!isset($routeAnnotations['uri'])) {
327
                            throw new ConfigException('Route annotations error');
328
                        }
329
330
                        $controllerName = $classReflector->getShortName();
331
                        $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...
332
                        $route = $controllerName . '/' . $actionName;
333
                        if (isset($routeAnnotations['regexp'])) {
334
                            $route .= '/' . $routeAnnotations['regexp'];
335
                        }
336
337
                        if (!isset($routeAnnotations['method'])) {
338
                            $routes[$routeAnnotations['uri']] = $route;
339
                        } else {
340
                            $routes[$routeAnnotations['uri']][$routeAnnotations['method']] = $route;
341
                        }
342
343
                        unset($route);
344
                    }
345
                }
346
            }
347
        } else {
348
            $routes = Container::get('config')->get('url_route');
349
        }
350
351
        $hostName = Container::get('request')->getHostName();
352
353
        if (isset($routes[$hostName])) {
354
            $routes = $routes[$hostName];
355
        }
356
357
        // Get HTTP verb
358
        $httpVerb = isset($_SERVER['REQUEST_METHOD']) ? strtolower($_SERVER['REQUEST_METHOD']) : 'cli';
359
360
        if (null != $routes) {
361
            foreach ($routes as $key => $val) {
362
                // Check if route format is using HTTP verbs
363
                if (is_array($val)) {
364
                    $val = array_change_key_case($val, CASE_LOWER);
365
                    if (isset($val[$httpVerb])) {
366
                        $val = $val[$httpVerb];
367
                    } else {
368
                        continue;
369
                    }
370
                }
371
372
                // Does the RegEx match?
373
                if (preg_match('#^' . $key . '$#', $uri, $matches)) {
374
                    // Are we using callbacks to process back-references?
375
                    if (!is_string($val) && is_callable($val)) {
376
                        // Remove the original string from the matches array.
377
                        array_shift($matches);
378
379
                        // Execute the callback using the values in matches as its parameters.
380
                        $val = call_user_func_array($val, $matches);
381
                    }
382
                    // Are we using the default routing method for back-references?
383
                    elseif (strpos($val, '$') !== false && strpos($key, '(') !== false) {
384
                        $val = preg_replace('#^' . $key . '$#', $val, $uri);
385
                    }
386
387
                    return $val;
388
                }
389
            }
390
        }
391
392
    }
393
394
    /**
395
     * Parse CLI arguments
396
     *
397
     * Take each command line argument and assume it is a URI segment.
398
     *
399
     * @return  string
400
     */
401 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...
402
    {
403 1
        $args = array_slice($_SERVER['argv'], 1);
404 1
        return $args ? implode('/', $args) : '';
405
    }
406
407
    /**
408
     * Build Full URL
409
     *
410
     * @param  string $uri
411
     * @param  string $module
412
     * @return string
413
     *
414
     * @throws \Kotori\Exception\ConfigException
415
     */
416
    public function url($uri = '', $module = null)
417
    {
418
        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...
419
            $appNames = Container::get('config')->get('app_name');
420
            if (is_array($appNames)) {
421
                foreach ($appNames as &$appName) {
422
                    $appName = str_replace('./', '', $appName);
423
                }
424
425
                $appNames = array_flip($appNames);
426
                $baseUrl = $appNames[$module];
427
                $baseUrl = '//' . $baseUrl . '/';
428
            }
429
        } else {
430
            $baseUrl = Container::get('request')->getBaseUrl();
431
        }
432
433
        $uri = is_array($uri) ? implode('/', $uri) : trim($uri, '/');
434
        $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...
435
436
        switch (strtolower(Container::get('config')->get('url_mode'))) {
437
            case 'path_info':
438
                return $uri == '' ? rtrim($baseUrl, '/') : $baseUrl . $uri;
439
            case 'query_string':
440
                return $uri == '' ? rtrim($baseUrl, '/') : $prefix . $uri;
441
            default:
442
                throw new ConfigException('`url_mode` Config ERROR');
443
        }
444
445
    }
446
447
}
448