Passed
Push — master ( d5c2bc...4e3da0 )
by Marcio
04:23 queued 11s
created

Router::criateRoute()   A

Complexity

Conditions 6
Paths 32

Size

Total Lines 17
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 13
c 1
b 0
f 0
dl 0
loc 17
rs 9.2222
cc 6
nc 32
nop 5
1
<?php
2
/**
3
 * @Package: Router - simple router class for php
4
 * @Class  : Router
5
 * @Author : izni burak demirtas / @izniburak <[email protected]>
6
 * @Web    : http://burakdemirtas.org
7
 * @URL    : https://github.com/izniburak/php-router
8
 * @Licence: The MIT License (MIT) - Copyright (c) - http://opensource.org/licenses/MIT
9
 */
10
11
namespace Ballybran\Routing;
12
13
use Ballybran\Routing\Router\RouterCommand;
14
use Ballybran\Routing\Router\RouterException;
15
use Ballybran\Routing\Router\RouterRequest;
16
use Ballybran\Routing\Router\RouteMiddleware;
17
use Closure;
18
use Exception;
19
use ReflectionMethod;
20
21
/**
22
 * Class Router
23
 *
24
 * @method $this any($route, $settings, $callback = null)
25
 * @method $this get($route, $settings, $callback = null)
26
 * @method $this post($route, $settings, $callback = null)
27
 * @method $this put($route, $settings, $callback = null)
28
 * @method $this delete($route, $settings, $callback = null)
29
 * @method $this patch($route, $settings, $callback = null)
30
 * @method $this head($route, $settings, $callback = null)
31
 * @method $this options($route, $settings, $callback = null)
32
 * @method $this xpost($route, $settings, $callback = null)
33
 * @method $this xput($route, $settings, $callback = null)
34
 * @method $this xdelete($route, $settings, $callback = null)
35
 * @method $this xpatch($route, $settings, $callback = null)
36
 *
37
 * @package Buki
38
 */
39
class Router extends RouteMiddleware
40
{
41
    /**
42
     * @var string
43
     */
44
    protected $documentRoot = '';
45
46
    /**
47
     * @var string
48
     */
49
    protected $runningPath = '';
50
51
    /**
52
     * @var string $baseFolder Pattern definitions for parameters of Route
53
     */
54
    protected $baseFolder;
55
56
    /**
57
     * @var array $routes Routes list
58
     */
59
    protected $routes = [];
60
61
    /**
62
     * @var array $groups List of group routes
63
     */
64
    protected $groups = [];
65
66
    /**
67
     * @var array $patterns Pattern definitions for parameters of Route
68
     */
69
    protected $patterns = [
70
        ':id' => '(\d+)',
71
        ':number' => '(\d+)',
72
        ':any' => '([^/]+)',
73
        ':all' => '(.*)',
74
        ':string' => '(\w+)',
75
        ':slug' => '([\w\-_]+)',
76
    ];
77
78
    /**
79
     * @var array $namespaces Namespaces of Controllers and Middlewares files
80
     */
81
    protected $namespaces = [
82
        'middlewares' => '',
83
        'controllers' => '',
84
    ];
85
86
    /**
87
     * @var array $path Paths of Controllers and Middlewares files
88
     */
89
    protected $paths = [
90
        'controllers' => 'Controllers',
91
        'middlewares' => 'Middlewares',
92
    ];
93
94
    /**
95
     * @var string $mainMethod Main method for controller
96
     */
97
    protected $mainMethod = 'main';
98
99
    /**
100
     * @var string $cacheFile Cache file
101
     */
102
    protected $cacheFile = null;
103
104
    /**
105
     * @var bool $cacheLoaded Cache is loaded?
106
     */
107
    protected $cacheLoaded = false;
108
109
    /**
110
     * @var Closure $errorCallback Route error callback function
111
     */
112
    protected $errorCallback;
113
114
    /**
115
     * @var array $middlewares General middlewares for per request
116
     */
117
    protected $middlewares = [];
118
119
    /**
120
     * @var array $routeMiddlewares Route middlewares
121
     */
122
    protected $routeMiddlewares = [];
123
124
    /**
125
     * @var array $middlewareGroups Middleware Groups
126
     */
127
    protected $middlewareGroups = [];
128
129
    /**
130
     * Router constructor method.
131
     *
132
     * @param array $params
133
     *
134
     * @return void
135
     */
136
    public function __construct(array $params = [])
137
    {
138
        $this->documentRoot = realpath($_SERVER['DOCUMENT_ROOT']);
139
        $this->runningPath = realpath(getcwd());
140
        $this->baseFolder = $this->runningPath;
141
142
        if (isset($params['debug']) && is_bool($params['debug'])) {
143
            RouterException::$debug = $params['debug'];
144
        }
145
146
        $this->setPaths($params);
147
        $this->loadCache();
148
    }
149
150
    /**
151
     * [TODO] This method implementation not completed yet.
152
     *
153
     * Set route name
154
     *
155
     * @param string $name
156
     *
157
     * @return $this
158
     */
159
    public function name($name)
160
    {
161
        if (!is_string($name)) {
0 ignored issues
show
introduced by
The condition is_string($name) is always true.
Loading history...
162
            return $this;
163
        }
164
165
        $currentRoute = end($this->routes);
166
        $currentRoute['name'] = $name;
167
        array_pop($this->routes);
168
        array_push($this->routes, $currentRoute);
169
170
        return $this;
171
    }
172
173
    /**
174
     * Add route method;
175
     * Get, Post, Put, Delete, Patch, Any, Ajax...
176
     *
177
     * @param $method
178
     * @param $params
179
     *
180
     * @return mixed
181
     * @throws
182
     */
183
    public function __call($method, $params)
184
    {
185
        if ($this->cacheLoaded) {
186
            return true;
187
        }
188
189
        if (is_null($params)) {
190
            return false;
191
        }
192
193
        if (!in_array(strtoupper($method), explode('|', RouterRequest::$validMethods))) {
194
            return $this->exception($method . ' is not valid.');
195
        }
196
197
        $route = $params[0];
198
        $callback = $params[1];
199
        $settings = null;
200
201
        if (count($params) > 2) {
202
            $settings = $params[1];
203
            $callback = $params[2];
204
        }
205
206
        if (strstr($route, ':')) {
207
            $route1 = $route2 = '';
208
            foreach (explode('/', $route) as $key => $value) {
209
                if ($value != '') {
210
                    if (!strpos($value, '?')) {
211
                        $route1 .= '/' . $value;
212
                    } else {
213
                        if ($route2 == '') {
214
                            $this->addRoute($route1, $method, $callback, $settings);
215
                        }
216
217
                        $route2 = $route1 . '/' . str_replace('?', '', $value);
218
                        $this->addRoute($route2, $method, $callback, $settings);
219
                        $route1 = $route2;
220
                    }
221
                }
222
            }
223
224
            if ($route2 == '') {
225
                $this->addRoute($route1, $method, $callback, $settings);
226
            }
227
        } else {
228
            $this->addRoute($route, $method, $callback, $settings);
229
        }
230
231
        return $this;
232
    }
233
234
    /**
235
     * Add new route method one or more http methods.
236
     *
237
     * @param string               $methods
238
     * @param string               $route
239
     * @param array|string|closure $settings
240
     * @param string|closure       $callback
241
     *
242
     * @return bool
243
     */
244
    public function add($methods, $route, $settings, $callback = null)
245
    {
246
        if ($this->cacheLoaded) {
247
            return true;
248
        }
249
250
        if (is_null($callback)) {
251
            $callback = $settings;
252
            $settings = null;
253
        }
254
255
        if (strstr($methods, '|')) {
256
            foreach (array_unique(explode('|', $methods)) as $method) {
257
                if (!empty($method)) {
258
                    call_user_func_array([$this, strtolower($method)], [$route, $settings, $callback]);
259
                }
260
            }
261
        } else {
262
            call_user_func_array([$this, strtolower($methods)], [$route, $settings, $callback]);
263
        }
264
265
        return true;
266
    }
267
268
    /**
269
     * Add new route rules pattern; String or Array
270
     *
271
     * @param string|array $pattern
272
     * @param null|string  $attr
273
     *
274
     * @return mixed
275
     * @throws
276
     */
277
    public function pattern($pattern, $attr = null)
278
    {
279
        if (is_array($pattern)) {
280
            foreach ($pattern as $key => $value) {
281
                if (in_array($key, array_keys($this->patterns))) {
282
                    return $this->exception($key . ' pattern cannot be changed.');
283
                }
284
                $this->patterns[$key] = '(' . $value . ')';
285
            }
286
        } else {
287
            if (in_array($pattern, array_keys($this->patterns))) {
288
                return $this->exception($pattern . ' pattern cannot be changed.');
289
            }
290
            $this->patterns[$pattern] = '(' . $attr . ')';
291
        }
292
293
        return true;
294
    }
295
296
    /**
297
     * Run Routes
298
     *
299
     * @return void
300
     * @throws
301
     */
302
    public function run()
303
    {
304
        $base = str_replace('\\', '/', str_replace($this->documentRoot, '', $this->runningPath));
305
        $uri = rtrim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/');
306
        if ($_SERVER['REQUEST_URI'] !== $_SERVER['PHP_SELF']) {
307
            $uri = str_replace(dirname($_SERVER['PHP_SELF']), '/', $uri);
308
        }
309
310
        if (($base !== $uri) && (substr($uri, -1) === '/')) {
311
            $uri = substr($uri, 0, (strlen($uri) - 1));
312
        }
313
314
        $uri = $this->clearRouteName($uri);
315
        $method = RouterRequest::getRequestMethod();
316
        $searches = array_keys($this->patterns);
317
        $replaces = array_values($this->patterns);
318
        $foundRoute = false;
319
320
        $routes = array_column($this->routes, 'route');
321
322
        // check if route is defined without regex
323
        if (in_array($uri, $routes)) {
324
            $currentRoute = array_filter($this->routes, function($r) use ($method, $uri) {
325
                return RouterRequest::validMethod($r['method'], $method) && $r['route'] === $uri;
326
            });
327
            if (!empty($currentRoute)) {
328
                $currentRoute = current($currentRoute);
329
                $foundRoute = true;
330
                $this->runRouteMiddleware($currentRoute, 'before');
331
                $this->runRouteCommand($currentRoute['callback']);
332
                $this->runRouteMiddleware($currentRoute, 'after');
333
            }
334
        } else {
335
            foreach ($this->routes as $data) {
336
                $route = $data['route'];
337
                if (strstr($route, ':') !== false) {
338
                    $route = str_replace($searches, $replaces, $route);
339
                }
340
341
                if (preg_match('#^' . $route . '$#', $uri, $matched)) {
342
                    if (RouterRequest::validMethod($data['method'], $method)) {
343
                        $foundRoute = true;
344
345
                        $this->runRouteMiddleware($data, 'before');
346
347
                        array_shift($matched);
348
                        $matched = array_map(function($value) {
349
                            return trim(urldecode($value));
350
                        }, $matched);
351
352
                        $this->runRouteCommand($data['callback'], $matched);
353
                        $this->runRouteMiddleware($data, 'after');
354
                        break;
355
                    }
356
                }
357
            }
358
        }
359
360
        // If it originally was a HEAD request, clean up after ourselves by emptying the output buffer
361
        if (strtoupper($_SERVER['REQUEST_METHOD']) === 'HEAD') {
362
            ob_end_clean();
363
        }
364
365
        if ($foundRoute === false) {
0 ignored issues
show
introduced by
The condition $foundRoute === false is always true.
Loading history...
366
            if (!$this->errorCallback) {
367
                $this->errorCallback = function() {
368
                    header($_SERVER['SERVER_PROTOCOL'] . ' 404 Not Found');
369
                    return $this->exception('Route not found. Looks like something went wrong. Please try again.');
370
                };
371
            }
372
            call_user_func($this->errorCallback);
373
        }
374
    }
375
376
    /**
377
     * Routes Group
378
     *
379
     * @param string        $name
380
     * @param closure|array $settings
381
     * @param null|closure  $callback
382
     *
383
     * @return bool
384
     */
385
    public function group($name, $settings = null, $callback = null)
386
    {
387
        if ($this->cacheLoaded) {
388
            return true;
389
        }
390
391
        $group = [];
392
        $group['route'] = $this->clearRouteName($name);
393
        $group['before'] = $group['after'] = null;
394
395
        if (is_null($callback)) {
396
            $callback = $settings;
397
        } else {
398
            $group['before'][] = !isset($settings['before']) ? null : $settings['before'];
399
            $group['after'][] = !isset($settings['after']) ? null : $settings['after'];
400
        }
401
402
        $groupCount = count($this->groups);
403
        if ($groupCount > 0) {
404
            $list = [];
405
            foreach ($this->groups as $key => $value) {
406
                if (is_array($value['before'])) {
407
                    foreach ($value['before'] as $k => $v) {
408
                        $list['before'][] = $v;
409
                    }
410
                    foreach ($value['after'] as $k => $v) {
411
                        $list['after'][] = $v;
412
                    }
413
                }
414
            }
415
416
            if (!is_null($group['before'])) {
417
                $list['before'][] = $group['before'][0];
418
            }
419
420
            if (!is_null($group['after'])) {
421
                $list['after'][] = $group['after'][0];
422
            }
423
424
            $group['before'] = $list['before'];
425
            $group['after'] = $list['after'];
426
        }
427
428
        $group['before'] = array_values(array_unique((array)$group['before']));
429
        $group['after'] = array_values(array_unique((array)$group['after']));
430
431
        array_push($this->groups, $group);
432
433
        if (is_object($callback)) {
434
            call_user_func_array($callback, [$this]);
435
        }
436
437
        $this->endGroup();
438
439
        return true;
440
    }
441
442
    /**
443
     * Added route from methods of Controller file.
444
     *
445
     * @param string       $route
446
     * @param string|array $settings
447
     * @param null|string  $controller
448
     *
449
     * @return mixed
450
     * @throws
451
     */
452
    public function controller($route, $settings, $controller = null)
453
    {
454
        if ($this->cacheLoaded) {
455
            return true;
456
        }
457
458
        if (is_null($controller)) {
459
            $controller = $settings;
460
            $settings = [];
461
        }
462
463
        $controller = $this->resolveClass($controller);
464
        $classMethods = get_class_methods($controller);
465
        if ($classMethods) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $classMethods of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
466
            foreach ($classMethods as $methodName) {
467
                if (!strstr($methodName, '__')) {
468
                    $method = 'any';
469
                    foreach (explode('|', RouterRequest::$validMethods) as $m) {
470
                        if (stripos($methodName, strtolower($m), 0) === 0) {
471
                            $method = strtolower($m);
472
                            break;
473
                        }
474
                    }
475
476
                    $methodVar = lcfirst(preg_replace('/' . $method . '/i', '', $methodName, 1));
477
                    $methodVar = strtolower(preg_replace('%([a-z]|[0-9])([A-Z])%', '\1-\2', $methodVar));
478
                    $r = new ReflectionMethod($controller, $methodName);
479
                    $endpoints = [];
480
                    foreach ($r->getParameters() as $param) {
481
                        $pattern = ':any';
482
                        $typeHint = $param->hasType() ? $param->getType()->getName() : null;
483
                        if (in_array($typeHint, ['int', 'bool'])) {
484
                            $pattern = ':id';
485
                        } elseif (in_array($typeHint, ['string', 'float'])) {
486
                            $pattern = ':slug';
487
                        } elseif ($typeHint === null) {
488
                            $pattern = ':any';
489
                        } else {
490
                            continue;
491
                        }
492
                        $endpoints[] = $param->isOptional() ? $pattern . '?' : $pattern;
493
                    }
494
495
                    $value = ($methodVar === $this->mainMethod ? $route : $route . '/' . $methodVar);
496
                    $this->{$method}(
497
                        ($value . '/' . implode('/', $endpoints)),
498
                        $settings,
499
                        ($controller . '@' . $methodName)
0 ignored issues
show
Bug introduced by
Are you sure $controller of type Ballybran\Routing\Router\RouterException|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

499
                        (/** @scrutinizer ignore-type */ $controller . '@' . $methodName)
Loading history...
500
                    );
501
                }
502
            }
503
            unset($r);
504
        }
505
506
        return true;
507
    }
508
509
    /**
510
     * Routes error function. (Closure)
511
     *
512
     * @param $callback
513
     *
514
     * @return void
515
     */
516
    public function error($callback)
517
    {
518
        $this->errorCallback = $callback;
519
    }
520
521
    /**
522
     * Display all Routes.
523
     *
524
     * @return void
525
     */
526
    public function getList()
527
    {
528
        echo '<pre>';
529
        var_dump($this->getRoutes());
0 ignored issues
show
Security Debugging Code introduced by
var_dump($this->getRoutes()) looks like debug code. Are you sure you do not want to remove it?
Loading history...
530
        echo '</pre>';
531
        die;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
532
    }
533
534
    /**
535
     * Get all Routes
536
     *
537
     * @return mixed
538
     */
539
    public function getRoutes()
540
    {
541
        return $this->routes;
542
    }
543
544
    /**
545
     * Throw new Exception for Router Error
546
     *
547
     * @param $message
548
     *
549
     * @return RouterException
550
     * @throws
551
     */
552
    public function exception($message = '')
553
    {
554
        return new RouterException($message);
555
    }
556
557
    /**
558
     * RouterCommand class
559
     *
560
     * @return RouterCommand
561
     */
562
    public function routerCommand()
563
    {
564
        return RouterCommand::getInstance($this->baseFolder, $this->paths, $this->namespaces);
565
    }
566
567
    /**
568
     * Cache all routes
569
     *
570
     * @return bool
571
     * @throws Exception
572
     */
573
    public function cache()
574
    {
575
        foreach ($this->getRoutes() as $key => $r) {
576
            if (!is_string($r['callback'])) {
577
                throw new Exception(sprintf('Routes cannot contain a Closure/Function callback while caching.'));
578
            }
579
        }
580
581
        $cacheContent = '<?php return ' . var_export($this->getRoutes(), true) . ';' . PHP_EOL;
582
        if (false === file_put_contents($this->cacheFile, $cacheContent)) {
583
            throw new Exception(sprintf('Routes cache file could not be written.'));
584
        }
585
586
        return true;
587
    }
588
589
    /**
590
     * Set paths and namespaces for Controllers and Middlewares.
591
     *
592
     * @param array $params
593
     *
594
     * @return void
595
     */
596
    protected function setPaths($params)
597
    {
598
        if (empty($params)) {
599
            return;
600
        }
601
602
        if (isset($params['paths']) && $paths = $params['paths']) {
603
            $this->paths['controllers'] = isset($paths['controllers'])
604
                ? trim($paths['controllers'], '/')
605
                : $this->paths['controllers'];
606
607
            $this->paths['middlewares'] = isset($paths['middlewares'])
608
                ? trim($paths['middlewares'], '/')
609
                : $this->paths['middlewares'];
610
        }
611
612
        if (isset($params['namespaces']) && $namespaces = $params['namespaces']) {
613
            $this->namespaces['controllers'] = isset($namespaces['controllers'])
614
                ? trim($namespaces['controllers'], '\\') . '\\'
615
                : '';
616
617
            $this->namespaces['middlewares'] = isset($namespaces['middlewares'])
618
                ? trim($namespaces['middlewares'], '\\') . '\\'
619
                : '';
620
        }
621
622
        if (isset($params['base_folder'])) {
623
            $this->baseFolder = rtrim($params['base_folder'], '/');
624
        }
625
626
        if (isset($params['main_method'])) {
627
            $this->mainMethod = $params['main_method'];
628
        }
629
630
        $this->cacheFile = isset($params['cache']) ? $params['cache'] : realpath(__DIR__ . '/../cache.php');
631
    }
632
633
    /**
634
     * @param $controller
635
     *
636
     * @return RouterException|mixed
637
     */
638
    protected function resolveClass($controller)
639
    {
640
        $controller = str_replace(['\\', '.'], '/', $controller);
641
        $controller = trim(
642
            preg_replace(
643
                '/' . str_replace('/', '\\/', $this->paths['controllers']) . '/i',
644
                '', $controller,
645
                1
646
            ),
647
            '/'
648
        );
649
        $file = realpath(rtrim($this->paths['controllers'], '/') . '/' . $controller . '.php');
650
651
        if (!file_exists($file)) {
652
            return $this->exception($controller . ' class is not found!');
653
        }
654
655
        $controller = $this->namespaces['controllers'] . str_replace('/', '\\', $controller);
656
        if (!class_exists($controller)) {
657
            require $file;
658
        }
659
660
        return $controller;
661
    }
662
663
    /**
664
     * Load Cache file
665
     *
666
     * @return bool
667
     */
668
    protected function loadCache()
669
    {
670
        if (file_exists($this->cacheFile)) {
671
            $this->routes = require $this->cacheFile;
672
            $this->cacheLoaded = true;
673
            return true;
674
        }
675
676
        return false;
677
    }
678
679
    /**
680
     * Add new Route and it's settings
681
     *
682
     * @param $uri
683
     * @param $method
684
     * @param $callback
685
     * @param $settings
686
     *
687
     * @return void
688
     */
689
    private function addRoute($uri, $method, $callback, $settings)
690
    {
691
        $groupItem = count($this->groups) - 1;
692
        $group = '';
693
        if ($groupItem > -1) {
694
            foreach ($this->groups as $key => $value) {
695
                $group .= $value['route'];
696
            }
697
        }
698
699
        $path = dirname($_SERVER['PHP_SELF']);
700
        $path = $path === '/' || strpos($this->runningPath, $path) !== 0 ? '' : $path;
701
702
        if (strstr($path, 'index.php')) {
703
            $data = implode('/', explode('/', $path));
704
            $path = str_replace($data, '', $path);
705
        }
706
707
        $route = $path . $group . '/' . trim($uri, '/');
708
        $route = rtrim($route, '/');
709
        if ($route === $path) {
710
            $route .= '/';
711
        }
712
            $this->criateRoute($route, $groupItem, $method, $callback, $settings);
713
        }
714
715
      private function criateRoute($route, $groupItem, $method, $callback, $settings){
716
            $routeName = is_string($callback)
717
            ? strtolower(preg_replace(
718
                '/[^\w]/i', '/', str_replace($this->namespaces['controllers'], '', $callback)
719
            ))
720
            : null;
721
        $data = [
722
            'route' => $this->clearRouteName($route),
723
            'method' => strtoupper($method),
724
            'callback' => $callback,
725
            'name' => isset($settings['name']) ? $settings['name'] : $routeName,
726
            'before' => isset($settings['before']) ? $settings['before'] : null,
727
            'after' => isset($settings['after']) ? $settings['after'] : null,
728
            'group' => $groupItem === -1 ? null : $this->groups[$groupItem],
729
        ];
730
731
        array_push($this->routes, $data);
732
    
733
        }
734
735
    /**
736
     * Run Route Command; Controller or Closure
737
     *
738
     * @param $command
739
     * @param $params
740
     *
741
     * @return void
742
     */
743
    private function runRouteCommand($command, $params = null)
744
    {
745
        $this->routerCommand()->runRoute($command, $params);
746
    }
747
748
    /**
749
     * Routes Group endpoint
750
     *
751
     * @return void
752
     */
753
    private function endGroup()
754
    {
755
        array_pop($this->groups);
756
    }
757
758
     /**
759
     * Detect Routes Middleware; before or after
760
     *
761
     * @param $middleware
762
     * @param $type
763
     *
764
     * @return void
765
     */
766
    public function runRouteMiddleware($middleware, $type)
767
    {
768
        if ($type === 'before') {
769
            if (!is_null($middleware['group'])) {
770
                $this->routerCommand()->beforeAfter($middleware['group'][$type]);
771
            }
772
            $this->routerCommand()->beforeAfter($middleware[$type]);
773
        } else {
774
            $this->routerCommand()->beforeAfter($middleware[$type]);
775
            if (!is_null($middleware['group'])) {
776
                $this->routerCommand()->beforeAfter($middleware['group'][$type]);
777
            }
778
        }
779
    }
780
781
    /**
782
     * @param string $route
783
     *
784
     * @return string
785
     */
786
    private function clearRouteName($route = '')
787
    {
788
        $route = trim(str_replace('//', '/', $route), '/');
789
        return $route === '' ? '/' : "/{$route}";
790
    }
791
}
792