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: 1 addition & 0 deletions config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ cognitive:
showOnlyMethodsExceedingThreshold: false
showHalsteadComplexity: false
showCyclomaticComplexity: false
groupByClass: true
metrics:
lineCount:
threshold: 60
Expand Down
14 changes: 14 additions & 0 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,17 @@ cognitive:
showHalsteadComplexity: false
showCyclomaticComplexity: false
```

## Grouping Results by Class

You can control how the analysis results are displayed by setting the `groupByClass` option.

```yaml
cognitive:
groupByClass: true
```

- **`true` (default)**: Results are grouped by class, showing all methods within each class together
- **`false`**: Results are displayed as a flat list without grouping

When `groupByClass` is enabled, the output will show separate tables for each class, making it easier to understand the complexity within specific classes. When disabled, all methods are shown in a single table sorted by their complexity score.
101 changes: 101 additions & 0 deletions docs/Sorting-and-Filtering.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Sorting and Filtering

The cognitive code analysis tool provides powerful sorting and filtering capabilities to help you organize and focus on the most relevant results from your code analysis.

## Sorting Results

You can sort analysis results by various metrics to identify the most complex or problematic code areas.

### Command Line Options

```bash
bin/phpcca analyse <path-to-folder> --sort-by=<field> --sort-order=<order>
```

#### Available Options

- `--sort-by, -s`: Field to sort by (optional)
- `--sort-order`: Sort order - `asc` (ascending) or `desc` (descending), default: `asc`

### Sortable Fields

The following fields are available for sorting:

| Field | Description |
|-------|-------------|
| `score` | Cognitive complexity score |
| `halstead` | Halstead complexity metrics |
| `cyclomatic` | Cyclomatic complexity |
| `class` | Class name (alphabetical) |
| `method` | Method name (alphabetical) |
| `lineCount` | Number of lines of code |
| `argCount` | Number of method arguments |
| `returnCount` | Number of return statements |
| `variableCount` | Number of variables used |
| `propertyCallCount` | Number of property accesses |
| `ifCount` | Number of if statements |
| `ifNestingLevel` | Maximum nesting level of if statements |
| `elseCount` | Number of else statements |

### Examples

Sort by cognitive complexity score (highest first):
```bash
bin/phpcca analyse src/ --sort-by=score --sort-order=desc
```

Sort by method name alphabetically:
```bash
bin/phpcca analyse src/ --sort-by=method --sort-order=asc
```

Sort by cyclomatic complexity:
```bash
bin/phpcca analyse src/ --sort-by=cyclomatic --sort-order=desc
```

## Filtering and Grouping

### Grouping by Class

By default, results are grouped by class to make it easier to understand complexity within specific classes. This behavior can be controlled via configuration:

```yaml
cognitive:
groupByClass: true # Default: true
```

- **`true`**: Results are grouped by class, showing separate tables for each class
- **`false`**: Results are displayed as a flat list without grouping

### Excluding Classes and Methods

You can exclude specific classes and methods from analysis using regex patterns in your configuration file:

```yaml
cognitive:
excludePatterns:
- '(.*)::__construct' # Exclude all constructors
- '(.*)::toArray' # Exclude all toArray methods
- '(.*)Transformer::(.*)' # Exclude all methods in Transformer classes
```

### Excluding Files

You can exclude entire files from analysis:

```yaml
cognitive:
excludeFilePatterns:
- '.*Cognitive.*' # Exclude files with "Cognitive" in the name
- '(.*)Test.php' # Exclude all test files
```

## Error Handling

If you specify an invalid sort field, the tool will display an error message with the list of available fields:

```bash
bin/phpcca analyse src/ --sort-by=invalidField
# Output: Sorting error: Invalid sort field "invalidField". Available fields: score, halstead, cyclomatic, class, method, lineCount, argCount, returnCount, variableCount, propertyCallCount, ifCount, ifNestingLevel, elseCount
```
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ bin/phpcca churn <path-to-folder>
* [Metrics Collected](./docs/Cognitive-Complexity-Analysis.md#metrics-collected)
* [Result Interpretation](./docs/Cognitive-Complexity-Analysis.md#result-interpretation)
* [Churn - Finding Hotspots](./docs/Churn-Finding-Hotspots.md)
* [Sorting and Filtering](./docs/Sorting-and-Filtering.md)
* [Configuration](./docs/Configuration.md#configuration)
* [Tuning the calculation](./docs/Configuration.md#tuning-the-calculation)
* [Examples](#examples)
Expand Down
5 changes: 5 additions & 0 deletions src/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Phauthentic\CognitiveCodeAnalysis\Business\Churn\ChurnCalculator;
use Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\Baseline;
use Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\CognitiveMetricsCollector;
use Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\CognitiveMetricsSorter;
use Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\Events\FileProcessed;
use Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\Events\ParserFailed;
use Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\Events\SourceFilesFound;
Expand Down Expand Up @@ -97,6 +98,9 @@ private function registerServices(): void
$this->containerBuilder->register(Baseline::class, Baseline::class)
->setPublic(true);

$this->containerBuilder->register(CognitiveMetricsSorter::class, CognitiveMetricsSorter::class)
->setPublic(true);

$this->containerBuilder->register(Processor::class, Processor::class)
->setPublic(true);

Expand Down Expand Up @@ -218,6 +222,7 @@ private function registerCommands(): void
new Reference(CognitiveMetricTextRendererInterface::class),
new Reference(Baseline::class),
new Reference(CognitiveMetricsReportHandler::class),
new Reference(CognitiveMetricsSorter::class),
])
->setPublic(true);

Expand Down
8 changes: 5 additions & 3 deletions src/Business/Cognitive/CognitiveMetricsCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
use InvalidArgumentException;
use IteratorAggregate;
use JsonSerializable;
use Traversable;

/**
* CognitiveMetricsCollection class
*
* @implements IteratorAggregate<int, CognitiveMetrics>
* @implements IteratorAggregate<string, CognitiveMetrics>
*/
class CognitiveMetricsCollection implements IteratorAggregate, Countable, JsonSerializable
{
Expand Down Expand Up @@ -51,9 +52,10 @@ public function filter(Closure $callback): self
/**
* Get an iterator for the collection
*
* @return ArrayIterator<int, CognitiveMetrics>
* @return Traversable<string, CognitiveMetrics>
*/
public function getIterator(): ArrayIterator
#[\ReturnTypeWillChange]
public function getIterator(): Traversable
{
return new ArrayIterator($this->metrics);
}
Expand Down
38 changes: 38 additions & 0 deletions src/Business/Cognitive/CognitiveMetricsCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
*/
class CognitiveMetricsCollector
{
/**
* @var array<string, array<string, string>>|null Cached ignored items from the last parsing operation
*/
private ?array $ignoredItems = null;

public function __construct(
protected readonly Parser $parser,
protected readonly DirectoryScanner $directoryScanner,
Expand Down Expand Up @@ -82,6 +87,9 @@ private function findMetrics(iterable $files): CognitiveMetricsCollection
$metrics = $this->parser->parse(
$this->getCodeFromFile($file)
);

// Store ignored items from the parser
$this->ignoredItems = $this->parser->getIgnored();
} catch (Throwable $exception) {
$this->messageBus->dispatch(new ParserFailed(
$file,
Expand Down Expand Up @@ -162,4 +170,34 @@ private function findSourceFiles(string $path, array $exclude = []): iterable
{
return $this->directoryScanner->scan([$path], ['^(?!.*\.php$).+'] + $exclude);
}

/**
* Get all ignored classes and methods from the last parsing operation.
*
* @return array<string, array<string, string>> Array with 'classes' and 'methods' keys
*/
public function getIgnored(): array
{
return $this->ignoredItems ?? ['classes' => [], 'methods' => []];
}

/**
* Get ignored classes from the last parsing operation.
*
* @return array<string, string> Array of ignored class FQCNs
*/
public function getIgnoredClasses(): array
{
return $this->ignoredItems['classes'] ?? [];
}

/**
* Get ignored methods from the last parsing operation.
*
* @return array<string, string> Array of ignored method keys (ClassName::methodName)
*/
public function getIgnoredMethods(): array
{
return $this->ignoredItems['methods'] ?? [];
}
}
Loading