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 |
||
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 \SplObjectStorage|HtmlMinDomObserverInterface[] |
||
248 | */ |
||
249 | private $domLoopObservers; |
||
250 | |||
251 | /** |
||
252 | * HtmlMin constructor. |
||
253 | */ |
||
254 | 37 | public function __construct() |
|
260 | |||
261 | /** |
||
262 | * @param HtmlMinDomObserverInterface $observer |
||
263 | * |
||
264 | * @return void |
||
265 | */ |
||
266 | 37 | public function attachObserverToTheDomLoop(HtmlMinDomObserverInterface $observer) |
|
270 | |||
271 | /** |
||
272 | * @param $domElement SimpleHtmlDom |
||
273 | * |
||
274 | * @return void |
||
275 | */ |
||
276 | 33 | private function notifyObserversAboutDomElementBeforeMinification(SimpleHtmlDom $domElement) |
|
282 | |||
283 | 33 | private function notifyObserversAboutDomElementAfterMinification(SimpleHtmlDom $domElement) |
|
289 | |||
290 | /** |
||
291 | * @param bool $doOptimizeAttributes |
||
292 | * |
||
293 | * @return $this |
||
294 | */ |
||
295 | 2 | public function doOptimizeAttributes(bool $doOptimizeAttributes = true): self |
|
301 | |||
302 | /** |
||
303 | * @param bool $doOptimizeViaHtmlDomParser |
||
304 | * |
||
305 | * @return $this |
||
306 | */ |
||
307 | 1 | public function doOptimizeViaHtmlDomParser(bool $doOptimizeViaHtmlDomParser = true): self |
|
313 | |||
314 | /** |
||
315 | * @param bool $doRemoveComments |
||
316 | * |
||
317 | * @return $this |
||
318 | */ |
||
319 | 3 | public function doRemoveComments(bool $doRemoveComments = true): self |
|
325 | |||
326 | /** |
||
327 | * @param bool $doRemoveDefaultAttributes |
||
328 | * |
||
329 | * @return $this |
||
330 | */ |
||
331 | 2 | public function doRemoveDefaultAttributes(bool $doRemoveDefaultAttributes = true): self |
|
337 | |||
338 | /** |
||
339 | * @param bool $doRemoveDeprecatedAnchorName |
||
340 | * |
||
341 | * @return $this |
||
342 | */ |
||
343 | 2 | public function doRemoveDeprecatedAnchorName(bool $doRemoveDeprecatedAnchorName = true): self |
|
349 | |||
350 | /** |
||
351 | * @param bool $doRemoveDeprecatedScriptCharsetAttribute |
||
352 | * |
||
353 | * @return $this |
||
354 | */ |
||
355 | 2 | public function doRemoveDeprecatedScriptCharsetAttribute(bool $doRemoveDeprecatedScriptCharsetAttribute = true): self |
|
361 | |||
362 | /** |
||
363 | * @param bool $doRemoveDeprecatedTypeFromScriptTag |
||
364 | * |
||
365 | * @return $this |
||
366 | */ |
||
367 | 2 | public function doRemoveDeprecatedTypeFromScriptTag(bool $doRemoveDeprecatedTypeFromScriptTag = true): self |
|
373 | |||
374 | /** |
||
375 | * @param bool $doRemoveDeprecatedTypeFromStylesheetLink |
||
376 | * |
||
377 | * @return $this |
||
378 | */ |
||
379 | 2 | public function doRemoveDeprecatedTypeFromStylesheetLink(bool $doRemoveDeprecatedTypeFromStylesheetLink = true): self |
|
385 | |||
386 | /** |
||
387 | * @param bool $doRemoveEmptyAttributes |
||
388 | * |
||
389 | * @return $this |
||
390 | */ |
||
391 | 2 | public function doRemoveEmptyAttributes(bool $doRemoveEmptyAttributes = true): self |
|
397 | |||
398 | /** |
||
399 | * @param bool $doRemoveHttpPrefixFromAttributes |
||
400 | * |
||
401 | * @return $this |
||
402 | */ |
||
403 | 4 | public function doRemoveHttpPrefixFromAttributes(bool $doRemoveHttpPrefixFromAttributes = true): self |
|
409 | |||
410 | /** |
||
411 | * @return bool |
||
412 | */ |
||
413 | 20 | public function isDoSortCssClassNames(): bool |
|
417 | |||
418 | /** |
||
419 | * @return bool |
||
420 | */ |
||
421 | 20 | public function isDoSortHtmlAttributes(): bool |
|
425 | |||
426 | /** |
||
427 | * @return bool |
||
428 | */ |
||
429 | 20 | public function isDoRemoveDeprecatedScriptCharsetAttribute(): bool |
|
433 | |||
434 | /** |
||
435 | * @return bool |
||
436 | */ |
||
437 | 20 | public function isDoRemoveDefaultAttributes(): bool |
|
441 | |||
442 | /** |
||
443 | * @return bool |
||
444 | */ |
||
445 | 20 | public function isDoRemoveDeprecatedAnchorName(): bool |
|
449 | |||
450 | /** |
||
451 | * @return bool |
||
452 | */ |
||
453 | 20 | public function isDoRemoveDeprecatedTypeFromStylesheetLink(): bool |
|
457 | |||
458 | /** |
||
459 | * @return bool |
||
460 | */ |
||
461 | 20 | public function isDoRemoveDeprecatedTypeFromScriptTag(): bool |
|
465 | |||
466 | /** |
||
467 | * @return bool |
||
468 | */ |
||
469 | 20 | public function isDoRemoveValueFromEmptyInput(): bool |
|
473 | |||
474 | /** |
||
475 | * @return bool |
||
476 | */ |
||
477 | 20 | public function isDoRemoveEmptyAttributes(): bool |
|
481 | |||
482 | /** |
||
483 | * @return bool |
||
484 | */ |
||
485 | public function isDoSumUpWhitespace(): bool |
||
489 | |||
490 | /** |
||
491 | * @return bool |
||
492 | */ |
||
493 | public function isDoRemoveSpacesBetweenTags(): bool |
||
497 | |||
498 | /** |
||
499 | * @return bool |
||
500 | */ |
||
501 | public function isDoOptimizeViaHtmlDomParser(): bool |
||
505 | |||
506 | /** |
||
507 | * @return bool |
||
508 | */ |
||
509 | public function isDoOptimizeAttributes(): bool |
||
513 | |||
514 | /** |
||
515 | * @return bool |
||
516 | */ |
||
517 | public function isDoRemoveComments(): bool |
||
521 | |||
522 | /** |
||
523 | * @return bool |
||
524 | */ |
||
525 | public function isDoRemoveWhitespaceAroundTags(): bool |
||
529 | |||
530 | /** |
||
531 | * @return bool |
||
532 | */ |
||
533 | public function isDoRemoveOmittedQuotes(): bool |
||
537 | |||
538 | /** |
||
539 | * @return bool |
||
540 | */ |
||
541 | public function isDoRemoveOmittedHtmlTags(): bool |
||
545 | |||
546 | /** |
||
547 | * @return bool |
||
548 | */ |
||
549 | 20 | public function isDoRemoveHttpPrefixFromAttributes(): bool |
|
553 | |||
554 | /** |
||
555 | * @return array |
||
556 | */ |
||
557 | public function getDomainsToRemoveHttpPrefixFromAttributes(): array |
||
561 | |||
562 | /** |
||
563 | * @param bool $doRemoveOmittedHtmlTags |
||
564 | * |
||
565 | * @return $this |
||
566 | */ |
||
567 | 1 | public function doRemoveOmittedHtmlTags(bool $doRemoveOmittedHtmlTags = true): self |
|
573 | |||
574 | /** |
||
575 | * @param bool $doRemoveOmittedQuotes |
||
576 | * |
||
577 | * @return $this |
||
578 | */ |
||
579 | 1 | public function doRemoveOmittedQuotes(bool $doRemoveOmittedQuotes = true): self |
|
585 | |||
586 | /** |
||
587 | * @param bool $doRemoveSpacesBetweenTags |
||
588 | * |
||
589 | * @return $this |
||
590 | */ |
||
591 | public function doRemoveSpacesBetweenTags(bool $doRemoveSpacesBetweenTags = true): self |
||
597 | |||
598 | /** |
||
599 | * @param bool $doRemoveValueFromEmptyInput |
||
600 | * |
||
601 | * @return $this |
||
602 | */ |
||
603 | 2 | public function doRemoveValueFromEmptyInput(bool $doRemoveValueFromEmptyInput = true): self |
|
609 | |||
610 | /** |
||
611 | * @param bool $doRemoveWhitespaceAroundTags |
||
612 | * |
||
613 | * @return $this |
||
614 | */ |
||
615 | 4 | public function doRemoveWhitespaceAroundTags(bool $doRemoveWhitespaceAroundTags = true): self |
|
621 | |||
622 | /** |
||
623 | * @param bool $doSortCssClassNames |
||
624 | * |
||
625 | * @return $this |
||
626 | */ |
||
627 | 2 | public function doSortCssClassNames(bool $doSortCssClassNames = true): self |
|
633 | |||
634 | /** |
||
635 | * @param bool $doSortHtmlAttributes |
||
636 | * |
||
637 | * @return $this |
||
638 | */ |
||
639 | 2 | public function doSortHtmlAttributes(bool $doSortHtmlAttributes = true): self |
|
645 | |||
646 | /** |
||
647 | * @param bool $doSumUpWhitespace |
||
648 | * |
||
649 | * @return $this |
||
650 | */ |
||
651 | 2 | public function doSumUpWhitespace(bool $doSumUpWhitespace = true): self |
|
657 | |||
658 | 33 | private function domNodeAttributesToString(\DOMNode $node): string |
|
693 | |||
694 | /** |
||
695 | * @param \DOMNode $node |
||
696 | * |
||
697 | * @return bool |
||
698 | */ |
||
699 | 32 | private function domNodeClosingTagOptional(\DOMNode $node): bool |
|
918 | |||
919 | 33 | protected function domNodeToString(\DOMNode $node): string |
|
1006 | |||
1007 | /** |
||
1008 | * @param \DOMNode $node |
||
1009 | * |
||
1010 | * @return \DOMNode|null |
||
1011 | */ |
||
1012 | 32 | protected function getNextSiblingOfTypeDOMElement(\DOMNode $node) |
|
1020 | |||
1021 | /** |
||
1022 | * Check if the current string is an conditional comment. |
||
1023 | * |
||
1024 | * INFO: since IE >= 10 conditional comment are not working anymore |
||
1025 | * |
||
1026 | * <!--[if expression]> HTML <![endif]--> |
||
1027 | * <![if expression]> HTML <![endif]> |
||
1028 | * |
||
1029 | * @param string $comment |
||
1030 | * |
||
1031 | * @return bool |
||
1032 | */ |
||
1033 | 4 | private function isConditionalComment($comment): bool |
|
1045 | |||
1046 | /** |
||
1047 | * @param string $html |
||
1048 | * @param bool $decodeUtf8Specials <p>Use this only in special cases, e.g. for PHP 5.3</p> |
||
1049 | * |
||
1050 | * @return string |
||
1051 | */ |
||
1052 | 37 | public function minify($html, $decodeUtf8Specials = false): string |
|
1176 | |||
1177 | /** |
||
1178 | * @param $html |
||
1179 | * @param $decodeUtf8Specials |
||
1180 | * |
||
1181 | * @return string |
||
1182 | */ |
||
1183 | 33 | private function minifyHtmlDom($html, $decodeUtf8Specials): string |
|
1247 | |||
1248 | /** |
||
1249 | * Prevent changes of inline "styles" and "scripts". |
||
1250 | * |
||
1251 | * @param HtmlDomParser $dom |
||
1252 | * |
||
1253 | * @return HtmlDomParser |
||
1254 | */ |
||
1255 | 33 | private function protectTags(HtmlDomParser $dom): HtmlDomParser |
|
1304 | |||
1305 | /** |
||
1306 | * Remove comments in the dom. |
||
1307 | * |
||
1308 | * @param HtmlDomParser $dom |
||
1309 | * |
||
1310 | * @return HtmlDomParser |
||
1311 | */ |
||
1312 | 31 | private function removeComments(HtmlDomParser $dom): HtmlDomParser |
|
1327 | |||
1328 | /** |
||
1329 | * Trim tags in the dom. |
||
1330 | * |
||
1331 | * @param SimpleHtmlDom $element |
||
1332 | * |
||
1333 | * @return void |
||
1334 | */ |
||
1335 | 3 | private function removeWhitespaceAroundTags(SimpleHtmlDom $element) |
|
1359 | |||
1360 | /** |
||
1361 | * Callback function for preg_replace_callback use. |
||
1362 | * |
||
1363 | * @param array $matches PREG matches |
||
1364 | * |
||
1365 | * @return string |
||
1366 | */ |
||
1367 | 4 | private function restoreProtectedHtml($matches): string |
|
1378 | |||
1379 | /** |
||
1380 | * @param array $domainsToRemoveHttpPrefixFromAttributes |
||
1381 | * |
||
1382 | * @return $this |
||
1383 | */ |
||
1384 | 2 | public function setDomainsToRemoveHttpPrefixFromAttributes($domainsToRemoveHttpPrefixFromAttributes): self |
|
1390 | |||
1391 | /** |
||
1392 | * Sum-up extra whitespace from dom-nodes. |
||
1393 | * |
||
1394 | * @param HtmlDomParser $dom |
||
1395 | * |
||
1396 | * @return HtmlDomParser |
||
1397 | */ |
||
1398 | 32 | private function sumUpWhitespace(HtmlDomParser $dom): HtmlDomParser |
|
1425 | |||
1426 | /** |
||
1427 | * WARNING: maybe bad for performance ... |
||
1428 | * |
||
1429 | * @param bool $keepBrokenHtml |
||
1430 | * |
||
1431 | * @return HtmlMin |
||
1432 | */ |
||
1433 | public function useKeepBrokenHtml(bool $keepBrokenHtml): self |
||
1439 | } |
||
1440 |
There are different options of fixing this problem.
If you want to be on the safe side, you can add an additional type-check:
If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:
Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.