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 Type 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 Type, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 8 | class Type |
||
| 9 | { |
||
| 10 | /** |
||
| 11 | * Short format [e.g. image/jpeg] for strings. |
||
| 12 | */ |
||
| 13 | const SHORT_TEXT = 0; |
||
| 14 | |||
| 15 | /** |
||
| 16 | * Full format [e.g. image/jpeg; p="1"] for strings. |
||
| 17 | */ |
||
| 18 | const FULL_TEXT = 1; |
||
| 19 | |||
| 20 | /** |
||
| 21 | * Full format with comments [e.g. image/jpeg; p="1" (comment)] for strings. |
||
| 22 | */ |
||
| 23 | const FULL_TEXT_WITH_COMMENTS = 2; |
||
| 24 | |||
| 25 | /** |
||
| 26 | * The MIME media type. |
||
| 27 | * |
||
| 28 | * @var string |
||
| 29 | */ |
||
| 30 | protected $media; |
||
| 31 | |||
| 32 | /** |
||
| 33 | * The MIME media type comment. |
||
| 34 | * |
||
| 35 | * @var string |
||
| 36 | */ |
||
| 37 | protected $mediaComment; |
||
| 38 | |||
| 39 | /** |
||
| 40 | * The MIME media sub-type. |
||
| 41 | * |
||
| 42 | * @var string |
||
| 43 | */ |
||
| 44 | protected $subType; |
||
| 45 | |||
| 46 | /** |
||
| 47 | * The MIME media sub-type comment. |
||
| 48 | * |
||
| 49 | * @var string |
||
| 50 | */ |
||
| 51 | protected $subTypeComment; |
||
| 52 | |||
| 53 | /** |
||
| 54 | * Optional MIME parameters |
||
| 55 | * |
||
| 56 | * @var TypeParameter[] |
||
| 57 | */ |
||
| 58 | protected $parameters = []; |
||
| 59 | |||
| 60 | /** |
||
| 61 | * Constructor. |
||
| 62 | * |
||
| 63 | * The type string will be parsed and the appropriate class vars set. |
||
| 64 | * |
||
| 65 | * @param string $type MIME type |
||
| 66 | */ |
||
| 67 | 54 | public function __construct($type) |
|
| 71 | |||
| 72 | /** |
||
| 73 | * Parse a mime-type and set the class variables. |
||
| 74 | * |
||
| 75 | * @param string $type MIME type to parse |
||
| 76 | * |
||
| 77 | * @return void |
||
| 78 | */ |
||
| 79 | 54 | protected function parse($type) |
|
| 80 | { |
||
| 81 | // Media and SubType are separated by a slash '/'. |
||
| 82 | 54 | $media = TypeParser::parseStringPart($type, 0, '/'); |
|
| 83 | |||
| 84 | 53 | if (!$media['string']) { |
|
| 85 | 3 | throw new MalformedTypeException('Media type not found'); |
|
| 86 | } |
||
| 87 | 50 | if (!$media['delimiter_matched']) { |
|
| 88 | 1 | throw new MalformedTypeException('Slash \'/\' to separate media type and subtype not found'); |
|
| 89 | } |
||
| 90 | |||
| 91 | 49 | $this->media = strtolower($media['string']); |
|
| 92 | 49 | $this->mediaComment = $media['comment']; |
|
| 93 | |||
| 94 | // SubType and Parameters are separated by semicolons ';'. |
||
| 95 | 49 | $sub = TypeParser::parseStringPart($type, $media['end_offset'] + 1, ';'); |
|
| 96 | |||
| 97 | 48 | if (!$sub['string']) { |
|
| 98 | 1 | throw new MalformedTypeException('Media subtype not found'); |
|
| 99 | } |
||
| 100 | |||
| 101 | 47 | $this->subType = strtolower($sub['string']); |
|
| 102 | 47 | $this->subTypeComment = $sub['comment']; |
|
| 103 | |||
| 104 | // Loops through the parameter. |
||
| 105 | 47 | while ($sub['delimiter_matched']) { |
|
| 106 | 26 | $sub = TypeParser::parseStringPart($type, $sub['end_offset'] + 1, ';'); |
|
| 107 | 26 | $tmp = explode('=', $sub['string'], 2); |
|
| 108 | 26 | $p_name = trim($tmp[0]); |
|
| 109 | 26 | $p_val = trim($tmp[1]); |
|
| 110 | 26 | if ($p_val[0] == '"' && $p_val[strlen($p_val) - 1] == '"') { |
|
| 111 | $p_val = substr($p_val, 1, -1); |
||
| 112 | } |
||
| 113 | 26 | $p_val = str_replace('\\"', '"', $p_val); |
|
| 114 | 26 | $this->addParameter($p_name, $p_val, $sub['comment']); |
|
| 115 | } |
||
| 116 | 47 | } |
|
| 117 | |||
| 118 | /** |
||
| 119 | * Does this type have any parameters? |
||
| 120 | * |
||
| 121 | * @return boolean true if type has parameters, false otherwise |
||
| 122 | */ |
||
| 123 | 28 | public function hasParameters() |
|
| 124 | { |
||
| 125 | 28 | return (bool) $this->parameters; |
|
| 126 | } |
||
| 127 | |||
| 128 | /** |
||
| 129 | * Get a MIME type's parameters |
||
| 130 | * |
||
| 131 | * @return TypeParameter[] Type's parameters |
||
| 132 | */ |
||
| 133 | 29 | public function getParameters() |
|
| 134 | { |
||
| 135 | 29 | return $this->parameters; |
|
| 136 | } |
||
| 137 | |||
| 138 | /** |
||
| 139 | * Get a MIME type's parameter |
||
| 140 | * |
||
| 141 | * @param string $name Parameter name |
||
| 142 | * |
||
| 143 | * @return TypeParameter|null |
||
| 144 | */ |
||
| 145 | 23 | public function getParameter($name) |
|
| 146 | { |
||
| 147 | 23 | return isset($this->parameters[$name]) ? $this->parameters[$name] : null; |
|
| 148 | } |
||
| 149 | |||
| 150 | /** |
||
| 151 | * Get a MIME type's media. |
||
| 152 | * |
||
| 153 | * Note: 'media' refers to the portion before the first slash. |
||
| 154 | * |
||
| 155 | * @return string Type's media. |
||
| 156 | */ |
||
| 157 | 41 | public function getMedia() |
|
| 158 | { |
||
| 159 | 41 | return $this->media; |
|
| 160 | } |
||
| 161 | |||
| 162 | /** |
||
| 163 | * Get a MIME type's media comment. |
||
| 164 | * |
||
| 165 | * @return string Type's media comment. |
||
| 166 | */ |
||
| 167 | 28 | public function getMediaComment() |
|
| 171 | |||
| 172 | /** |
||
| 173 | * Get a MIME type's subtype. |
||
| 174 | * |
||
| 175 | * @return string Type's subtype, null if invalid mime type. |
||
| 176 | */ |
||
| 177 | 42 | public function getSubType() |
|
| 181 | |||
| 182 | /** |
||
| 183 | * Get a MIME type's subtype comment. |
||
| 184 | * |
||
| 185 | * @return string Type's subtype comment, null if invalid mime type. |
||
| 186 | */ |
||
| 187 | 28 | public function getSubTypeComment() |
|
| 191 | |||
| 192 | /** |
||
| 193 | * Create a textual MIME type from object values |
||
| 194 | * |
||
| 195 | * This function performs the opposite function of parse(). |
||
| 196 | * |
||
| 197 | * @param int $format The format of the output string. |
||
| 198 | * |
||
| 199 | * @return string MIME type string |
||
| 200 | */ |
||
| 201 | 41 | public function toString($format = Type::FULL_TEXT) |
|
| 218 | |||
| 219 | /** |
||
| 220 | * Is this type experimental? |
||
| 221 | * |
||
| 222 | * Note: Experimental types are denoted by a leading 'x-' in the media or |
||
| 223 | * subtype, e.g. text/x-vcard or x-world/x-vrml. |
||
| 224 | * |
||
| 225 | * @return boolean true if type is experimental, false otherwise |
||
| 226 | */ |
||
| 227 | 1 | public function isExperimental() |
|
| 234 | |||
| 235 | /** |
||
| 236 | * Is this a vendor MIME type? |
||
| 237 | * |
||
| 238 | * Note: Vendor types are denoted with a leading 'vnd. in the subtype. |
||
| 239 | * |
||
| 240 | * @return boolean true if type is a vendor type, false otherwise |
||
| 241 | */ |
||
| 242 | 1 | public function isVendor() |
|
| 249 | |||
| 250 | /** |
||
| 251 | * Is this a wildcard type? |
||
| 252 | * |
||
| 253 | * @return boolean true if type is a wildcard, false otherwise |
||
| 254 | */ |
||
| 255 | 12 | public function isWildcard() |
|
| 262 | |||
| 263 | /** |
||
| 264 | * Perform a wildcard match on a MIME type |
||
| 265 | * |
||
| 266 | * Example: |
||
| 267 | * $type = new Type('image/png'); |
||
| 268 | * $type->wildcardMatch('image/*'); |
||
| 269 | * |
||
| 270 | * @param string $wildcard Wildcard to check against |
||
| 271 | * |
||
| 272 | * @return boolean true if there was a match, false otherwise |
||
| 273 | */ |
||
| 274 | 1 | public function wildcardMatch($wildcard) |
|
| 290 | |||
| 291 | /** |
||
| 292 | * Add a parameter to this type |
||
| 293 | * |
||
| 294 | * @param string $name Parameter name |
||
| 295 | * @param string $value Parameter value |
||
| 296 | * @param string $comment Comment for this parameter |
||
| 297 | * |
||
| 298 | * @return void |
||
| 299 | */ |
||
| 300 | 26 | public function addParameter($name, $value, $comment = null) |
|
| 304 | |||
| 305 | /** |
||
| 306 | * Remove a parameter from this type |
||
| 307 | * |
||
| 308 | * @param string $name Parameter name |
||
| 309 | * |
||
| 310 | * @return void |
||
| 311 | */ |
||
| 312 | 1 | public function removeParameter($name) |
|
| 316 | |||
| 317 | /** |
||
| 318 | * Builds a list of MIME types. |
||
| 319 | * |
||
| 320 | * If the current type is a wildcard, than all the types matching the |
||
| 321 | * wildcard will be returned. |
||
| 322 | * |
||
| 323 | * @param bool $strict |
||
| 324 | * (Optional) if true a MappingException is thrown when no type is |
||
| 325 | * found, if false it returns an empty array as a default. |
||
| 326 | * Defaults to true. |
||
| 327 | * |
||
| 328 | * @throws MappingException if no mapping found and $strict is true. |
||
| 329 | * |
||
| 330 | * @return string[] |
||
| 331 | */ |
||
| 332 | 6 | public function buildTypesList($strict = true) |
|
| 360 | |||
| 361 | /** |
||
| 362 | * Returns all the aliases related to the MIME type(s). |
||
| 363 | * |
||
| 364 | * If the current type is a wildcard, than all aliases of all the |
||
| 365 | * types matching the wildcard will be returned. |
||
| 366 | * |
||
| 367 | * @param bool $strict |
||
| 368 | * (Optional) if true a MappingException is thrown when no mapping is |
||
| 369 | * found, if false it returns an empty array as a default. |
||
| 370 | * Defaults to true. |
||
| 371 | * |
||
| 372 | * @throws MappingException if no mapping found and $strict is true. |
||
| 373 | * |
||
| 374 | * @return string[] |
||
| 375 | */ |
||
| 376 | 1 | View Code Duplication | public function getAliases($strict = true) |
| 391 | |||
| 392 | /** |
||
| 393 | * Returns the MIME type's preferred file extension. |
||
| 394 | * |
||
| 395 | * @param bool $strict |
||
| 396 | * (Optional) if true a MappingException is thrown when no mapping is |
||
| 397 | * found, if false it returns null as a default. |
||
| 398 | * Defaults to true. |
||
| 399 | * |
||
| 400 | * @throws MappingException if no mapping found and $strict is true. |
||
| 401 | * |
||
| 402 | * @return string |
||
| 403 | */ |
||
| 404 | 7 | public function getDefaultExtension($strict = true) |
|
| 425 | |||
| 426 | /** |
||
| 427 | * Returns all the file extensions related to the MIME type(s). |
||
| 428 | * |
||
| 429 | * If the current type is a wildcard, than all extensions of all the |
||
| 430 | * types matching the wildcard will be returned. |
||
| 431 | * |
||
| 432 | * @param bool $strict |
||
| 433 | * (Optional) if true a MappingException is thrown when no mapping is |
||
| 434 | * found, if false it returns an empty array as a default. |
||
| 435 | * Defaults to true. |
||
| 436 | * |
||
| 437 | * @throws MappingException if no mapping found and $strict is true. |
||
| 438 | * |
||
| 439 | * @return string[] |
||
| 440 | */ |
||
| 441 | 6 | View Code Duplication | public function getExtensions($strict = true) |
| 456 | } |
||
| 457 |
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.