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 |
||
12 | class Analyzer { |
||
13 | |||
14 | /** @var Parser */ |
||
15 | private $parser; |
||
16 | private $matcher; |
||
17 | |||
18 | private $detectedUnit = null; |
||
19 | private $detectedUnitType = null; |
||
20 | private $currentUnit = null; |
||
21 | private $units; |
||
22 | |||
23 | 10 | public function __construct(Parser $parser) { |
|
33 | |||
34 | 5 | public function getUnits() { |
|
37 | |||
38 | 10 | public function analyze(TokenCollection $tokens) { |
|
39 | 10 | foreach ($tokens as $token) { |
|
40 | 10 | $this->parser->getTracker()->visitToken($token); |
|
41 | 10 | $this->findUnitStart($token); |
|
42 | 10 | $this->findUnitEnd($token); |
|
43 | 10 | $this->finish($token); |
|
44 | } |
||
45 | 10 | } |
|
46 | |||
47 | 10 | private function findUnitStart(Token $token) { |
|
48 | 10 | $detectedUnit = null; |
|
49 | 10 | $detectedUnitType = null; |
|
50 | |||
51 | 10 | if ($this->detectedUnit === null && ($this->matcher->isModifier($token) |
|
52 | 10 | || $this->matcher->isUnitIdentifier($token))) { |
|
53 | 5 | $detectedUnit = $token; |
|
54 | } |
||
55 | |||
56 | 10 | if ($detectedUnit !== null) { |
|
57 | |||
58 | // traits = use statements in struct body |
||
59 | 5 | if ($token->type == T_USE && $this->parser->getContext()->getCurrentContext() == Context::CONTEXT_STRUCT) { |
|
60 | 3 | $detectedUnitType = Unit::UNIT_TRAITS; |
|
61 | } |
||
62 | |||
63 | // line statements |
||
64 | 5 | else if ($this->matcher->isUnitIdentifier($token)) { |
|
65 | 5 | $detectedUnitType = Unit::getType($token); |
|
66 | } |
||
67 | |||
68 | // check for properties |
||
69 | 5 | else if ($this->matcher->isModifier($token)) { |
|
70 | 5 | $nextToken = $token; |
|
71 | do { |
||
72 | 5 | $nextToken = $this->parser->getTracker()->nextToken($nextToken); |
|
73 | 5 | if ($nextToken !== null && $nextToken->type == T_VARIABLE) { |
|
74 | 5 | $detectedUnitType = Unit::UNIT_FIELDS; |
|
75 | 5 | break; |
|
76 | 5 | } else if ($nextToken !== null && $nextToken->type == T_FUNCTION) { |
|
77 | 5 | $detectedUnitType = Unit::UNIT_METHODS; |
|
78 | 5 | break; |
|
79 | 2 | } else if ($nextToken !== null && $nextToken->type == T_CLASS) { |
|
80 | 2 | return; |
|
81 | } |
||
82 | 2 | } while ($this->matcher->isModifier($nextToken)); |
|
83 | } |
||
84 | |||
85 | // continue last unit, or start new unit? |
||
86 | 5 | if ($detectedUnitType !== Unit::UNIT_METHODS |
|
87 | 5 | && $this->currentUnit !== null |
|
88 | 5 | && $detectedUnitType === $this->currentUnit->type) { |
|
89 | 5 | $prevToken = $token; |
|
90 | do { |
||
91 | 5 | $prevToken = $this->parser->getTracker()->prevToken($prevToken); |
|
92 | 5 | } while (CommentsFormatter::isComment($prevToken) |
|
93 | 5 | || $this->matcher->isModifier($prevToken)); |
|
94 | |||
95 | // yes, new unit |
||
96 | 5 | if ($prevToken !== $this->currentUnit->end) { |
|
97 | $this->dumpCurrentUnit(); |
||
98 | $this->detectedUnit = $detectedUnit; |
||
99 | 5 | $this->detectedUnitType = $detectedUnitType; |
|
100 | } |
||
101 | } else { |
||
102 | 5 | $this->dumpCurrentUnit(); |
|
103 | 5 | $this->detectedUnit = $detectedUnit; |
|
104 | 5 | $this->detectedUnitType = $detectedUnitType; |
|
105 | } |
||
106 | } |
||
107 | 10 | } |
|
108 | |||
109 | 10 | private function findUnitEnd(Token $token) { |
|
110 | 10 | if ($this->detectedUnit !== null) { |
|
111 | 5 | if ($token->contents == ';') { |
|
112 | 5 | $this->flushDetection($token); |
|
113 | } |
||
114 | } |
||
115 | 10 | } |
|
116 | |||
117 | 5 | private function flushDetection(Token $token) { |
|
126 | |||
127 | 5 | public function onRoutineClosed(BlockEvent $event) { |
|
128 | 5 | if ($this->parser->getContext()->getCurrentContext() == Context::CONTEXT_STRUCT) { |
|
129 | |||
141 | |||
142 | 10 | public function onBlockClosed(BlockEvent $event) { |
|
150 | |||
151 | 10 | private function finish(Token $token) { |
|
156 | |||
157 | 10 | private function dumpCurrentUnit() { |
|
163 | |||
164 | } |
||
165 |