1 | <?php |
||||||
2 | |||||||
3 | namespace bultonFr\MethodsHeaderGenerator; |
||||||
4 | |||||||
5 | use \phpDocumentor\Reflection\DocBlockFactory; |
||||||
6 | use \phpDocumentor\Reflection\DocBlock\Tag; |
||||||
7 | |||||||
8 | /** |
||||||
9 | * Parse a class and find all methods into |
||||||
10 | */ |
||||||
11 | class ClassParser |
||||||
12 | { |
||||||
13 | /** |
||||||
14 | * @var string $className The name (with namespace) of the class to parse |
||||||
15 | */ |
||||||
16 | protected $className = ''; |
||||||
17 | |||||||
18 | /** |
||||||
19 | * @var \bultonFr\MethodsHeaderGenerator\ProjectParser|null $projectParser The |
||||||
20 | * instance of the ProjectParser class who instanciate this class |
||||||
21 | */ |
||||||
22 | protected $projectParser; |
||||||
23 | |||||||
24 | /** |
||||||
25 | * @var \ReflectionClass $reflection The reflection instance who describe |
||||||
26 | * the class |
||||||
27 | */ |
||||||
28 | protected $reflection; |
||||||
29 | |||||||
30 | /** |
||||||
31 | * @var \ReflectionClass|false $reflectionParent The reflection instance of |
||||||
32 | * the parent class. False if no parent class. |
||||||
33 | */ |
||||||
34 | protected $reflectionParent; |
||||||
35 | |||||||
36 | /** |
||||||
37 | * @var \bultonFr\MethodsHeaderGenerator\ClassParser|null $pârserParent The |
||||||
38 | * parser instance of the parent class |
||||||
39 | */ |
||||||
40 | protected $parserParent; |
||||||
41 | |||||||
42 | /** |
||||||
43 | * @var \bultonFr\MethodsHeaderGenerator\ProjectParser[] $parserInterface |
||||||
44 | * parsers instances for each interface implemented by the class |
||||||
45 | */ |
||||||
46 | protected $parserInterfaces = []; |
||||||
47 | |||||||
48 | /** |
||||||
49 | * @var \phpDocumentor\Reflection\DocBlock\Tags\Method[] $dynamicMethods |
||||||
50 | * All methods find into class docBlock with @method |
||||||
51 | */ |
||||||
52 | protected $dynamicMethods = []; |
||||||
53 | |||||||
54 | /** |
||||||
55 | * @var \bultonFr\MethodsHeaderGenerator\MethodParser[] $methods All methods |
||||||
56 | * find into the class who are parsed. |
||||||
57 | */ |
||||||
58 | protected $methods = []; |
||||||
59 | |||||||
60 | /** |
||||||
61 | * @var int $runStatus The current status of the run method. |
||||||
62 | * * 0 : run() has never be called |
||||||
63 | * * 1 : run() is currently called |
||||||
64 | * * 2 : run() has been called |
||||||
65 | * It's a security to not re-call run when we are already on it. When a |
||||||
66 | * class have a dependency loop (should not be existing). |
||||||
67 | */ |
||||||
68 | protected $runStatus = 0; |
||||||
69 | |||||||
70 | /** |
||||||
71 | * Construct |
||||||
72 | * Instanciate ReflectionClass for the asked class and get the |
||||||
73 | * ReflectionClass for parent class too. |
||||||
74 | * |
||||||
75 | * @param string $className |
||||||
76 | * @param \bultonFr\MethodsHeaderGenerator\ProjectParser|null $projectParser |
||||||
77 | * The instance of ProjectParser If the class is instanciate from him, |
||||||
78 | * else null. |
||||||
79 | * It's used to improve performance (not re-parse a class) |
||||||
80 | */ |
||||||
81 | public function __construct($className, $projectParser=null) |
||||||
82 | { |
||||||
83 | $this->className = $className; |
||||||
84 | $this->projectParser = $projectParser; |
||||||
85 | $this->reflection = new \ReflectionClass($this->className); |
||||||
86 | |||||||
87 | $this->reflectionParent = $this->reflection->getParentClass(); |
||||||
88 | } |
||||||
89 | |||||||
90 | /** |
||||||
91 | * Getter accessor to property className |
||||||
92 | * |
||||||
93 | * @return string |
||||||
94 | */ |
||||||
95 | public function getClassName() |
||||||
96 | { |
||||||
97 | return $this->className; |
||||||
98 | } |
||||||
99 | |||||||
100 | /** |
||||||
101 | * Getter accessor to property projectParser |
||||||
102 | * |
||||||
103 | * @return \bultonFr\MethodsHeaderGenerator\ProjectParser|null |
||||||
104 | */ |
||||||
105 | public function getProjectParser() |
||||||
106 | { |
||||||
107 | return $this->projectParser; |
||||||
108 | } |
||||||
109 | |||||||
110 | /** |
||||||
111 | * Getter accessor to property reflection |
||||||
112 | * |
||||||
113 | * @return \ReflectionClass |
||||||
114 | */ |
||||||
115 | public function getReflection() |
||||||
116 | { |
||||||
117 | return $this->reflection; |
||||||
118 | } |
||||||
119 | |||||||
120 | /** |
||||||
121 | * Getter accessor to property reflectionParent |
||||||
122 | * |
||||||
123 | * @return \ReflectionClass|false |
||||||
124 | */ |
||||||
125 | public function getReflectionParent() |
||||||
126 | { |
||||||
127 | return $this->reflectionParent; |
||||||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
![]() |
|||||||
128 | } |
||||||
129 | |||||||
130 | /** |
||||||
131 | * Getter accessor to property parserParent |
||||||
132 | * |
||||||
133 | * @return \bultonFr\MethodsHeaderGenerator\ClassParser|null |
||||||
134 | */ |
||||||
135 | public function getParserParent() |
||||||
136 | { |
||||||
137 | return $this->parserParent; |
||||||
138 | } |
||||||
139 | |||||||
140 | /** |
||||||
141 | * Getter accessor to property parserInterfaces |
||||||
142 | * |
||||||
143 | * @return \bultonFr\MethodsHeaderGenerator\ProjectParser[] |
||||||
144 | */ |
||||||
145 | public function getParserInterfaces() |
||||||
146 | { |
||||||
147 | return $this->parserInterfaces; |
||||||
148 | } |
||||||
149 | |||||||
150 | /** |
||||||
151 | * Getter accessor to property dynamicMethods |
||||||
152 | * |
||||||
153 | * @return \phpDocumentor\Reflection\DocBlock\Tags\Method[] |
||||||
154 | */ |
||||||
155 | public function getDynamicMethods() |
||||||
156 | { |
||||||
157 | return $this->dynamicMethods; |
||||||
158 | } |
||||||
159 | |||||||
160 | /** |
||||||
161 | * Getter accessor to property methods |
||||||
162 | * |
||||||
163 | * @return \bultonFr\MethodsHeaderGenerator\MethodParser[] |
||||||
164 | */ |
||||||
165 | public function getMethods() |
||||||
166 | { |
||||||
167 | return $this->methods; |
||||||
168 | } |
||||||
169 | |||||||
170 | /** |
||||||
171 | * Getter accessor to property runStatus |
||||||
172 | * |
||||||
173 | * @return int |
||||||
174 | */ |
||||||
175 | public function getRunStatus() |
||||||
176 | { |
||||||
177 | return $this->runStatus; |
||||||
178 | } |
||||||
179 | |||||||
180 | /** |
||||||
181 | * Run the parser to analyse class methods |
||||||
182 | * |
||||||
183 | * @throws \Exception |
||||||
184 | * |
||||||
185 | * @return void |
||||||
186 | */ |
||||||
187 | public function run() |
||||||
188 | { |
||||||
189 | if ($this->runStatus === 1) { |
||||||
190 | throw new \Exception( |
||||||
191 | 'You are already on the run method.' |
||||||
192 | .' Maybe you have an dependency loop.' |
||||||
193 | ); |
||||||
194 | } |
||||||
195 | |||||||
196 | $this->runStatus = 1; |
||||||
197 | |||||||
198 | $this->instantiateParentParser(); |
||||||
199 | $this->instantiateInterfacesParser(); |
||||||
200 | |||||||
201 | $this->analyseDocBlock(); |
||||||
202 | $this->analyseMethods(); |
||||||
203 | |||||||
204 | $this->runStatus = 2; |
||||||
205 | } |
||||||
206 | |||||||
207 | /** |
||||||
208 | * Instantiate de parser for the parent class |
||||||
209 | * |
||||||
210 | * @return void |
||||||
211 | */ |
||||||
212 | protected function instantiateParentParser() |
||||||
213 | { |
||||||
214 | if ($this->reflectionParent === false) { |
||||||
215 | return; |
||||||
216 | } |
||||||
217 | |||||||
218 | $this->parserParent = $this->newParser($this->reflectionParent->name); |
||||||
219 | $this->parserParent->run(); |
||||||
220 | } |
||||||
221 | |||||||
222 | /** |
||||||
223 | * Instantiate the parser for all interfaces |
||||||
224 | * |
||||||
225 | * @return void |
||||||
226 | */ |
||||||
227 | protected function instantiateInterfacesParser() |
||||||
228 | { |
||||||
229 | foreach ($this->reflection->getInterfaces() as $interface) { |
||||||
230 | $interfaceParser = $this->newParser($interface->name); |
||||||
231 | $interfaceParser->run(); |
||||||
232 | |||||||
233 | $this->parserInterfaces[] = $interfaceParser; |
||||||
234 | } |
||||||
235 | } |
||||||
236 | |||||||
237 | /** |
||||||
238 | * Analyse all tags into the class docBlock. |
||||||
239 | * |
||||||
240 | * @return void |
||||||
241 | */ |
||||||
242 | protected function analyseDocBlock() |
||||||
243 | { |
||||||
244 | $classDocBlock = $this->reflection->getDocComment(); |
||||||
245 | if ($classDocBlock === false) { |
||||||
246 | return; |
||||||
247 | } |
||||||
248 | |||||||
249 | $docBlockFactory = DocBlockFactory::createInstance(); |
||||||
250 | $docBlock = $docBlockFactory->create($classDocBlock); |
||||||
0 ignored issues
–
show
It seems like
$classDocBlock can also be of type true ; however, parameter $docblock of phpDocumentor\Reflection\DocBlockFactory::create() does only seem to accept object|string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
251 | $tagsList = $docBlock->getTags(); |
||||||
252 | |||||||
253 | foreach ($tagsList as $tagInfos) { |
||||||
254 | $this->analyseDocBlockTag($tagInfos); |
||||||
255 | } |
||||||
256 | } |
||||||
257 | |||||||
258 | /** |
||||||
259 | * Analyse all methods find into the class by the reflection class |
||||||
260 | * |
||||||
261 | * @return void |
||||||
262 | */ |
||||||
263 | protected function analyseMethods() |
||||||
264 | { |
||||||
265 | $methods = $this->reflection->getMethods(); |
||||||
266 | |||||||
267 | foreach ($methods as $method) { |
||||||
268 | $methodParser = new MethodParser($this, $method); |
||||||
269 | $methodParser->run(); |
||||||
270 | |||||||
271 | $this->methods[$method->name] = $methodParser; |
||||||
272 | } |
||||||
273 | } |
||||||
274 | |||||||
275 | /** |
||||||
276 | * Instantiate a new ClassParser object. |
||||||
277 | * Usefull if we use ProjectParser. In this case, we check ProjectParser |
||||||
278 | * before to know if the class to parse has not been already parsed. If the |
||||||
279 | * class was already parsed, we use it and not re-parse it again. |
||||||
280 | * |
||||||
281 | * @param string $className The class name to parse |
||||||
282 | * |
||||||
283 | * @return \bultonFr\MethodsHeaderGenerator\ClassParser |
||||||
284 | */ |
||||||
285 | protected function newParser($className) |
||||||
286 | { |
||||||
287 | if ($this->projectParser === null) { |
||||||
288 | return new ClassParser($className); |
||||||
289 | } |
||||||
290 | |||||||
291 | if ($this->projectParser->hasClasses($className)) { |
||||||
292 | return $this->projectParser->getClassesByName($className); |
||||||
293 | } |
||||||
294 | |||||||
295 | $parser = new ClassParser($className); |
||||||
296 | $this->projectParser->addToClasses($parser); |
||||||
297 | return $parser; |
||||||
298 | } |
||||||
299 | |||||||
300 | /** |
||||||
301 | * Analyse all tag find into the class docBlock |
||||||
302 | * |
||||||
303 | * @param \phpDocumentor\Reflection\DocBlock\Tag $tagInfos The tag instance |
||||||
304 | * |
||||||
305 | * @return void |
||||||
306 | */ |
||||||
307 | protected function analyseDocBlockTag(Tag $tagInfos) |
||||||
308 | { |
||||||
309 | if ($tagInfos->getName() === 'method') { |
||||||
310 | $this->parseTagMethod($tagInfos); |
||||||
311 | } |
||||||
312 | } |
||||||
313 | |||||||
314 | /** |
||||||
315 | * Parse the tag @method and get datas from it |
||||||
316 | * |
||||||
317 | * @param \phpDocumentor\Reflection\DocBlock\Tag $tagInfos The tag instance |
||||||
318 | * |
||||||
319 | * @return void |
||||||
320 | */ |
||||||
321 | protected function parseTagMethod(Tag $tagInfos) |
||||||
322 | { |
||||||
323 | $methodName = $tagInfos->getMethodName(); |
||||||
0 ignored issues
–
show
The method
getMethodName() does not exist on phpDocumentor\Reflection\DocBlock\Tag . It seems like you code against a sub-type of phpDocumentor\Reflection\DocBlock\Tag such as phpDocumentor\Reflection\DocBlock\Tags\Method .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
324 | |||||||
325 | $this->dynamicMethods[$methodName] = $tagInfos->__toString(); |
||||||
326 | } |
||||||
327 | |||||||
328 | /** |
||||||
329 | * PHP Magic method __toString |
||||||
330 | * Called when a class is treated like a string. |
||||||
331 | * |
||||||
332 | * Display class name, parent class name and interfaces names. After show |
||||||
333 | * all methods find into docblock with tag @method, and show all methods |
||||||
334 | * declared into the class. |
||||||
335 | * |
||||||
336 | * @link http://php.net/manual/en/language.oop5.magic.php#object.tostring |
||||||
337 | * |
||||||
338 | * @return string |
||||||
339 | */ |
||||||
340 | public function __toString() |
||||||
341 | { |
||||||
342 | $str = $this->obtainClassNameInfos()."\n"; |
||||||
343 | |||||||
344 | //Sort methods by name |
||||||
345 | ksort($this->dynamicMethods); |
||||||
346 | ksort($this->methods); |
||||||
347 | |||||||
348 | foreach ($this->dynamicMethods as $methodStr) { |
||||||
349 | $str .= $methodStr."\n"; |
||||||
350 | } |
||||||
351 | |||||||
352 | foreach ($this->methods as $method) { |
||||||
353 | //Not display parent class methods who are not be override |
||||||
354 | if ($method->getReflection()->class !== $this->reflection->name) { |
||||||
355 | continue; |
||||||
356 | } |
||||||
357 | |||||||
358 | $str .= $method."\n"; |
||||||
359 | } |
||||||
360 | |||||||
361 | return $str; |
||||||
362 | } |
||||||
363 | |||||||
364 | /** |
||||||
365 | * Obtain class name with parent class name and interfaces names |
||||||
366 | * |
||||||
367 | * @return string |
||||||
368 | */ |
||||||
369 | public function obtainClassNameInfos() |
||||||
370 | { |
||||||
371 | $str = $this->className; |
||||||
372 | |||||||
373 | if ($this->reflectionParent !== false) { |
||||||
374 | $str .= ' extends '.$this->reflectionParent->name; |
||||||
375 | } |
||||||
376 | |||||||
377 | $interfaces = $this->reflection->getInterfaces(); |
||||||
378 | if (!empty($interfaces)) { |
||||||
379 | $str .= ' implements '; |
||||||
380 | |||||||
381 | foreach ($interfaces as $interfaceIndex => $interfaceInfos) { |
||||||
382 | if ($interfaceIndex > 0) { |
||||||
383 | $str .= ', '; |
||||||
384 | } |
||||||
385 | |||||||
386 | $str .= $interfaceInfos->name; |
||||||
387 | } |
||||||
388 | } |
||||||
389 | |||||||
390 | return $str; |
||||||
391 | } |
||||||
392 | } |
||||||
393 |