diff --git a/src/Hal/Metric/Class_/Complexity/CyclomaticComplexityVisitor.php b/src/Hal/Metric/Class_/Complexity/CyclomaticComplexityVisitor.php index 9eb87956..396b9789 100644 --- a/src/Hal/Metric/Class_/Complexity/CyclomaticComplexityVisitor.php +++ b/src/Hal/Metric/Class_/Complexity/CyclomaticComplexityVisitor.php @@ -50,6 +50,7 @@ public function leaveNode(Node $node) $class = $this->metrics->get($node->namespacedName->toString()); $ccn = 1; + $ccnByMethod = array(); foreach ($node->stmts as $stmt) { if ($stmt instanceof Stmt\ClassMethod) { @@ -88,11 +89,19 @@ public function leaveNode(Node $node) return $ccn; }; - $ccn += $cb($stmt); + $methodCcn = $cb($stmt); + + $ccn += $methodCcn; + $ccnByMethod[] = $methodCcn + 1; // each method by default is CCN 1 even if it's empty } } $class->set('ccn', $ccn); + + $class->set('ccnMethodMax', 0); + if (count($ccnByMethod)) { + $class->set('ccnMethodMax', max($ccnByMethod)); + } } } } diff --git a/src/Hal/Report/Html/template/complexity.php b/src/Hal/Report/Html/template/complexity.php index 8344d151..7b2c50a6 100644 --- a/src/Hal/Report/Html/template/complexity.php +++ b/src/Hal/Report/Html/template/complexity.php @@ -50,7 +50,8 @@ Class - Cyclomatic + Class cycl. + Max method cycl. has('junit')) { ?> Unit testsuites calling it Relative system complexity @@ -65,6 +66,7 @@ + has('junit')) { ?> diff --git a/src/Hal/Report/Html/template/oop.php b/src/Hal/Report/Html/template/oop.php index ef8fe073..4bae6c08 100644 --- a/src/Hal/Report/Html/template/oop.php +++ b/src/Hal/Report/Html/template/oop.php @@ -65,7 +65,8 @@ Class LCOM Volume - Cyclomatic + Class cycl. + Max method cycl. Bugs Difficulty @@ -77,6 +78,7 @@ + diff --git a/src/Hal/Violation/Class_/TooComplexCode.php b/src/Hal/Violation/Class_/TooComplexClassCode.php similarity index 78% rename from src/Hal/Violation/Class_/TooComplexCode.php rename to src/Hal/Violation/Class_/TooComplexClassCode.php index 06f3f704..4e13c710 100644 --- a/src/Hal/Violation/Class_/TooComplexCode.php +++ b/src/Hal/Violation/Class_/TooComplexClassCode.php @@ -6,7 +6,7 @@ use Hal\Metric\Metric; use Hal\Violation\Violation; -class TooComplexCode implements Violation +class TooComplexClassCode implements Violation { /** @@ -14,7 +14,7 @@ class TooComplexCode implements Violation */ public function getName() { - return 'Too complex code'; + return 'Too complex class code'; } /** @@ -51,10 +51,10 @@ public function getDescription() return <<metric->get('ccn')}) +* Algorithms are complex (Total cyclomatic complexity of class is {$this->metric->get('ccn')}) * Component uses {$this->metric->get('number_operators')} operators -Maybe you should delegate some code to another objects. +Maybe you should delegate some code to other objects. EOT; } diff --git a/src/Hal/Violation/Class_/TooComplexMethodCode.php b/src/Hal/Violation/Class_/TooComplexMethodCode.php new file mode 100644 index 00000000..f3308a45 --- /dev/null +++ b/src/Hal/Violation/Class_/TooComplexMethodCode.php @@ -0,0 +1,60 @@ +metric = $metric; + + if ($metric->get('ccnMethodMax') >= 8) { + $metric->get('violations')->add($this); + return; + } + + } + + /** + * @inheritdoc + */ + public function getLevel() + { + return Violation::ERROR; + } + + /** + * @inheritdoc + */ + public function getDescription() + { + return <<metric->get('ccnMethodMax')}) + +Maybe you should delegate some code to other objects or split complex method. +EOT; + + } +} diff --git a/src/Hal/Violation/ViolationParser.php b/src/Hal/Violation/ViolationParser.php index bfb34cdd..567b1d61 100644 --- a/src/Hal/Violation/ViolationParser.php +++ b/src/Hal/Violation/ViolationParser.php @@ -16,7 +16,8 @@ public function apply(Metrics $metrics) $violations = [ new Class_\Blob(), - new Class_\TooComplexCode(), + new Class_\TooComplexClassCode(), + new Class_\TooComplexMethodCode(), new Class_\ProbablyBugged(), new Class_\TooLong(), new Class_\TooDependent(), diff --git a/tests/Metric/Class_/Complexity/CyclomaticComplexityVisitorTest.php b/tests/Metric/Class_/Complexity/CyclomaticComplexityVisitorTest.php index e87bcad7..8778ea5b 100644 --- a/tests/Metric/Class_/Complexity/CyclomaticComplexityVisitorTest.php +++ b/tests/Metric/Class_/Complexity/CyclomaticComplexityVisitorTest.php @@ -11,9 +11,9 @@ class CyclomaticComplexityVisitorTest extends \PHPUnit_Framework_TestCase { /** - * @dataProvider provideExamples + * @dataProvider provideExamplesForClasses */ - public function testLackOfCohesionOfMethodsIsWellCalculated($example, $classname, $expected) + public function testCyclomaticComplexityOfClassesIsWellCalculated($example, $classname, $expectedCcn) { $metrics = new Metrics(); @@ -27,14 +27,42 @@ public function testLackOfCohesionOfMethodsIsWellCalculated($example, $classname $stmts = $parser->parse($code); $traverser->traverse($stmts); - $this->assertSame($expected, $metrics->get($classname)->get('ccn')); + $this->assertSame($expectedCcn, $metrics->get($classname)->get('ccn')); } - public function provideExamples() + /** + * @dataProvider provideExamplesForMethods + */ + public function testCyclomaticComplexityOfMethodsIsWellCalculated($example, $classname, $expectedCcnMethodMax) + { + $metrics = new Metrics(); + + $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); + $traverser = new \PhpParser\NodeTraverser(); + $traverser->addVisitor(new \PhpParser\NodeVisitor\NameResolver()); + $traverser->addVisitor(new ClassEnumVisitor($metrics)); + $traverser->addVisitor(new CyclomaticComplexityVisitor($metrics)); + + $code = file_get_contents($example); + $stmts = $parser->parse($code); + $traverser->traverse($stmts); + + $this->assertSame($expectedCcnMethodMax, $metrics->get($classname)->get('ccnMethodMax')); + } + + public function provideExamplesForClasses() + { + return [ + [ __DIR__.'/../../examples/cyclomatic1.php', 'A', 4], + [ __DIR__.'/../../examples/cyclomatic1.php', 'B', 5], + ]; + } + + public function provideExamplesForMethods() { return [ [ __DIR__.'/../../examples/cyclomatic1.php', 'A', 3], - [ __DIR__.'/../../examples/cyclomatic1.php', 'B', 3], + [ __DIR__.'/../../examples/cyclomatic1.php', 'B', 5], ]; } diff --git a/tests/Metric/examples/cyclomatic1.php b/tests/Metric/examples/cyclomatic1.php index 9668b206..b7ed03bf 100644 --- a/tests/Metric/examples/cyclomatic1.php +++ b/tests/Metric/examples/cyclomatic1.php @@ -1,6 +1,6 @@