Completed
Pull Request — master (#272)
by Derek Stephen
25:44 queued 45s
created

Router::removeRoute()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 3
nc 3
nop 1
crap 12
1
<?php declare(strict_types=1);
2
3
namespace League\Route;
4
5
use FastRoute\{DataGenerator, RouteCollector, RouteParser};
6
use InvalidArgumentException;
7
use League\Route\Middleware\{MiddlewareAwareInterface, MiddlewareAwareTrait};
8
use League\Route\Strategy\{ApplicationStrategy, StrategyAwareInterface, StrategyAwareTrait};
9
use Psr\Http\Message\{ResponseInterface, ServerRequestInterface};
10
11
class Router extends RouteCollector implements
12
    MiddlewareAwareInterface,
13
    RouteCollectionInterface,
14
    StrategyAwareInterface
15
{
16
    use MiddlewareAwareTrait;
17
    use RouteCollectionTrait;
18
    use StrategyAwareTrait;
19
20
    /**
21
     * @var Route[]
22
     */
23
    protected $routes = [];
24
25
    /**
26
     * @var Route[]
27
     */
28
    protected $namedRoutes = [];
29
30
    /**
31
     * @var RouteGroup[]
32
     */
33
    protected $groups = [];
34
35
    /**
36
     * @var array
37
     */
38
    protected $patternMatchers = [
39
        '/{(.+?):number}/'        => '{$1:[0-9]+}',
40
        '/{(.+?):word}/'          => '{$1:[a-zA-Z]+}',
41
        '/{(.+?):alphanum_dash}/' => '{$1:[a-zA-Z0-9-_]+}',
42
        '/{(.+?):slug}/'          => '{$1:[a-z0-9-]+}',
43
        '/{(.+?):uuid}/'          => '{$1:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}+}'
44
    ];
45
46
    /**
47
     * Constructor
48
     *
49
     * @param RouteParser $parser
50
     * @param DataGenerator $generator
51
     */
52 63
    public function __construct(?RouteParser $parser = null, ?DataGenerator $generator = null)
53
    {
54
        // build parent route collector
55 63
        $parser    = $parser ?? new RouteParser\Std;
56 63
        $generator = $generator ?? new DataGenerator\GroupCountBased;
57 63
        parent::__construct($parser, $generator);
58 63
    }
59
60
    /**
61
     * {@inheritdoc}
62
     */
63 45
    public function map(string $method, string $path, $handler): Route
64
    {
65 45
        $path  = sprintf('/%s', ltrim($path, '/'));
66 45
        $route = new Route($method, $path, $handler);
67
68 45
        $this->routes[] = $route;
69
70 45
        return $route;
71
    }
72
73
    /**
74
     * Add a group of routes to the collection
75
     *
76
     * @param string   $prefix
77
     * @param callable $group
78
     *
79
     * @return RouteGroup
80
     */
81 12
    public function group(string $prefix, callable $group): RouteGroup
82
    {
83 12
        $group          = new RouteGroup($prefix, $group, $this);
84 12
        $this->groups[] = $group;
85
86 12
        return $group;
87
    }
88
89
    /**
90
     * {@inheritdoc}
91
     */
92 48
    public function dispatch(ServerRequestInterface $request): ResponseInterface
93
    {
94 48
        if ($this->getStrategy() === null) {
95 30
            $this->setStrategy(new ApplicationStrategy);
96
        }
97
98 48
        $this->prepRoutes($request);
99
100
        /** @var Dispatcher $dispatcher */
101 48
        $dispatcher = (new Dispatcher($this->getData()))->setStrategy($this->getStrategy());
102
103 48
        foreach ($this->getMiddlewareStack() as $middleware) {
104 3
            if (is_string($middleware)) {
105 3
                $dispatcher->lazyMiddleware($middleware);
106 3
                continue;
107
            }
108
109 3
            $dispatcher->middleware($middleware);
110
        }
111
112 48
        return $dispatcher->dispatchRequest($request);
113
    }
114
115
    /**
116
     * Prepare all routes, build name index and filter out none matching
117
     * routes before being passed off to the parser.
118
     *
119
     * @param ServerRequestInterface $request
120
     *
121
     * @return void
122
     */
123 48
    protected function prepRoutes(ServerRequestInterface $request): void
124
    {
125 48
        $this->processGroups($request);
126 48
        $this->buildNameIndex();
127
128 48
        $routes = array_merge(array_values($this->routes), array_values($this->namedRoutes));
129
130
        /** @var Route $route */
131 48
        foreach ($routes as $key => $route) {
132
            // check for scheme condition
133 39
            $scheme = $route->getScheme();
134 39
            if ($scheme !== null && $scheme !== $request->getUri()->getScheme()) {
135 3
                continue;
136
            }
137
138
            // check for domain condition
139 36
            $host = $route->getHost();
140 36
            if ($host !== null && $host !== $request->getUri()->getHost()) {
141 3
                continue;
142
            }
143
144
            // check for port condition
145 33
            $port = $route->getPort();
146 33
            if ($port !== null && $port !== $request->getUri()->getPort()) {
147 3
                continue;
148
            }
149
150 30
            if ($route->getStrategy() === null) {
151 21
                $route->setStrategy($this->getStrategy());
152
            }
153
154 30
            $this->addRoute($route->getMethod(), $this->parseRoutePath($route->getPath()), $route);
155
        }
156 48
    }
157
158
    /**
159
     * Build an index of named routes.
160
     *
161
     * @return void
162
     */
163 54
    protected function buildNameIndex(): void
164
    {
165 54
        foreach ($this->routes as $key => $route) {
166 42
            if ($route->getName() !== null) {
167 3
                unset($this->routes[$key]);
168 16
                $this->namedRoutes[$route->getName()] = $route;
169
            }
170
        }
171 54
    }
172
173
    /**
174
     * Process all groups
175
     *
176
     * Adds all of the group routes to the collection and determines if the group
177
     * strategy should be be used.
178
     *
179
     * @param ServerRequestInterface $request
180
     *
181
     * @return void
182
     */
183 48
    protected function processGroups(ServerRequestInterface $request): void
184
    {
185 48
        $activePath = $request->getUri()->getPath();
186
187 48
        foreach ($this->groups as $key => $group) {
188
            // we want to determine if we are technically in a group even if the
189
            // route is not matched so exceptions are handled correctly
190 9
            if ($group->getStrategy() !== null
191 9
                && strncmp($activePath, $group->getPrefix(), strlen($group->getPrefix())) === 0
192
            ) {
193 6
                $this->setStrategy($group->getStrategy());
194
            }
195
196 9
            unset($this->groups[$key]);
197 9
            $group();
198
        }
199 48
    }
200
201
    /**
202
     * Get a named route
203
     *
204
     * @param string $name
205
     *
206
     * @return Route
207
     *
208
     * @throws InvalidArgumentException when no route of the provided name exists
209
     */
210 6
    public function getNamedRoute(string $name): Route
211
    {
212 6
        $this->buildNameIndex();
213
214 6
        if (isset($this->namedRoutes[$name])) {
215 3
            return $this->namedRoutes[$name];
216
        }
217
218 3
        throw new InvalidArgumentException(sprintf('No route of the name (%s) exists', $name));
219
    }
220
221
    /**
222
     * Add a convenient pattern matcher to the internal array for use with all routes
223
     *
224
     * @param string $alias
225
     * @param string $regex
226
     *
227
     * @return self
228
     */
229 3
    public function addPatternMatcher(string $alias, string $regex): self
230
    {
231 3
        $pattern = '/{(.+?):' . $alias . '}/';
232 3
        $regex   = '{$1:' . $regex . '}';
233
234 3
        $this->patternMatchers[$pattern] = $regex;
235
236 3
        return $this;
237
    }
238
239
    /**
240
     * Replace word patterns with regex in route path
241
     *
242
     * @param string $path
243
     *
244
     * @return string
245
     */
246 30
    protected function parseRoutePath(string $path): string
247
    {
248 30
        return preg_replace(array_keys($this->patternMatchers), array_values($this->patternMatchers), $path);
249
    }
250
251
    /**
252
     * @return Route[]
253
     */
254
    public function getRoutes(): array
255
    {
256
        return $this->routes;
257
    }
258
259
    /**
260
     * @param Route $route
0 ignored issues
show
Bug introduced by
There is no parameter named $route. Was it maybe removed?

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

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

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

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

Loading history...
261
     */
262
    public function removeRoute(Route $routeToRemove): void
263
    {
264
        foreach ($this->routes as $index => $route) {
265
            if ($route === $routeToRemove) {
266
                unset($this->routes[$index]);
267
            }
268
        }
269
    }
270
}
271