ClassParser::run()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 10
nc 2
nop 0
dl 0
loc 18
rs 9.9332
c 0
b 0
f 0
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
The expression return $this->reflectionParent also could return the type boolean which is incompatible with the documented return type false|ReflectionClass.
Loading history...
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
Bug introduced by
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 ignore-type  annotation

250
        $docBlock        = $docBlockFactory->create(/** @scrutinizer ignore-type */ $classDocBlock);
Loading history...
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
Bug introduced by
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 ignore-call  annotation

323
        /** @scrutinizer ignore-call */ 
324
        $methodName = $tagInfos->getMethodName();
Loading history...
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