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 MimeParser 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 MimeParser, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
8 | class MimeParser |
||
9 | { |
||
10 | protected $removeHeaders = array("Received", "From", "X-Original-To", "MIME-Version", "Received-SPF", "Delivered-To"); |
||
11 | protected $allowedHeaders = array("return-path", "subject"); |
||
12 | private $cache; |
||
13 | private $grammar; |
||
14 | private $contentDecoder; |
||
15 | private $headerDecoder; |
||
16 | |||
17 | 5 | public function __construct(array $allowedHeaders = array(), array $removeHeaders = array()) |
|
27 | |||
28 | 1 | public function parseString($string, $fillHeaders = false, \Swift_Mime_MimeEntity $message = null) |
|
37 | |||
38 | /** |
||
39 | * |
||
40 | * @param stream $stream |
||
41 | * @param boolean $fillHeaders (default to false) |
||
42 | * @param \Swift_Mime_MimeEntity $message (default to null) |
||
43 | * @return Swift_Mime_MimeEntity|\Swift_Message |
||
44 | */ |
||
45 | 5 | public function parseStream($stream, $fillHeaders = false, \Swift_Mime_MimeEntity $message = null) |
|
46 | { |
||
47 | 5 | $partHeaders = $this->extractHeaders($stream); |
|
48 | |||
49 | 5 | $filteredHeaders = $this->filterHeaders($partHeaders); |
|
50 | |||
51 | 5 | $parts = $this->parseParts($stream, $partHeaders); |
|
52 | |||
53 | 5 | if (!$message) { |
|
54 | 5 | $message = new \Swift_Message (); |
|
55 | 5 | } |
|
56 | |||
57 | 5 | $headers = $this->createHeadersSet($filteredHeaders); |
|
58 | |||
59 | 5 | foreach ($headers->getAll() as $name => $header) { |
|
60 | if ($fillHeaders || in_array(strtolower($header->getFieldName()), $this->allowedHeaders)) { |
||
61 | $message->getHeaders()->set($header); |
||
62 | } |
||
63 | 5 | } |
|
64 | 5 | $this->createMessage($parts, $message); |
|
65 | |||
66 | 5 | return $message; |
|
67 | } |
||
68 | |||
69 | 5 | protected function extractHeaders($stream) |
|
70 | { |
||
71 | 5 | $headers = array(); |
|
72 | 5 | $hName = null; |
|
73 | 5 | while (!feof($stream)) { |
|
74 | 5 | $row = fgets($stream); |
|
75 | 5 | if ($row == "\r\n" || $row == "\n" || $row == "\r") { |
|
76 | 5 | break; |
|
77 | } |
||
78 | 5 | if (preg_match('/^([a-z0-9\-]+)\s*:(.*)/i', $row, $mch)) { |
|
79 | 5 | $hName = strtolower($mch [1]); |
|
80 | 5 | if (!in_array($hName, array("content-type", "content-transfer-encoding"))) { |
|
81 | 5 | $hName = $mch [1]; |
|
82 | 5 | } |
|
83 | 5 | $row = $mch [2]; |
|
84 | 5 | } |
|
85 | 5 | if (!empty($hName)) { |
|
86 | 5 | continue; |
|
87 | } |
||
88 | $headers [$hName] [] = trim($row); |
||
89 | } |
||
90 | 5 | foreach ($headers as $header => $values) { |
|
91 | $headers [$header] = $this->headerDecoder->decode(trim(implode(" ", $values))); |
||
92 | 5 | } |
|
93 | 5 | return $headers; |
|
94 | } |
||
95 | |||
96 | 5 | private function filterHeaders(array $headers) |
|
97 | { |
||
98 | 5 | foreach ($headers as $header => $values) { |
|
99 | if (in_array(strtolower($header), $this->removeHeaders) && !in_array(strtolower($header), $this->allowedHeaders)) { |
||
100 | unset ($headers [$header]); |
||
101 | } |
||
102 | 5 | } |
|
103 | 5 | return $headers; |
|
104 | } |
||
105 | |||
106 | 5 | protected function parseParts($stream, $partHeaders) |
|
107 | { |
||
108 | 5 | $parts = array(); |
|
109 | 5 | $contentType = $this->extractValueHeader($this->getContentType($partHeaders)); |
|
110 | |||
111 | 5 | if (stripos($contentType, 'multipart/') !== false) { |
|
112 | $headerParts = $this->extractHeaderParts($this->getContentType($partHeaders)); |
||
113 | $boundary = $headerParts ["boundary"]; |
||
114 | } else { |
||
115 | 5 | $boundary = null; |
|
116 | } |
||
117 | |||
118 | try { |
||
119 | // body |
||
120 | 5 | $this->extractPart($stream, $boundary, $this->getTransferEncoding($partHeaders)); |
|
121 | 5 | } catch (Exception\EndOfPartReachedException $e) { |
|
122 | $parts = array( |
||
123 | 5 | "type" => $contentType, |
|
124 | 5 | "headers" => $partHeaders, |
|
125 | 5 | "body" => $e->getData(), |
|
126 | 5 | "boundary" => $boundary, |
|
127 | 5 | "parts" => array() |
|
128 | 5 | ); |
|
129 | } |
||
130 | |||
131 | 5 | if ($boundary) { |
|
132 | while (!feof($stream)) { |
||
133 | try { |
||
134 | $partHeaders = $this->extractHeaders($stream); |
||
135 | $childContentType = $this->extractValueHeader($this->getContentType($partHeaders)); |
||
136 | |||
137 | if (stripos($childContentType, 'multipart/') !== false) { |
||
138 | $parts ["parts"] [] = $this->parseParts($stream, $partHeaders); |
||
139 | try { |
||
140 | $this->extractPart($stream, $boundary, $this->getTransferEncoding($partHeaders)); |
||
141 | } catch (Exception\EndOfPartReachedException $e) { |
||
142 | } |
||
143 | } else { |
||
144 | $this->extractPart($stream, $boundary, $this->getTransferEncoding($partHeaders)); |
||
145 | } |
||
146 | } catch (Exception\EndOfPartReachedException $e) { |
||
147 | $parts ["parts"] [] = array( |
||
148 | "type" => $childContentType, |
||
149 | "parent-type" => $contentType, |
||
150 | "headers" => $partHeaders, |
||
151 | "body" => $e->getData(), |
||
152 | "parts" => array() |
||
153 | ); |
||
154 | |||
155 | if ($e instanceof Exception\EndOfMultiPartReachedException) { |
||
156 | break; |
||
157 | } |
||
158 | } |
||
159 | } |
||
160 | } |
||
161 | 5 | return $parts; |
|
162 | } |
||
163 | |||
164 | 5 | private function extractValueHeader($header) |
|
165 | { |
||
166 | 5 | $pos = stripos($header, ';'); |
|
167 | 5 | if ($pos !== false) { |
|
168 | return substr($header, 0, $pos); |
||
169 | } else { |
||
170 | 5 | return $header; |
|
171 | } |
||
172 | } |
||
173 | |||
174 | 5 | private function getContentType(array $partHeaders) |
|
182 | |||
183 | private function extractHeaderParts($header) |
||
184 | { |
||
185 | if (stripos($header, ';') !== false) { |
||
186 | |||
187 | $parts = explode(";", $header); |
||
220 | |||
221 | 5 | private function getTransferEncoding(array $partHeaders) |
|
229 | |||
230 | /** |
||
231 | * |
||
232 | * @param array $headersRaw |
||
233 | * @return \Swift_Mime_HeaderSet |
||
234 | */ |
||
235 | 5 | protected function createHeadersSet(array $headersRaw) |
|
284 | |||
285 | 5 | protected function createMessage(array $message, \Swift_Mime_MimeEntity $entity) |
|
325 | |||
326 | /** |
||
327 | * |
||
328 | * @param string $type |
||
329 | * @return \Swift_Mime_ContentEncoder |
||
330 | */ |
||
331 | protected function getEncoder($type) |
||
348 | |||
349 | /** |
||
350 | * |
||
351 | * @param string $path |
||
352 | * The file containg a MIME message |
||
353 | * @return \Swift_Message |
||
354 | */ |
||
355 | 1 | public function parseFile($path, $fillHeaders = false, \Swift_Mime_MimeEntity $message = null) |
|
362 | } |
||
363 |
It seems like the type of the argument is not accepted by the function/method which you are calling.
In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.
We suggest to add an explicit type cast like in the following example: