Complex classes like Analyzer 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 Analyzer, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
25 | class Analyzer |
||
26 | { |
||
27 | /** |
||
28 | * @var array|RuleDelegate[] |
||
29 | */ |
||
30 | protected $rules = []; |
||
31 | |||
32 | /** |
||
33 | * Parsed rules. |
||
34 | * @var array|AbstractBuilder[] |
||
35 | */ |
||
36 | protected $parsedRules; |
||
37 | |||
38 | /** |
||
39 | * Counter to auto-name transitional rules. |
||
40 | * @var int |
||
41 | */ |
||
42 | protected $transitionalRuleCounter = 0; |
||
43 | |||
44 | /** |
||
45 | * Rule name being analyzed. |
||
46 | * @var string |
||
47 | */ |
||
48 | private $ruleName; |
||
49 | |||
50 | /** |
||
51 | * @param RuleDelegate $delegate |
||
52 | */ |
||
53 | public function addRuleDelegate(RuleDelegate $delegate): void |
||
57 | |||
58 | /** |
||
59 | * Build the analyzer of the rules (does not analyze the rules). |
||
60 | * |
||
61 | * @return Rule[]|\Traversable |
||
62 | * @throws GrammarException |
||
63 | */ |
||
64 | public function analyze(): iterable |
||
99 | |||
100 | /** |
||
101 | * Implementation of “rule”. |
||
102 | * |
||
103 | * @param LookaheadIterator $tokens |
||
104 | * @param string|null $pNodeId |
||
105 | * @return string|int|null |
||
106 | * @throws GrammarException |
||
107 | */ |
||
108 | protected function rule(LookaheadIterator $tokens, &$pNodeId) |
||
112 | |||
113 | /** |
||
114 | * Implementation of “choice”. |
||
115 | * |
||
116 | * @param LookaheadIterator $tokens |
||
117 | * @param string|null $pNodeId |
||
118 | * @return string|int|null |
||
119 | * @throws GrammarException |
||
120 | */ |
||
121 | protected function choice(LookaheadIterator $tokens, &$pNodeId) |
||
170 | |||
171 | /** |
||
172 | * Implementation of “concatenation”. |
||
173 | * |
||
174 | * @param LookaheadIterator $tokens |
||
175 | * @param string|null $pNodeId |
||
176 | * @return string|int|null |
||
177 | * @throws GrammarException |
||
178 | */ |
||
179 | protected function concatenation(LookaheadIterator $tokens, &$pNodeId) |
||
209 | |||
210 | /** |
||
211 | * Implementation of “repetition”. |
||
212 | * |
||
213 | * @param LookaheadIterator $tokens |
||
214 | * @param string|null $pNodeId |
||
215 | * @return string|int|null |
||
216 | * @throws GrammarException |
||
217 | */ |
||
218 | protected function repetition(LookaheadIterator $tokens, &$pNodeId) |
||
291 | |||
292 | /** |
||
293 | * Implementation of “simple”. |
||
294 | * |
||
295 | * @param LookaheadIterator $tokens |
||
296 | * @param int|string|null $pNodeId |
||
297 | * @return string|int|null |
||
298 | * @throws GrammarException |
||
299 | */ |
||
300 | protected function simple(LookaheadIterator $tokens, &$pNodeId) |
||
319 | |||
320 | /** |
||
321 | * @param LookaheadIterator $tokens |
||
322 | * @param int|string|null $pNodeId |
||
323 | * @return int|null|string |
||
324 | * @throws GrammarException |
||
325 | */ |
||
326 | protected function group(LookaheadIterator $tokens, &$pNodeId) |
||
343 | |||
344 | /** |
||
345 | * @param LookaheadIterator $tokens |
||
346 | * @param bool $kept |
||
347 | * @return int|string|null |
||
348 | */ |
||
349 | protected function token(LookaheadIterator $tokens, bool $kept = true) |
||
360 | |||
361 | /** |
||
362 | * @param LookaheadIterator $tokens |
||
363 | * @return int|string |
||
364 | * @throws GrammarException |
||
365 | */ |
||
366 | protected function invoke(LookaheadIterator $tokens) |
||
390 | } |
||
391 |
This error can happen if you refactor code and forget to move the variable initialization.
Let’s take a look at a simple example:
The above code is perfectly fine. Now imagine that we re-order the statements:
In that case,
$x
would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.