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 MimePart 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 MimePart, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
24 | class MimePart |
||
25 | { |
||
26 | /** |
||
27 | * @var \ZBateson\MailMimeParser\Header\HeaderFactory the HeaderFactory |
||
28 | * object used for created headers |
||
29 | */ |
||
30 | protected $headerFactory; |
||
31 | |||
32 | /** |
||
33 | * @var \ZBateson\MailMimeParser\Header\AbstractHeader[] array of header |
||
34 | * objects |
||
35 | */ |
||
36 | protected $headers; |
||
37 | |||
38 | /** |
||
39 | * @var \ZBateson\MailMimeParser\Message\MimePart parent part |
||
40 | */ |
||
41 | protected $parent; |
||
42 | |||
43 | /** |
||
44 | * @var resource the content's resource handle |
||
45 | */ |
||
46 | protected $handle; |
||
47 | |||
48 | /** |
||
49 | * |
||
50 | */ |
||
51 | protected $originalStreamHandle; |
||
52 | |||
53 | /** |
||
54 | * @var \ZBateson\MailMimeParser\Message\MimePart[] array of parts in this |
||
55 | * message |
||
56 | */ |
||
57 | protected $parts = []; |
||
58 | |||
59 | /** |
||
60 | * @var \ZBateson\MailMimeParser\Message\Writer\MimePartWriter the part |
||
61 | * writer for this MimePart |
||
62 | */ |
||
63 | protected $partWriter = null; |
||
64 | |||
65 | /** |
||
66 | * Sets up class dependencies. |
||
67 | * |
||
68 | * @param HeaderFactory $headerFactory |
||
69 | * @param MimePartWriter $partWriter |
||
70 | */ |
||
71 | 106 | public function __construct(HeaderFactory $headerFactory, MimePartWriter $partWriter) |
|
76 | |||
77 | /** |
||
78 | * Closes the attached resource handle. |
||
79 | */ |
||
80 | 98 | public function __destruct() |
|
89 | |||
90 | /** |
||
91 | * Registers the passed part as a child of the current part. |
||
92 | * |
||
93 | * If the $position parameter is non-null, adds the part at the passed |
||
94 | * position index. |
||
95 | * |
||
96 | * @param \ZBateson\MailMimeParser\Message\MimePart $part |
||
97 | * @param int $position |
||
98 | */ |
||
99 | 72 | public function addPart(MimePart $part, $position = null) |
|
106 | |||
107 | /** |
||
108 | * Removes the child part from this part and returns its position or |
||
109 | * null if it wasn't found. |
||
110 | * |
||
111 | * Note that if the part is not a direct child of this part, the returned |
||
112 | * position is its index within its parent (calls removePart on its direct |
||
113 | * parent). |
||
114 | * |
||
115 | * @param \ZBateson\MailMimeParser\Message\MimePart $part |
||
116 | * @return int or null if not found |
||
117 | */ |
||
118 | 14 | public function removePart(MimePart $part) |
|
132 | |||
133 | /** |
||
134 | * Removes all parts that are matched by the passed PartFilter. |
||
135 | * |
||
136 | * @param \ZBateson\MailMimeParser\Message\PartFilter $filter |
||
137 | */ |
||
138 | public function removeAllParts(PartFilter $filter = null) |
||
144 | |||
145 | /** |
||
146 | * Returns the part at the given 0-based index, or null if none is set. |
||
147 | * |
||
148 | * Note that the first part returned is the current part itself. This is |
||
149 | * often desirable for queries with a PartFilter, e.g. looking for a |
||
150 | * MimePart with a specific Content-Type that may be satisfied by the |
||
151 | * current part. |
||
152 | * |
||
153 | * @param int $index |
||
154 | * @param PartFilter $filter |
||
155 | * @return \ZBateson\MailMimeParser\Message\MimePart |
||
156 | */ |
||
157 | 78 | View Code Duplication | public function getPart($index, PartFilter $filter = null) |
165 | |||
166 | /** |
||
167 | * Returns the current part, all child parts, and child parts of all |
||
168 | * children optionally filtering them with the provided PartFilter. |
||
169 | * |
||
170 | * The first part returned is always the current MimePart. This is often |
||
171 | * desirable as it may be a valid MimePart for the provided PartFilter. |
||
172 | * |
||
173 | * @param PartFilter $filter an optional filter |
||
174 | * @return \ZBateson\MailMimeParser\Message\MimePart[] |
||
175 | */ |
||
176 | 88 | public function getAllParts(PartFilter $filter = null) |
|
190 | |||
191 | /** |
||
192 | * Returns the total number of parts in this and all children. |
||
193 | * |
||
194 | * Note that the current part is considered, so the minimum getPartCount is |
||
195 | * 1 without a filter. |
||
196 | * |
||
197 | * @param PartFilter $filter |
||
198 | * @return int |
||
199 | */ |
||
200 | 5 | public function getPartCount(PartFilter $filter = null) |
|
204 | |||
205 | /** |
||
206 | * Returns the direct child at the given 0-based index, or null if none is |
||
207 | * set. |
||
208 | * |
||
209 | * @param int $index |
||
210 | * @param PartFilter $filter |
||
211 | * @return \ZBateson\MailMimeParser\Message\MimePart |
||
212 | */ |
||
213 | 22 | View Code Duplication | public function getChild($index, PartFilter $filter = null) |
221 | |||
222 | /** |
||
223 | * Returns all direct child parts. |
||
224 | * |
||
225 | * If a PartFilter is provided, the PartFilter is applied before returning. |
||
226 | * |
||
227 | * @param PartFilter $filter |
||
228 | * @return \ZBateson\MailMimeParser\Message\MimePart[] |
||
229 | */ |
||
230 | 90 | public function getChildParts(PartFilter $filter = null) |
|
237 | |||
238 | /** |
||
239 | * Returns the number of direct children under this part. |
||
240 | * |
||
241 | * @param PartFilter $filter |
||
242 | * @return int |
||
243 | */ |
||
244 | 9 | public function getChildCount(PartFilter $filter = null) |
|
248 | |||
249 | /** |
||
250 | * Returns the part associated with the passed mime type if it exists. |
||
251 | * |
||
252 | * @param string $mimeType |
||
253 | * @return \ZBateson\MailMimeParser\Message\MimePart or null |
||
254 | */ |
||
255 | 10 | public function getPartByMimeType($mimeType, $index = 0) |
|
259 | |||
260 | /** |
||
261 | * Returns an array of all parts associated with the passed mime type if any |
||
262 | * exist or null otherwise. |
||
263 | * |
||
264 | * @param string $mimeType |
||
265 | * @return \ZBateson\MailMimeParser\Message\MimePart[] or null |
||
266 | */ |
||
267 | 1 | public function getAllPartsByMimeType($mimeType) |
|
271 | |||
272 | /** |
||
273 | * Returns the number of parts matching the passed $mimeType |
||
274 | * |
||
275 | * @param string $mimeType |
||
276 | * @return int |
||
277 | */ |
||
278 | 1 | public function getCountOfPartsByMimeType($mimeType) |
|
282 | |||
283 | /** |
||
284 | * Returns true if there's a content stream associated with the part. |
||
285 | * |
||
286 | * @return boolean |
||
287 | */ |
||
288 | 23 | public function hasContent() |
|
295 | |||
296 | /** |
||
297 | * Returns true if this part's mime type is multipart/* |
||
298 | * |
||
299 | * @return bool |
||
300 | */ |
||
301 | 69 | public function isMultiPart() |
|
309 | |||
310 | /** |
||
311 | * Returns true if this part's mime type is text/plain, text/html or has a |
||
312 | * text/* and has a defined 'charset' attribute. |
||
313 | * |
||
314 | * @return bool |
||
315 | */ |
||
316 | 93 | public function isTextPart() |
|
328 | |||
329 | /** |
||
330 | * Attaches the resource handle for the part's content. The attached handle |
||
331 | * is closed when the MimePart object is destroyed. |
||
332 | * |
||
333 | * @param resource $contentHandle |
||
334 | */ |
||
335 | 93 | public function attachContentResourceHandle($contentHandle) |
|
342 | |||
343 | /** |
||
344 | * Attaches the resource handle representing the original stream that |
||
345 | * created this part (including any sub-parts). The attached handle is |
||
346 | * closed when the MimePart object is destroyed. |
||
347 | * |
||
348 | * This stream is not modified or changed as the part is changed and is only |
||
349 | * set during parsing in MessageParser. |
||
350 | * |
||
351 | * @param resource $handle |
||
352 | */ |
||
353 | 88 | public function attachOriginalStreamHandle($handle) |
|
360 | |||
361 | /** |
||
362 | * Returns a resource stream handle allowing a user to read the original |
||
363 | * stream (including headers and child parts) that was used to create the |
||
364 | * current part. |
||
365 | * |
||
366 | * The part contains an original stream handle only if it was explicitly set |
||
367 | * by a call to MimePart::attachOriginalStreamHandle. MailMimeParser only |
||
368 | * sets this during the parsing phase in MessageParser, and is not otherwise |
||
369 | * changed or updated. New parts added below this part, changed headers, |
||
370 | * etc... would not be reflected in the returned stream handle. |
||
371 | * |
||
372 | * @return resource the resource handle or null if not set |
||
373 | */ |
||
374 | 12 | public function getOriginalStreamHandle() |
|
381 | |||
382 | /** |
||
383 | * Detaches the content resource handle from this part but does not close |
||
384 | * it. |
||
385 | */ |
||
386 | 17 | protected function detachContentResourceHandle() |
|
390 | |||
391 | /** |
||
392 | * Sets the content of the part to the passed string (effectively creates |
||
393 | * a php://temp stream with the passed content and calls |
||
394 | * attachContentResourceHandle with the opened stream). |
||
395 | * |
||
396 | * @param string $string |
||
397 | */ |
||
398 | 10 | public function setContent($string) |
|
405 | |||
406 | /** |
||
407 | * Returns the resource stream handle for the part's content or null if not |
||
408 | * set. rewind() is called on the stream before returning it. |
||
409 | * |
||
410 | * The resource is automatically closed by MimePart's destructor and should |
||
411 | * not be closed otherwise. |
||
412 | * |
||
413 | * The returned resource handle is a stream with decoding filters appended |
||
414 | * to it. The attached filters are determined by looking at the part's |
||
415 | * Content-Encoding header. The following encodings are currently |
||
416 | * supported: |
||
417 | * |
||
418 | * - Quoted-Printable |
||
419 | * - Base64 |
||
420 | * - X-UUEncode |
||
421 | * |
||
422 | * UUEncode may be automatically attached for a message without a defined |
||
423 | * Content-Encoding and Content-Type if it has a UUEncoded part to support |
||
424 | * older non-mime message attachments. |
||
425 | * |
||
426 | * In addition, character encoding for text streams is converted to UTF-8 |
||
427 | * if {@link \ZBateson\MailMimeParser\Message\MimePart::isTextPart |
||
428 | * MimePart::isTextPart} returns true. |
||
429 | * |
||
430 | * @return resource |
||
431 | */ |
||
432 | 88 | public function getContentResourceHandle() |
|
439 | |||
440 | /** |
||
441 | * Shortcut to reading stream content and assigning it to a string. Returns |
||
442 | * null if the part doesn't have a content stream. |
||
443 | * |
||
444 | * @return string |
||
445 | */ |
||
446 | 22 | public function getContent() |
|
455 | |||
456 | /** |
||
457 | * Adds a header with the given $name and $value. |
||
458 | * |
||
459 | * Creates a new \ZBateson\MailMimeParser\Header\AbstractHeader object and |
||
460 | * registers it as a header. |
||
461 | * |
||
462 | * @param string $name |
||
463 | * @param string $value |
||
464 | */ |
||
465 | 98 | public function setRawHeader($name, $value) |
|
469 | |||
470 | /** |
||
471 | * Removes the header with the given name |
||
472 | * |
||
473 | * @param string $name |
||
474 | */ |
||
475 | 17 | public function removeHeader($name) |
|
479 | |||
480 | /** |
||
481 | * Returns the AbstractHeader object for the header with the given $name |
||
482 | * |
||
483 | * Note that mime headers aren't case sensitive. |
||
484 | * |
||
485 | * @param string $name |
||
486 | * @return \ZBateson\MailMimeParser\Header\AbstractHeader |
||
487 | */ |
||
488 | 100 | public function getHeader($name) |
|
495 | |||
496 | /** |
||
497 | * Returns the string value for the header with the given $name. |
||
498 | * |
||
499 | * Note that mime headers aren't case sensitive. |
||
500 | * |
||
501 | * @param string $name |
||
502 | * @param string $defaultValue |
||
503 | * @return string |
||
504 | */ |
||
505 | 97 | public function getHeaderValue($name, $defaultValue = null) |
|
513 | |||
514 | /** |
||
515 | * Returns the full array of headers for this part. |
||
516 | * |
||
517 | * @return \ZBateson\MailMimeParser\Header\AbstractHeader[] |
||
518 | */ |
||
519 | 84 | public function getHeaders() |
|
523 | |||
524 | /** |
||
525 | * Returns a parameter of the header $header, given the parameter named |
||
526 | * $param. |
||
527 | * |
||
528 | * Only headers of type |
||
529 | * \ZBateson\MailMimeParser\Header\ParameterHeader have parameters. |
||
530 | * Content-Type and Content-Disposition are examples of headers with |
||
531 | * parameters. "Charset" is a common parameter of Content-Type. |
||
532 | * |
||
533 | * @param string $header |
||
534 | * @param string $param |
||
535 | * @param string $defaultValue |
||
536 | * @return string |
||
537 | */ |
||
538 | 96 | public function getHeaderParameter($header, $param, $defaultValue = null) |
|
546 | |||
547 | /** |
||
548 | * Sets the parent part. |
||
549 | * |
||
550 | * @param \ZBateson\MailMimeParser\Message\MimePart $part |
||
551 | */ |
||
552 | 97 | public function setParent(MimePart $part) |
|
556 | |||
557 | /** |
||
558 | * Returns this part's parent. |
||
559 | * |
||
560 | * @return \ZBateson\MailMimeParser\Message\MimePart |
||
561 | */ |
||
562 | 96 | public function getParent() |
|
566 | } |
||
567 |
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.