AccessRouter   A
last analyzed

Complexity

Total Complexity 37

Size/Duplication

Total Lines 232
Duplicated Lines 0 %

Test Coverage

Coverage 69.88%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 70
c 1
b 0
f 0
dl 0
loc 232
ccs 58
cts 83
cp 0.6988
rs 9.44
wmc 37

14 Methods

Rating   Name   Duplication   Size   Complexity  
A getBasePath() 0 3 1
A createPattern() 0 3 1
A setBasePath() 0 5 1
A makeGETRoutePermissionsMap() 0 22 4
A clear() 0 3 1
A add() 0 14 2
A resolve() 0 17 5
A getMatchingRoutes() 0 21 5
A remove() 0 14 6
A getIterator() 0 3 1
A __construct() 0 5 1
A count() 0 9 2
A callCallable() 0 3 1
A getCallable() 0 19 6
1
<?php
2
3
namespace miBadger\Router;
4
5
use miBadger\Http\ServerRequest;
6
use miBadger\Http\ServerResponse;
7
use miBadger\Http\ServerResponseException;
8
9
class AccessRouter
10
{
11
	private $routes; // $routes[method] = (route, pattern, callable, permission)
12
13
	private $basePath;
14
15
	private $user;
16
17 9
	public function __construct(PermissionCheckable $user = null, $basePath = '')
18
	{
19 9
		$this->user = $user;
20 9
		$this->routes = [];
21 9
		$this->basePath = $basePath;
22 9
	}
23
	/**
24
	 * Returns the base path.
25
	 *
26
	 * @return string the base path.
27
	 */
28
	public function getBasePath()
29
	{
30
		return $this->basePath;
31
	}
32
33
	/**
34
	 * Set the base path.
35
	 *
36
	 * @param string $basePath
37
	 * @return $this
38
	 */
39
	public function setBasePath($basePath)
40
	{
41
		$this->basePath = $basePath;
42
43
		return $this;
44
	}
45
46
	/**
47
	 * {@inheritdoc}
48
	 */
49
	public function getIterator()
50
	{
51
		return new \RecursiveArrayIterator($this->routes);
52
	}
53
54
	/**
55
	 * Returns the number of key-value mappings in the router map.
56
	 *
57
	 * @return int the number of key-value mappings in the router map.
58
	 */
59
	public function count()
60
	{
61
		$result = 0;
62
63
		foreach ($this->routes as $routes) {
64
			$result += count($routes);
65
		}
66
67
		return $result;
68
	}
69
70 9
	public function add(string $method, string $route, $permission, callable $callable)
71
	{
72
		$entry = (object)[
73 9
			'route' => $route,
74 9
			'pattern' => $this->createPattern($route),
75 9
			'permission' => $permission,
76 9
			'callable' => $callable
77
		];
78
79 9
		if (!array_key_exists($method, $this->routes)) {
80 9
			$this->routes[$method] = [];
81
		}
82
83 9
		$this->routes[$method][] = $entry;
84 9
	}
85
86
	/**
87
	 * Creates a regex-enabled pattern from the route
88
	 *
89
	 * @param string $route
90
	 * @return string the pattern string.
91
	 */
92 9
	private function createPattern(string $route)
93
	{
94 9
		return '|^' . preg_replace('|\{[^\}]+\}|', '([^\/]+)', $route) . '$|';
95
	}
96
97
	/**
98
	 * Removes the mapping for the specified route from the router map if present.
99
	 *
100
	 * @param string $method
101
	 * @param string $route
102
	 * @return null
103
	 */
104
	public function remove(string $method, string $route, $permission)
105
	{
106
		if (!array_key_exists($method, $this->routes)) {
107
			return;
108
		}
109
110
		foreach ($this->routes[$method] as $key => $entry) {
111
			if ($entry->route == $route && $entry->permission == $permission) {
112
				unset($this->routes[$method][$key]);
113
			}
114
		}
115
116
		if (count($this->routes[$method]) == 0) {
117
			unset($this->routes[$method]);
118
		}
119
	}
120
121
	/**
122
	 * Removes all of the mappings from the router map.
123
	 *
124
	 * @return null
125
	 */
126
	public function clear()
127
	{
128
		$this->routes = [];
129
	}
130
131
132
	/**
133
	 * Returns the result of the given route's callable.
134
	 *
135
	 * @param string|null $method = new ServerRequest()->getMethod()
136
	 * @param string|null $route = new ServerRequest()->getUri()->getPath()
137
	 * @return mixed the result of the given route's callable.
138
	 * @throws ServerResponseException
139
	 */
140 8
	public function resolve($method = null, $route = null)
141
	{
142 8
		$serverRequest = new ServerRequest();
143
144 8
		if ($method === null) {
145 1
			$method = $serverRequest->getMethod();
146
		}
147
148 8
		if ($route === null) {
149 1
			$route = $serverRequest->getUri()->getPath();
150
		}
151
152 8
		if ($this->basePath !== '' && strpos($route, $this->basePath, 0) === 0) {
153
			$route = substr($route, strlen($this->basePath));
154
		}
155
156 8
		return $this->callCallable($this->getCallable($method, $route));
157
	}
158
159
	/**
160
	 * Returns the result of the callable.
161
	 *
162
	 * @param array the callable and the route matches.
0 ignored issues
show
Bug introduced by
The type miBadger\Router\the 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...
163
	 * @return mixed the result the callable.
164
	 */
165 4
	protected function callCallable($callable)
166
	{
167 4
		return call_user_func_array($callable[0], $callable[1]);
168
	}
169
170 8
	public function getMatchingRoutes($method, $route)
171
	{
172 8
		if (!array_key_exists($method, $this->routes)) {
173 1
			throw new ServerResponseException(new ServerResponse(404));
174
		}
175
176 7
		$matchedRoutes = [];
177
178 7
		foreach ($this->routes[$method] as $entry) {
179 7
			if (preg_match($entry->pattern, $route, $matches) > 0) {
180
				// At this point the route is matched
181 6
				array_shift($matches);
182 7
				$matchedRoutes[] = [$route, $entry, $matches];
183
			}
184
		}
185
186 7
		if (count($matchedRoutes) == 0) {
187 1
			throw new ServerResponseException(new ServerResponse(404));		
188
		}
189
190 6
		return $matchedRoutes;
191
	}
192
193 8
	protected function getCallable($method, $route)
194
	{
195
		// Check if permissions are alright
196 8
		$matchedRoutes = $this->getMatchingRoutes($method, $route);
197
198 6
		foreach ($matchedRoutes as $match) {
199 6
			$route = $match[0];
0 ignored issues
show
Unused Code introduced by
The assignment to $route is dead and can be removed.
Loading history...
200 6
			$entry = $match[1];
201 6
			$matches = $match[2];
202
203 6
			if ($entry->permission === null 
204 6
				|| ($entry->permission !== null && $this->user !== null && $this->user->hasPermission($entry->permission)))
205
			{
206 6
				return [$entry->callable, $matches];
207
			}
208
209
		}
210
211 2
		throw new ServerResponseException(new ServerResponse(403));
212
	}
213
214
	/**
215
	 * Returns the cleaned ({token} => %s) permission map for the router 
216
	 * (associative map from route to the set of permissions that have access to that route), 
217
	 * 		null if a route has wildcard permissions
218
	 */
219 1
	public function makeGETRoutePermissionsMap()
220
	{
221 1
		$outputMap = [];
222
223 1
		foreach ($this->routes["GET"] as $entry) {
224
225
			// Extract clean route by replacing every slug occurrence with %s
226 1
			$slugFreeRoute = preg_replace('/({.*?})/', '%s', $entry->route);
227
228 1
			if ($entry->permission === null) {
229 1
				$outputMap[$slugFreeRoute] = null;	
230 1
				continue;
231
			}
232
233 1
			if (!isset($outputMap[$slugFreeRoute])) {
234 1
				$outputMap[$slugFreeRoute] = [];
235
			}
236
237 1
			$outputMap[$slugFreeRoute][] = $entry->permission;
238
		}
239
240 1
		return $outputMap;
241
	}
242
}