Skip to content

Commit 49ddb60

Browse files
author
Jean-François Lépine
committed
Merge pull request #6 from Halleck45/HenryAndKafura
Coupling
2 parents 783ef92 + 8387243 commit 49ddb60

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1707
-26
lines changed

README.md

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,32 @@ Will output:
1717

1818
## Bubbles chart and complete report
1919

20-
If you want to get the HTMl report (with charts):
20+
If you want to get the summary HTML report (with charts):
2121

2222
php ./bin/metrics.php --summary-html=/path/of/your/choice.html <folder or filename>
2323

24+
You can change the depth of the summary report with the `--level=<value>` option.
25+
26+
If you want to have a detailled view (file by file):
27+
28+
php ./bin/metrics.php --details-html=/path/of/your/choice.html <folder or filename>
29+
30+
## Informations about OOP model
31+
32+
If you want to get informations about OOP model (coupling, instability...), you should pass the `--oop` parameter:
33+
34+
php ./bin/metrics.php --oop <folder or filename>
35+
36+
Remember that this feature parse all files, extract declared classes, dependencies of each method... and is really *very slow*.
37+
38+
## Jenkins and PIC integration
39+
40+
You can easily export resut to XML with the `--summary-xml` option:
41+
42+
php ./bin/metrics.php --summary-xml=/path/of/your/choice.xml <folder or filename>
43+
44+
You will find a tutorial to [integrate PhpMetrics report to Jenkins here](blog.lepine.pro/industrialisation/indice-de-maintenabilite-dun-projet-php-et-jenkins) (in French).
45+
2446
### Read report
2547

2648
+ Each file is symbolized by a circle
@@ -86,6 +108,17 @@ Comment weight represents the impact of documentation in code.
86108
perCM = commentLoc / loc
87109
MIcw = 50 * sin(sqrt(2.4 * perCM))
88110

111+
## Coupling and instability
112+
113+
Coupling use two metrics:
114+
115+
+ Afferent coupling (CA): number of classes that your classes affects
116+
+ Efferent coupling (CE) : number of classes used by your class
117+
118+
Instability concerns the risk of your class, according coupling:
119+
120+
I = CE / (CA + CE)
121+
89122
# Use it in code
90123

91124
## Halstead
@@ -113,6 +146,47 @@ $maintenability = new \MaintenabilityIndex\MaintenabilityIndex;
113146
$rMaintenability = $maintenability->calculate($rHalstead, $rLoc);
114147
var_dump($rMaintenability);
115148
```
149+
150+
## OOP Extractor
151+
152+
Extracts OOP model of files, and map classes and files:
153+
154+
```php
155+
$extractor = new Extractor();
156+
$rOOP = $extractor->extract($filename);
157+
var_dump($rOOP);
158+
```
159+
160+
## Coupling
161+
162+
Calculate coupling.
163+
164+
```php
165+
// build class map
166+
$classMap = new ClassMap;
167+
foreach($files as $filename) {
168+
$extractor = new Extractor();
169+
$rOOP = $extractor->extract($filename);
170+
$classMap->push($filename, $rOOP);
171+
}
172+
// coupling
173+
$coupling = new Coupling;
174+
$couplingMap = $coupling->calculate($classMap);
175+
$rCoupling = $couplingMap->get('\My\Namespace\ClassName');
176+
var_dump($rCoupling);
177+
```
178+
179+
If you want to work with files instead of classes:
180+
181+
```php
182+
// reuse code above, then
183+
$fileCoupling = new FileCoupling($classMap, $couplingMap);
184+
$rCoupling = $fileCoupling->calculate('/path/to/file.php');
185+
var_dump($rCoupling);
186+
```
187+
188+
189+
116190
# Contribute
117191

118192
In order to run unit tests, please install dev dependencies:

bin/metrics.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ function includeIfExists($file)
1818
);
1919
}
2020

21-
$app = new Hal\Console\PhpMetricsApplication('PhpMetrics, by Jean-François Lépine (https://twitter.com/Halleck45)', '0.0.3');
21+
$app = new Hal\Console\PhpMetricsApplication('PhpMetrics, by Jean-François Lépine (https://twitter.com/Halleck45)', '0.0.4');
2222
$app->run();

build.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
$phar->addFromString($path, file_get_contents($file));
2020
}
2121

22-
$phar->addFromString('init.php', file_get_contents(__DIR__.'/init.php'));
23-
2422
$phar->setStub(<<<STUB
2523
<?php
2624
@@ -36,7 +34,7 @@
3634
Phar::mapPhar('metrics.phar');
3735
3836
require_once 'phar://metrics.phar/vendor/autoload.php';
39-
\$app = new Hal\Console\PhpMetricsApplication('PhpMetrics, by Jean-François Lépine (https://twitter.com/Halleck45)', '0.0.3');
37+
\$app = new Hal\Console\PhpMetricsApplication('PhpMetrics, by Jean-François Lépine (https://twitter.com/Halleck45)', '0.0.4');
4038
\$app->run();
4139
4240
__HALT_COMPILER();

build/metrics.phar

897 KB
Binary file not shown.

src/Hal/Bounds/Result/BoundsResult.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ public function getSum($key) {
9797
return isset($this->sum[$key]) ? $this->sum[$key] : null;
9898
}
9999

100+
/**
101+
* @inheritdoc
102+
*/
103+
public function has($key) {
104+
return isset($this->sum[$key]);
105+
}
106+
100107
/**
101108
* @inheritdoc
102109
*/

src/Hal/Bounds/Result/DirectoryResult.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,13 @@ public function asArray() {
8888
));
8989
}
9090

91+
/**
92+
* @inheritdoc
93+
*/
94+
public function has($key) {
95+
return $this->bounds->has($key);
96+
}
97+
9198
/**
9299
* @inheritdoc
93100
*/

src/Hal/Bounds/Result/ResultInterface.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,12 @@ public function getSum($key);
5757
* @return null|float
5858
*/
5959
public function get($type, $key);
60+
61+
/**
62+
* Has key ?
63+
*
64+
* @param $key
65+
* @return mixed
66+
*/
67+
public function has($key);
6068
}

src/Hal/Command/Job/DoAnalyze.php

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@
88
*/
99

1010
namespace Hal\Command\Job;
11+
use Hal\Coupling\Coupling;
12+
use Hal\Coupling\FileCoupling;
1113
use Hal\File\Finder;
14+
use Hal\OOP\Extractor\ClassMap;
15+
use Hal\OOP\Extractor\Extractor;
16+
use Hal\OOP\Extractor\Result;
1217
use Hal\Result\ResultCollection;
1318
use Symfony\Component\Console\Helper\ProgressHelper;
1419
use Symfony\Component\Console\Output\OutputInterface;
@@ -43,18 +48,27 @@ class DoAnalyze implements JobInterface
4348
*/
4449
private $finder;
4550

51+
/**
52+
* do OOP analyze ?
53+
*
54+
* @var bool
55+
*/
56+
private $withOOP;
57+
4658
/**
4759
* Constructor
4860
*
4961
* @param OutputInterface $output
5062
* @param Finder $finder
5163
* @param string $path
64+
* @param bool $withOOP
5265
*/
53-
function __construct(OutputInterface $output, Finder $finder, $path)
66+
function __construct(OutputInterface $output, Finder $finder, $path, $withOOP)
5467
{
5568
$this->output = $output;
5669
$this->finder = $finder;
5770
$this->path = $path;
71+
$this->withOOP = $withOOP;
5872
}
5973

6074
/**
@@ -71,32 +85,62 @@ public function execute(ResultCollection $collection) {
7185
$progress = new ProgressHelper();
7286
$progress->start($this->output, sizeof($files, COUNT_NORMAL));
7387

88+
// class map
89+
$classMap = new ClassMap();
90+
91+
$halstead = new \Hal\Halstead\Halstead(new \Hal\Token\TokenType());
92+
$maintenability = new \Hal\MaintenabilityIndex\MaintenabilityIndex;
93+
$loc = new \Hal\Loc\Loc();
94+
$extractor = new Extractor();
95+
7496
foreach($files as $filename) {
7597

7698
$progress->advance();
7799

78-
// calculates
79-
$halstead = new \Hal\Halstead\Halstead(new \Hal\Token\TokenType());
100+
// HALSTEAD
80101
$rHalstead = $halstead->calculate($filename);
81102

82-
$loc = new \Hal\Loc\Loc();
103+
// LOC
83104
$rLoc = $loc->calculate($filename);
84105

85-
$maintenability = new \Hal\MaintenabilityIndex\MaintenabilityIndex;
106+
// Maintenability Index
86107
$rMaintenability = $maintenability->calculate($rHalstead, $rLoc);
87108

109+
88110
// formats
89-
$resultSet = new \Hal\Result\ResultSet(basename($this->path) . str_replace($this->path, '', $filename));
111+
$resultSet = new \Hal\Result\ResultSet($filename);
90112
$resultSet
91113
->setLoc($rLoc)
92114
->setHalstead($rHalstead)
93115
->setMaintenabilityIndex($rMaintenability);
94116

117+
if($this->withOOP) {
118+
// OOP
119+
$rOOP = $extractor->extract($filename);
120+
$classMap->push($filename, $rOOP);
121+
$resultSet->setOOP($rOOP);
122+
}
123+
124+
95125
$collection->push($resultSet);
96126
}
97-
98127
$progress->clear();
99128
$progress->finish();
129+
130+
if($this->withOOP) {
131+
// COUPLING (should be done after parsing files)
132+
$this->output->writeln('Analyzing coupling. This will take few minutes...');
133+
134+
$coupling = new Coupling();
135+
$couplingMap = $coupling->calculate($classMap);
136+
137+
// link between coupling and files
138+
$fileCoupling = new FileCoupling($classMap, $couplingMap);
139+
foreach($files as $filename) {
140+
$rCoupling = $fileCoupling->calculate($filename);
141+
$collection->get($filename)->setCoupling($rCoupling);
142+
}
143+
}
100144
}
101145

102146
}

src/Hal/Command/RunMetricsCommand.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ protected function configure()
5757
->addOption(
5858
'extensions', null, InputOption::VALUE_REQUIRED, 'Regex of extensions to include', 'php'
5959
)
60+
->addOption(
61+
'oop', null, InputOption::VALUE_NONE, 'If provided, tool will extract informations about OOP model'
62+
)
6063
;
6164
}
6265

@@ -85,7 +88,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
8588
// jobs queue planning
8689
$queue = new Queue();
8790
$queue
88-
->push(new DoAnalyze($output, $finder, $input->getArgument('path')))
91+
->push(new DoAnalyze($output, $finder, $input->getArgument('path'), $input->getOption('oop')))
8992
->push(new SearchBounds($output, $bounds))
9093
->push(new SearchBounds($output, $directoryBounds))
9194
->push(new ReportRenderer($output, new Summary\Cli($validator, $bounds, $directoryBounds)))

src/Hal/Coupling/Coupling.php

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
3+
/*
4+
* (c) Jean-François Lépine <https://twitter.com/Halleck45>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
namespace Hal\Coupling;
11+
use Hal\OOP\Extractor\ClassMap;
12+
13+
14+
/**
15+
* Estimates coupling (based on work of Henry And Kafura)
16+
*
17+
* @author Jean-François Lépine <https://twitter.com/Halleck45>
18+
*/
19+
class Coupling {
20+
21+
/**
22+
* Calculates coupling
23+
*
24+
* @param ClassMap $result
25+
* @return ResultMap
26+
*/
27+
public function calculate(ClassMap $result)
28+
{
29+
$map = $this->extractCoupling($result);
30+
31+
// instability
32+
foreach($map as &$class) {
33+
if($class->getAfferentCoupling() + $class->getEfferentCoupling() > 0) {
34+
$class->setInstability($class->getEfferentCoupling() / ($class->getAfferentCoupling() + $class->getEfferentCoupling()));
35+
}
36+
}
37+
38+
return new ResultMap($map);
39+
}
40+
41+
42+
/**
43+
* Extracts afferent and efferent coupling
44+
*
45+
* @param ClassMap $result
46+
* @return array
47+
*/
48+
private function extractCoupling(ClassMap $result) {
49+
$results = $result->all();
50+
51+
$classes = array();
52+
foreach($results as $result) {
53+
$classes = array_merge($classes, $result->getClasses());
54+
}
55+
56+
$map = array();
57+
foreach($classes as $class) {
58+
59+
if(!isset($map[$class->getName()])) {
60+
$map[$class->getName()] = new Result($class->getName());
61+
}
62+
63+
$dependencies = $class->getDependencies();
64+
$map[$class->getName()]->setEfferentCoupling(sizeof($dependencies, COUNT_NORMAL));
65+
66+
foreach($dependencies as $dependency) {
67+
68+
if(!isset($map[$dependency])) {
69+
$map[$dependency] = new Result($dependency);
70+
}
71+
$map[$dependency]->setAfferentCoupling($map[$dependency]->getAfferentCoupling() + 1);
72+
}
73+
}
74+
return $map;
75+
}
76+
};

0 commit comments

Comments
 (0)