-
Notifications
You must be signed in to change notification settings - Fork 259
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #283 from UFOMelkor/feature/package-metrics
Package metrics
- Loading branch information
Showing
29 changed files
with
1,683 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?php | ||
|
||
namespace Hal\Metric\Package; | ||
|
||
use Hal\Metric\Metrics; | ||
use Hal\Metric\PackageMetric; | ||
|
||
class PackageAbstraction | ||
{ | ||
public function calculate(Metrics $metrics) | ||
{ | ||
/* @var $packages PackageMetric[] */ | ||
foreach ($metrics->all() as $eachPackage) { | ||
if (! $eachPackage instanceof PackageMetric) { | ||
continue; | ||
} | ||
$abstractClassCount = 0; | ||
$classCount = count($eachPackage->getClasses()); | ||
foreach ($eachPackage->getClasses() as $eachClassName) { | ||
$eachClass = $metrics->get($eachClassName); | ||
$abstractClassCount += $eachClass->get('abstract'); | ||
} | ||
$eachPackage->setAbstraction($abstractClassCount / $classCount); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
<?php | ||
|
||
namespace Hal\Metric\Package; | ||
|
||
use Hal\Metric\Metrics; | ||
use Hal\Metric\PackageMetric; | ||
use PhpParser\Node; | ||
use PhpParser\Node\Stmt\Class_; | ||
use PhpParser\Node\Stmt\Interface_; | ||
use PhpParser\Node\Stmt\Namespace_; | ||
use PhpParser\Node\Stmt\Trait_; | ||
use PhpParser\NodeVisitorAbstract; | ||
|
||
class PackageCollectingVisitor extends NodeVisitorAbstract | ||
{ | ||
/** @var string */ | ||
private $namespace = ''; | ||
|
||
/** @var Metrics */ | ||
private $metrics; | ||
|
||
public function __construct(Metrics $metrics) | ||
{ | ||
$this->metrics = $metrics; | ||
} | ||
|
||
public function enterNode(Node $node) | ||
{ | ||
if ($node instanceof Namespace_) { | ||
$this->namespace = (string) $node->name; | ||
} | ||
} | ||
|
||
public function leaveNode(Node $node) | ||
{ | ||
if ($node instanceof Class_ || $node instanceof Interface_ || $node instanceof Trait_) { | ||
$package = $this->namespace; | ||
|
||
$docComment = $node->getDocComment(); | ||
$docBlockText = $docComment ? $docComment->getText() : ''; | ||
if (preg_match('/^\s*\* @package (.*)/m', $docBlockText, $matches)) { | ||
$package = $matches[1]; | ||
} | ||
if (preg_match('/^\s*\* @subpackage (.*)/m', $docBlockText, $matches)) { | ||
$package = $package . '\\' . $matches[1]; | ||
} | ||
|
||
$packageName = $package . '\\'; | ||
if (! $packageMetric = $this->metrics->get($packageName)) { | ||
$packageMetric = new PackageMetric($packageName); | ||
$this->metrics->attach($packageMetric); | ||
} | ||
/* @var PackageMetric $packageMetric */ | ||
$elementName = isset($node->namespacedName) ? $node->namespacedName : 'anonymous@'.spl_object_hash($node); | ||
$elementName = (string) $elementName; | ||
$packageMetric->addClass($elementName); | ||
|
||
$this->metrics->get($elementName)->set('package', $packageName); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
<?php | ||
|
||
namespace Hal\Metric\Package; | ||
|
||
use Hal\Metric\ClassMetric; | ||
use Hal\Metric\InterfaceMetric; | ||
use Hal\Metric\Metric; | ||
use Hal\Metric\Metrics; | ||
use Hal\Metric\PackageMetric; | ||
|
||
class PackageDependencies | ||
{ | ||
public function calculate(Metrics $metrics) | ||
{ | ||
$classes = array_filter($metrics->all(), function (Metric $metric) { | ||
return $metric instanceof ClassMetric || $metric instanceof InterfaceMetric; | ||
}); | ||
|
||
foreach ($classes as $each) { | ||
$this->increaseDependencies($each, $metrics); | ||
} | ||
} | ||
|
||
/** | ||
* @param ClassMetric|InterfaceMetric|Metric $class | ||
* @param Metrics $metrics | ||
*/ | ||
private function increaseDependencies(Metric $class, Metrics $metrics) | ||
{ | ||
if (! $class->has('package') || ! $class->has('externals')) { | ||
return; | ||
} | ||
$incomingPackage = $metrics->get($class->get('package')); /* @var $incomingPackage PackageMetric */ | ||
foreach ($class->get('externals') as $outgoingClassName) { | ||
// same package? | ||
if (in_array($outgoingClassName, $incomingPackage->getClasses())) { | ||
continue; | ||
} | ||
$outgoingPackageName = $this->getPackageOfClass($outgoingClassName, $metrics); | ||
$incomingPackage->addOutgoingClassDependency($outgoingClassName, $outgoingPackageName); | ||
$outgoingPackage = $metrics->get($outgoingPackageName); | ||
|
||
if ($outgoingPackage instanceof PackageMetric) { | ||
$outgoingPackage->addIncomingClassDependency($class->getName(), $incomingPackage->getName()); | ||
} | ||
} | ||
} | ||
|
||
private function getPackageOfClass($className, Metrics $metrics) | ||
{ | ||
if ($metrics->has($className) && $metrics->get($className)->has('package')) { | ||
return $metrics->get($className)->get('package'); | ||
} | ||
if (strpos($className, '\\') === false) { | ||
return '\\'; | ||
} | ||
$parts = explode('\\', $className); | ||
array_pop($parts); | ||
return implode('\\', $parts) . '\\'; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?php | ||
declare(strict_types=1); | ||
namespace Hal\Metric\Package; | ||
|
||
use Hal\Metric\Metrics; | ||
use Hal\Metric\PackageMetric; | ||
|
||
class PackageDistance | ||
{ | ||
public function calculate(Metrics $metrics) | ||
{ | ||
foreach ($metrics->all() as $each) { | ||
if ($each instanceof PackageMetric && $each->getAbstraction() !== null && $each->getInstability() !== null) { | ||
$each->setNormalizedDistance(abs($each->getAbstraction() + $each->getInstability() - 1)); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<?php | ||
|
||
namespace Hal\Metric\Package; | ||
|
||
use Hal\Metric\Metrics; | ||
use Hal\Metric\PackageMetric; | ||
|
||
class PackageInstability | ||
{ | ||
public function calculate(Metrics $metrics) | ||
{ | ||
/* @var $packages PackageMetric[] */ | ||
$packages = array_filter($metrics->all(), function ($metric) { | ||
return $metric instanceof PackageMetric; | ||
}); | ||
|
||
// Calculate instability | ||
$instabilitiesByPackage = []; | ||
foreach ($packages as $eachPackage) { | ||
$afferentCoupling = count($eachPackage->getIncomingClassDependencies()); | ||
$efferentCoupling = count($eachPackage->getOutgoingClassDependencies()); | ||
if ($afferentCoupling + $efferentCoupling !== 0) { | ||
$eachPackage->setInstability( | ||
$efferentCoupling / ($afferentCoupling + $efferentCoupling) | ||
); | ||
$instabilitiesByPackage[$eachPackage->getName()] = $eachPackage->getInstability(); | ||
} | ||
} | ||
// Set depending instabilities | ||
foreach ($packages as $eachPackage) { | ||
$dependentInstabilities = array_map(function ($packageName) use ($instabilitiesByPackage) { | ||
return isset($instabilitiesByPackage[$packageName]) ? $instabilitiesByPackage[$packageName] : null; | ||
}, $eachPackage->getOutgoingPackageDependencies()); | ||
$dependentInstabilities = array_combine( | ||
$eachPackage->getOutgoingPackageDependencies(), | ||
$dependentInstabilities | ||
); | ||
$dependentInstabilities = array_filter($dependentInstabilities, 'is_float'); | ||
$eachPackage->setDependentInstabilities($dependentInstabilities); | ||
} | ||
} | ||
} |
Oops, something went wrong.