Test Setup Failed
Push — develop ( 94cd86...02c7ee )
by Niels
07:18
created

Router::createPattern()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 2
1
<?php
2
3
/**
4
 * This file is part of the miBadger package.
5
 *
6
 * @author Michael Webbers <[email protected]>
7
 * @license http://opensource.org/licenses/Apache-2.0 Apache v2 License
8
 * @version 1.0.0
9
 */
10
11
namespace miBadger\Router;
12
13
use miBadger\Http\ServerRequest;
14
use miBadger\Http\ServerResponse;
15
use miBadger\Http\ServerResponseException;
16
17
/**
18
 * The router class.
19
 *
20
 * @since 1.0.0
21
 */
22
class Router implements \IteratorAggregate
23
{
24
	/** @var string The base path. */
25
	private $basePath;
26
27
	/** @var array The routes. */
28
	private $routes;
29
30
	/**
31
	 * Construct a Router object with the given routers.
32
	 *
33
	 * @param string $basePath = ''
34
	 * @param array $routes = []
0 ignored issues
show
Bug introduced by
There is no parameter named $routes. 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...
35
	 */
36
	public function __construct($basePath = '')
37
	{
38
		$this->basePath = $basePath;
39
		$this->routes = [];
40 16
	}
41
42 16
	/**
43 16
	 * Returns the base path.
44 16
	 *
45 16
	 * @return string the base path.
46
	 */
47
	public function getBasePath()
48
	{
49
		return $this->basePath;
50
	}
51
52 2
	/**
53
	 * Set the base path.
54 2
	 *
55
	 * @param string $basePath
56
	 * @return $this
57
	 */
58
	public function setBasePath($basePath)
59
	{
60
		$this->basePath = $basePath;
61
62
		return $this;
63 3
	}
64
65 2
	/**
66
	 * {@inheritdoc}
67 3
	 */
68
	public function getIterator()
69
	{
70
		return new \RecursiveArrayIterator($this->routes);
71
	}
72
73 1
	/**
74
	 * Returns the number of key-value mappings in the router map.
75 1
	 *
76
	 * @return int the number of key-value mappings in the router map.
77
	 */
78
	public function count()
79
	{
80
		$result = 0;
81
82
		foreach ($this->routes as $routes) {
83 1
			$result += count($routes);
84
		}
85 1
86
		return $result;
87 1
	}
88 1
89 1
	/**
90
	 * Returns true if the router map contains no key-value mappings.
91 1
	 *
92
	 * @return bool true if the router map contains no key-value mappings.
93
	 */
94
	public function isEmpty()
95
	{
96
		return empty($this->routes);
97
	}
98
99 2
	/**
100
	 * Returns true if the router map contains a mapping for the specified method & route.
101 2
	 *
102
	 * @param string $method
103
	 * @param string $route
104
	 * @return bool true if the static route map contains a mapping for the specified method & route.
105
	 */
106 View Code Duplication
	public function contains($method, $route)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
107
	{
108
		if (!array_key_exists($method, $this->routes)) {
109
			return false;
110
		}
111 9
112
		foreach ($this->routes[$method] as $entry) {
113 9
			if ($entry->route == $route) {
114
				return true;
115
			}
116
		}
117
118
		return false;
119
	}
120
121
	/**
122 1
	 * Returns true if the router map maps one or more routes to the specified callable.
123
	 *
124 1
	 * @param callable $callable
125 1
	 * @return bool true if the router map maps one or more routes to the specified callable.
126 1
	 */
127 1
	public function containsCallable(callable $callable)
128
	{
129 1
		foreach ($this->routes as $method) {
130 1
			foreach ($method as $entry)
131
			{
132 1
				if ($entry->callable === $callable) {
133
					return true;
134
				}
135
			}
136
		}
137
138
		return false;
139
	}
140
141
	/**
142 6
	 * Returns the callable to which the specified route is mapped, or null if the router map contains no mapping for the route.
143
	 *
144 6
	 * @param string $method
145
	 * @param string $route
146
	 * @return callable the callable to which the specified route is mapped, or null if the router map contains no mapping for the route.
147
	 */
148 View Code Duplication
	public function get($method, $route)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
149
	{
150
		if (!array_key_exists($method, $this->routes)) {
151
			return null;
152
		}
153
154
		foreach ($this->routes[$method] as $key => $entry) {
155
			if ($entry->route == $route) {
156 3
				return $entry->callable;
157
			}
158 3
		}
159
160 3
		return null;
161
	}
162
163
	/**
164
	 * Associates the specified callable with the specified method & route in the route map.
165
	 *
166
	 * @param string|array $methods
167
	 * @param string $route
168
	 * @param callable $callable
169
	 * @return $this
170 1
	 */
171
	public function set($methods, $route, callable $callable)
172 1
	{
173 1
		if (is_string($methods)) {
174
			$methods = [$methods];
175
		}
176
177
		foreach ($methods as $method) {
178
			$this->add($method, $route, $callable);
179
		}
180 1
181
		return $this;
182 1
	}
183 1
184
	/**
185
	 * Adds a method & route to the to the route map.
186
	 *
187
	 * @param string $methods
0 ignored issues
show
Documentation introduced by
There is no parameter named $methods. Did you maybe mean $method?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

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

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

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

Loading history...
188
	 * @param string $route
189
	 * @param callable $callable
190
	 */
191
	private function add(string $method, string $route, callable $callable)
192
	{
193 5
		$entry = (object)[
194
			'route' => $route,
195 5
			'pattern' => $this->createPattern($route),
196
			'callable' => $callable
197 5
		];
198 1
199 1
		if (!array_key_exists($method, $this->routes)) {
200
			$this->routes[$method] = [];
201 5
		}
202 1
203 1
		$this->routes[$method][] = $entry;
204
	}
205 5
206 1
	/**
207 1
	 * Creates a regex-enabled pattern from the route
208
	 *
209 5
	 * @param string $route
210
	 * @return string the pattern string.
211
	 */
212
	private function createPattern(string $route)
213
	{
214
		return '|^' . preg_replace("|\{[^\}]+\}|", "([^\/]+)", $route) . '$|';
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal |\{[^\}]+\}| does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
Coding Style Comprehensibility introduced by
The string literal ([^\/]+) does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
215
	}
216
217
	/**
218
	 * Removes the mapping for the specified route from the router map if present.
219
	 *
220 5
	 * @param string $method
221
	 * @param string $route
222 5
	 * @return null
223 4
	 */
224
	public function remove($method, $route)
225
	{
226 1
		if (!array_key_exists($method, $this->routes)) {
227
			return;
228
		}
229
230
		foreach ($this->routes[$method] as $key => $entry) {
231
			if ($entry->route == $route) {
232
				unset($this->routes[$method][$key]);
233
			}
234
		}
235 4
236
		if (count($this->routes[$method]) == 0) {
237 4
			unset($this->routes[$method]);
238
		}
239
	}
240
241
	/**
242
	 * Removes all of the mappings from the router map.
243
	 *
244
	 * @return null
245
	 */
246
	public function clear()
247
	{
248
		$this->routes = [];
249
	}
250
251
	/**
252
	 * Returns the result of the given route's callable.
253
	 *
254
	 * @param string|null $method = new ServerRequest()->getMethod()
255
	 * @param string|null $route = new ServerRequest()->getUri()->getPath()
256
	 * @return mixed the result of the given route's callable.
257
	 * @throws ServerResponseException
258
	 */
259
	public function resolve($method = null, $route = null)
260
	{
261
		$serverRequest = new ServerRequest();
262
263
		if ($method === null) {
264
			$method = $serverRequest->getMethod();
265
		}
266
267
		if ($route === null) {
268
			$route = $serverRequest->getUri()->getPath();
269
		}
270
271
		if ($this->basePath !== '' && strpos($route, $this->basePath, 0) === 0) {
272
			$route = substr($route, strlen($this->basePath));
273
		}
274
275
		return $this->callCallable($this->getCallable($method, $route));
276
	}
277
278
	/**
279
	 * Returns the callable to which the specied method and route are mapped.
280
	 *
281
	 * @param string $method
282
	 * @param string $route
283
	 * @return array the callable to which the specied method and route are mapped and the route matches.
284
	 * @throws ServerResponseException
285
	 */
286
	private function getCallable($method, $route)
287
	{
288
		if (!array_key_exists($method, $this->routes)) {
289
			throw new ServerResponseException(new ServerResponse(404));
290
		}
291
292
		foreach ($this->routes[$method] as $entry) {
293
			if (preg_match($entry->pattern, $route, $matches) > 0) {
294
				array_shift($matches);
295
				return [$entry->callable, $matches];
296
			}
297
		}
298
299
		throw new ServerResponseException(new ServerResponse(404));
300
	}
301
302
	/**
303
	 * Returns the result of the callable.
304
	 *
305
	 * @param array the callable and the route matches.
306
	 * @return mixed the result the callable.
307
	 */
308
	private function callCallable($callable)
309
	{
310
		return call_user_func_array($callable[0], $callable[1]);
311
	}
312
}
313