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 Token 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 Token, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
26 | class Token |
||
27 | { |
||
28 | const NAME = null; |
||
29 | const START = 0x1; |
||
30 | const END = 0x2; |
||
31 | |||
32 | protected static $_id = 0; |
||
33 | |||
34 | public $pos; |
||
35 | public $name; |
||
36 | public $id; |
||
37 | public $rule; |
||
38 | public $options; |
||
39 | |||
40 | # region >>> cache |
||
41 | /** |
||
42 | * @var static|null|false |
||
43 | */ |
||
44 | protected $_end; |
||
45 | |||
46 | /** |
||
47 | * @var static|null|false |
||
48 | */ |
||
49 | protected $_start; |
||
50 | |||
51 | protected $_valid; |
||
52 | protected $_length; |
||
53 | # endregion |
||
54 | |||
55 | /** |
||
56 | * Token constructor. |
||
57 | * |
||
58 | * @param null $name |
||
59 | * @param array $options |
||
60 | */ |
||
61 | 52 | public function __construct($name = null, array $options = []) |
|
62 | { |
||
63 | 52 | if (isset($options['pos'])) { |
|
64 | 45 | $this->pos = $options['pos']; |
|
65 | 45 | } |
|
66 | |||
67 | 52 | $this->name = $name; |
|
68 | 52 | $this->rule = isset($options['rule']) ? $options['rule'] : new Rule(); |
|
69 | 52 | $this->options &= $options; |
|
70 | |||
71 | 52 | $this->id = ++self::$_id; |
|
72 | 52 | } |
|
73 | |||
74 | 33 | public function isStart() |
|
75 | 11 | { |
|
76 | 33 | return $this->_start === null; |
|
77 | 1 | } |
|
78 | |||
79 | 31 | public function isEnd() |
|
80 | 1 | { |
|
81 | 31 | return $this->_end === null; |
|
82 | } |
||
83 | |||
84 | 14 | public function isValid(Context $context) |
|
85 | { |
||
86 | 14 | if ($this->_valid === null) { |
|
87 | 13 | $this->validate($context); |
|
88 | 13 | } |
|
89 | |||
90 | 14 | return $this->_valid; |
|
91 | } |
||
92 | |||
93 | 11 | protected function validate(Context $context) |
|
94 | { |
||
95 | 11 | $this->setValid( |
|
96 | 11 | $context->language === $this->rule->language && |
|
97 | 11 | $this->rule->validator->validate($context, $this->isEnd() ? [$this->name => Validator::CONTEXT_IN] : []) |
|
98 | 11 | ); |
|
99 | 11 | } |
|
100 | |||
101 | 14 | public function setValid($valid = true) |
|
102 | { |
||
103 | 14 | $this->_valid = $valid; |
|
104 | |||
105 | 14 | if ($this->_end) { |
|
106 | 11 | $this->_end->_valid = $this->_valid; |
|
107 | 14 | } elseif ($this->_start) { |
|
108 | 12 | $this->_start->_valid = $this->_valid; |
|
109 | 12 | } |
|
110 | 14 | } |
|
111 | |||
112 | /** |
||
113 | * @return Token|null|false |
||
114 | */ |
||
115 | 30 | public function getStart() |
|
116 | { |
||
117 | 30 | return $this->_start; |
|
118 | } |
||
119 | |||
120 | /** |
||
121 | * @param Token|null|false $start |
||
122 | */ |
||
123 | 11 | View Code Duplication | public function setStart($start = null) |
124 | { |
||
125 | 11 | $this->_end = null; |
|
126 | 11 | $this->_start = $start; |
|
127 | |||
128 | 11 | if ($start instanceof Token) { |
|
129 | 11 | $this->_start->_length = null; |
|
130 | 11 | $start->_end = $this; |
|
131 | 11 | } |
|
132 | 11 | } |
|
133 | |||
134 | /** |
||
135 | * @return Token|null|false |
||
136 | */ |
||
137 | 38 | public function getEnd() |
|
138 | { |
||
139 | 38 | return $this->_end; |
|
140 | } |
||
141 | |||
142 | /** |
||
143 | * @param Token|null|false $end |
||
144 | */ |
||
145 | 46 | View Code Duplication | public function setEnd($end = null) |
146 | { |
||
147 | 46 | $this->_start = null; |
|
148 | 46 | $this->_end = $end; |
|
149 | 46 | $this->_length = null; |
|
150 | |||
151 | 46 | if ($end instanceof Token) { |
|
152 | 46 | $end->_start = $this; |
|
153 | 46 | } |
|
154 | 46 | } |
|
155 | |||
156 | 2 | public function getLength() |
|
157 | { |
||
158 | 2 | if ($this->_length === null) { |
|
159 | 2 | $this->_length = !$this->_end ? 0 : $this->_end->pos - $this->pos; |
|
160 | 2 | } |
|
161 | |||
162 | 2 | return $this->_length; |
|
163 | } |
||
164 | |||
165 | 11 | public function __get($name) |
|
166 | { |
||
167 | 11 | return $this->rule->$name; |
|
168 | } |
||
169 | |||
170 | /** |
||
171 | * @param Context $context |
||
172 | * @param Language $language |
||
173 | * @param Result $result |
||
174 | * @param TokenIterator $tokens |
||
175 | * |
||
176 | * todo: Documentation |
||
177 | * |
||
178 | * @return bool Return true to continue processing, false to return already processed tokens. |
||
179 | */ |
||
180 | 11 | public function process(Context $context, Language $language, Result $result, TokenIterator $tokens) { |
|
181 | 11 | if(!$this->isValid($context)) { |
|
182 | 3 | return true; |
|
183 | } |
||
184 | |||
185 | 11 | return $this->isStart() ? |
|
186 | 11 | $this->processStart($context, $language, $result, $tokens) : |
|
187 | 11 | $this->processEnd($context, $language, $result, $tokens); |
|
188 | } |
||
189 | |||
190 | 10 | protected function processStart(Context $context, Language $language, Result $result, TokenIterator $tokens) { |
|
196 | |||
197 | 9 | protected function processEnd(Context $context, Language $language, Result $result, TokenIterator $tokens) { |
|
1 ignored issue
–
show
|
|||
198 | 9 | if($this->_start) { |
|
199 | 8 | $context->pop($this->_start); |
|
200 | 8 | } else { |
|
201 | 1 | if (($start = $context->find($this->name)) !== false) { |
|
202 | 1 | $this->setStart($tokens[$start]); |
|
203 | |||
204 | 1 | unset($context->stack[$start]); |
|
205 | 1 | } |
|
206 | } |
||
207 | |||
208 | 9 | if (!$this->_start instanceof MetaToken) { |
|
209 | 9 | $result->append($this); |
|
210 | 9 | } |
|
211 | |||
212 | 9 | return true; |
|
213 | } |
||
214 | |||
215 | 12 | public static function compare(Token $a, Token $b) |
|
233 | } |
||
234 |
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.