| Total Complexity | 57 |
| Total Lines | 249 |
| Duplicated Lines | 0 % |
| Coverage | 94.12% |
| Changes | 2 | ||
| Bugs | 0 | Features | 1 |
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 |
||
| 22 | class Router { |
||
| 23 | use RouterModifierTrait,RouterAdminTrait,RouterTestTrait; |
||
| 24 | protected static $routes; |
||
| 25 | protected static $statusCode; |
||
| 26 | |||
| 27 | 10 | private static function cleanParam(string $param): string { |
|
| 28 | 10 | if (\substr ( $param, - 1 ) === '/') { |
|
| 29 | return \substr ( $param, 0, - 1 ); |
||
| 30 | } |
||
| 31 | 10 | return $param; |
|
| 32 | } |
||
| 33 | |||
| 34 | 30 | private static function getRoute_(&$routeDetails, $routePath, $matches, $cachedResponse) { |
|
| 35 | 30 | self::$statusCode=RouterStatus::OK; |
|
| 36 | 30 | if (! isset ( $routeDetails ['controller'] )) { |
|
| 37 | 9 | $method = \strtolower ( $_SERVER ['REQUEST_METHOD'] ); |
|
| 38 | 9 | if (isset ( $routeDetails [$method] )) { |
|
| 39 | 9 | $routeDetailsMethod = $routeDetails [$method]; |
|
| 40 | 9 | return self::getRouteUrlParts ( [ 'path' => $routePath,'details' => $routeDetailsMethod ], $matches, $routeDetailsMethod ['cache'] ?? false, $routeDetailsMethod ['duration'] ?? null, $cachedResponse ); |
|
| 41 | } |
||
| 42 | 2 | self::$statusCode=RouterStatus::METHOD_NOT_ALLOWED; |
|
| 43 | } else { |
||
| 44 | 21 | return self::getRouteUrlParts ( [ 'path' => $routePath,'details' => $routeDetails ], $matches, $routeDetails ['cache'] ?? false, $routeDetails ['duration'] ?? null, $cachedResponse ); |
|
| 45 | } |
||
| 46 | 2 | if(self::$statusCode===RouterStatus::OK) { |
|
| 47 | self::$statusCode = RouterStatus::NOT_FOUND; |
||
| 48 | } |
||
| 49 | 2 | return false; |
|
| 50 | } |
||
| 51 | |||
| 52 | 3 | protected static function _getURL($routePath, $params) { |
|
| 60 | } |
||
| 61 | |||
| 62 | 6 | protected static function checkRouteName($routeDetails, $name) { |
|
| 63 | 6 | 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 | 6 | return isset ( $routeDetails ['name'] ) && $routeDetails ['name'] == $name; |
|
| 71 | } |
||
| 72 | |||
| 73 | 13 | protected static function setParamsInOrder($paramsOrder, $params) { |
|
| 74 | 13 | $index = 0; |
|
| 75 | 13 | $newParams=[]; |
|
| 76 | 13 | foreach ( $paramsOrder as $order ) { |
|
| 77 | 13 | if ($order === '*') { |
|
| 78 | 1 | if (isset ( $params [$index] )) { |
|
| 79 | 1 | $newParams = \array_merge ( $newParams, \array_diff ( \explode ( '/', $params [$index] ), [ '' ] ) ); |
|
| 80 | } |
||
| 81 | 1 | break; |
|
| 82 | } |
||
| 83 | 12 | if (($order [0] ?? '') === '~') { |
|
| 84 | 5 | $order = \intval ( \substr ( $order, 1, 1 ) ); |
|
| 85 | 5 | if (isset ( $params [$order] )) { |
|
| 86 | 5 | $newParams = \array_merge ( $newParams, \array_diff ( \explode ( '/', $params [$order] ), [ '' ] ) ); |
|
| 87 | 5 | break; |
|
| 88 | } |
||
| 89 | } |
||
| 90 | 10 | $newParams [] = self::cleanParam ( $params [$order] ); |
|
| 91 | 10 | unset ( $params [$order] ); |
|
| 92 | 10 | $index ++; |
|
| 93 | } |
||
| 94 | 13 | 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 | 52 | public static function startAll(): void { |
|
| 115 | 52 | self::$routes = \array_merge ( CacheManager::getControllerCache (), CacheManager::getControllerCache ( true ) ); |
|
| 116 | 52 | } |
|
| 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 | 65 | public static function getRoute($path, $cachedResponse = true, $debug = false) { |
|
| 126 | 65 | $path = self::slashPath ( $path ); |
|
| 127 | 65 | 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 | 64 | foreach ( self::$routes as $routePath => $routeDetails ) { |
|
| 131 | 64 | if (\preg_match ( "@^{$routePath}\$@s", $path, $matches )) { |
|
| 132 | 27 | if (($r = self::getRoute_ ( $routeDetails, $routePath, $matches, $cachedResponse )) !== false) { |
|
| 133 | 27 | return $r; |
|
| 134 | } |
||
| 135 | } |
||
| 136 | } |
||
| 137 | 46 | 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 | 4 | public static function getRouteByName($name, $parameters = [], $absolute = true) { |
|
| 148 | 4 | foreach ( self::$routes as $routePath => $routeDetails ) { |
|
| 149 | 4 | if (self::checkRouteName ( $routeDetails, $name )) { |
|
| 150 | 4 | if (\trim ( $routePath, '/' ) == '_default') { |
|
| 151 | return ($absolute)?'/':''; |
||
| 152 | } |
||
| 153 | 4 | if (\count ( $parameters ) > 0) { |
|
| 154 | 3 | $routePath = self::_getURL ( $routePath, $parameters ); |
|
| 155 | } |
||
| 156 | 4 | $routePath = \str_replace('//', '/',\preg_replace('~\((.*?)\)~', '', $routePath)); |
|
| 157 | 4 | 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 | 2 | public static function path($name, $parameters = [], $absolute = false) { |
|
| 181 | 2 | 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 { |
|
| 193 | } |
||
| 194 | |||
| 195 | 30 | public static function getRouteUrlParts($routeArray, $params, $cached = false, $duration = NULL, $cachedResponse = true) { |
|
| 196 | 30 | $realPath = \current ( $params ); |
|
| 197 | 30 | \array_shift ( $params ); |
|
| 198 | 30 | $routeDetails = $routeArray ['details']; |
|
| 199 | 30 | if ($routeDetails ['controller'] instanceof \Closure) { |
|
| 200 | 1 | $result = [ 'callback'=>$routeDetails ['controller'] ]; |
|
| 201 | 1 | $resultStr = 'callable function'; |
|
| 202 | } else { |
||
| 203 | 29 | $mainParams=null; |
|
| 204 | 29 | if(($mainMethodParams=$routeDetails['main.params']??null)!==null){ |
|
| 205 | 1 | foreach ($mainMethodParams as $index=>$mainMethodParam) { |
|
| 206 | 1 | $mainParams[$mainMethodParam]=$params[$index]; |
|
| 207 | } |
||
| 208 | 1 | $params=\array_slice ( $params, $index+1); |
|
|
1 ignored issue
–
show
|
|||
| 209 | } |
||
| 210 | 29 | $result = [ 'controller'=>\str_replace ( "\\\\", "\\", $routeDetails ['controller'] ),'action'=>$routeDetails ['action'],'mainParams'=>$mainParams]; |
|
| 211 | 29 | $resultStr = \json_encode($result); |
|
| 212 | } |
||
| 213 | 30 | if (($paramsOrder = $routeDetails ['parameters']) && (\count ( $paramsOrder ) > 0)) { |
|
| 214 | 13 | $result['params']=self::setParamsInOrder ( $paramsOrder, $params ); |
|
| 215 | } |
||
| 216 | 30 | if (! $cached || ! $cachedResponse) { |
|
| 217 | 30 | Logger::info ( 'Router', \sprintf ( 'Route found for %s : %s', $routeArray ['path'], $resultStr ), 'getRouteUrlParts' ); |
|
| 218 | 30 | if (isset ( $routeDetails ['callback'] )) { |
|
| 219 | // Used for maintenance mode |
||
| 220 | 1 | if ($routeDetails ['callback'] instanceof \Closure) { |
|
| 221 | 1 | return $routeDetails ['callback'] ( $result ); |
|
| 222 | } |
||
| 223 | } |
||
| 224 | 29 | 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 | 68 | public static function slashPath($path): string { |
|
| 237 | 68 | if (\substr ( $path, 0, 1 ) !== '/') { |
|
| 238 | 66 | $path = '/' . $path; |
|
| 239 | } |
||
| 240 | 68 | if (\substr ( $path, - 1 ) !== '/') { |
|
| 241 | 64 | $path = $path . '/'; |
|
| 242 | } |
||
| 243 | 68 | 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 | 65 | public static function getRoutes() { |
|
| 262 | } |
||
| 263 | |||
| 264 | /** |
||
| 265 | * Return router response status code. |
||
| 266 | * @return int |
||
| 267 | * @since 2.4.5 |
||
| 268 | */ |
||
| 269 | 1 | public static function getStatusCode():int{ |
|
| 271 | } |
||
| 272 | } |
||
| 273 |