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 Document 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 Document, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
28 | class Document implements IConfigProvider |
||
29 | { |
||
30 | use TGetter; |
||
31 | |||
32 | /** |
||
33 | * Htsl main object owns this document. |
||
34 | * |
||
35 | * @access private |
||
36 | * |
||
37 | * @var \Htsl\Htsl |
||
38 | */ |
||
39 | private $htsl; |
||
40 | |||
41 | /** |
||
42 | * Parent document. |
||
43 | * |
||
44 | * @access private |
||
45 | * |
||
46 | * @var \Htsl\Parser\Document | null |
||
47 | */ |
||
48 | private $parent; |
||
49 | |||
50 | /** |
||
51 | * Reading buffer of this document. |
||
52 | * |
||
53 | * @access private |
||
54 | * |
||
55 | * @var \Htsl\ReadingBuffer\Contracts\ABuffer |
||
56 | */ |
||
57 | private $buffer; |
||
58 | |||
59 | /** |
||
60 | * The indentation and whether output with linefeed and indent. |
||
61 | * |
||
62 | * @access private |
||
63 | * |
||
64 | * @var string | bool |
||
65 | */ |
||
66 | private $indentation; |
||
67 | |||
68 | /** |
||
69 | * Type of this document. |
||
70 | * |
||
71 | * @access private |
||
72 | * |
||
73 | * @var string |
||
74 | */ |
||
75 | private $docType; |
||
76 | |||
77 | /** |
||
78 | * Current embead script. |
||
79 | * |
||
80 | * @access private |
||
81 | * |
||
82 | * @var \Htsl\Embedment\Contract\Embedment |
||
83 | */ |
||
84 | private $embedment; |
||
85 | |||
86 | /** |
||
87 | * Current indent level. |
||
88 | * |
||
89 | * @access private |
||
90 | * |
||
91 | * @var int |
||
92 | */ |
||
93 | private $level= 0; |
||
94 | |||
95 | /** |
||
96 | * Section indent level. |
||
97 | * |
||
98 | * @access private |
||
99 | * |
||
100 | * @var int |
||
101 | */ |
||
102 | private $sectionLevel= 0; |
||
103 | |||
104 | /** |
||
105 | * Opened nodes. |
||
106 | * |
||
107 | * @access private |
||
108 | * |
||
109 | * @var [ Htsl\Parser\Node\Contracts\ANode, ] |
||
110 | */ |
||
111 | private $openedNodes= []; |
||
112 | |||
113 | /** |
||
114 | * Current scopes. |
||
115 | * |
||
116 | * @access private |
||
117 | * |
||
118 | * @var [ Htsl\Parser\Node\Contracts\ANode, ] |
||
119 | */ |
||
120 | private $scopes= []; |
||
121 | |||
122 | /** |
||
123 | * Current line number. |
||
124 | * |
||
125 | * @access private |
||
126 | * |
||
127 | * @var int |
||
128 | */ |
||
129 | private $lineNumber= 0; |
||
130 | |||
131 | /** |
||
132 | * Current line. |
||
133 | * |
||
134 | * @access private |
||
135 | * |
||
136 | * @var \Htsl\ReadingBuffer\Line |
||
137 | */ |
||
138 | private $currentLine; |
||
139 | |||
140 | /** |
||
141 | * Sections that can be show. |
||
142 | * |
||
143 | * @access private |
||
144 | * |
||
145 | * @var [ Htsl\Parser\Section, ] |
||
146 | */ |
||
147 | private $sections=[]; |
||
148 | |||
149 | /** |
||
150 | * Whether the document is executed. |
||
151 | * |
||
152 | * @access private |
||
153 | * |
||
154 | * @var bool |
||
155 | */ |
||
156 | private $isExecuted; |
||
157 | |||
158 | /** |
||
159 | * Current Section. |
||
160 | * |
||
161 | * @access private |
||
162 | * |
||
163 | * @var \Htsl\Parser\Section |
||
164 | */ |
||
165 | private $currentSection; |
||
166 | |||
167 | /** |
||
168 | * The content of this document. |
||
169 | * |
||
170 | * @access private |
||
171 | * |
||
172 | * @var string |
||
173 | */ |
||
174 | private $content; |
||
175 | |||
176 | /** |
||
177 | * Constructor of the Document. |
||
178 | * |
||
179 | * @access public |
||
180 | * |
||
181 | * @param \Htsl\Htsl $htsl |
||
182 | * @param \Htsl\ReadingBuffer\Contracts\ABuffer $buffer |
||
183 | * @param \Htsl\Parser\Document | null $parent |
||
184 | */ |
||
185 | public function __construct( Htsl$htsl, Buffer$buffer, self$parent=null ) |
||
198 | |||
199 | /** |
||
200 | * Executing the document. |
||
201 | * |
||
202 | * @access public |
||
203 | * |
||
204 | * @return \Htsl\Parser\Document |
||
205 | */ |
||
206 | public function execute():self |
||
215 | |||
216 | /** |
||
217 | * Alias of getContent. |
||
218 | * |
||
219 | * @access public |
||
220 | * |
||
221 | * @return string |
||
222 | */ |
||
223 | public function __toString():string |
||
227 | |||
228 | /** |
||
229 | * Getting the result of document executing. |
||
230 | * |
||
231 | * @access protected |
||
232 | * |
||
233 | * @return string |
||
234 | */ |
||
235 | protected function getContent():string |
||
243 | |||
244 | /** |
||
245 | * Getting the next line. |
||
246 | * |
||
247 | * @access protected |
||
248 | * |
||
249 | * @return \Htsl\ReadingBuffer\Line |
||
250 | */ |
||
251 | protected function getLine():Line |
||
259 | |||
260 | /** |
||
261 | * Getting the config of type of this document. |
||
262 | * |
||
263 | * @access public |
||
264 | * |
||
265 | * @param [ string, ] ...$keys |
||
266 | * |
||
267 | * @return mixed |
||
268 | */ |
||
269 | public function getConfig( string...$keys ) |
||
273 | |||
274 | /** |
||
275 | * Getting the type of this document. |
||
276 | * |
||
277 | * @access public |
||
278 | * |
||
279 | * @return string |
||
280 | */ |
||
281 | public function getDoctype():string |
||
285 | |||
286 | /** |
||
287 | * Getting the indentation. |
||
288 | * |
||
289 | * @access public |
||
290 | * |
||
291 | * @return string | bool |
||
292 | */ |
||
293 | public function getIndentation() |
||
297 | |||
298 | /** |
||
299 | * Getting the indent level. |
||
300 | * |
||
301 | * @access public |
||
302 | * |
||
303 | * @return int |
||
304 | */ |
||
305 | public function getIndentLevel():int |
||
309 | |||
310 | /** |
||
311 | * Parsing the first line. |
||
312 | * |
||
313 | * @access protected |
||
314 | * |
||
315 | * @return \Htsl\Parser\Document |
||
316 | */ |
||
317 | protected function parseFirstLine():self |
||
334 | |||
335 | /** |
||
336 | * Setting that this document extends another document. |
||
337 | * |
||
338 | * @access protected |
||
339 | * |
||
340 | * @param \Htsl\ReadingBuffer\Line $firstLine |
||
341 | * |
||
342 | * @return \Htsl\Parser\Document |
||
343 | */ |
||
344 | protected function setExtending( Line$firstLine ):self |
||
362 | |||
363 | /** |
||
364 | * Parsing this document line by line. |
||
365 | * |
||
366 | * @access protected |
||
367 | * |
||
368 | * @return \Htsl\Parser\Document |
||
369 | */ |
||
370 | protected function lineByLine():self |
||
390 | |||
391 | /** |
||
392 | * Parsing embedded line. |
||
393 | * |
||
394 | * @access protected |
||
395 | * |
||
396 | * @param \Htsl\ReadingBuffer\Line $line |
||
397 | * |
||
398 | * @return \Htsl\Parser\Document |
||
399 | */ |
||
400 | protected function embeddingParse( Line$line ):self |
||
409 | |||
410 | /** |
||
411 | * Starting the embedding. |
||
412 | * |
||
413 | * @access protected |
||
414 | * |
||
415 | * @param string $embedType |
||
416 | * |
||
417 | * @return \Htsl\Parser\Document |
||
418 | */ |
||
419 | protected function startEmbedding( string$embedType ):self |
||
428 | |||
429 | /** |
||
430 | * Ending the embedding. |
||
431 | * |
||
432 | * @access public |
||
433 | * |
||
434 | * @return \Htsl\Parser\Document |
||
435 | */ |
||
436 | public function breakEmbedding():self |
||
443 | |||
444 | /** |
||
445 | * Parsing line. |
||
446 | * |
||
447 | * @access protected |
||
448 | * |
||
449 | * @param \Htsl\ReadingBuffer\Line $line |
||
450 | * |
||
451 | * @return \Htsl\Parser\Document |
||
452 | */ |
||
453 | protected function parseLine( Line$line ):self |
||
462 | |||
463 | /** |
||
464 | * Getting line type by analyzing first or first two characters of line. |
||
465 | * |
||
466 | * @access protected |
||
467 | * |
||
468 | * @param \Htsl\ReadingBuffer\Line $line |
||
469 | * |
||
470 | * @return string |
||
471 | */ |
||
472 | protected function getLineType( Line$line ):string |
||
482 | |||
483 | /** |
||
484 | * Possible line types. |
||
485 | * |
||
486 | * @access public |
||
487 | * |
||
488 | * @return array |
||
489 | * |
||
490 | * @todo Make this const private when php 7.1 |
||
491 | */ |
||
492 | const POSSIBLE_LINE_TYPES= [ |
||
493 | '`'=> [ |
||
494 | '='=> 'expressionHtmlLine', |
||
495 | ' '=> 'htmlLine', |
||
496 | ], |
||
497 | '='=> 'expressionLine', |
||
498 | '!'=> 'commentLine', |
||
499 | '-'=> 'tagLine', |
||
500 | '~'=> 'controlLine', |
||
501 | '@'=> 'docControlLine', |
||
502 | ' '=> 'stringLine', |
||
503 | ]; |
||
504 | |||
505 | /** |
||
506 | * Parsing line as HTML content. |
||
507 | * |
||
508 | * @access protected |
||
509 | * |
||
510 | * @param \Htsl\ReadingBuffer\Line $line |
||
511 | * |
||
512 | * @return \Htsl\Parser\Document |
||
513 | */ |
||
514 | View Code Duplication | protected function parseHtmlLine( Line$line ):self |
|
524 | |||
525 | /** |
||
526 | * Parsing line as string content. |
||
527 | * |
||
528 | * @access protected |
||
529 | * |
||
530 | * @param \Htsl\ReadingBuffer\Line $line |
||
531 | * |
||
532 | * @return \Htsl\Parser\Document |
||
533 | */ |
||
534 | protected function parseStringLine( Line$line ):self |
||
544 | |||
545 | /** |
||
546 | * Parsing line as PHP expression. |
||
547 | * |
||
548 | * @access protected |
||
549 | * |
||
550 | * @param \Htsl\ReadingBuffer\Line $line |
||
551 | * |
||
552 | * @return \Htsl\Parser\Document |
||
553 | */ |
||
554 | protected function parseExpressionLine( Line$line ):self |
||
568 | |||
569 | /** |
||
570 | * Parsing line as PHP expression with HTML result. |
||
571 | * |
||
572 | * @access protected |
||
573 | * |
||
574 | * @param \Htsl\ReadingBuffer\Line $line |
||
575 | * |
||
576 | * @return \Htsl\Parser\Document |
||
577 | */ |
||
578 | View Code Duplication | protected function parseExpressionHtmlLine( Line$line ):self |
|
590 | |||
591 | /** |
||
592 | * Parsing line as comment. |
||
593 | * |
||
594 | * @access protected |
||
595 | * |
||
596 | * @param \Htsl\ReadingBuffer\Line $line |
||
597 | * |
||
598 | * @return \Htsl\Parser\Document |
||
599 | */ |
||
600 | protected function parseCommentLine( Line$line ):self |
||
610 | |||
611 | /** |
||
612 | * Parsing line as HTSL tag. |
||
613 | * |
||
614 | * @access protected |
||
615 | * |
||
616 | * @param \Htsl\ReadingBuffer\Line $line |
||
617 | * |
||
618 | * @return \Htsl\Parser\Document |
||
619 | */ |
||
620 | protected function parseTagLine( Line$line ):self |
||
632 | |||
633 | /** |
||
634 | * Parsing line as control node of Htsl.php. |
||
635 | * |
||
636 | * @access protected |
||
637 | * |
||
638 | * @param \Htsl\ReadingBuffer\Line $line |
||
639 | * |
||
640 | * @return \Htsl\Parser\Document |
||
641 | */ |
||
642 | protected function parseControlLine( Line$line ):self |
||
652 | |||
653 | /** |
||
654 | * Parsing line as document control node of Htsl.php. |
||
655 | * |
||
656 | * @access protected |
||
657 | * |
||
658 | * @param \Htsl\ReadingBuffer\Line $line |
||
659 | * |
||
660 | * @return \Htsl\Parser\Document |
||
661 | */ |
||
662 | protected function parseDocControlLine( Line$line ):self |
||
684 | |||
685 | /** |
||
686 | * Parsing extending defination. |
||
687 | * |
||
688 | * @access protected |
||
689 | * |
||
690 | * @param string $fileName |
||
691 | * |
||
692 | * @return \Htsl\Parser\Document |
||
693 | */ |
||
694 | protected function extend( string$fileName ):self |
||
703 | |||
704 | /** |
||
705 | * Include another document. |
||
706 | * |
||
707 | * @access protected |
||
708 | * |
||
709 | * @param \Htsl\ReadingBuffer\Line $line |
||
710 | * |
||
711 | * @return \Htsl\Parser\Document |
||
712 | */ |
||
713 | protected function include( Line$line ):self |
||
729 | |||
730 | /** |
||
731 | * Starting to define a section. |
||
732 | * |
||
733 | * @access protected |
||
734 | * |
||
735 | * @param \Htsl\ReadingBuffer\Line $line |
||
736 | * |
||
737 | * @return \Htsl\Parser\Document |
||
738 | */ |
||
739 | protected function defineSection( Line$line ):self |
||
749 | |||
750 | /** |
||
751 | * Showing a section. |
||
752 | * |
||
753 | * @access protected |
||
754 | * |
||
755 | * @param \Htsl\ReadingBuffer\Line $line |
||
756 | * |
||
757 | * @return \Htsl\Parser\Document |
||
758 | */ |
||
759 | protected function showSection( Line$line ):self |
||
784 | |||
785 | /** |
||
786 | * Setting document as section definer. |
||
787 | * |
||
788 | * @access public |
||
789 | * |
||
790 | * @param Section | null $section |
||
791 | */ |
||
792 | public function setSection( Section$section=null ):self |
||
819 | |||
820 | /** |
||
821 | * Bubble the sections to parent document. |
||
822 | * |
||
823 | * @access protected |
||
824 | * |
||
825 | * @return \Htsl\Parser\Document |
||
826 | */ |
||
827 | protected function bubbleSections():self |
||
839 | |||
840 | /** |
||
841 | * Escaping characters to HTML entities. |
||
842 | * |
||
843 | * @access public |
||
844 | * |
||
845 | * @param string $input |
||
846 | * |
||
847 | * @return string |
||
848 | */ |
||
849 | public function htmlEntities( string$input ):string |
||
853 | |||
854 | /** |
||
855 | * Setting indent level of this document. |
||
856 | * |
||
857 | * @access protected |
||
858 | * |
||
859 | * @param int $level |
||
860 | */ |
||
861 | protected function setLevel( int$level ):self |
||
875 | |||
876 | /** |
||
877 | * Opening a node. |
||
878 | * |
||
879 | * @access protected |
||
880 | * |
||
881 | * @param \Htsl\Parser\Node\Contracts\ANode $node |
||
882 | * |
||
883 | * @return \Htsl\Parser\Document |
||
884 | */ |
||
885 | protected function openNode( Node$node ):self |
||
893 | |||
894 | /** |
||
895 | * Closing open node or nodes. |
||
896 | * |
||
897 | * @access protected |
||
898 | * |
||
899 | * @param int $level |
||
900 | * |
||
901 | * @return \Htsl\Parser\Document |
||
902 | */ |
||
903 | protected function closeNodes( int$level=0 ):self |
||
919 | |||
920 | /** |
||
921 | * Pushing a scope to stack. |
||
922 | * |
||
923 | * @access protected |
||
924 | * |
||
925 | * @param \Htsl\Parser\Node\Contracts\ANode $scope |
||
926 | */ |
||
927 | protected function setScope( Node$scope ):int |
||
931 | |||
932 | /** |
||
933 | * Getting current scope on top of stack. |
||
934 | * |
||
935 | * @access public |
||
936 | * |
||
937 | * @return \Htsl\Parser\Node\Contracts\ANode | null |
||
938 | */ |
||
939 | public function getScope() |
||
943 | |||
944 | /** |
||
945 | * Pop a scope from stack. |
||
946 | * |
||
947 | * @access protected |
||
948 | * |
||
949 | * @param \Htsl\Parser\Node\Contracts\ANode $scope |
||
950 | * |
||
951 | * @return \Htsl\Parser\Document |
||
952 | */ |
||
953 | protected function removeScope( Node$scope ):self |
||
961 | |||
962 | /** |
||
963 | * Appending a line of content to parsing result. |
||
964 | * |
||
965 | * @access protected |
||
966 | * |
||
967 | * @param string $content |
||
968 | * |
||
969 | * @return \Htsl\Parser\Document |
||
970 | */ |
||
971 | protected function appendLine( string$content ):self |
||
979 | |||
980 | /** |
||
981 | * Appending some content to parsing result. |
||
982 | * |
||
983 | * @access protected |
||
984 | * |
||
985 | * @param string $content |
||
986 | * |
||
987 | * @return \Htsl\Parser\Document |
||
988 | */ |
||
989 | protected function append( string$content ):self |
||
999 | |||
1000 | /** |
||
1001 | * Getting the Htsl main object. |
||
1002 | * |
||
1003 | * @access public |
||
1004 | * |
||
1005 | * @return \Htsl\Htsl |
||
1006 | */ |
||
1007 | public function getHtsl() |
||
1011 | |||
1012 | /** |
||
1013 | * Throw exception with document name and line number. |
||
1014 | * |
||
1015 | * @access public |
||
1016 | * |
||
1017 | * @param string $message |
||
1018 | * |
||
1019 | * @throw \Htsl\Parser\HtslParsingException |
||
1020 | */ |
||
1021 | public function throw( string$message ) |
||
1025 | } |
||
1026 |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.