Complex classes like HtmlMin 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 HtmlMin, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
21 | class HtmlMin implements HtmlMinInterface |
||
22 | { |
||
23 | /** |
||
24 | * @var string |
||
25 | */ |
||
26 | private static $regExSpace = "/[[:space:]]{2,}|[\r\n]+/u"; |
||
27 | |||
28 | /** |
||
29 | * @var array |
||
30 | */ |
||
31 | private static $optional_end_tags = [ |
||
32 | 'html', |
||
33 | 'head', |
||
34 | 'body', |
||
35 | ]; |
||
36 | |||
37 | private static $selfClosingTags = [ |
||
38 | 'area', |
||
39 | 'base', |
||
40 | 'basefont', |
||
41 | 'br', |
||
42 | 'col', |
||
43 | 'command', |
||
44 | 'embed', |
||
45 | 'frame', |
||
46 | 'hr', |
||
47 | 'img', |
||
48 | 'input', |
||
49 | 'isindex', |
||
50 | 'keygen', |
||
51 | 'link', |
||
52 | 'meta', |
||
53 | 'param', |
||
54 | 'source', |
||
55 | 'track', |
||
56 | 'wbr', |
||
57 | ]; |
||
58 | |||
59 | private static $trimWhitespaceFromTags = [ |
||
60 | 'article' => '', |
||
61 | 'br' => '', |
||
62 | 'div' => '', |
||
63 | 'footer' => '', |
||
64 | 'hr' => '', |
||
65 | 'nav' => '', |
||
66 | 'p' => '', |
||
67 | 'script' => '', |
||
68 | ]; |
||
69 | |||
70 | /** |
||
71 | * @var array |
||
72 | */ |
||
73 | private static $booleanAttributes = [ |
||
74 | 'allowfullscreen' => '', |
||
75 | 'async' => '', |
||
76 | 'autofocus' => '', |
||
77 | 'autoplay' => '', |
||
78 | 'checked' => '', |
||
79 | 'compact' => '', |
||
80 | 'controls' => '', |
||
81 | 'declare' => '', |
||
82 | 'default' => '', |
||
83 | 'defaultchecked' => '', |
||
84 | 'defaultmuted' => '', |
||
85 | 'defaultselected' => '', |
||
86 | 'defer' => '', |
||
87 | 'disabled' => '', |
||
88 | 'enabled' => '', |
||
89 | 'formnovalidate' => '', |
||
90 | 'hidden' => '', |
||
91 | 'indeterminate' => '', |
||
92 | 'inert' => '', |
||
93 | 'ismap' => '', |
||
94 | 'itemscope' => '', |
||
95 | 'loop' => '', |
||
96 | 'multiple' => '', |
||
97 | 'muted' => '', |
||
98 | 'nohref' => '', |
||
99 | 'noresize' => '', |
||
100 | 'noshade' => '', |
||
101 | 'novalidate' => '', |
||
102 | 'nowrap' => '', |
||
103 | 'open' => '', |
||
104 | 'pauseonexit' => '', |
||
105 | 'readonly' => '', |
||
106 | 'required' => '', |
||
107 | 'reversed' => '', |
||
108 | 'scoped' => '', |
||
109 | 'seamless' => '', |
||
110 | 'selected' => '', |
||
111 | 'sortable' => '', |
||
112 | 'truespeed' => '', |
||
113 | 'typemustmatch' => '', |
||
114 | 'visible' => '', |
||
115 | ]; |
||
116 | |||
117 | /** |
||
118 | * @var array |
||
119 | */ |
||
120 | private static $skipTagsForRemoveWhitespace = [ |
||
121 | 'code', |
||
122 | 'pre', |
||
123 | 'script', |
||
124 | 'style', |
||
125 | 'textarea', |
||
126 | ]; |
||
127 | |||
128 | /** |
||
129 | * @var array |
||
130 | */ |
||
131 | private $protectedChildNodes = []; |
||
132 | |||
133 | /** |
||
134 | * @var string |
||
135 | */ |
||
136 | private $protectedChildNodesHelper = 'html-min--voku--saved-content'; |
||
137 | |||
138 | /** |
||
139 | * @var bool |
||
140 | */ |
||
141 | private $doOptimizeViaHtmlDomParser = true; |
||
142 | |||
143 | /** |
||
144 | * @var bool |
||
145 | */ |
||
146 | private $doOptimizeAttributes = true; |
||
147 | |||
148 | /** |
||
149 | * @var bool |
||
150 | */ |
||
151 | private $doRemoveComments = true; |
||
152 | |||
153 | /** |
||
154 | * @var bool |
||
155 | */ |
||
156 | private $doRemoveWhitespaceAroundTags = false; |
||
157 | |||
158 | /** |
||
159 | * @var bool |
||
160 | */ |
||
161 | private $doRemoveOmittedQuotes = true; |
||
162 | |||
163 | /** |
||
164 | * @var bool |
||
165 | */ |
||
166 | private $doRemoveOmittedHtmlTags = true; |
||
167 | |||
168 | /** |
||
169 | * @var bool |
||
170 | */ |
||
171 | private $doRemoveHttpPrefixFromAttributes = false; |
||
172 | |||
173 | /** |
||
174 | * @var array |
||
175 | */ |
||
176 | private $domainsToRemoveHttpPrefixFromAttributes = [ |
||
177 | 'google.com', |
||
178 | 'google.de', |
||
179 | ]; |
||
180 | |||
181 | /** |
||
182 | * @var bool |
||
183 | */ |
||
184 | private $doSortCssClassNames = true; |
||
185 | |||
186 | /** |
||
187 | * @var bool |
||
188 | */ |
||
189 | private $doSortHtmlAttributes = true; |
||
190 | |||
191 | /** |
||
192 | * @var bool |
||
193 | */ |
||
194 | private $doRemoveDeprecatedScriptCharsetAttribute = true; |
||
195 | |||
196 | /** |
||
197 | * @var bool |
||
198 | */ |
||
199 | private $doRemoveDefaultAttributes = false; |
||
200 | |||
201 | /** |
||
202 | * @var bool |
||
203 | */ |
||
204 | private $doRemoveDeprecatedAnchorName = true; |
||
205 | |||
206 | /** |
||
207 | * @var bool |
||
208 | */ |
||
209 | private $doRemoveDeprecatedTypeFromStylesheetLink = true; |
||
210 | |||
211 | /** |
||
212 | * @var bool |
||
213 | */ |
||
214 | private $doRemoveDeprecatedTypeFromScriptTag = true; |
||
215 | |||
216 | /** |
||
217 | * @var bool |
||
218 | */ |
||
219 | private $doRemoveValueFromEmptyInput = true; |
||
220 | |||
221 | /** |
||
222 | * @var bool |
||
223 | */ |
||
224 | private $doRemoveEmptyAttributes = true; |
||
225 | |||
226 | /** |
||
227 | * @var bool |
||
228 | */ |
||
229 | private $doSumUpWhitespace = true; |
||
230 | |||
231 | /** |
||
232 | * @var bool |
||
233 | */ |
||
234 | private $doRemoveSpacesBetweenTags = false; |
||
235 | |||
236 | /** |
||
237 | * @var bool |
||
238 | */ |
||
239 | private $keepBrokenHtml = false; |
||
240 | |||
241 | /** |
||
242 | * @var bool |
||
243 | */ |
||
244 | private $withDocType = false; |
||
245 | |||
246 | /** |
||
247 | * @var HtmlMinDomObserverInterface[]|\SplObjectStorage |
||
248 | */ |
||
249 | private $domLoopObservers; |
||
250 | |||
251 | /** |
||
252 | * HtmlMin constructor. |
||
253 | */ |
||
254 | 48 | public function __construct() |
|
260 | |||
261 | /** |
||
262 | * @param HtmlMinDomObserverInterface $observer |
||
263 | * |
||
264 | * @return void |
||
265 | */ |
||
266 | 48 | public function attachObserverToTheDomLoop(HtmlMinDomObserverInterface $observer) |
|
270 | |||
271 | /** |
||
272 | * @param $domElement SimpleHtmlDomInterface |
||
273 | * |
||
274 | * @return void |
||
275 | */ |
||
276 | 44 | private function notifyObserversAboutDomElementBeforeMinification(SimpleHtmlDomInterface $domElement) |
|
282 | |||
283 | /** |
||
284 | * @param SimpleHtmlDomInterface $domElement |
||
285 | * |
||
286 | * @return void |
||
287 | */ |
||
288 | 44 | private function notifyObserversAboutDomElementAfterMinification(SimpleHtmlDomInterface $domElement) |
|
294 | |||
295 | /** |
||
296 | * @param bool $doOptimizeAttributes |
||
297 | * |
||
298 | * @return $this |
||
299 | */ |
||
300 | 2 | public function doOptimizeAttributes(bool $doOptimizeAttributes = true): self |
|
306 | |||
307 | /** |
||
308 | * @param bool $doOptimizeViaHtmlDomParser |
||
309 | * |
||
310 | * @return $this |
||
311 | */ |
||
312 | 1 | public function doOptimizeViaHtmlDomParser(bool $doOptimizeViaHtmlDomParser = true): self |
|
318 | |||
319 | /** |
||
320 | * @param bool $doRemoveComments |
||
321 | * |
||
322 | * @return $this |
||
323 | */ |
||
324 | 3 | public function doRemoveComments(bool $doRemoveComments = true): self |
|
330 | |||
331 | /** |
||
332 | * @param bool $doRemoveDefaultAttributes |
||
333 | * |
||
334 | * @return $this |
||
335 | */ |
||
336 | 2 | public function doRemoveDefaultAttributes(bool $doRemoveDefaultAttributes = true): self |
|
342 | |||
343 | /** |
||
344 | * @param bool $doRemoveDeprecatedAnchorName |
||
345 | * |
||
346 | * @return $this |
||
347 | */ |
||
348 | 2 | public function doRemoveDeprecatedAnchorName(bool $doRemoveDeprecatedAnchorName = true): self |
|
354 | |||
355 | /** |
||
356 | * @param bool $doRemoveDeprecatedScriptCharsetAttribute |
||
357 | * |
||
358 | * @return $this |
||
359 | */ |
||
360 | 2 | public function doRemoveDeprecatedScriptCharsetAttribute(bool $doRemoveDeprecatedScriptCharsetAttribute = true): self |
|
366 | |||
367 | /** |
||
368 | * @param bool $doRemoveDeprecatedTypeFromScriptTag |
||
369 | * |
||
370 | * @return $this |
||
371 | */ |
||
372 | 2 | public function doRemoveDeprecatedTypeFromScriptTag(bool $doRemoveDeprecatedTypeFromScriptTag = true): self |
|
378 | |||
379 | /** |
||
380 | * @param bool $doRemoveDeprecatedTypeFromStylesheetLink |
||
381 | * |
||
382 | * @return $this |
||
383 | */ |
||
384 | 2 | public function doRemoveDeprecatedTypeFromStylesheetLink(bool $doRemoveDeprecatedTypeFromStylesheetLink = true): self |
|
390 | |||
391 | /** |
||
392 | * @param bool $doRemoveEmptyAttributes |
||
393 | * |
||
394 | * @return $this |
||
395 | */ |
||
396 | 2 | public function doRemoveEmptyAttributes(bool $doRemoveEmptyAttributes = true): self |
|
402 | |||
403 | /** |
||
404 | * @param bool $doRemoveHttpPrefixFromAttributes |
||
405 | * |
||
406 | * @return $this |
||
407 | */ |
||
408 | 4 | public function doRemoveHttpPrefixFromAttributes(bool $doRemoveHttpPrefixFromAttributes = true): self |
|
414 | |||
415 | /** |
||
416 | * @return bool |
||
417 | */ |
||
418 | 27 | public function isDoSortCssClassNames(): bool |
|
422 | |||
423 | /** |
||
424 | * @return bool |
||
425 | */ |
||
426 | 27 | public function isDoSortHtmlAttributes(): bool |
|
430 | |||
431 | /** |
||
432 | * @return bool |
||
433 | */ |
||
434 | 27 | public function isDoRemoveDeprecatedScriptCharsetAttribute(): bool |
|
438 | |||
439 | /** |
||
440 | * @return bool |
||
441 | */ |
||
442 | 27 | public function isDoRemoveDefaultAttributes(): bool |
|
446 | |||
447 | /** |
||
448 | * @return bool |
||
449 | */ |
||
450 | 27 | public function isDoRemoveDeprecatedAnchorName(): bool |
|
454 | |||
455 | /** |
||
456 | * @return bool |
||
457 | */ |
||
458 | 27 | public function isDoRemoveDeprecatedTypeFromStylesheetLink(): bool |
|
462 | |||
463 | /** |
||
464 | * @return bool |
||
465 | */ |
||
466 | 27 | public function isDoRemoveDeprecatedTypeFromScriptTag(): bool |
|
470 | |||
471 | /** |
||
472 | * @return bool |
||
473 | */ |
||
474 | 27 | public function isDoRemoveValueFromEmptyInput(): bool |
|
478 | |||
479 | /** |
||
480 | * @return bool |
||
481 | */ |
||
482 | 27 | public function isDoRemoveEmptyAttributes(): bool |
|
486 | |||
487 | /** |
||
488 | * @return bool |
||
489 | */ |
||
490 | public function isDoSumUpWhitespace(): bool |
||
494 | |||
495 | /** |
||
496 | * @return bool |
||
497 | */ |
||
498 | public function isDoRemoveSpacesBetweenTags(): bool |
||
502 | |||
503 | /** |
||
504 | * @return bool |
||
505 | */ |
||
506 | public function isDoOptimizeViaHtmlDomParser(): bool |
||
510 | |||
511 | /** |
||
512 | * @return bool |
||
513 | */ |
||
514 | public function isDoOptimizeAttributes(): bool |
||
518 | |||
519 | /** |
||
520 | * @return bool |
||
521 | */ |
||
522 | public function isDoRemoveComments(): bool |
||
526 | |||
527 | /** |
||
528 | * @return bool |
||
529 | */ |
||
530 | public function isDoRemoveWhitespaceAroundTags(): bool |
||
534 | |||
535 | /** |
||
536 | * @return bool |
||
537 | */ |
||
538 | public function isDoRemoveOmittedQuotes(): bool |
||
542 | |||
543 | /** |
||
544 | * @return bool |
||
545 | */ |
||
546 | public function isDoRemoveOmittedHtmlTags(): bool |
||
550 | |||
551 | /** |
||
552 | * @return bool |
||
553 | */ |
||
554 | 27 | public function isDoRemoveHttpPrefixFromAttributes(): bool |
|
558 | |||
559 | /** |
||
560 | * @return array |
||
561 | */ |
||
562 | public function getDomainsToRemoveHttpPrefixFromAttributes(): array |
||
566 | |||
567 | /** |
||
568 | * @param bool $doRemoveOmittedHtmlTags |
||
569 | * |
||
570 | * @return $this |
||
571 | */ |
||
572 | 1 | public function doRemoveOmittedHtmlTags(bool $doRemoveOmittedHtmlTags = true): self |
|
578 | |||
579 | /** |
||
580 | * @param bool $doRemoveOmittedQuotes |
||
581 | * |
||
582 | * @return $this |
||
583 | */ |
||
584 | 1 | public function doRemoveOmittedQuotes(bool $doRemoveOmittedQuotes = true): self |
|
590 | |||
591 | /** |
||
592 | * @param bool $doRemoveSpacesBetweenTags |
||
593 | * |
||
594 | * @return $this |
||
595 | */ |
||
596 | 1 | public function doRemoveSpacesBetweenTags(bool $doRemoveSpacesBetweenTags = true): self |
|
602 | |||
603 | /** |
||
604 | * @param bool $doRemoveValueFromEmptyInput |
||
605 | * |
||
606 | * @return $this |
||
607 | */ |
||
608 | 2 | public function doRemoveValueFromEmptyInput(bool $doRemoveValueFromEmptyInput = true): self |
|
614 | |||
615 | /** |
||
616 | * @param bool $doRemoveWhitespaceAroundTags |
||
617 | * |
||
618 | * @return $this |
||
619 | */ |
||
620 | 4 | public function doRemoveWhitespaceAroundTags(bool $doRemoveWhitespaceAroundTags = true): self |
|
626 | |||
627 | /** |
||
628 | * @param bool $doSortCssClassNames |
||
629 | * |
||
630 | * @return $this |
||
631 | */ |
||
632 | 2 | public function doSortCssClassNames(bool $doSortCssClassNames = true): self |
|
638 | |||
639 | /** |
||
640 | * @param bool $doSortHtmlAttributes |
||
641 | * |
||
642 | * @return $this |
||
643 | */ |
||
644 | 2 | public function doSortHtmlAttributes(bool $doSortHtmlAttributes = true): self |
|
650 | |||
651 | /** |
||
652 | * @param bool $doSumUpWhitespace |
||
653 | * |
||
654 | * @return $this |
||
655 | */ |
||
656 | 2 | public function doSumUpWhitespace(bool $doSumUpWhitespace = true): self |
|
662 | |||
663 | 44 | private function domNodeAttributesToString(\DOMNode $node): string |
|
724 | |||
725 | /** |
||
726 | * @param \DOMNode $node |
||
727 | * |
||
728 | * @return bool |
||
729 | */ |
||
730 | 43 | private function domNodeClosingTagOptional(\DOMNode $node): bool |
|
982 | |||
983 | 44 | protected function domNodeToString(\DOMNode $node): string |
|
1071 | |||
1072 | /** |
||
1073 | * @param \DOMNode $node |
||
1074 | * |
||
1075 | * @return \DOMNode|null |
||
1076 | */ |
||
1077 | 43 | protected function getNextSiblingOfTypeDOMElement(\DOMNode $node) |
|
1085 | |||
1086 | /** |
||
1087 | * Check if the current string is an conditional comment. |
||
1088 | * |
||
1089 | * INFO: since IE >= 10 conditional comment are not working anymore |
||
1090 | * |
||
1091 | * <!--[if expression]> HTML <![endif]--> |
||
1092 | * <![if expression]> HTML <![endif]> |
||
1093 | * |
||
1094 | * @param string $comment |
||
1095 | * |
||
1096 | * @return bool |
||
1097 | */ |
||
1098 | 4 | private function isConditionalComment($comment): bool |
|
1110 | |||
1111 | /** |
||
1112 | * @param string $html |
||
1113 | * @param bool $decodeUtf8Specials <p>Use this only in special cases, e.g. for PHP 5.3</p> |
||
1114 | * |
||
1115 | * @return string |
||
1116 | */ |
||
1117 | 48 | public function minify($html, $decodeUtf8Specials = false): string |
|
1241 | |||
1242 | /** |
||
1243 | * @param $html |
||
1244 | * @param $decodeUtf8Specials |
||
1245 | * |
||
1246 | * @return string |
||
1247 | */ |
||
1248 | 44 | private function minifyHtmlDom($html, $decodeUtf8Specials): string |
|
1312 | |||
1313 | /** |
||
1314 | * Prevent changes of inline "styles" and "scripts". |
||
1315 | * |
||
1316 | * @param HtmlDomParser $dom |
||
1317 | * |
||
1318 | * @return HtmlDomParser |
||
1319 | */ |
||
1320 | 44 | private function protectTags(HtmlDomParser $dom): HtmlDomParser |
|
1373 | |||
1374 | /** |
||
1375 | * Remove comments in the dom. |
||
1376 | * |
||
1377 | * @param HtmlDomParser $dom |
||
1378 | * |
||
1379 | * @return HtmlDomParser |
||
1380 | */ |
||
1381 | 42 | private function removeComments(HtmlDomParser $dom): HtmlDomParser |
|
1396 | |||
1397 | /** |
||
1398 | * Trim tags in the dom. |
||
1399 | * |
||
1400 | * @param SimpleHtmlDomInterface $element |
||
1401 | * |
||
1402 | * @return void |
||
1403 | */ |
||
1404 | 3 | private function removeWhitespaceAroundTags(SimpleHtmlDomInterface $element) |
|
1429 | |||
1430 | /** |
||
1431 | * Callback function for preg_replace_callback use. |
||
1432 | * |
||
1433 | * @param array $matches PREG matches |
||
1434 | * |
||
1435 | * @return string |
||
1436 | */ |
||
1437 | 7 | private function restoreProtectedHtml($matches): string |
|
1448 | |||
1449 | /** |
||
1450 | * @param array $domainsToRemoveHttpPrefixFromAttributes |
||
1451 | * |
||
1452 | * @return $this |
||
1453 | */ |
||
1454 | 2 | public function setDomainsToRemoveHttpPrefixFromAttributes($domainsToRemoveHttpPrefixFromAttributes): self |
|
1460 | |||
1461 | /** |
||
1462 | * Sum-up extra whitespace from dom-nodes. |
||
1463 | * |
||
1464 | * @param HtmlDomParser $dom |
||
1465 | * |
||
1466 | * @return HtmlDomParser |
||
1467 | */ |
||
1468 | 43 | private function sumUpWhitespace(HtmlDomParser $dom): HtmlDomParser |
|
1495 | |||
1496 | /** |
||
1497 | * WARNING: maybe bad for performance ... |
||
1498 | * |
||
1499 | * @param bool $keepBrokenHtml |
||
1500 | * |
||
1501 | * @return HtmlMin |
||
1502 | */ |
||
1503 | 2 | public function useKeepBrokenHtml(bool $keepBrokenHtml): self |
|
1509 | } |
||
1510 |
If you access a property on an interface, you most likely code against a concrete implementation of the interface.
Available Fixes
Adding an additional type check:
Changing the type hint: