1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @author @jayS-de <[email protected]> |
4
|
|
|
*/ |
5
|
|
|
|
6
|
|
|
namespace Commercetools\Core\Helper\Annotate; |
7
|
|
|
|
8
|
|
|
/** |
9
|
|
|
* @package Commercetools\Core\Helper\Annotate |
10
|
|
|
*/ |
11
|
|
|
class ReflectedClass |
12
|
|
|
{ |
13
|
|
|
/** |
14
|
|
|
* @var bool |
15
|
|
|
*/ |
16
|
|
|
protected $abstract; |
17
|
|
|
|
18
|
|
|
protected $fileName; |
19
|
|
|
|
20
|
|
|
protected $className; |
21
|
|
|
protected $shortClassName; |
22
|
|
|
|
23
|
|
|
protected $namespace; |
24
|
|
|
protected $uses = []; |
25
|
|
|
protected $methods = []; |
26
|
|
|
protected $magicGetSetMethods = []; |
27
|
|
|
protected $docBlockLines = []; |
28
|
|
|
|
29
|
|
|
protected $constructorArgs = []; |
30
|
|
|
|
31
|
|
|
public function __construct($className) |
32
|
|
|
{ |
33
|
|
|
$this->className = $className; |
34
|
|
|
$this->reflectClass(); |
35
|
|
|
$this->reflectUseStatements(); |
36
|
|
|
$this->reflectDocBlock(); |
37
|
|
|
$this->reflectConstructorArgs(); |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
public function addMagicGetSetMethod( |
41
|
|
|
$type, |
42
|
|
|
$fieldName, |
43
|
|
|
$args, |
44
|
|
|
$returnTypeHint = null, |
45
|
|
|
$returnsReference = null, |
46
|
|
|
$shortDescription = null |
47
|
|
|
) { |
48
|
|
|
$name = $type . ucfirst($fieldName); |
49
|
|
|
if ($this->hasMethod($name)) { |
50
|
|
|
return; |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
$magicMethod = [ |
54
|
|
|
'name' => $name, |
55
|
|
|
'returnTypeHint' => $returnTypeHint, |
56
|
|
|
'returnsReference' => $returnsReference, |
57
|
|
|
'type' => $type, |
58
|
|
|
'fieldName' => $fieldName, |
59
|
|
|
'args' => $args, |
60
|
|
|
'shortDescription' => $shortDescription |
61
|
|
|
]; |
62
|
|
|
|
63
|
|
|
if (isset($this->magicGetSetMethods[$name])) { |
64
|
|
|
$magicMethod = array_merge($this->magicGetSetMethods[$name], $magicMethod); |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
$this->magicGetSetMethods[$name] = $magicMethod; |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
public function addMagicMethod( |
71
|
|
|
$methodName, |
72
|
|
|
$args, |
73
|
|
|
$returnTypeHint = null, |
74
|
|
|
$returnsReference = null, |
75
|
|
|
$shortDescription = null, |
76
|
|
|
$static = false, |
77
|
|
|
$force = false |
78
|
|
|
) { |
79
|
|
|
if (!$force && $this->hasMethod($methodName)) { |
80
|
|
|
return; |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
$magicMethod = [ |
84
|
|
|
'name' => $methodName, |
85
|
|
|
'returnTypeHint' => $returnTypeHint, |
86
|
|
|
'returnsReference' => $returnsReference, |
87
|
|
|
'args' => $args, |
88
|
|
|
'shortDescription' => $shortDescription, |
89
|
|
|
'static' => $static |
90
|
|
|
]; |
91
|
|
|
|
92
|
|
|
if (isset($this->magicGetSetMethods[$methodName])) { |
93
|
|
|
$magicMethod = array_merge($this->magicGetSetMethods[$methodName], $magicMethod); |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
$this->magicGetSetMethods[$methodName] = $magicMethod; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
public function hasMethod($methodName) |
100
|
|
|
{ |
101
|
|
|
return isset($this->methods[$methodName]); |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
public function hasMagicGetSetMethod($methodName) |
105
|
|
|
{ |
106
|
|
|
return isset($this->magicGetSetMethods[$methodName]); |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
public function getMethod($methodName) |
110
|
|
|
{ |
111
|
|
|
if ($this->hasMethod($methodName)) { |
112
|
|
|
return $this->methods[$methodName]; |
113
|
|
|
} |
114
|
|
|
return false; |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
public function getMagicGetSetMethod($methodName) |
118
|
|
|
{ |
119
|
|
|
if ($this->hasMethod($methodName)) { |
120
|
|
|
return $this->magicGetSetMethods[$methodName]; |
121
|
|
|
} |
122
|
|
|
return false; |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* @return array |
127
|
|
|
*/ |
128
|
|
|
public function getConstructorArgs() |
129
|
|
|
{ |
130
|
|
|
return $this->constructorArgs; |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* @param $className |
135
|
|
|
* @param $alias |
136
|
|
|
*/ |
137
|
|
|
public function addUse($className, $alias = null) |
138
|
|
|
{ |
139
|
|
|
$className = trim($className, '\\'); |
140
|
|
|
try { |
141
|
|
|
$reflect = new \ReflectionClass($className); |
142
|
|
|
} catch (\ReflectionException $e) { |
143
|
|
|
return; |
144
|
|
|
} |
145
|
|
|
if ($reflect->getNamespaceName() !== $this->namespace && !isset($this->uses[$className])) { |
146
|
|
|
$this->uses[$className] = [ |
147
|
|
|
'class' => $className, |
148
|
|
|
'alias' => $alias |
149
|
|
|
]; |
150
|
|
|
} |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
protected function reflectClass() |
154
|
|
|
{ |
155
|
|
|
$reflectionClass = new \ReflectionClass($this->getClassName()); |
156
|
|
|
$this->shortClassName = $reflectionClass->getShortName(); |
157
|
|
|
$this->namespace = $reflectionClass->getNamespaceName(); |
158
|
|
|
$this->fileName = $reflectionClass->getFileName(); |
159
|
|
|
$this->abstract = $reflectionClass->isAbstract(); |
160
|
|
|
|
161
|
|
|
$methods = []; |
162
|
|
|
foreach ($reflectionClass->getMethods() as $method) { |
163
|
|
|
$methods[$method->getName()] = $method; |
164
|
|
|
} |
165
|
|
|
$this->methods = $methods; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
protected function reflectUseStatements() |
169
|
|
|
{ |
170
|
|
|
$content = file_get_contents($this->getFileName()); |
171
|
|
|
$tokens = token_get_all($content); |
172
|
|
|
for ($index = 0; isset($tokens[$index]); $index++) { |
173
|
|
|
if (!isset($tokens[$index][0])) { |
174
|
|
|
continue; |
175
|
|
|
} |
176
|
|
|
if (T_USE == $tokens[$index][0]) { |
177
|
|
|
$use = ''; |
178
|
|
|
$index += 2; |
179
|
|
|
while ($tokens[$index][0] != T_AS && isset($tokens[$index]) && is_array($tokens[$index])) { |
180
|
|
|
if ($tokens[$index][0] == T_WHITESPACE) { |
181
|
|
|
$index++; |
182
|
|
|
continue; |
183
|
|
|
} |
184
|
|
|
$use .= $tokens[$index++][1]; |
185
|
|
|
} |
186
|
|
|
$alias = null; |
187
|
|
View Code Duplication |
if ($tokens[$index][0] == T_AS) { |
|
|
|
|
188
|
|
|
$alias = ''; |
189
|
|
|
$index += 2; |
190
|
|
|
while (isset($tokens[$index]) && is_array($tokens[$index])) { |
191
|
|
|
$alias .= $tokens[$index++][1]; |
192
|
|
|
} |
193
|
|
|
} |
194
|
|
|
$this->addUse($use, $alias); |
195
|
|
|
} |
196
|
|
|
if (T_CLASS == $tokens[$index][0]) { |
197
|
|
|
break; |
198
|
|
|
} |
199
|
|
|
} |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
protected function reflectDocBlock() |
203
|
|
|
{ |
204
|
|
|
$reflectionClass = new \ReflectionClass($this->getClassName()); |
205
|
|
|
$docBlock = $reflectionClass->getDocComment(); |
206
|
|
|
|
207
|
|
|
$docBlockLines = explode(PHP_EOL, $docBlock); |
208
|
|
|
|
209
|
|
|
$lines = []; |
210
|
|
|
foreach ($docBlockLines as $line) { |
211
|
|
|
if ($this->skipDocBlockLine($line)) { |
212
|
|
|
continue; |
213
|
|
|
} elseif (preg_match($this->getMethodPattern(), $line, $matches)) { |
214
|
|
|
$this->reflectMagicMethods($matches); |
215
|
|
|
} else { |
216
|
|
|
$lines[] = trim(preg_replace('/^ \*/', '', $line)); |
217
|
|
|
} |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
$this->docBlockLines = $lines; |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
protected function reflectConstructorArgs() |
224
|
|
|
{ |
225
|
|
|
$reflectionClass = new \ReflectionClass($this->getClassName()); |
226
|
|
|
$constructor = $reflectionClass->getConstructor(); |
227
|
|
|
$parameters = $constructor->getParameters(); |
228
|
|
|
|
229
|
|
|
$args = []; |
230
|
|
|
foreach ($parameters as $parameter) { |
231
|
|
|
if ($parameter->isOptional()) { |
232
|
|
|
continue; |
233
|
|
|
} |
234
|
|
|
$typeClass = $parameter->getClass(); |
235
|
|
|
$typeName = ''; |
236
|
|
|
if (!is_null($typeClass)) { |
237
|
|
|
$typeName = $typeClass->getShortName(); |
238
|
|
|
} |
239
|
|
|
$args[] = trim($typeName . ' $' . $parameter->getName()); |
240
|
|
|
} |
241
|
|
|
$this->constructorArgs = $args; |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
/** |
245
|
|
|
* @param $type |
246
|
|
|
* @param $fieldName |
247
|
|
|
* @return string |
248
|
|
|
*/ |
249
|
|
|
protected function getMethodName($type, $fieldName) |
250
|
|
|
{ |
251
|
|
|
return $type . ucfirst($fieldName); |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
protected function reflectMagicMethods($matches) |
255
|
|
|
{ |
256
|
|
|
list(, $static, $returnTypeHint, $returnsReference, $name, $args, $shortDescription) = $matches; |
257
|
|
|
$type = $fieldName = ''; |
258
|
|
|
if (preg_match('~^(set|get)(.*)~', $name, $matches)) { |
259
|
|
|
$type = $matches[1]; |
260
|
|
|
$fieldName = $matches[2]; |
261
|
|
|
} |
262
|
|
|
$args = array_map('trim', explode(',', $args)); |
263
|
|
|
if (empty($type) || !$this->hasMethod($name)) { |
264
|
|
|
$this->magicGetSetMethods[$name] = [ |
265
|
|
|
'name' => $name, |
266
|
|
|
'returnTypeHint' => $returnTypeHint, |
267
|
|
|
'returnsReference' => $returnsReference, |
268
|
|
|
'type' => $type, |
269
|
|
|
'fieldName' => $fieldName, |
270
|
|
|
'args' => $args, |
271
|
|
|
'static' => ($static === 'static'), |
272
|
|
|
'shortDescription' => $shortDescription |
273
|
|
|
]; |
274
|
|
|
} |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
protected function skipDocBlockLine($line) |
278
|
|
|
{ |
279
|
|
|
return (strpos($line, '/**') === 0) |
280
|
|
|
|| (strpos($line, ' */') === 0) |
281
|
|
|
|| (strpos($line, '@package') > 0) |
282
|
|
|
|| (strpos($line, '* Class') > 0); |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
/** |
286
|
|
|
* @return string |
287
|
|
|
*/ |
288
|
|
|
protected function getMethodPattern() |
289
|
|
|
{ |
290
|
|
|
return '~@method (?:(static)\\s+)?(?:([\\w\\\\]+(?:\\|[\\w\\\\]+)*)\\s+)?' . |
291
|
|
|
'(&)?\\s*(\\w+)\\s*\\(\\s*(.*)\\s*\\)\\s*(.*|$)~s'; |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
/** |
295
|
|
|
* @return mixed |
296
|
|
|
*/ |
297
|
|
|
public function getClassName() |
298
|
|
|
{ |
299
|
|
|
return $this->className; |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
/** |
303
|
|
|
* @return mixed |
304
|
|
|
*/ |
305
|
|
|
public function getNamespace() |
306
|
|
|
{ |
307
|
|
|
return $this->namespace; |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* @return mixed |
312
|
|
|
*/ |
313
|
|
|
public function getUses() |
314
|
|
|
{ |
315
|
|
|
return $this->uses; |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
/** |
319
|
|
|
* @return mixed |
320
|
|
|
*/ |
321
|
|
|
public function getMethods() |
322
|
|
|
{ |
323
|
|
|
return $this->methods; |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
/** |
327
|
|
|
* @return mixed |
328
|
|
|
*/ |
329
|
|
|
public function getMagicGetSetMethods() |
330
|
|
|
{ |
331
|
|
|
return $this->magicGetSetMethods; |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
/** |
335
|
|
|
* @return array |
336
|
|
|
*/ |
337
|
|
|
public function getDocBlockLines() |
338
|
|
|
{ |
339
|
|
|
return $this->docBlockLines; |
340
|
|
|
} |
341
|
|
|
|
342
|
|
|
/** |
343
|
|
|
* @return mixed |
344
|
|
|
*/ |
345
|
|
|
public function getFileName() |
346
|
|
|
{ |
347
|
|
|
return $this->fileName; |
348
|
|
|
} |
349
|
|
|
|
350
|
|
|
/** |
351
|
|
|
* @return bool |
352
|
|
|
*/ |
353
|
|
|
public function isAbstract() |
354
|
|
|
{ |
355
|
|
|
return $this->abstract; |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
/** |
359
|
|
|
* @return mixed |
360
|
|
|
*/ |
361
|
|
|
public function getShortClassName() |
362
|
|
|
{ |
363
|
|
|
return $this->shortClassName; |
364
|
|
|
} |
365
|
|
|
} |
366
|
|
|
|
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.