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 Uri 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 Uri, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 17 | class Uri implements UriInterface |
||
| 18 | { |
||
| 19 | private static $schemes = [ |
||
| 20 | 'http' => 80, |
||
| 21 | 'https' => 443, |
||
| 22 | ]; |
||
| 23 | |||
| 24 | private static $charUnreserved = 'a-zA-Z0-9_\-\.~'; |
||
| 25 | private static $charSubDelims = '!\$&\'\(\)\*\+,;='; |
||
| 26 | private static $replaceQuery = ['=' => '%3D', '&' => '%26']; |
||
| 27 | |||
| 28 | /** |
||
| 29 | * @var string Uri scheme. |
||
| 30 | */ |
||
| 31 | private $scheme = ''; |
||
| 32 | |||
| 33 | /** |
||
| 34 | * @var string Uri user info. |
||
| 35 | */ |
||
| 36 | private $userInfo = ''; |
||
| 37 | |||
| 38 | /** |
||
| 39 | * @var string Uri host. |
||
| 40 | */ |
||
| 41 | private $host = ''; |
||
| 42 | |||
| 43 | /** |
||
| 44 | * @var int|null Uri port. |
||
| 45 | */ |
||
| 46 | private $port; |
||
| 47 | |||
| 48 | /** |
||
| 49 | * @var string Uri path. |
||
| 50 | */ |
||
| 51 | private $path = ''; |
||
| 52 | |||
| 53 | /** |
||
| 54 | * @var string Uri query string. |
||
| 55 | */ |
||
| 56 | private $query = ''; |
||
| 57 | |||
| 58 | /** |
||
| 59 | * @var string Uri fragment. |
||
| 60 | */ |
||
| 61 | private $fragment = ''; |
||
| 62 | |||
| 63 | /** |
||
| 64 | * @param string $uri |
||
| 65 | */ |
||
| 66 | public function __construct($uri = '') |
||
| 77 | |||
| 78 | /** |
||
| 79 | * {@inheritdoc} |
||
| 80 | */ |
||
| 81 | public function __toString(): string |
||
| 91 | |||
| 92 | /** |
||
| 93 | * {@inheritdoc} |
||
| 94 | */ |
||
| 95 | public function getScheme(): string |
||
| 99 | |||
| 100 | /** |
||
| 101 | * {@inheritdoc} |
||
| 102 | */ |
||
| 103 | public function getAuthority(): string |
||
| 120 | |||
| 121 | /** |
||
| 122 | * {@inheritdoc} |
||
| 123 | */ |
||
| 124 | public function getUserInfo(): string |
||
| 128 | |||
| 129 | /** |
||
| 130 | * {@inheritdoc} |
||
| 131 | */ |
||
| 132 | public function getHost(): string |
||
| 136 | |||
| 137 | /** |
||
| 138 | * {@inheritdoc} |
||
| 139 | */ |
||
| 140 | public function getPort() |
||
| 144 | |||
| 145 | /** |
||
| 146 | * {@inheritdoc} |
||
| 147 | */ |
||
| 148 | public function getPath(): string |
||
| 152 | |||
| 153 | /** |
||
| 154 | * {@inheritdoc} |
||
| 155 | */ |
||
| 156 | public function getQuery(): string |
||
| 160 | |||
| 161 | /** |
||
| 162 | * {@inheritdoc} |
||
| 163 | */ |
||
| 164 | public function getFragment(): string |
||
| 168 | |||
| 169 | /** |
||
| 170 | * {@inheritdoc} |
||
| 171 | */ |
||
| 172 | public function withScheme($scheme): self |
||
| 186 | |||
| 187 | /** |
||
| 188 | * {@inheritdoc} |
||
| 189 | */ |
||
| 190 | public function withUserInfo($user, $password = null): self |
||
| 206 | |||
| 207 | /** |
||
| 208 | * {@inheritdoc} |
||
| 209 | */ |
||
| 210 | public function withHost($host): self |
||
| 223 | |||
| 224 | /** |
||
| 225 | * {@inheritdoc} |
||
| 226 | */ |
||
| 227 | public function withPort($port): self |
||
| 240 | |||
| 241 | /** |
||
| 242 | * {@inheritdoc} |
||
| 243 | */ |
||
| 244 | public function withPath($path): self |
||
| 257 | |||
| 258 | /** |
||
| 259 | * {@inheritdoc} |
||
| 260 | */ |
||
| 261 | public function withQuery($query): self |
||
| 274 | |||
| 275 | /** |
||
| 276 | * {@inheritdoc} |
||
| 277 | */ |
||
| 278 | public function withFragment($fragment): self |
||
| 291 | |||
| 292 | /** |
||
| 293 | * Apply parse_url parts to a URI. |
||
| 294 | * |
||
| 295 | * @param array $parts Array of parse_url parts to apply. |
||
| 296 | */ |
||
| 297 | private function applyParts(array $parts) |
||
| 322 | |||
| 323 | /** |
||
| 324 | * Create a URI string from its various parts. |
||
| 325 | * |
||
| 326 | * @param string $scheme |
||
| 327 | * @param string $authority |
||
| 328 | * @param string $path |
||
| 329 | * @param string $query |
||
| 330 | * @param string $fragment |
||
| 331 | * |
||
| 332 | * @return string |
||
| 333 | */ |
||
| 334 | private static function createUriString($scheme, $authority, $path, $query, $fragment): string |
||
| 373 | |||
| 374 | /** |
||
| 375 | * Is a given port non-standard for the current scheme? |
||
| 376 | * |
||
| 377 | * @param string $scheme |
||
| 378 | * @param int $port |
||
| 379 | * |
||
| 380 | * @return bool |
||
| 381 | */ |
||
| 382 | private static function isNonStandardPort($scheme, $port): bool |
||
| 386 | |||
| 387 | /** |
||
| 388 | * @param string $scheme |
||
| 389 | * |
||
| 390 | * @return string |
||
| 391 | * |
||
| 392 | * @throws \InvalidArgumentException If the scheme is invalid. |
||
| 393 | */ |
||
| 394 | private function filterScheme($scheme): string |
||
| 402 | |||
| 403 | /** |
||
| 404 | * @param string $host |
||
| 405 | * |
||
| 406 | * @return string |
||
| 407 | * |
||
| 408 | * @throws \InvalidArgumentException If the host is invalid. |
||
| 409 | */ |
||
| 410 | private function filterHost($host): string |
||
| 418 | |||
| 419 | /** |
||
| 420 | * @param int|null $port |
||
| 421 | * |
||
| 422 | * @return int|null |
||
| 423 | * |
||
| 424 | * @throws \InvalidArgumentException If the port is invalid. |
||
| 425 | */ |
||
| 426 | private function filterPort($port) |
||
| 441 | |||
| 442 | /** |
||
| 443 | * Filters the path of a URI. |
||
| 444 | * |
||
| 445 | * @param string $path |
||
| 446 | * |
||
| 447 | * @return string |
||
| 448 | * |
||
| 449 | * @throws \InvalidArgumentException If the path is invalid. |
||
| 450 | */ |
||
| 451 | View Code Duplication | private function filterPath($path): string |
|
| 463 | |||
| 464 | /** |
||
| 465 | * Filters the query string or fragment of a URI. |
||
| 466 | * |
||
| 467 | * @param string $str |
||
| 468 | * |
||
| 469 | * @return string |
||
| 470 | * |
||
| 471 | * @throws \InvalidArgumentException If the query or fragment is invalid. |
||
| 472 | */ |
||
| 473 | View Code Duplication | private function filterQueryAndFragment($str): string |
|
| 485 | |||
| 486 | private function rawurlencodeMatchZero(array $match): string |
||
| 490 | } |
||
| 491 |
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.