|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace Inspector\Analysis\Complexity; |
|
4
|
|
|
|
|
5
|
|
|
use PhpParser\Node; |
|
6
|
|
|
use PhpParser\Node\Stmt\If_; |
|
7
|
|
|
use PhpParser\Node\Stmt\Do_; |
|
8
|
|
|
use PhpParser\Node\Stmt\For_; |
|
9
|
|
|
use PhpParser\Node\Stmt\Case_; |
|
10
|
|
|
use PhpParser\Node\Stmt\While_; |
|
11
|
|
|
use PhpParser\Node\Stmt\Catch_; |
|
12
|
|
|
use PhpParser\Node\Expr\Closure; |
|
13
|
|
|
use PhpParser\Node\Stmt\ElseIf_; |
|
14
|
|
|
use PhpParser\Node\Stmt\Foreach_; |
|
15
|
|
|
use PhpParser\Node\Stmt\Function_; |
|
16
|
|
|
use PhpParser\Node\Expr\BooleanNot; |
|
17
|
|
|
use PhpParser\Node\Stmt\ClassMethod; |
|
18
|
|
|
use PhpParser\Node\Expr\BinaryOp\BooleanOr; |
|
19
|
|
|
use PhpParser\Node\Expr\BinaryOp\LogicalOr; |
|
20
|
|
|
use PhpParser\Node\Expr\BinaryOp\BooleanAnd; |
|
21
|
|
|
use PhpParser\Node\Expr\BinaryOp\LogicalAnd; |
|
22
|
|
|
use PhpParser\Node\Expr\BinaryOp\LogicalXor; |
|
23
|
|
|
|
|
24
|
|
|
/** |
|
25
|
|
|
* Class responsible for checking the Cylomatic Complexity Number (CCN) value of a node. |
|
26
|
|
|
* |
|
27
|
|
|
* @author Kabir Baidhya |
|
28
|
|
|
*/ |
|
29
|
|
|
class CCNChecker implements ValueCheckerInterface |
|
30
|
|
|
{ |
|
31
|
|
|
|
|
32
|
|
|
/** |
|
33
|
|
|
* Checks the cyclomatic complexity value of a given node |
|
34
|
|
|
* |
|
35
|
|
|
* @param Node $node |
|
36
|
|
|
* @return int |
|
37
|
|
|
*/ |
|
38
|
|
|
public function getValue(Node $node) |
|
39
|
|
|
{ |
|
40
|
|
|
$complexityAdders = $this->getComplexityAdders(); |
|
41
|
|
|
|
|
42
|
|
|
foreach ($complexityAdders as $nodeClass) { |
|
43
|
|
|
if ($this->nodePassesFor($nodeClass, $node)) { |
|
44
|
|
|
return 1; |
|
45
|
|
|
} |
|
46
|
|
|
} |
|
47
|
|
|
|
|
48
|
|
|
return 0; |
|
49
|
|
|
} |
|
50
|
|
|
|
|
51
|
|
|
/** |
|
52
|
|
|
* Checks to make sure the node is an instance of complexity adder node classes, |
|
53
|
|
|
* and also passes any extra conditions (if any) |
|
54
|
|
|
* |
|
55
|
|
|
* @param array $nodeClass |
|
56
|
|
|
* @param Node $node |
|
57
|
|
|
* @return bool |
|
58
|
|
|
*/ |
|
59
|
|
|
protected function nodePassesFor(array $nodeClass, Node $node) |
|
60
|
|
|
{ |
|
61
|
|
|
$class = array_shift($nodeClass); |
|
62
|
|
|
$callbackCondition = array_shift($nodeClass); |
|
63
|
|
|
|
|
64
|
|
|
// If node is an instance of a complexity adder class |
|
65
|
|
|
if (is_a($node, $class)) { |
|
66
|
|
|
|
|
67
|
|
|
if (!is_callable($callbackCondition)) { |
|
68
|
|
|
// if it doesn't have any extra callback condition then |
|
69
|
|
|
return true; |
|
70
|
|
|
} elseif ($callbackCondition($node)) { |
|
71
|
|
|
// if it has extra callback condition then that condition also |
|
72
|
|
|
// must hold true |
|
73
|
|
|
return true; |
|
74
|
|
|
} |
|
75
|
|
|
} |
|
76
|
|
|
|
|
77
|
|
|
return false; |
|
78
|
|
|
} |
|
79
|
|
|
|
|
80
|
|
|
/** |
|
81
|
|
|
* Get the class names of all the nodes which adds complexity in the code |
|
82
|
|
|
* |
|
83
|
|
|
* @return array |
|
84
|
|
|
*/ |
|
85
|
|
|
protected function getComplexityAdders() |
|
86
|
|
|
{ |
|
87
|
|
|
return [ |
|
88
|
|
|
// the control structures |
|
89
|
|
|
[If_::class], |
|
90
|
|
|
[ElseIf_::class], |
|
91
|
|
|
[For_::class], |
|
92
|
|
|
[Foreach_::class], |
|
93
|
|
|
[While_::class], |
|
94
|
|
|
[Do_::class], |
|
95
|
|
|
[Catch_::class], |
|
96
|
|
|
[ |
|
97
|
|
|
Case_::class, |
|
98
|
|
|
function (Node $node) { |
|
99
|
|
|
// Return true for all switch 'case' statements |
|
|
|
|
|
|
100
|
|
|
// except the 'default' case |
|
101
|
|
|
return !empty($node->cond); |
|
|
|
|
|
|
102
|
|
|
} |
|
103
|
|
|
], |
|
104
|
|
|
// class methods, functions or closures(anonymous functions) |
|
105
|
|
|
[ClassMethod::class], |
|
106
|
|
|
[Function_::class], |
|
107
|
|
|
[Closure::class], |
|
108
|
|
|
// logical operators |
|
109
|
|
|
[LogicalAnd::class], |
|
110
|
|
|
[BooleanAnd::class], |
|
111
|
|
|
[LogicalOr::class], |
|
112
|
|
|
[BooleanOr::class], |
|
113
|
|
|
[BooleanNot::class], |
|
114
|
|
|
[LogicalXor::class], |
|
115
|
|
|
]; |
|
116
|
|
|
} |
|
117
|
|
|
} |
|
118
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.