Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like WebRequest 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
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 WebRequest, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 23 | class WebRequest |
||
| 24 | { |
||
| 25 | /** |
||
| 26 | * @var IGlobalStateProvider Provides access to the global state. |
||
| 27 | */ |
||
| 28 | private static $globalStateProvider; |
||
| 29 | |||
| 30 | /** |
||
| 31 | * Returns a boolean value if the request was submitted with the HTTP POST method. |
||
| 32 | * @return bool |
||
| 33 | */ |
||
| 34 | 3 | public static function wasPosted() |
|
| 35 | { |
||
| 36 | 3 | return self::method() === 'POST'; |
|
| 37 | } |
||
| 38 | |||
| 39 | /** |
||
| 40 | * Gets the HTTP Method used |
||
| 41 | * @return string|null |
||
| 42 | */ |
||
| 43 | 3 | View Code Duplication | public static function method() |
| 53 | |||
| 54 | /** |
||
| 55 | * Gets a boolean value stating whether the request was served over HTTPS or not. |
||
| 56 | * @return bool |
||
| 57 | */ |
||
| 58 | 7 | public static function isHttps() |
|
| 59 | { |
||
| 60 | 7 | $server = &self::$globalStateProvider->getServerSuperGlobal(); |
|
| 61 | |||
| 62 | 7 | View Code Duplication | if (isset($server['HTTP_X_FORWARDED_PROTO'])) { |
| 63 | 3 | if ($server['HTTP_X_FORWARDED_PROTO'] === 'https') { |
|
| 64 | // Client <=> Proxy is encrypted |
||
| 65 | 2 | return true; |
|
| 66 | } |
||
| 67 | else { |
||
| 68 | // Proxy <=> Server link unknown, Client <=> Proxy is not encrypted. |
||
| 69 | 1 | return false; |
|
| 70 | } |
||
| 71 | } |
||
| 72 | |||
| 73 | 4 | View Code Duplication | if (isset($server['HTTPS'])) { |
| 74 | 3 | if ($server['HTTPS'] === 'off') { |
|
| 75 | // ISAPI on IIS breaks the spec. :( |
||
| 76 | 1 | return false; |
|
| 77 | } |
||
| 78 | |||
| 79 | 2 | if ($server['HTTPS'] !== '') { |
|
| 80 | // Set to a non-empty value |
||
| 81 | 2 | return true; |
|
| 82 | } |
||
| 83 | } |
||
| 84 | |||
| 85 | 1 | return false; |
|
| 86 | } |
||
| 87 | |||
| 88 | /** |
||
| 89 | * Gets the path info |
||
| 90 | * |
||
| 91 | * @return array Array of path info segments |
||
| 92 | */ |
||
| 93 | 13 | public static function pathInfo() |
|
| 94 | { |
||
| 95 | 13 | $server = &self::$globalStateProvider->getServerSuperGlobal(); |
|
| 96 | 13 | if (!isset($server['PATH_INFO'])) { |
|
| 97 | 1 | return array(); |
|
| 98 | } |
||
| 99 | |||
| 100 | 12 | $exploded = explode('/', $server['PATH_INFO']); |
|
| 101 | |||
| 102 | // filter out empty values, and reindex from zero. Notably, the first element is always zero, since it starts |
||
| 103 | // with a / |
||
| 104 | 12 | return array_values(array_filter($exploded)); |
|
| 105 | } |
||
| 106 | |||
| 107 | /** |
||
| 108 | * Gets the remote address of the web request |
||
| 109 | * @return null|string |
||
| 110 | */ |
||
| 111 | 2 | View Code Duplication | public static function remoteAddress() |
| 112 | { |
||
| 113 | 2 | $server = &self::$globalStateProvider->getServerSuperGlobal(); |
|
| 114 | |||
| 115 | 2 | if (isset($server['REMOTE_ADDR'])) { |
|
| 116 | 1 | return $server['REMOTE_ADDR']; |
|
| 117 | } |
||
| 118 | |||
| 119 | 1 | return null; |
|
| 120 | } |
||
| 121 | |||
| 122 | /** |
||
| 123 | * Gets the XFF header contents for the web request |
||
| 124 | * @return null|string |
||
| 125 | */ |
||
| 126 | 2 | View Code Duplication | public static function forwardedAddress() |
| 127 | { |
||
| 128 | 2 | $server = &self::$globalStateProvider->getServerSuperGlobal(); |
|
| 129 | |||
| 130 | 2 | if (isset($server['HTTP_X_FORWARDED_FOR'])) { |
|
| 131 | 1 | return $server['HTTP_X_FORWARDED_FOR']; |
|
| 132 | } |
||
| 133 | |||
| 134 | 1 | return null; |
|
| 135 | } |
||
| 136 | |||
| 137 | /** |
||
| 138 | * Sets the global state provider. |
||
| 139 | * |
||
| 140 | * Almost guaranteed this is not the method you want in production code. |
||
| 141 | * |
||
| 142 | * @param IGlobalStateProvider $globalState |
||
| 143 | */ |
||
| 144 | 45 | public static function setGlobalStateProvider($globalState) |
|
| 148 | |||
| 149 | #region POST variables |
||
| 150 | |||
| 151 | /** |
||
| 152 | * @param string $key |
||
| 153 | * |
||
| 154 | * @return null|string |
||
| 155 | */ |
||
| 156 | 1 | View Code Duplication | public static function postString($key) |
| 157 | { |
||
| 158 | 1 | $post = &self::$globalStateProvider->getPostSuperGlobal(); |
|
| 159 | 1 | if (!array_key_exists($key, $post)) { |
|
| 160 | return null; |
||
| 161 | } |
||
| 162 | |||
| 163 | 1 | if ($post[$key] === "") { |
|
| 164 | 1 | return null; |
|
| 165 | } |
||
| 166 | |||
| 167 | 1 | return (string)$post[$key]; |
|
| 168 | } |
||
| 169 | |||
| 170 | /** |
||
| 171 | * @param string $key |
||
| 172 | * |
||
| 173 | * @return null|string |
||
| 174 | */ |
||
| 175 | View Code Duplication | public static function postEmail($key) |
|
| 176 | { |
||
| 177 | $post = &self::$globalStateProvider->getPostSuperGlobal(); |
||
| 178 | if (!array_key_exists($key, $post)) { |
||
| 179 | return null; |
||
| 180 | } |
||
| 181 | |||
| 182 | $filteredValue = filter_var($post[$key], FILTER_SANITIZE_EMAIL); |
||
| 183 | |||
| 184 | if ($filteredValue === false) { |
||
| 185 | return null; |
||
| 186 | } |
||
| 187 | |||
| 188 | return (string)$filteredValue; |
||
| 189 | } |
||
| 190 | |||
| 191 | /** |
||
| 192 | * @param string $key |
||
| 193 | * |
||
| 194 | * @return int|null |
||
| 195 | */ |
||
| 196 | 1 | View Code Duplication | public static function postInt($key) |
| 197 | { |
||
| 198 | 1 | $post = &self::$globalStateProvider->getPostSuperGlobal(); |
|
| 199 | 1 | if (!array_key_exists($key, $post)) { |
|
| 200 | 1 | return null; |
|
| 201 | } |
||
| 202 | |||
| 203 | 1 | $filteredValue = filter_var($post[$key], FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE); |
|
| 204 | |||
| 205 | 1 | if ($filteredValue === null) { |
|
| 206 | 1 | return null; |
|
| 207 | } |
||
| 208 | |||
| 209 | 1 | return (int)$filteredValue; |
|
| 210 | } |
||
| 211 | |||
| 212 | /** |
||
| 213 | * @param string $key |
||
| 214 | * |
||
| 215 | * @return bool |
||
| 216 | */ |
||
| 217 | 1 | View Code Duplication | public static function postBoolean($key) |
| 218 | { |
||
| 219 | 1 | $get = &self::$globalStateProvider->getPostSuperGlobal(); |
|
| 220 | 1 | if (!array_key_exists($key, $get)) { |
|
| 221 | 1 | return false; |
|
| 222 | } |
||
| 223 | |||
| 224 | // presence of parameter only |
||
| 225 | 1 | if ($get[$key] === "") { |
|
| 226 | 1 | return true; |
|
| 227 | } |
||
| 228 | |||
| 229 | 1 | if (in_array($get[$key], array(false, 'no', 'off', 0, 'false'), true)) { |
|
| 230 | 1 | return false; |
|
| 231 | } |
||
| 232 | |||
| 233 | 1 | return true; |
|
| 234 | } |
||
| 235 | |||
| 236 | #endregion |
||
| 237 | |||
| 238 | #region GET variables |
||
| 239 | |||
| 240 | /** |
||
| 241 | * @param string $key |
||
| 242 | * |
||
| 243 | * @return bool |
||
| 244 | */ |
||
| 245 | 1 | View Code Duplication | public static function getBoolean($key) |
| 246 | { |
||
| 247 | 1 | $get = &self::$globalStateProvider->getGetSuperGlobal(); |
|
| 248 | 1 | if (!array_key_exists($key, $get)) { |
|
| 249 | 1 | return false; |
|
| 250 | } |
||
| 251 | |||
| 252 | // presence of parameter only |
||
| 253 | 1 | if ($get[$key] === "") { |
|
| 254 | 1 | return true; |
|
| 255 | } |
||
| 256 | |||
| 257 | 1 | if (in_array($get[$key], array(false, 'no', 'off', 0, 'false'), true)) { |
|
| 258 | 1 | return false; |
|
| 259 | } |
||
| 260 | |||
| 261 | 1 | return true; |
|
| 262 | } |
||
| 263 | |||
| 264 | /** |
||
| 265 | * @param string $key |
||
| 266 | * |
||
| 267 | * @return int|null |
||
| 268 | */ |
||
| 269 | 1 | View Code Duplication | public static function getInt($key) |
| 270 | { |
||
| 271 | 1 | $get = &self::$globalStateProvider->getGetSuperGlobal(); |
|
| 272 | 1 | if (!array_key_exists($key, $get)) { |
|
| 273 | 1 | return null; |
|
| 274 | } |
||
| 275 | |||
| 276 | 1 | $filteredValue = filter_var($get[$key], FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE); |
|
| 277 | |||
| 278 | 1 | if ($filteredValue === null) { |
|
| 279 | 1 | return null; |
|
| 280 | } |
||
| 281 | |||
| 282 | 1 | return (int)$filteredValue; |
|
| 283 | } |
||
| 284 | |||
| 285 | /** |
||
| 286 | * @param string $key |
||
| 287 | * |
||
| 288 | * @return null|string |
||
| 289 | */ |
||
| 290 | 1 | View Code Duplication | public static function getString($key) |
| 291 | { |
||
| 292 | 1 | $get = &self::$globalStateProvider->getGetSuperGlobal(); |
|
| 293 | 1 | if (!array_key_exists($key, $get)) { |
|
| 294 | return null; |
||
| 295 | } |
||
| 296 | |||
| 297 | 1 | if ($get[$key] === "") { |
|
| 298 | 1 | return null; |
|
| 299 | } |
||
| 300 | |||
| 301 | 1 | return (string)$get[$key]; |
|
| 302 | } |
||
| 303 | |||
| 304 | #endregion |
||
| 305 | |||
| 306 | /** |
||
| 307 | * Sets the logged-in user to the specified user. |
||
| 308 | * |
||
| 309 | * @param User $user |
||
| 310 | */ |
||
| 311 | 1 | public static function setLoggedInUser(User $user) |
|
| 312 | { |
||
| 313 | 1 | $session = &self::$globalStateProvider->getSessionSuperGlobal(); |
|
| 314 | |||
| 315 | 1 | $session['userID'] = $user->getId(); |
|
| 316 | 1 | unset($session['partialLogin']); |
|
| 317 | 1 | } |
|
| 318 | |||
| 319 | /** |
||
| 320 | * Sets the post-login redirect |
||
| 321 | */ |
||
| 322 | 2 | public static function setPostLoginRedirect() |
|
| 327 | |||
| 328 | /** |
||
| 329 | * @return string|null |
||
| 330 | */ |
||
| 331 | 2 | View Code Duplication | public static function requestUri() |
| 332 | { |
||
| 333 | 2 | $server = &self::$globalStateProvider->getServerSuperGlobal(); |
|
| 334 | |||
| 335 | 2 | if (isset($server['REQUEST_URI'])) { |
|
| 336 | 1 | return $server['REQUEST_URI']; |
|
| 337 | } |
||
| 338 | |||
| 339 | 1 | return null; |
|
| 340 | } |
||
| 341 | |||
| 342 | /** |
||
| 343 | * Clears the post-login redirect |
||
| 344 | * @return string |
||
| 345 | */ |
||
| 346 | 2 | public static function clearPostLoginRedirect() |
|
| 358 | |||
| 359 | /** |
||
| 360 | * @return string|null |
||
| 361 | */ |
||
| 362 | View Code Duplication | public static function serverName() |
|
| 363 | { |
||
| 364 | $server = &self::$globalStateProvider->getServerSuperGlobal(); |
||
| 365 | |||
| 366 | if (isset($server['SERVER_NAME'])) { |
||
| 367 | return $server['SERVER_NAME']; |
||
| 368 | } |
||
| 369 | |||
| 370 | return null; |
||
| 371 | } |
||
| 372 | |||
| 373 | /** |
||
| 374 | * You probably only want to deal with this through SessionAlert. |
||
| 375 | * @return void |
||
| 376 | */ |
||
| 377 | public static function clearSessionAlertData() |
||
| 378 | { |
||
| 379 | $session = &self::$globalStateProvider->getSessionSuperGlobal(); |
||
| 380 | if (array_key_exists('alerts', $session)) { |
||
| 381 | unset($session['alerts']); |
||
| 382 | } |
||
| 383 | } |
||
| 384 | |||
| 385 | /** |
||
| 386 | * You probably only want to deal with this through SessionAlert. |
||
| 387 | * |
||
| 388 | * @return string[] |
||
| 389 | */ |
||
| 390 | public static function getSessionAlertData() |
||
| 399 | |||
| 400 | /** |
||
| 401 | * You probably only want to deal with this through SessionAlert. |
||
| 402 | * |
||
| 403 | * @param string[] $data |
||
| 404 | */ |
||
| 405 | public static function setSessionAlertData($data) |
||
| 410 | |||
| 411 | /** |
||
| 412 | * You probably only want to deal with this through TokenManager. |
||
| 413 | * |
||
| 414 | * @return string[] |
||
| 415 | */ |
||
| 416 | public static function getSessionTokenData() |
||
| 425 | |||
| 426 | /** |
||
| 427 | * You probably only want to deal with this through TokenManager. |
||
| 428 | * |
||
| 429 | * @param string[] $data |
||
| 430 | */ |
||
| 431 | public static function setSessionTokenData($data) |
||
| 436 | |||
| 437 | /** |
||
| 438 | * @param string $key |
||
| 439 | * @return mixed |
||
| 440 | */ |
||
| 441 | public static function getSessionContext($key){ |
||
| 442 | $session = &self::$globalStateProvider->getSessionSuperGlobal(); |
||
| 443 | |||
| 444 | if(!isset($session['context'])) { |
||
| 445 | $session['context'] = array(); |
||
| 446 | } |
||
| 447 | |||
| 448 | if(!isset($session['context'][$key])){ |
||
| 449 | return null; |
||
| 450 | } |
||
| 451 | |||
| 452 | return $session['context'][$key]; |
||
| 454 | |||
| 455 | /** |
||
| 456 | * @param string $key |
||
| 457 | * @param mixed $data |
||
| 458 | */ |
||
| 459 | public static function setSessionContext($key, $data){ |
||
| 468 | |||
| 469 | /** |
||
| 470 | * @return int|null |
||
| 471 | */ |
||
| 472 | public static function getSessionUserId() |
||
| 478 | |||
| 479 | /** |
||
| 480 | * @param User $user |
||
| 481 | */ |
||
| 482 | public static function setPartialLogin(User $user) |
||
| 487 | |||
| 488 | /** |
||
| 489 | * @return int|null |
||
| 490 | */ |
||
| 491 | public static function getPartialLogin() |
||
| 497 | |||
| 498 | /** |
||
| 499 | * @return null|string |
||
| 500 | */ |
||
| 501 | View Code Duplication | public static function userAgent() |
|
| 511 | |||
| 512 | /** |
||
| 513 | * @return null|string |
||
| 514 | */ |
||
| 515 | View Code Duplication | public static function scriptName() |
|
| 525 | |||
| 526 | /** |
||
| 527 | * @return null|string |
||
| 528 | */ |
||
| 529 | View Code Duplication | public static function origin() |
|
| 539 | } |
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.