Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ private function registerServices(): void

$this->containerBuilder->register(CognitiveMetricTextRendererInterface::class, CognitiveMetricTextRenderer::class)
->setArguments([
new Reference(OutputInterface::class),
new Reference(ConfigService::class)
])
->setPublic(true);
Expand Down
31 changes: 30 additions & 1 deletion src/Business/Cognitive/CognitiveMetricsCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,19 @@ private function findMetrics(iterable $files): CognitiveMetricsCollection
continue;
}

$filename = $file->getRealPath();

if (getenv('APP_ENV') === 'test') {
$projectRoot = $this->getProjectRoot();
if ($projectRoot && str_starts_with($filename, $projectRoot)) {
$filename = substr($filename, strlen($projectRoot) + 1);
}
}

$metricsCollection = $this->processMethodMetrics(
$metrics,
$metricsCollection,
$file->getRealPath()
$filename
);

$this->messageBus->dispatch(new FileProcessed(
Expand Down Expand Up @@ -202,4 +211,24 @@ public function getIgnoredMethods(): array
{
return $this->ignoredItems['methods'] ?? [];
}

/**
* Get the project root directory path.
*
* @return string|null The project root path or null if not found
*/
private function getProjectRoot(): ?string
{
// Start from the current file's directory and traverse up to find composer.json
$currentDir = __DIR__;

while ($currentDir !== dirname($currentDir)) {
if (file_exists($currentDir . DIRECTORY_SEPARATOR . 'composer.json')) {
return $currentDir;
}
$currentDir = dirname($currentDir);
}

return null;
}
}
35 changes: 34 additions & 1 deletion src/Business/DirectoryScanner.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,44 @@ private function traverseDirectory(string $directory, array $exclude): Generator
RecursiveIteratorIterator::LEAVES_ONLY
);

$files = $this->getFilesFromIterator($iterator, $exclude);
$this->sortFilesByPathname($files);

// Yield sorted files
foreach ($files as $fileInfo) {
yield $fileInfo;
}
}

/**
* Sort files by their pathname to ensure consistent order across platforms.
*
* @param array<SplFileInfo> $files Array of SplFileInfo objects to sort
*/
private function sortFilesByPathname(array &$files): void
{
usort($files, function (SplFileInfo $alpha, SplFileInfo $beta) {
return strcmp($alpha->getPathname(), $beta->getPathname());
});
}

/**
* Collect files from an iterator, applying exclusion filters.
*
* @param \Iterator<SplFileInfo> $iterator Iterator to collect files from
* @param array<string> $exclude Array of regex patterns to exclude files
* @return array<SplFileInfo> Array of SplFileInfo objects
*/
private function getFilesFromIterator(\Iterator $iterator, array $exclude): array
{
$files = [];
foreach ($iterator as $fileInfo) {
if ($fileInfo->isFile() && !$this->isExcluded($fileInfo, $exclude)) {
yield $fileInfo;
$files[] = $fileInfo;
}
}

return $files;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Command/CognitiveMetricsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return $this->reportHandler->handle($metricsCollection, $reportType, $reportFile);
}

$this->renderer->render($metricsCollection);
$this->renderer->render($metricsCollection, $output);

return Command::SUCCESS;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@
class CognitiveMetricSummaryTextRenderer implements CognitiveMetricTextRendererInterface
{
public function __construct(
private readonly OutputInterface $output,
private readonly ConfigService $configService,
) {
}

public function render(CognitiveMetricsCollection $metricsCollection): void
public function render(CognitiveMetricsCollection $metricsCollection, OutputInterface $output): void
{
$highlighted = [];
foreach ($metricsCollection as $metric) {
Expand All @@ -35,9 +34,9 @@ public function render(CognitiveMetricsCollection $metricsCollection): void
static fn (CognitiveMetrics $alpha, CognitiveMetrics $beta) => $beta->getScore() <=> $alpha->getScore()
);

$this->output->writeln('<info>Most Complex Methods</info>');
$output->writeln('<info>Most Complex Methods</info>');

$table = new Table($this->output);
$table = new Table($output);
$table->setStyle('box');
$table->setHeaders(['Method', 'Score']);

Expand All @@ -51,6 +50,6 @@ public function render(CognitiveMetricsCollection $metricsCollection): void
}

$table->render();
$this->output->writeln('');
$output->writeln('');
}
}
48 changes: 27 additions & 21 deletions src/Command/Presentation/CognitiveMetricTextRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,9 @@ class CognitiveMetricTextRenderer implements CognitiveMetricTextRendererInterfac
private TableHeaderBuilder $headerBuilder;

public function __construct(
private readonly OutputInterface $output,
private readonly ConfigService $configService,
) {
$config = $this->configService->getConfig();
$this->formatter = new MetricFormatter($config);
$this->rowBuilder = new TableRowBuilder($this->formatter, $config);
$this->headerBuilder = new TableHeaderBuilder($config);
// Don't initialize components here - they'll be created with current config when rendering
}

private function metricExceedsThreshold(CognitiveMetrics $metric, CognitiveConfig $config): bool
Expand All @@ -43,26 +39,33 @@ private function metricExceedsThreshold(CognitiveMetrics $metric, CognitiveConfi

/**
* @param CognitiveMetricsCollection $metricsCollection
* @param OutputInterface $output
* @throws CognitiveAnalysisException
*/
public function render(CognitiveMetricsCollection $metricsCollection): void
public function render(CognitiveMetricsCollection $metricsCollection, OutputInterface $output): void
{
$config = $this->configService->getConfig();

// Recreate components with current configuration
$this->formatter = new MetricFormatter($config);
$this->rowBuilder = new TableRowBuilder($this->formatter, $config);
$this->headerBuilder = new TableHeaderBuilder($config);

if ($config->groupByClass) {
$this->renderGroupedByClass($metricsCollection, $config);
$this->renderGroupedByClass($metricsCollection, $config, $output);
return;
}

$this->renderAllMethodsInSingleTable($metricsCollection, $config);
$this->renderAllMethodsInSingleTable($metricsCollection, $config, $output);
}

/**
* @param CognitiveMetricsCollection $metricsCollection
* @param CognitiveConfig $config
* @param OutputInterface $output
* @throws CognitiveAnalysisException
*/
private function renderGroupedByClass(CognitiveMetricsCollection $metricsCollection, CognitiveConfig $config): void
private function renderGroupedByClass(CognitiveMetricsCollection $metricsCollection, CognitiveConfig $config, OutputInterface $output): void
{
$groupedByClass = $metricsCollection->groupBy('class');

Expand All @@ -74,7 +77,7 @@ private function renderGroupedByClass(CognitiveMetricsCollection $metricsCollect
$rows = $this->buildRowsForClass($metrics, $config);
if (count($rows) > 0) {
$filename = $this->getFilenameFromMetrics($metrics);
$this->renderTable((string)$className, $rows, $filename);
$this->renderTable((string)$className, $rows, $filename, $output);
}
}
}
Expand Down Expand Up @@ -110,15 +113,16 @@ private function getFilenameFromMetrics(CognitiveMetricsCollection $metrics): st
/**
* @param CognitiveMetricsCollection $metricsCollection
* @param CognitiveConfig $config
* @param OutputInterface $output
* @throws CognitiveAnalysisException
*/
private function renderAllMethodsInSingleTable(CognitiveMetricsCollection $metricsCollection, CognitiveConfig $config): void
private function renderAllMethodsInSingleTable(CognitiveMetricsCollection $metricsCollection, CognitiveConfig $config, OutputInterface $output): void
{
$rows = $this->buildRowsForSingleTable($metricsCollection, $config);
$totalMethods = count($rows);

if ($totalMethods > 0) {
$this->renderSingleTable($rows, $totalMethods);
$this->renderSingleTable($rows, $totalMethods, $output);
}
}

Expand All @@ -143,38 +147,40 @@ private function buildRowsForSingleTable(CognitiveMetricsCollection $metricsColl
* @param string $className
* @param array<int, mixed> $rows
* @param string $filename
* @param OutputInterface $output
*/
private function renderTable(string $className, array $rows, string $filename): void
private function renderTable(string $className, array $rows, string $filename, OutputInterface $output): void
{
$table = new Table($this->output);
$table = new Table($output);
$table->setStyle('box');
$table->setHeaders($this->getTableHeaders());

$this->output->writeln("<info>Class: $className</info>");
$this->output->writeln("<info>File: $filename</info>");
$output->writeln("<info>Class: $className</info>");
$output->writeln("<info>File: $filename</info>");

$table->setRows($rows);
$table->render();

$this->output->writeln("");
$output->writeln("");
}

/**
* @param array<int, mixed> $rows
* @param int $totalMethods
* @param OutputInterface $output
*/
private function renderSingleTable(array $rows, int $totalMethods): void
private function renderSingleTable(array $rows, int $totalMethods, OutputInterface $output): void
{
$table = new Table($this->output);
$table = new Table($output);
$table->setStyle('box');
$table->setHeaders($this->getSingleTableHeaders());

$this->output->writeln("<info>All Methods ($totalMethods total)</info>");
$output->writeln("<info>All Methods ($totalMethods total)</info>");

$table->setRows($rows);
$table->render();

$this->output->writeln("");
$output->writeln("");
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\CognitiveMetricsCollection;
use Phauthentic\CognitiveCodeAnalysis\CognitiveAnalysisException;
use Symfony\Component\Console\Output\OutputInterface;

/**
*
Expand All @@ -14,7 +15,8 @@ interface CognitiveMetricTextRendererInterface
{
/**
* @param CognitiveMetricsCollection $metricsCollection
* @param OutputInterface $output
* @throws CognitiveAnalysisException
*/
public function render(CognitiveMetricsCollection $metricsCollection): void;
public function render(CognitiveMetricsCollection $metricsCollection, OutputInterface $output): void;
}
9 changes: 9 additions & 0 deletions tests/Fixtures/all-metrics-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
cognitive:
excludeFilePatterns:
excludePatterns:
scoreThreshold: 0.5
showOnlyMethodsExceedingThreshold: false
showHalsteadComplexity: true
showCyclomaticComplexity: true
showDetailedCognitiveMetrics: true
groupByClass: true
9 changes: 9 additions & 0 deletions tests/Fixtures/cyclomatic-only-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
cognitive:
excludeFilePatterns:
excludePatterns:
scoreThreshold: 0.5
showOnlyMethodsExceedingThreshold: false
showHalsteadComplexity: false
showCyclomaticComplexity: true
showDetailedCognitiveMetrics: true
groupByClass: true
9 changes: 9 additions & 0 deletions tests/Fixtures/halstead-only-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
cognitive:
excludeFilePatterns:
excludePatterns:
scoreThreshold: 0.5
showOnlyMethodsExceedingThreshold: false
showHalsteadComplexity: true
showCyclomaticComplexity: false
showDetailedCognitiveMetrics: true
groupByClass: true
9 changes: 9 additions & 0 deletions tests/Fixtures/minimal-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
cognitive:
excludeFilePatterns:
excludePatterns:
scoreThreshold: 0.5
showOnlyMethodsExceedingThreshold: false
showHalsteadComplexity: false
showCyclomaticComplexity: false
showDetailedCognitiveMetrics: false
groupByClass: true
9 changes: 9 additions & 0 deletions tests/Fixtures/no-detailed-metrics-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
cognitive:
excludeFilePatterns:
excludePatterns:
scoreThreshold: 0.5
showOnlyMethodsExceedingThreshold: false
showHalsteadComplexity: true
showCyclomaticComplexity: true
showDetailedCognitiveMetrics: false
groupByClass: true
9 changes: 9 additions & 0 deletions tests/Fixtures/single-table-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
cognitive:
excludeFilePatterns:
excludePatterns:
scoreThreshold: 0.5
showOnlyMethodsExceedingThreshold: false
showHalsteadComplexity: true
showCyclomaticComplexity: true
showDetailedCognitiveMetrics: true
groupByClass: false
9 changes: 9 additions & 0 deletions tests/Fixtures/threshold-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
cognitive:
excludeFilePatterns:
excludePatterns:
scoreThreshold: 0.1
showOnlyMethodsExceedingThreshold: true
showHalsteadComplexity: true
showCyclomaticComplexity: true
showDetailedCognitiveMetrics: true
groupByClass: true
Loading