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 TokenCollection 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 TokenCollection, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 16 | class TokenCollection implements \IteratorAggregate |
||
| 17 | { |
||
| 18 | /** @var Token[] */ |
||
| 19 | protected $tokens =array (); |
||
| 20 | |||
| 21 | /** @var string */ |
||
| 22 | protected $sourceHash; |
||
| 23 | |||
| 24 | /** @var boolean */ |
||
| 25 | protected $ignoreUnknownFilters = True; |
||
| 26 | |||
| 27 | /** |
||
| 28 | * @param Token[] $tokens Array of Token with tokenString as keys. |
||
| 29 | */ |
||
| 30 | 15 | public function __construct(array $tokens = null) |
|
| 36 | |||
| 37 | /** |
||
| 38 | * Get the ignore unknown filters flag. |
||
| 39 | * |
||
| 40 | * @return boolean |
||
| 41 | */ |
||
| 42 | public function getIgnoreUnknownFilters() |
||
| 46 | |||
| 47 | /** |
||
| 48 | * Set the ignore unknown filters flag. |
||
| 49 | * |
||
| 50 | * @param boolean $ignoreUnknownFilters The new value for the ignore unknown filters flag. |
||
| 51 | * @return $this |
||
| 52 | */ |
||
| 53 | public function setIgnoreUnknownFilters($ignoreUnknownFilters) |
||
| 58 | |||
| 59 | /** |
||
| 60 | * (PHP 5 >= 5.0.0)<br/> |
||
| 61 | * Retrieve an external iterator |
||
| 62 | * @link http://php.net/manual/en/iteratoraggregate.getiterator.php |
||
| 63 | * @return \Traversable An instance of an object implementing <b>Iterator</b> or <b>Traversable</b> |
||
| 64 | */ |
||
| 65 | public function getIterator() |
||
| 69 | |||
| 70 | |||
| 71 | /** |
||
| 72 | * Check if the source hash was set. |
||
| 73 | * |
||
| 74 | * @return boolean |
||
| 75 | */ |
||
| 76 | 1 | public function hasSourceHash() |
|
| 80 | |||
| 81 | /** |
||
| 82 | * Get the source hash. |
||
| 83 | * |
||
| 84 | * @return string|null |
||
| 85 | */ |
||
| 86 | 1 | public function getSourceHash() |
|
| 93 | |||
| 94 | /** |
||
| 95 | * Set the source hash. |
||
| 96 | * |
||
| 97 | * @param string $value The new value. |
||
| 98 | * @return $this |
||
| 99 | */ |
||
| 100 | 14 | public function setSourceHash($value) |
|
| 105 | |||
| 106 | /** |
||
| 107 | * Check if the list of tokens is empty. |
||
| 108 | * |
||
| 109 | * @return boolean |
||
| 110 | */ |
||
| 111 | 6 | public function isEmpty() |
|
| 115 | |||
| 116 | /** |
||
| 117 | * Get the number of tokens in the list |
||
| 118 | * |
||
| 119 | * @return integer |
||
| 120 | */ |
||
| 121 | 1 | public function getCount() |
|
| 125 | |||
| 126 | /** |
||
| 127 | * Add a new token to the list. |
||
| 128 | * If another token already exists with the same tokenString then it will be overwritten. |
||
| 129 | * |
||
| 130 | * @param Token $token |
||
| 131 | * @return $this |
||
| 132 | */ |
||
| 133 | 14 | public function add(Token $token) |
|
| 139 | |||
| 140 | /** |
||
| 141 | * Import add all tokens from another list. |
||
| 142 | * Existing tokens will be overwritten. The hash will be unset. |
||
| 143 | * |
||
| 144 | * @param TokenCollection $tokens |
||
| 145 | * @return $this |
||
| 146 | */ |
||
| 147 | 1 | public function import(TokenCollection $tokens) |
|
| 156 | |||
| 157 | /** |
||
| 158 | * Remove the given token from the list. |
||
| 159 | * |
||
| 160 | * @param Token $token |
||
| 161 | * @return $this |
||
| 162 | */ |
||
| 163 | 1 | public function remove(Token $token) |
|
| 168 | |||
| 169 | /** |
||
| 170 | * Clear the list of tokens. |
||
| 171 | * |
||
| 172 | * @return $this |
||
| 173 | */ |
||
| 174 | 1 | public function clear() |
|
| 179 | |||
| 180 | /** |
||
| 181 | * Check if a token with the given token string is in this list. |
||
| 182 | * |
||
| 183 | * @param string $tokenString |
||
| 184 | * @return boolean |
||
| 185 | */ |
||
| 186 | 14 | public function has($tokenString) |
|
| 190 | |||
| 191 | /** |
||
| 192 | * Get a token from this list having the given token string. |
||
| 193 | * |
||
| 194 | * @param string $tokenString |
||
| 195 | * @return Token |
||
| 196 | */ |
||
| 197 | 2 | public function get($tokenString) |
|
| 201 | |||
| 202 | /** |
||
| 203 | * Get the list of tokens as an associative array of Token with tokenString as keys. |
||
| 204 | * |
||
| 205 | * @return Token[] |
||
| 206 | */ |
||
| 207 | 7 | public function getArray() |
|
| 211 | |||
| 212 | /** |
||
| 213 | * Get all token names. |
||
| 214 | * |
||
| 215 | * @return string[] |
||
| 216 | */ |
||
| 217 | public function getNames() |
||
| 226 | |||
| 227 | /** |
||
| 228 | * Find tokens having the given token name. |
||
| 229 | * |
||
| 230 | * @param string|string[]|null $tokenNameToFind Null to find all. String to find a certain token. Array of strings |
||
| 231 | * To find multiple tokens. |
||
| 232 | * @return Token[]|array Array of Token if $tokenNameToFind is string, Array of arrays of Token with token names |
||
| 233 | * as keys if $tokenNameToFind is string[] or null. |
||
| 234 | */ |
||
| 235 | 1 | public function findByName($tokenNameToFind = null) |
|
| 254 | |||
| 255 | /** |
||
| 256 | * Find all resolved tokens. |
||
| 257 | * |
||
| 258 | * @return Token[] |
||
| 259 | */ |
||
| 260 | 1 | View Code Duplication | public function findResolved() |
| 271 | |||
| 272 | /** |
||
| 273 | * Find all tokens resolved but not yet injected. |
||
| 274 | * |
||
| 275 | * @return Token[] |
||
| 276 | */ |
||
| 277 | 1 | View Code Duplication | public function findResolvedAndNotInjected() |
| 288 | |||
| 289 | /** |
||
| 290 | * Find all unresolved tokens. |
||
| 291 | * |
||
| 292 | * @return Token[] |
||
| 293 | */ |
||
| 294 | 1 | View Code Duplication | public function findUnresolved() |
| 305 | |||
| 306 | /** |
||
| 307 | * Check if at least one unresolved token exists in the list. |
||
| 308 | * |
||
| 309 | * @return boolean |
||
| 310 | */ |
||
| 311 | 1 | public function hasUnresolved() |
|
| 321 | |||
| 322 | /** |
||
| 323 | * Find all tokens with unresolved filters. |
||
| 324 | * |
||
| 325 | * @return Token[] |
||
| 326 | */ |
||
| 327 | 1 | View Code Duplication | public function findWithUnresolvedFilters() |
| 338 | |||
| 339 | /** |
||
| 340 | * Attempt to resolve the values for all tokens in the list. |
||
| 341 | * |
||
| 342 | * @param TokenResolverInterface $tokenResolver |
||
| 343 | * @param boolean|null $ignoreUnknownTokens Null to use token resolver option. |
||
| 344 | * @param boolean|null $ignoreUnknownFilters Null to use collection option. |
||
| 345 | * @throws UnknownFilterException |
||
| 346 | * |
||
| 347 | * If using RegisteredTokenResolver: |
||
| 348 | * @throws UnknownTokenException If the token name was not registered and set not to ignore unknown tokens. |
||
| 349 | * |
||
| 350 | * If using ScopeTokenResolver: |
||
| 351 | * @throws UnknownTokenException |
||
| 352 | * @throws OutOfScopeException |
||
| 353 | * @throws ScopeTokenValueSerializationException |
||
| 354 | * @throws TokenFormatException |
||
| 355 | * @return $this |
||
| 356 | */ |
||
| 357 | 14 | public function resolve(TokenResolverInterface $tokenResolver, |
|
| 379 | |||
| 380 | } |
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.