Completed
Push — master ( 99f10a...f4bdff )
by Thomas
01:46
created

Analyzer   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 153
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 94.62%

Importance

Changes 0
Metric Value
wmc 41
lcom 1
cbo 10
dl 0
loc 153
ccs 88
cts 93
cp 0.9462
rs 9.1199
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A getUnits() 0 3 1
A flushDetection() 0 9 1
A analyze() 0 8 2
D findUnitStart() 0 61 22
A findUnitEnd() 0 7 3
A onRoutineClosed() 0 14 4
A onBlockClosed() 0 8 3
A finish() 0 5 2
A dumpCurrentUnit() 0 6 2

How to fix   Complexity   

Complex Class

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
2
namespace gossi\formatter\parser;
3
4
use gossi\formatter\collections\UnitCollection;
5
use gossi\formatter\entities\Block;
6
use gossi\formatter\entities\Unit;
7
use gossi\formatter\events\BlockEvent;
8
use gossi\formatter\formatters\CommentsFormatter;
9
use phootwork\tokenizer\Token;
10
use phootwork\tokenizer\TokenCollection;
11
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) {
24 10
		$this->parser = $parser;
25 10
		$this->matcher = $parser->getMatcher();
26 10
		$this->units = new UnitCollection();
27
28
		// register listeners
29 10
		$context = $parser->getContext();
30 10
		$context->addListener(Context::EVENT_ROUTINE_LEAVE, [$this, 'onRoutineClosed']);
31 10
		$context->addListener(Context::EVENT_BLOCK_LEAVE, [$this, 'onBlockClosed']);
32 10
	}
33
34 5
	public function getUnits() {
35 5
		return $this->units;
36
	}
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) {
118 5
		$this->currentUnit = new Unit();
119 5
		$this->currentUnit->start = $this->detectedUnit;
120 5
		$this->currentUnit->type = $this->detectedUnitType;
121 5
		$this->currentUnit->end = $token;
122
123 5
		$this->detectedUnit = null;
124 5
		$this->detectedUnitType = null;
125 5
	}
126
127 5
	public function onRoutineClosed(BlockEvent $event) {
128 5
		if ($this->parser->getContext()->getCurrentContext() == Context::CONTEXT_STRUCT) {
129
130 5
			$block = $event->getBlock();
131
132 5
			if ($this->currentUnit === null || $block->start != $this->currentUnit->start) {
133 5
				$this->detectedUnit = $block->start;
134 5
				$this->detectedUnitType = Unit::UNIT_METHODS;
135 5
				$this->flushDetection($event->getToken());
136
			}
137
138 5
			$this->dumpCurrentUnit();
139
		}
140 5
	}
141
142 10
	public function onBlockClosed(BlockEvent $event) {
143 10
		$block = $event->getBlock();
144 10
		if ($block->type == Block::TYPE_USE || $block->type == Block::TYPE_NAMESPACE) {
145
			$this->detectedUnit = $block->start;
146
			$this->detectedUnitType = $block->type;
147
			$this->flushDetection($event->getToken());
148
		}
149 10
	}
150
151 10
	private function finish(Token $token) {
152 10
		if ($this->parser->getTracker()->isLastToken($token)) {
153 10
			$this->dumpCurrentUnit();
154
		}
155 10
	}
156
157 10
	private function dumpCurrentUnit() {
158 10
		if ($this->currentUnit !== null) {
159 5
			$this->units->add($this->currentUnit);
160 5
			$this->currentUnit = null;
161
		}
162 10
	}
163
164
}
165