Passed
Push — master ( e727bc...399bba )
by Jean-Christophe
07:00
created

Router   C

Complexity

Total Complexity 57

Size/Duplication

Total Lines 249
Duplicated Lines 0 %

Test Coverage

Coverage 80%

Importance

Changes 2
Bugs 0 Features 1
Metric Value
wmc 57
eloc 104
c 2
b 0
f 1
dl 0
loc 249
ccs 96
cts 120
cp 0.8
rs 5.04

18 Methods

Rating   Name   Duplication   Size   Complexity  
A url() 0 2 1
A cleanParam() 0 5 2
A checkRouteName() 0 9 6
A getRoute() 0 13 6
A startAll() 0 2 1
A getRouteInfoByName() 0 7 3
A getRoutes() 0 2 1
A setExpired() 0 2 1
A start() 0 2 1
A path() 0 2 1
B getRouteByName() 0 14 7
A slashPath() 0 8 3
A startRest() 0 2 1
A setParamsInOrder() 0 22 6
B getRouteUrlParts() 0 33 10
A _getURL() 0 8 2
A getStatusCode() 0 2 1
A getRoute_() 0 16 4

How to fix   Complexity   

Complex Class

Complex classes like Router often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Router, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Ubiquity\controllers;
4
5
use Ubiquity\cache\CacheManager;
6
use Ubiquity\controllers\traits\RouterAdminTrait;
7
use Ubiquity\controllers\traits\RouterModifierTrait;
8
use Ubiquity\controllers\traits\RouterTestTrait;
9
use Ubiquity\log\Logger;
10
use Ubiquity\utils\http\URequest;
11
use Ubiquity\controllers\router\RouterStatus;
12
13
/**
14
 * Router manager.
15
 * Ubiquity\controllers$Router
16
 * This class is part of Ubiquity
17
 *
18
 * @author jcheron <[email protected]>
19
 * @version 1.1.0
20
 *
21
 */
22
class Router {
23
	use RouterModifierTrait,RouterAdminTrait,RouterTestTrait;
24
	protected static $routes;
25
	protected static $statusCode;
26
27 7
	private static function cleanParam(string $param): string {
28 7
		if (\substr ( $param, - 1 ) === '/') {
29
			return \substr ( $param, 0, - 1 );
30
		}
31 7
		return $param;
32
	}
33
34 13
	private static function getRoute_(&$routeDetails, $routePath, $matches, $cachedResponse) {
35 13
		self::$statusCode=RouterStatus::OK;
36 13
		if (! isset ( $routeDetails ['controller'] )) {
37
			$method = \strtolower ( $_SERVER ['REQUEST_METHOD'] );
38
			if (isset ( $routeDetails [$method] )) {
39
				$routeDetailsMethod = $routeDetails [$method];
40
				return self::getRouteUrlParts ( [ 'path' => $routePath,'details' => $routeDetailsMethod ], $matches, $routeDetailsMethod ['cache'] ?? false, $routeDetailsMethod ['duration'] ?? null, $cachedResponse );
41
			}
42
			self::$statusCode=RouterStatus::METHOD_NOT_ALLOWED;
43
		} else {
44 13
			return self::getRouteUrlParts ( [ 'path' => $routePath,'details' => $routeDetails ], $matches, $routeDetails ['cache'] ?? false, $routeDetails ['duration'] ?? null, $cachedResponse );
45
		}
46
		if(self::$statusCode===RouterStatus::OK) {
47
			self::$statusCode = RouterStatus::NOT_FOUND;
48
		}
49
		return false;
50
	}
51
52 2
	protected static function _getURL($routePath, $params) {
53 2
		$result = \preg_replace_callback ( '~\((.*?)\)~', function () use (&$params) {
54 2
			return \array_shift ( $params );
55 2
		}, $routePath );
56 2
		if (\count ( $params ) > 0) {
57
			$result = \rtrim ( $result, '/' ) . '/' . \implode ( '/', $params );
58
		}
59 2
		return $result;
60
	}
61
62 5
	protected static function checkRouteName($routeDetails, $name) {
63 5
		if (! isset ( $routeDetails ['name'] )) {
64 4
			foreach ( $routeDetails as $methodRouteDetail ) {
65 4
				if (isset ( $methodRouteDetail ['name'] ) && $methodRouteDetail ['name'] == $name) {
66
					return true;
67
				}
68
			}
69
		}
70 5
		return isset ( $routeDetails ['name'] ) && $routeDetails ['name'] == $name;
71
	}
72
73 7
	protected static function setParamsInOrder($paramsOrder, $params) {
74 7
		$index = 0;
75 7
		$newParams=[];
76 7
		foreach ( $paramsOrder as $order ) {
77 7
			if ($order === '*') {
78
				if (isset ( $params [$index] )) {
79
					$newParams = \array_merge ( $newParams, \array_diff ( \explode ( '/', $params [$index] ), [ '' ] ) );
80
				}
81
				break;
82
			}
83 7
			if (($order [0] ?? '') === '~') {
84 3
				$order = \intval ( \substr ( $order, 1, 1 ) );
85 3
				if (isset ( $params [$order] )) {
86 3
					$newParams = \array_merge ( $newParams, \array_diff ( \explode ( '/', $params [$order] ), [ '' ] ) );
87 3
					break;
88
				}
89
			}
90 7
			$newParams [] = self::cleanParam ( $params [$order] );
91 7
			unset ( $params [$order] );
92 7
			$index ++;
93
		}
94 7
		return $newParams;
95
	}
96
97
	/**
98
	 * Starts the router by loading normal routes (not rest)
99
	 */
100 43
	public static function start(): void {
101 43
		self::$routes = CacheManager::getControllerCache ();
102 43
	}
103
104
	/**
105
	 * Starts the router by loading rest routes (not normal routes)
106
	 */
107 5
	public static function startRest(): void {
108 5
		self::$routes = CacheManager::getControllerCache ( true );
109 5
	}
110
111
	/**
112
	 * Starts the router by loading all routes (normal + rest routes)
113
	 */
114 15
	public static function startAll(): void {
115 15
		self::$routes = \array_merge ( CacheManager::getControllerCache (), CacheManager::getControllerCache ( true ) );
116 15
	}
117
118
	/**
119
	 * Returns the route corresponding to a path
120
	 *
121
	 * @param string $path The route path
122
	 * @param boolean $cachedResponse
123
	 * @return boolean|mixed[]|string
124
	 */
125 28
	public static function getRoute($path, $cachedResponse = true, $debug = false) {
126 28
		$path = self::slashPath ( $path );
127 28
		if (isset ( self::$routes [$path] ) && ! $debug) { // No direct access to route in debug mode (for maintenance mode activation)
128 5
			return self::getRoute_ ( self::$routes [$path], $path, [ $path ], $cachedResponse );
129
		}
130 27
		foreach ( self::$routes as $routePath => $routeDetails ) {
131 27
			if (\preg_match ( "@^{$routePath}\$@s", $path, $matches )) {
132 10
				if (($r = self::getRoute_ ( $routeDetails, $routePath, $matches, $cachedResponse )) !== false) {
133 10
					return $r;
134
				}
135
			}
136
		}
137 21
		return false;
138
	}
139
140
	/**
141
	 * Returns the generated path from a route
142
	 *
143
	 * @param string $name name of the route
144
	 * @param array $parameters array of the route parameters. default : []
145
	 * @param boolean $absolute
146
	 */
147 3
	public static function getRouteByName($name, $parameters = [], $absolute = true) {
148 3
		foreach ( self::$routes as $routePath => $routeDetails ) {
149 3
			if (self::checkRouteName ( $routeDetails, $name )) {
150 3
				if (\trim ( $routePath, '/' ) == '_default') {
151
					return ($absolute)?'/':'';
152
				}
153 3
				if (\count ( $parameters ) > 0) {
154 2
					$routePath = self::_getURL ( $routePath, $parameters );
155
				}
156 3
				$routePath = \str_replace('//', '/',\preg_replace('~\((.*?)\)~', '', $routePath));
157 3
				return ($absolute)?$routePath:\ltrim ( $routePath, '/' );
158
			}
159
		}
160 3
		return false;
161
	}
162
163 2
	public static function getRouteInfoByName($name) {
164 2
		foreach ( self::$routes as $routeDetails ) {
165 2
			if (self::checkRouteName ( $routeDetails, $name )) {
166 2
				return $routeDetails;
167
			}
168
		}
169 1
		return false;
170
	}
171
172
	/**
173
	 * Returns the generated path from a route
174
	 *
175
	 * @param string $name The route name
176
	 * @param array $parameters default: []
177
	 * @param boolean $absolute true if the path is absolute (/ at first)
178
	 * @return boolean|string|array|mixed the generated path (/path/to/route)
179
	 */
180 1
	public static function path($name, $parameters = [], $absolute = false) {
181 1
		return self::getRouteByName ( $name, $parameters, $absolute );
182
	}
183
184
	/**
185
	 * Returns the generated url from a route
186
	 *
187
	 * @param string $name the route name
188
	 * @param array $parameters default: []
189
	 * @return string the generated url (http://myApp/path/to/route)
190
	 */
191 1
	public static function url($name, $parameters = []): string {
192 1
		return URequest::getUrl ( self::getRouteByName ( $name, $parameters, false ) );
193
	}
194
195 13
	public static function getRouteUrlParts($routeArray, $params, $cached = false, $duration = NULL, $cachedResponse = true) {
196 13
		$realPath = \current ( $params );
197 13
		\array_shift ( $params );
198 13
		$routeDetails = $routeArray ['details'];
199 13
		if ($routeDetails ['controller'] instanceof \Closure) {
200 1
			$result = [ 'callback'=>$routeDetails ['controller'] ];
201 1
			$resultStr = 'callable function';
202
		} else {
203 12
			$mainParams=null;
204 12
			if(($mainMethodParams=$routeDetails['main.params']??null)!==null){
205
				foreach ($mainMethodParams as $index=>$mainMethodParam) {
206
					$mainParams[$mainMethodParam]=$params[$index];
207
				}
208
				$params=\array_slice ( $params, $index+1);
209
			}
210 12
			$result = [ 'controller'=>\str_replace ( "\\\\", "\\", $routeDetails ['controller'] ),'action'=>$routeDetails ['action'],'mainParams'=>$mainParams];
211 12
			$resultStr = \json_encode($result);
212
		}
213 13
		if (($paramsOrder = $routeDetails ['parameters']) && (\count ( $paramsOrder ) > 0)) {
214 7
			$result['params']=self::setParamsInOrder ( $paramsOrder, $params );
215
		}
216 13
		if (! $cached || ! $cachedResponse) {
217 13
			Logger::info ( 'Router', \sprintf ( 'Route found for %s : %s', $routeArray ['path'], $resultStr ), 'getRouteUrlParts' );
218 13
			if (isset ( $routeDetails ['callback'] )) {
219
				// Used for maintenance mode
220
				if ($routeDetails ['callback'] instanceof \Closure) {
221
					return $routeDetails ['callback'] ( $result );
222
				}
223
			}
224 13
			return $result;
225
		}
226
		Logger::info ( 'Router', sprintf ( 'Route found for %s (from cache) : %s', $realPath, $resultStr ), 'getRouteUrlParts' );
227
		return CacheManager::getRouteCache ( $realPath, $result, $duration );
228
	}
229
230
	/**
231
	 * Adds a slash before and after a path
232
	 *
233
	 * @param string $path The path to modify
234
	 * @return string The path with slashes
235
	 */
236 31
	public static function slashPath($path): string {
237 31
		if (\substr ( $path, 0, 1 ) !== '/') {
238 29
			$path = '/' . $path;
239
		}
240 31
		if (\substr ( $path, - 1 ) !== '/') {
241 28
			$path = $path . '/';
242
		}
243 31
		return $path;
244
	}
245
246
	/**
247
	 * Declare a route as expired
248
	 *
249
	 * @param string $routePath
250
	 */
251 1
	public static function setExpired($routePath): void {
252 1
		CacheManager::setExpired ( $routePath );
253 1
	}
254
255
	/**
256
	 * Returns the array of loaded routes
257
	 *
258
	 * @return array|mixed
259
	 */
260 28
	public static function getRoutes() {
261 28
		return self::$routes;
262
	}
263
	
264
	/**
265
	 * Return router response status code.
266
	 * @return int
267
	 * @since 2.4.5
268
	 */
269
	public static function getStatusCode():int{
270
		return self::$statusCode;
271
	}
272
}
273