Router::getDispatcher()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
1
<?php
2
3
/*
4
 * This file is part of the jade/jade package.
5
 *
6
 * (c) Slince <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Jade\Routing;
13
14
use FastRoute\Dispatcher;
15
use FastRoute\RouteCollector;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Jade\Routing\RouteCollector.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
16
use FastRoute\RouteParser;
17
use FastRoute\RouteParser\Std as StdParser;
18
use Psr\Http\Message\ServerRequestInterface;
19
20
class Router
21
{
22
    /**
23
     * @var RouteCollection|Route[]
24
     */
25
    protected $routes;
26
27
    /**
28
     * @var
29
     */
30
    protected $routeParser;
31
32
    /**
33
     * @var Dispatcher
34
     */
35
    protected $dispatcher;
36
37
    /**
38
     * 缓存路由文件,默认不缓存
39
     *
40
     * @var string|False
41
     */
42
    protected $cacheFile = false;
43
44
    public function __construct(RouteCollection $routes, RouteParser $parser = null)
45
    {
46
        $this->routes = $routes;
47
        $this->routeParser = $parser ?: new StdParser();
48
    }
49
50
    /**
51
     * 设置路由集合
52
     *
53
     * @param RouteCollection $routes
54
     */
55
    public function setRoutes($routes): void
56
    {
57
        $this->routes = $routes;
58
    }
59
60
    /**
61
     * 返回路由集合
62
     *
63
     * @return RouteCollection
64
     */
65
    public function getRoutes(): RouteCollection
66
    {
67
        return $this->routes;
68
    }
69
70
    /**
71
     * 搜索路由
72
     *
73
     * @param string $name
74
     * @return Route
75
     */
76
    public function searchRoute($name)
77
    {
78
        return $this->routes->search($name);
79
    }
80
81
    /**
82
     * 调度请求
83
     *
84
     * @param ServerRequestInterface $request
85
     * @return array
86
     */
87
    public function dispatch(ServerRequestInterface $request)
88
    {
89
        $uri = '/' . ltrim($request->getUri()->getPath(), '/');
90
        return $this->getDispatcher()->dispatch(
91
            $request->getMethod(),
92
            $uri
93
        );
94
    }
95
96
    /**
97
     * 设置自定义路由调度器
98
     *
99
     * @param Dispatcher $dispatcher
100
     */
101
    public function setDispatcher(Dispatcher $dispatcher)
102
    {
103
        $this->dispatcher = $dispatcher;
104
    }
105
106
    /**
107
     * 获取路由调度器
108
     *
109
     * @return Dispatcher
110
     */
111
    public function getDispatcher()
112
    {
113
        if ($this->dispatcher) {
114
            return $this->dispatcher;
115
        }
116
        return $this->dispatcher = $this->createDispatcher();
117
    }
118
119
    /**
120
     * 设置路由缓存文件,为空表示禁用路由缓存
121
     *
122
     * @param string|false $cacheFile
123
     *
124
     * @return static
125
     *
126
     * @throws \InvalidArgumentException If cacheFile is not a string or not false
127
     * @throws \RuntimeException         If cacheFile directory is not writable
128
     */
129
    public function setCacheFile($cacheFile)
130
    {
131
        if (!is_string($cacheFile) && $cacheFile !== false) {
132
            throw new \InvalidArgumentException('Router cache file must be a string or false');
133
        }
134
        if ($cacheFile && file_exists($cacheFile) && !is_readable($cacheFile)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $cacheFile of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
135
            throw new \RuntimeException(
136
                sprintf('Router cache file `%s` is not readable', $cacheFile)
137
            );
138
        }
139
        if ($cacheFile && !file_exists($cacheFile) && !is_writable(dirname($cacheFile))) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $cacheFile of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
140
            throw new \RuntimeException(
141
                sprintf('Router cache file directory `%s` is not writable', dirname($cacheFile))
142
            );
143
        }
144
        $this->cacheFile = $cacheFile;
145
        return $this;
146
    }
147
148
    /**
149
     * 创建路由调度
150
     *
151
     * @return Dispatcher
152
     */
153
    protected function createDispatcher()
154
    {
155
        $routeDefinitionCallback = function (RouteCollector $r) {
156
            foreach ($this->routes as $route) {
157
                $r->addRoute($route->getMethods(), $route->getPattern(), $route->getName());
158
            }
159
        };
160
        if ($this->cacheFile) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->cacheFile of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
161
            $dispatcher = \FastRoute\cachedDispatcher($routeDefinitionCallback, [
162
                'routeParser' => $this->routeParser,
163
                'cacheFile' => $this->cacheFile,
164
            ]);
165
        } else {
166
            $dispatcher = \FastRoute\simpleDispatcher($routeDefinitionCallback, [
167
                'routeParser' => $this->routeParser,
168
            ]);
169
        }
170
        return $dispatcher;
171
    }
172
}