1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Padawan\Domain\Project; |
4
|
|
|
|
5
|
|
|
use Padawan\Domain\Project\Node\InterfaceData; |
6
|
|
|
use Padawan\Domain\Project\Node\ClassData; |
7
|
|
|
use Padawan\Domain\Project\Node\FunctionData; |
8
|
|
|
|
9
|
|
|
class Index |
10
|
|
|
{ |
11
|
|
|
private $fqcns = []; |
12
|
|
|
private $classes = []; |
13
|
|
|
private $interfaces = []; |
14
|
|
|
private $classMap = []; |
15
|
|
|
private $flippedClassMap = []; |
16
|
|
|
private $extends = []; |
17
|
|
|
private $implements = []; |
18
|
|
|
private $parsedFiles = []; |
19
|
|
|
private $functions = []; |
20
|
|
|
/** @var Index $coreIndex */ |
21
|
|
|
private static $coreIndex; |
22
|
|
|
|
23
|
|
|
public function getFQCNs() |
24
|
|
|
{ |
25
|
|
|
return $this->fqcns; |
26
|
|
|
} |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* @return FQCN |
30
|
|
|
*/ |
31
|
|
|
public function findFQCNByFile($file) |
32
|
|
|
{ |
33
|
|
|
if (!array_key_exists($file, $this->flippedClassMap)) { |
34
|
|
|
return null; |
35
|
|
|
} |
36
|
|
|
$fqcnStr = $this->flippedClassMap[$file]; |
37
|
|
|
if (empty($fqcnStr)) { |
38
|
|
|
return null; |
39
|
|
|
} |
40
|
|
|
if (!array_key_exists($fqcnStr, $this->fqcns)) { |
41
|
|
|
return null; |
42
|
|
|
} |
43
|
|
|
return $this->fqcns[$fqcnStr]; |
44
|
|
|
} |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* @return ClassData |
48
|
|
|
*/ |
49
|
|
View Code Duplication |
public function findClassByFQCN(FQCN $fqcn) { |
|
|
|
|
50
|
|
|
$str = $fqcn->toString(); |
51
|
|
|
if (array_key_exists($str, $this->classes)) { |
52
|
|
|
return $this->classes[$str]; |
53
|
|
|
} |
54
|
|
|
if ($this->hasCoreIndex()) { |
55
|
|
|
return self::$coreIndex->findClassByFQCN($fqcn); |
56
|
|
|
} |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* @return InterfaceData |
61
|
|
|
*/ |
62
|
|
View Code Duplication |
public function findInterfaceByFQCN(FQCN $fqcn) { |
|
|
|
|
63
|
|
|
$str = $fqcn->toString(); |
64
|
|
|
if (array_key_exists($str, $this->interfaces)) { |
65
|
|
|
return $this->interfaces[$str]; |
66
|
|
|
} |
67
|
|
|
if ($this->hasCoreIndex()) { |
68
|
|
|
return self::$coreIndex->findInterfaceByFQCN($fqcn); |
69
|
|
|
} |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* @return FunctionData |
74
|
|
|
*/ |
75
|
|
|
public function findFunctionByName($functionName) |
76
|
|
|
{ |
77
|
|
|
if (array_key_exists($functionName, $this->functions)) { |
78
|
|
|
return $this->functions[$functionName]; |
79
|
|
|
} |
80
|
|
|
if ($this->hasCoreIndex()) { |
81
|
|
|
return self::$coreIndex->findFunctionByName($functionName); |
82
|
|
|
} |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* @return ClassData[] |
87
|
|
|
*/ |
88
|
|
View Code Duplication |
public function findClassChildren(FQCN $class) { |
|
|
|
|
89
|
|
|
if (!array_key_exists($class->toString(), $this->extends) |
90
|
|
|
|| !is_array($this->extends[$class->toString()]) |
91
|
|
|
) { |
92
|
|
|
$this->extends[$class->toString()] = []; |
93
|
|
|
} |
94
|
|
|
return $this->extends[$class->toString()]; |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* @return ClassData[] |
99
|
|
|
*/ |
100
|
|
View Code Duplication |
public function findInterfaceChildrenClasses(FQCN $interface) { |
|
|
|
|
101
|
|
|
if (!array_key_exists($interface->toString(), $this->implements) |
102
|
|
|
|| !is_array($this->implements[$interface->toString()]) |
103
|
|
|
) { |
104
|
|
|
$this->implements[$interface->toString()] = []; |
105
|
|
|
} |
106
|
|
|
return $this->implements[$interface->toString()]; |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* @return ClassData[] |
111
|
|
|
*/ |
112
|
|
|
public function getClasses() |
113
|
|
|
{ |
114
|
|
|
$classes = $this->classes; |
115
|
|
|
if ($this->hasCoreIndex()) { |
116
|
|
|
$classes = array_merge($classes, self::$coreIndex->getClasses()); |
117
|
|
|
} |
118
|
|
|
return $classes; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* @return InterfaceData[] |
123
|
|
|
*/ |
124
|
|
|
public function getInterfaces() |
125
|
|
|
{ |
126
|
|
|
$interfaces = $this->interfaces; |
127
|
|
|
if ($this->hasCoreIndex()) { |
128
|
|
|
$interfaces = array_merge($interfaces, self::$coreIndex->getInterfaces()); |
129
|
|
|
} |
130
|
|
|
return $interfaces; |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* @return FunctionData[] |
135
|
|
|
*/ |
136
|
|
|
public function getFunctions() |
137
|
|
|
{ |
138
|
|
|
$functions = $this->functions; |
139
|
|
|
if ($this->hasCoreIndex()) { |
140
|
|
|
$functions = array_merge($functions, self::$coreIndex->getFunctions()); |
141
|
|
|
} |
142
|
|
|
return $functions; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
public function addClass(ClassData $class) { |
146
|
|
|
$this->classes[$class->fqcn->toString()] = $class; |
147
|
|
|
if ($class->getParent() instanceof FQCN) { |
148
|
|
|
$this->addExtend($class, $class->getParent()); |
149
|
|
|
} |
150
|
|
|
foreach ($class->getInterfaces() as $interface) { |
151
|
|
|
if ($interface instanceof FQCN) { |
152
|
|
|
$this->addImplement($class, $interface); |
153
|
|
|
} |
154
|
|
|
} |
155
|
|
|
foreach ($this->findClassChildren($class->fqcn) AS $child) { |
156
|
|
|
$child->setParent($class); |
157
|
|
|
} |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
public function addInterface(InterfaceData $interface) { |
161
|
|
|
$this->interfaces[$interface->fqcn->toString()] = $interface; |
162
|
|
|
foreach ($this->findInterfaceChildrenClasses($interface->fqcn) as $child) { |
163
|
|
|
$this->addImplement($child, $interface->fqcn); |
164
|
|
|
} |
165
|
|
|
foreach ($interface->getInterfaces() as $parent) { |
166
|
|
|
if ($parent instanceof FQCN) { |
167
|
|
|
$this->addImplement($interface, $parent); |
168
|
|
|
} |
169
|
|
|
} |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
public function addFunction(FunctionData $function) |
173
|
|
|
{ |
174
|
|
|
$this->functions[$function->name] = $function; |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
public function addFQCN(FQCN $fqcn) { |
178
|
|
|
$this->fqcns[$fqcn->toString()] = $fqcn; |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
public function getClassMap() { |
182
|
|
|
return $this->classMap; |
183
|
|
|
} |
184
|
|
|
public function getFlippedClassMap() { |
185
|
|
|
return $this->classMap; |
186
|
|
|
} |
187
|
|
|
public function getImplements() { |
188
|
|
|
return $this->implements; |
189
|
|
|
} |
190
|
|
|
public function getExtends() { |
191
|
|
|
return $this->extends; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
public function setClassMap(array $classMap) { |
195
|
|
|
$this->classMap = $classMap; |
196
|
|
|
$this->flippedClassMap = array_flip($classMap); |
197
|
|
|
} |
198
|
|
|
protected function addExtend(ClassData $class, FQCN $parent) { |
199
|
|
|
$this->findClassChildren($parent); |
200
|
|
|
$this->extends[$parent->toString()][$class->fqcn->toString()] = $class; |
201
|
|
|
$parentClass = $this->findClassByFQCN($parent); |
202
|
|
|
if ($parentClass instanceof ClassData) { |
203
|
|
|
$class->setParent($parentClass); |
204
|
|
|
} |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
protected function addImplement($class, FQCN $fqcn) { |
208
|
|
|
$this->findInterfaceChildrenClasses($fqcn); |
209
|
|
|
$this->implements[$fqcn->toString()][$class->fqcn->toString()] = $class; |
210
|
|
|
$interface = $this->findInterfaceByFQCN($fqcn); |
211
|
|
|
if ($interface instanceof InterfaceData) { |
212
|
|
|
$class->addInterface($interface); |
213
|
|
|
} |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
public function isParsed($file) { |
217
|
|
|
return array_key_exists( |
218
|
|
|
$file, |
219
|
|
|
$this->parsedFiles |
220
|
|
|
); |
221
|
|
|
} |
222
|
|
|
public function addParsedFile($file) { |
223
|
|
|
$this->parsedFiles[$file] = $file; |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
private function hasCoreIndex() |
227
|
|
|
{ |
228
|
|
|
return $this !== self::$coreIndex && !empty(self::$coreIndex); |
229
|
|
|
} |
230
|
|
|
} |
231
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.