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 Parser 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 Parser, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
28 | final class Parser |
||
29 | { |
||
30 | const TAG_OUTLINE = 0x1; |
||
31 | const TAG_INLINE = 0x2; |
||
32 | const TAG_PRE = 0x4; |
||
33 | const TAG_SINGLE = 0x8; |
||
34 | const TAG_CLEAR_CONTENT = 0x10; |
||
35 | const TAG_FORCE_PARAGRAPHS = 0x20; |
||
36 | |||
37 | const PC_IMG_ALIGN_LEFT = 0; |
||
38 | const PC_IMG_ALIGN_RIGHT = 1; |
||
39 | const PC_IMG_ALIGN_CENTER = 2; |
||
40 | |||
41 | /* "German" style |
||
42 | const PC_PARSER_QUOTE_LEFT = '„'; |
||
43 | const PC_PARSER_QUOTE_RIGHT = '“'); |
||
44 | /**/ |
||
45 | /* "French" style */ |
||
46 | const PC_PARSER_QUOTE_LEFT = '»'; |
||
47 | const PC_PARSER_QUOTE_RIGHT = '«'; |
||
48 | /**/ |
||
49 | |||
50 | private $s = ''; |
||
51 | private $m_stack; |
||
52 | private $m_handlers; |
||
53 | private $m_vars; |
||
54 | |||
55 | /** |
||
56 | * |
||
57 | * @param Config $config |
||
58 | */ |
||
59 | 18 | public function addDefaultHandlers(Config $config) |
|
60 | { |
||
61 | 18 | $this->addSetting('image-dir', $config->getResourceBasePath() . '/'); |
|
62 | |||
63 | // Example for multiple tags being displayed in the same way |
||
64 | 18 | $this |
|
65 | 18 | ->addHandler(new SimpleHandler('b|strong', Parser::TAG_INLINE, '<strong>', '</strong>')) |
|
66 | 18 | ->addHandler(new SimpleHandler('i|em', Parser::TAG_INLINE, '<em>', '</em>')) |
|
67 | |||
68 | 18 | ->addHandler(new SimpleHandler('icode', Parser::TAG_INLINE, '<code>', '</code>')) |
|
69 | |||
70 | 18 | ->addHandler(new SimpleHandler('u', Parser::TAG_INLINE, '<u>', '</u>')) |
|
71 | 18 | ->addHandler(new SimpleHandler('s|strike', Parser::TAG_INLINE, '<s>', '</s>')) |
|
72 | |||
73 | // Used to display other tags. Tags with type Parser::TAG_PRE will not be parsed |
||
74 | // This tag belongs also to two types |
||
75 | |||
76 | 18 | ->addHandler(new SimpleHandler('off|noparse', Parser::TAG_INLINE | Parser::TAG_PRE, '', '')) |
|
77 | 18 | ->addHandler(new SimpleHandler('var', Parser::TAG_INLINE | Parser::TAG_PRE, '<var>', '</var>')) |
|
78 | // ->addHandler(new SimpleHandler( |
||
|
|||
79 | // 'quote', |
||
80 | // Parser::TAG_OUTLINE | Parser::TAG_FORCE_PARAGRAPHS, |
||
81 | // '<blockquote>', |
||
82 | // "</blockquote>\n\n" |
||
83 | // )) |
||
84 | |||
85 | 18 | ->addHandler(new QuoteHandler()) |
|
86 | |||
87 | /* Most replacements are rather simple */ |
||
88 | 18 | ->addHandler(new SimpleHandler('h1', Parser::TAG_OUTLINE, "<h1>", "</h1>\n\n")) |
|
89 | 18 | ->addHandler(new SimpleHandler('h2', Parser::TAG_OUTLINE, "<h2>", "</h2>\n\n")) |
|
90 | 18 | ->addHandler(new SimpleHandler('h3', Parser::TAG_OUTLINE, "<h3>", "</h3>\n\n")) |
|
91 | 18 | ->addHandler(new SimpleHandler('h4', Parser::TAG_OUTLINE, "<h4>", "</h4>\n\n")) |
|
92 | 18 | ->addHandler(new SimpleHandler('h5', Parser::TAG_OUTLINE, "<h5>", "</h5>\n\n")) |
|
93 | 18 | ->addHandler(new SimpleHandler('h6', Parser::TAG_OUTLINE, "<h6>", "</h6>\n\n")) |
|
94 | 18 | ->addHandler(new SimpleHandler('dl', Parser::TAG_OUTLINE, "<dl>", "\n\n</dl>\n\n")) |
|
95 | 18 | ->addHandler(new SimpleHandler('dt', Parser::TAG_OUTLINE, "\n\n<dt>", "</dt>")) |
|
96 | 18 | ->addHandler(new SimpleHandler('dd', Parser::TAG_OUTLINE, "\n<dd>", "</dd>")) |
|
97 | 18 | ->addHandler(new SimpleHandler('ul', Parser::TAG_OUTLINE, "<ul>", "\n</ul>\n\n")) |
|
98 | 18 | ->addHandler(new SimpleHandler('ol', Parser::TAG_OUTLINE, "<ol>", "\n</ol>\n\n")) |
|
99 | 18 | ->addHandler(new SimpleHandler('li', Parser::TAG_OUTLINE, "\n<li>", "</li>")) |
|
100 | 18 | ->addHandler(new SimpleHandler('table', Parser::TAG_OUTLINE, "<table>", "\n</table>\n\n")) |
|
101 | 18 | ->addHandler(new SimpleHandler('tr', Parser::TAG_OUTLINE, "\n<tr>", "\n</tr>")) |
|
102 | 18 | ->addHandler(new SimpleHandler('td', Parser::TAG_OUTLINE, "\n<td>", "</td>")) |
|
103 | 18 | ->addHandler(new SimpleHandler('th', Parser::TAG_OUTLINE, "\n<th>", "</th>")) |
|
104 | |||
105 | 18 | ->addHandler(new SimpleHandler('indent', Parser::TAG_OUTLINE, "<div style=\"margin-left: 30px;\">", "</div>\n\n")) |
|
106 | 18 | ->addHandler(new SimpleHandler('center', Parser::TAG_OUTLINE, "<div style=\"text-align: center;\">", "</div>\n\n")) |
|
107 | |||
108 | 18 | ->addHandler(new UrlHandler()) |
|
109 | 18 | ->addHandler(new ImgHandler()) |
|
110 | 18 | ->addHandler(new AmazonHandler()) |
|
111 | 18 | ->addHandler(new AbbrHandler()) |
|
112 | 18 | ->addHandler(new HTMLHandler()) |
|
113 | 18 | ->addHandler(new CodeHandler($config->getSyntaxHighlighter())) |
|
114 | 18 | ->addHandler(new FootnotesHandler()) |
|
115 | 18 | ->addHandler(new YouTubeHandler()); |
|
116 | 18 | } |
|
117 | |||
118 | /** |
||
119 | * |
||
120 | */ |
||
121 | 18 | public function addHandler(ProtoHandler $class) |
|
122 | { |
||
123 | 18 | $tags = explode('|', $class->name); |
|
124 | |||
125 | 18 | $j = 0; |
|
126 | |||
127 | 18 | foreach ($tags as $tag) { |
|
128 | 18 | if (trim($tag) !== '') { |
|
129 | 18 | $temp = array(); |
|
130 | 18 | $temp['name'] = $tag; |
|
131 | |||
132 | 18 | if (is_array($class->type)) { |
|
133 | 18 | $temp['type'] = $class->type[$j]; |
|
134 | 18 | } else { |
|
135 | 18 | $temp['type'] = $class->type; |
|
136 | } |
||
137 | 18 | $temp['function'] = $class; |
|
138 | |||
139 | 18 | $this->m_handlers[] = $temp; |
|
140 | 18 | } |
|
141 | |||
142 | 18 | $j++; |
|
143 | 18 | } |
|
144 | |||
145 | 18 | return $this; |
|
146 | } |
||
147 | |||
148 | /** |
||
149 | * |
||
150 | * @param string $name |
||
151 | */ |
||
152 | 18 | public function addSetting($name, $value = '') |
|
153 | { |
||
154 | 18 | $this->m_vars[$name] = $value; |
|
155 | 18 | } |
|
156 | |||
157 | /** |
||
158 | * |
||
159 | */ |
||
160 | 17 | private function printHandlerMarkup(Tag $tag, $front = true, $tag_content = '') |
|
161 | { |
||
162 | 17 | $data = array(); |
|
163 | |||
164 | 17 | $data['tag'] = $tag->getName(); |
|
165 | 17 | $data['params'] = $tag->getAttributes(); |
|
166 | 17 | $data['front'] = $front; |
|
167 | 17 | $data['vars'] = $this->m_vars; |
|
168 | |||
169 | 17 | if ($tag_content !== '') { |
|
170 | 4 | $data['content'] = $tag_content; |
|
171 | 4 | } |
|
172 | |||
173 | 17 | $i = 0; |
|
174 | |||
175 | 17 | $tagCnt = count($this->m_handlers); |
|
176 | |||
177 | 17 | while (($i < $tagCnt) && ($this->m_handlers[$i]['name'] !== $data['tag'])) { |
|
178 | 17 | $i++; |
|
179 | 17 | } |
|
180 | |||
181 | 17 | return $this->m_handlers[$i]['function']->draw($data); |
|
182 | } |
||
183 | |||
184 | /** |
||
185 | * Gets the next tag |
||
186 | * |
||
187 | * @param string $s String to parse |
||
188 | * @param int $i Offset where search begins |
||
189 | * @param int $position Will be filled with next tag's offset (FALSE if |
||
190 | * there are no more tags) |
||
191 | * @return Tag |
||
192 | */ |
||
193 | 17 | private function getNextTag(&$s, $i, &$position) |
|
194 | { |
||
195 | 17 | $j = mb_strpos($s, '[', $i); |
|
196 | 17 | $k = mb_strpos($s, ']', $j + 1); |
|
197 | |||
198 | 17 | if ($j === false || $k === false) { |
|
199 | 16 | $position = false; |
|
200 | 16 | return null; |
|
201 | } |
||
202 | |||
203 | 17 | $t = mb_substr($s, $j + 1, $k - ($j + 1)); |
|
204 | 17 | $l = mb_strrpos($t, '['); |
|
205 | |||
206 | 17 | if ($l !== false) { |
|
207 | 1 | $j += $l + 1; |
|
208 | 1 | } |
|
209 | |||
210 | 17 | $position = $j; |
|
211 | |||
212 | 17 | $tagString = mb_substr($s, $j, $k - $j + 1); |
|
213 | |||
214 | 17 | return new Tag($tagString, $this->getHandlers()); |
|
215 | } |
||
216 | |||
217 | /** |
||
218 | * |
||
219 | * @return |
||
220 | */ |
||
221 | 18 | public function getHandlers() |
|
222 | { |
||
223 | 18 | return $this->m_handlers; |
|
224 | } |
||
225 | |||
226 | /** |
||
227 | * |
||
228 | * @param string $s |
||
229 | */ |
||
230 | 17 | public function parse($s) |
|
263 | |||
264 | /** |
||
265 | * |
||
266 | * @param Tag $tag |
||
267 | * @return bool |
||
268 | */ |
||
269 | 17 | private function fitsStack(Tag $tag) |
|
273 | |||
274 | /** |
||
275 | * |
||
276 | */ |
||
277 | 17 | private function parseEx() |
|
402 | |||
403 | /** |
||
404 | * Formats small pieces of CDATA |
||
405 | * |
||
406 | * @param string $s |
||
407 | * @return string |
||
408 | */ |
||
409 | 17 | private function formatString($s) |
|
461 | |||
462 | /** |
||
463 | * Formats whole blocks of CDATA |
||
464 | * |
||
465 | * @param string $cdata |
||
466 | * @param boolean $outline |
||
467 | * @return string |
||
468 | */ |
||
469 | 17 | private function printCData(&$cdata, $outline = false) |
|
535 | } |
||
536 |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.