Test Failed
Pull Request — master (#16)
by Divine Niiquaye
10:38
created

RouteMatcher::match()   B

Complexity

Conditions 8
Paths 9

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 7
Bugs 1 Features 0
Metric Value
cc 8
eloc 17
c 7
b 1
f 0
nc 9
nop 2
dl 0
loc 30
rs 8.4444
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of Flight Routing.
7
 *
8
 * PHP version 7.1 and above required
9
 *
10
 * @author    Divine Niiquaye Ibok <[email protected]>
11
 * @copyright 2019 Biurad Group (https://biurad.com/)
12
 * @license   https://opensource.org/licenses/BSD-3-Clause License
13
 *
14
 * For the full copyright and license information, please view the LICENSE
15
 * file that was distributed with this source code.
16
 */
17
18
namespace Flight\Routing;
19
20
use Flight\Routing\Exceptions\{UriHandlerException, UrlGenerationException};
21
use Flight\Routing\Interfaces\{RouteCompilerInterface, RouteMapInterface, RouteMatcherInterface};
22
use Psr\Http\Message\{ServerRequestInterface, UriInterface};
23
24
/**
25
 * The bidirectional route matcher responsible for matching
26
 * HTTP request and generating url from routes.
27
 *
28
 * @author Divine Niiquaye Ibok <[email protected]>
29
 */
30
class RouteMatcher implements RouteMatcherInterface, \Countable
31
{
32
    /** @var array<int,Route> */
33
    protected $routes;
34
35
    /** @var array */
36
    protected $staticRouteMap;
37
38
    /** @var array */
39
    protected $dynamicRouteMap;
40
41
    /** @var DebugRoute|null */
0 ignored issues
show
Bug introduced by
The type Flight\Routing\DebugRoute was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
42
    protected $debug;
43
44
    /** @var RouteCompilerInterface */
45
    private $compiler;
46
47
    public function __construct(RouteMapInterface $collection)
48
    {
49
        $this->compiler = $collection->getCompiler();
50
51
        $this->routes = $collection['routes'] ?? [];
52
        $this->staticRouteMap = $collection['staticRoutesMap'] ?? [];
53
        $this->dynamicRouteMap = $collection['dynamicRoutesMap'] ?? [];
54
    }
55
56
    /**
57
     * Get the total number of routes.
58
     */
59
    public function count(): int
60
    {
61
        return \count($this->routes);
62
    }
63
64
    /**
65
     * {@inheritdoc}
66
     */
67
    public function matchRequest(ServerRequestInterface $request): ?Route
68
    {
69
        $requestUri = $request->getUri();
70
71
        // Resolve request path to match sub-directory or /index.php/path
72
        if (!empty($pathInfo = $request->getServerParams()['PATH_INFO'] ?? '')) {
73
            $requestUri = $requestUri->withPath($pathInfo);
74
        }
75
76
        return $this->match($request->getMethod(), $requestUri);
77
    }
78
79
    /**
80
     * {@inheritdoc}
81
     */
82
    public function match(string $method, UriInterface $uri): ?Route
83
    {
84
        $pathInfo = $uri->getPath();
85
        $requestPath = \rtrim($pathInfo, Route::URL_PREFIX_SLASHES[$pathInfo[-1]] ?? '/') ?: '/';
86
87
        if (isset($this->staticRouteMap[$requestPath])) {
88
            [$routeId, $hostsRegex, $variables] = $this->staticRouteMap[$requestPath];
89
            $route = $this->routes[$routeId]->match($method, $uri);
90
91
            if (null !== $hostsRegex) {
92
                $variables = $this->matchStaticRouteHost($uri, $hostsRegex, $variables);
93
94
                if (null === $variables) {
95
                    if (!empty($this->dynamicRouteMap)) {
96
                        goto retry_routing;
97
                    }
98
99
                    throw new UriHandlerException(\sprintf('Unfortunately current host "%s" is not allowed on requested static path [%s].', $uri->getHost(), $uri->getPath()), 400);
100
                }
101
            }
102
103
            return empty($variables) ? $route : $route->arguments($variables);
104
        }
105
106
        retry_routing:
107
        if ($pathInfo !== $requestPath) {
108
            $uri = $uri->withPath($requestPath);
109
        }
110
111
        return $this->matchVariableRoute($method, $uri);
112
    }
113
114
    /**
115
     * {@inheritdoc}
116
     */
117
    public function generateUri(string $routeName, array $parameters = []): GeneratedUri
118
    {
119
        foreach ($this->routes as $route) {
120
            if ($routeName === $route->get('name')) {
121
                $defaults = $route->get('defaults');
122
                unset($defaults['_arguments']);
123
124
                return $this->compiler->generateUri($route, $parameters, $defaults);
125
            }
126
        }
127
128
        throw new UrlGenerationException(\sprintf('Unable to generate a URL for the named route "%s" as such route does not exist.', $routeName), 404);
129
    }
130
131
    public function getCompiler(): RouteCompilerInterface
132
    {
133
        return $this->compiler;
134
    }
135
136
    protected function matchVariableRoute(string $method, UriInterface $uri): ?Route
137
    {
138
        $requestPath = \strpbrk((string) $uri, '/');
139
140
        foreach ($this->dynamicRouteMap[0] ?? [] as $regex) {
141
            if (!\preg_match($regex, $requestPath, $matches)) {
142
                continue;
143
            }
144
145
            $route = $this->routes[$routeId = (int) $matches['MARK']];
146
            $matchVar = 0;
147
148
            foreach ($this->dynamicRouteMap[1][$routeId] ?? [] as $key => $value) {
149
                $route->argument($key, $matches[++$matchVar] ?? $value);
150
            }
151
152
            return $route->match($method, $uri);
153
        }
154
155
        return null;
156
    }
157
158
    /**
159
     * @param array<string,string|null> $variables
160
     *
161
     * @return array<string,string|null>|null
162
     */
163
    protected function matchStaticRouteHost(UriInterface $uri, string $hostsRegex, array $variables): ?array
164
    {
165
        $hostAndPost = $uri->getHost() . (null !== $uri->getPort() ? ':' . $uri->getPort() : '');
166
167
        if (!\preg_match($hostsRegex, $hostAndPost, $hostsVar)) {
168
            return null;
169
        }
170
171
        foreach ($variables as $key => $var) {
172
            if (isset($hostsVar[$key])) {
173
                $variables[$key] = $hostsVar[$key] ?? $var;
174
            }
175
        }
176
177
        return $variables;
178
    }
179
}
180