Skip to content
Closed
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
18 changes: 17 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,29 @@ Cognitive Code Analysis is an approach to understanding and improving code by fo

[Source: Human Cognitive Limitations. Broad, Consistent, Clinical Application of Physiological Principles Will Require Decision Support](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5822395/)

## Features 💎

* Cognitive Complexity Analysis:
* Calculates a cognitive complexity score for each class and method
* Provides detailed cognitive complexity metrics
* Generate reports in various formats (JSON, CSV, HTML)
* Baseline comparison to track complexity changes over time
* Configurable thresholds and weights for complexity analysis
* Optional result cache for faster subsequent runs (must be enabled in config)
* Custom report generators
* Also provides Halstead Complexity Metrics (must be enabled in config)
* Also provides Cyclomatic Complexity Metrics (must be enabled in config)
* Cognitive Complexity Churn Analysis to identify hotspots in the codebase
* Generate reports in various formats (JSON, CSV, HTML)
* Custom report generators

## Installation ⚙️

```bash
composer require --dev phauthentic/cognitive-code-analysis
```

## Running it
## Running it 🧑‍💻

Cognitive Complexity Analysis

Expand Down
21 changes: 18 additions & 3 deletions src/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
use Phauthentic\CognitiveCodeAnalysis\Command\Handler\CognitiveAnalysis\BaselineHandler;
use Phauthentic\CognitiveCodeAnalysis\Command\Handler\CognitiveAnalysis\ConfigurationLoadHandler;
use Phauthentic\CognitiveCodeAnalysis\Command\Handler\CognitiveAnalysis\CoverageLoadHandler;
use Phauthentic\CognitiveCodeAnalysis\Command\Handler\CognitiveAnalysis\OutputHandler;
use Phauthentic\CognitiveCodeAnalysis\Command\Handler\CognitiveAnalysis\SortingHandler;
use Phauthentic\CognitiveCodeAnalysis\Command\Handler\CognitiveAnalysis\ValidationHandler;
use Phauthentic\CognitiveCodeAnalysis\Command\Handler\CognitiveMetricsReportHandler;
use Phauthentic\CognitiveCodeAnalysis\Command\Presentation\ChurnTextRenderer;
use Phauthentic\CognitiveCodeAnalysis\Command\Presentation\CognitiveMetricTextRenderer;
Expand Down Expand Up @@ -230,6 +232,20 @@ private function registerCommandHandlers(): void
new Reference(CognitiveMetricsSorter::class),
])
->setPublic(true);

$this->containerBuilder->register(ValidationHandler::class, ValidationHandler::class)
->setArguments([
new Reference(CognitiveMetricsValidationSpecificationFactory::class),
new Reference(CognitiveMetricsReportHandler::class),
])
->setPublic(true);

$this->containerBuilder->register(OutputHandler::class, OutputHandler::class)
->setArguments([
new Reference(CognitiveMetricsReportHandler::class),
new Reference(CognitiveMetricTextRendererInterface::class),
])
->setPublic(true);
}

private function bootstrap(): void
Expand Down Expand Up @@ -306,13 +322,12 @@ private function registerCommands(): void
$this->containerBuilder->register(CognitiveMetricsCommand::class, CognitiveMetricsCommand::class)
->setArguments([
new Reference(MetricsFacade::class),
new Reference(CognitiveMetricTextRendererInterface::class),
new Reference(CognitiveMetricsReportHandler::class),
new Reference(ConfigurationLoadHandler::class),
new Reference(CoverageLoadHandler::class),
new Reference(BaselineHandler::class),
new Reference(SortingHandler::class),
new Reference(CognitiveMetricsValidationSpecificationFactory::class),
new Reference(ValidationHandler::class),
new Reference(OutputHandler::class),
])
->setPublic(true);

Expand Down
70 changes: 14 additions & 56 deletions src/Command/CognitiveMetricsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,10 @@
use Phauthentic\CognitiveCodeAnalysis\Command\Handler\CognitiveAnalysis\BaselineHandler;
use Phauthentic\CognitiveCodeAnalysis\Command\Handler\CognitiveAnalysis\ConfigurationLoadHandler;
use Phauthentic\CognitiveCodeAnalysis\Command\Handler\CognitiveAnalysis\CoverageLoadHandler;
use Phauthentic\CognitiveCodeAnalysis\Command\Handler\CognitiveAnalysis\OutputHandler;
use Phauthentic\CognitiveCodeAnalysis\Command\Handler\CognitiveAnalysis\SortingHandler;
use Phauthentic\CognitiveCodeAnalysis\Command\Handler\CognitiveMetricsReportHandler;
use Phauthentic\CognitiveCodeAnalysis\Command\Presentation\CognitiveMetricTextRendererInterface;
use Phauthentic\CognitiveCodeAnalysis\Command\Handler\CognitiveAnalysis\ValidationHandler;
use Phauthentic\CognitiveCodeAnalysis\Command\CognitiveMetricsSpecifications\CognitiveMetricsCommandContext;
use Phauthentic\CognitiveCodeAnalysis\Command\CognitiveMetricsSpecifications\CompositeCognitiveMetricsValidationSpecification;
use Phauthentic\CognitiveCodeAnalysis\Command\CognitiveMetricsSpecifications\CognitiveMetricsValidationSpecificationFactory;
use Phauthentic\CognitiveCodeAnalysis\Command\CognitiveMetricsSpecifications\CustomExporterValidation;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
Expand All @@ -23,8 +20,6 @@

/**
* Command to parse PHP files or directories and output method metrics.
*
* @SuppressWarnings("CyclomaticComplexity")
*/
#[AsCommand(
name: 'analyse',
Expand All @@ -43,20 +38,16 @@ class CognitiveMetricsCommand extends Command
public const OPTION_COVERAGE_CLOVER = 'coverage-clover';
private const ARGUMENT_PATH = 'path';

private CompositeCognitiveMetricsValidationSpecification $specification;

public function __construct(
readonly private MetricsFacade $metricsFacade,
readonly private CognitiveMetricTextRendererInterface $renderer,
readonly private CognitiveMetricsReportHandler $reportHandler,
readonly private ConfigurationLoadHandler $configHandler,
readonly private CoverageLoadHandler $coverageHandler,
readonly private BaselineHandler $baselineHandler,
readonly private SortingHandler $sortingHandler,
readonly private CognitiveMetricsValidationSpecificationFactory $specificationFactory
readonly private ValidationHandler $validationHandler,
readonly private OutputHandler $outputHandler
) {
parent::__construct();
$this->specification = $this->specificationFactory->create();
}


Expand Down Expand Up @@ -137,9 +128,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int
{
$context = new CognitiveMetricsCommandContext($input);

// Validate all specifications
if (!$this->specification->isSatisfiedBy($context)) {
return $this->handleValidationError($context, $output);
// Run initial validation
$validationResult = $this->validationHandler->validate($context);
if ($validationResult->isFailure()) {
return $validationResult->toCommandStatus($output);
}

// Load configuration
Expand All @@ -148,16 +140,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return $configResult->toCommandStatus($output);
}

// Validate custom exporters after config is loaded
if ($context->hasReportOptions()) {
$customExporterValidation = new CustomExporterValidation(
$this->reportHandler->getReportFactory(),
$this->reportHandler->getConfigService()
);

if (!$customExporterValidation->isSatisfiedBy($context)) {
return $this->handleValidationError($context, $output, $customExporterValidation);
}
// Run custom exporter validation after config is loaded
$customExporterResult = $this->validationHandler->validateCustomExporter($context);
if ($customExporterResult->isFailure()) {
return $customExporterResult->toCommandStatus($output);
}

// Load coverage reader
Expand All @@ -184,35 +170,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return $sortResult->toCommandStatus($output);
}

// Generate report or display results
if ($context->hasReportOptions()) {
return $this->reportHandler->handle(
$sortResult->getData(),
$context->getReportType(),
$context->getReportFile()
);
}

$this->renderer->render($sortResult->getData(), $output);

return Command::SUCCESS;
}


/**
* Handle validation errors with consistent error output.
*/
private function handleValidationError(
CognitiveMetricsCommandContext $context,
OutputInterface $output,
?CustomExporterValidation $customExporterValidation = null
): int {
$errorMessage = $customExporterValidation !== null
? $customExporterValidation->getErrorMessageWithContext($context)
: $this->specification->getDetailedErrorMessage($context);

$output->writeln('<error>' . $errorMessage . '</error>');

return Command::FAILURE;
// Handle output (report or console rendering)
return $this->outputHandler->handle($sortResult->getData(), $context, $output);
}
}
47 changes: 47 additions & 0 deletions src/Command/Handler/CognitiveAnalysis/OutputHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace Phauthentic\CognitiveCodeAnalysis\Command\Handler\CognitiveAnalysis;

use Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\CognitiveMetricsCollection;
use Phauthentic\CognitiveCodeAnalysis\Command\CognitiveMetricsSpecifications\CognitiveMetricsCommandContext;
use Phauthentic\CognitiveCodeAnalysis\Command\Handler\CognitiveMetricsReportHandler;
use Phauthentic\CognitiveCodeAnalysis\Command\Presentation\CognitiveMetricTextRendererInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Output\OutputInterface;

/**
* Handler for output operations in cognitive metrics command.
* Encapsulates logic for generating reports or rendering console output.
*/
class OutputHandler
{
public function __construct(
private readonly CognitiveMetricsReportHandler $reportHandler,
private readonly CognitiveMetricTextRendererInterface $renderer
) {
}

/**
* Handle output based on context options.
* Generates report if report options are provided, otherwise renders to console.
* Returns appropriate command status code.
*/
public function handle(
CognitiveMetricsCollection $collection,
CognitiveMetricsCommandContext $context,
OutputInterface $output
): int {
if ($context->hasReportOptions()) {
return $this->reportHandler->handle(
$collection,
$context->getReportType(),
$context->getReportFile()
);
}

$this->renderer->render($collection, $output);
return Command::SUCCESS;
}
}
66 changes: 66 additions & 0 deletions src/Command/Handler/CognitiveAnalysis/ValidationHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

declare(strict_types=1);

namespace Phauthentic\CognitiveCodeAnalysis\Command\Handler\CognitiveAnalysis;

use Phauthentic\CognitiveCodeAnalysis\Command\CognitiveMetricsSpecifications\CognitiveMetricsCommandContext;
use Phauthentic\CognitiveCodeAnalysis\Command\CognitiveMetricsSpecifications\CustomExporterValidation;
use Phauthentic\CognitiveCodeAnalysis\Command\CognitiveMetricsSpecifications\CognitiveMetricsValidationSpecificationFactory;
use Phauthentic\CognitiveCodeAnalysis\Command\Handler\CognitiveMetricsReportHandler;
use Phauthentic\CognitiveCodeAnalysis\Command\Result\OperationResult;

/**
* Handler for validation operations in cognitive metrics command.
* Encapsulates all validation logic including initial validation and custom exporter validation.
*/
class ValidationHandler
{
public function __construct(
private readonly CognitiveMetricsValidationSpecificationFactory $specificationFactory,
private readonly CognitiveMetricsReportHandler $reportHandler
) {
}

/**
* Run initial validation using composite specification.
* Returns success result if validation passes.
* Returns failure result with detailed error message if validation fails.
*/
public function validate(CognitiveMetricsCommandContext $context): OperationResult
{
$specification = $this->specificationFactory->create();

if (!$specification->isSatisfiedBy($context)) {
$errorMessage = $specification->getDetailedErrorMessage($context);
return OperationResult::failure($errorMessage);
}

return OperationResult::success();
}

/**
* Run custom exporter validation after configuration is loaded.
* Only validates if report options are provided.
* Returns success result if no report options or validation passes.
* Returns failure result with detailed error message if validation fails.
*/
public function validateCustomExporter(CognitiveMetricsCommandContext $context): OperationResult
{
if (!$context->hasReportOptions()) {
return OperationResult::success();
}

$customExporterValidation = new CustomExporterValidation(
$this->reportHandler->getReportFactory(),
$this->reportHandler->getConfigService()
);

if (!$customExporterValidation->isSatisfiedBy($context)) {
$errorMessage = $customExporterValidation->getErrorMessageWithContext($context);
return OperationResult::failure($errorMessage);
}

return OperationResult::success();
}
}