Skip to content

Commit 0f04916

Browse files
Implementing the Sorting Feature (#42)
# New Features Added * Sorting functionality with command-line options --sort-by and --sort-order * 13 sortable fields: score, halstead, cyclomatic, class, method, lineCount, argCount, returnCount, variableCount, propertyCallCount, ifCount, ifNestingLevel, elseCount * Sort orders: ascending (asc) and descending (desc) with asc as default * Grouping by class via new groupByClass configuration option (default: true) * Enhanced filtering with better handling of ignored items and method filtering # New Classes and Components # Configuration Changes * Added groupByClass: true option to config.yml * Updated ConfigFactory and ConfigLoader to support the new option Enhanced CognitiveConfig class with groupByClass property
1 parent 4f7c51a commit 0f04916

30 files changed

+2419
-297
lines changed

config.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ cognitive:
55
showOnlyMethodsExceedingThreshold: false
66
showHalsteadComplexity: false
77
showCyclomaticComplexity: false
8+
groupByClass: true
89
metrics:
910
lineCount:
1011
threshold: 60

docs/Configuration.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,17 @@ cognitive:
7979
showHalsteadComplexity: false
8080
showCyclomaticComplexity: false
8181
```
82+
83+
## Grouping Results by Class
84+
85+
You can control how the analysis results are displayed by setting the `groupByClass` option.
86+
87+
```yaml
88+
cognitive:
89+
groupByClass: true
90+
```
91+
92+
- **`true` (default)**: Results are grouped by class, showing all methods within each class together
93+
- **`false`**: Results are displayed as a flat list without grouping
94+
95+
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.

docs/Sorting-and-Filtering.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Sorting and Filtering
2+
3+
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.
4+
5+
## Sorting Results
6+
7+
You can sort analysis results by various metrics to identify the most complex or problematic code areas.
8+
9+
### Command Line Options
10+
11+
```bash
12+
bin/phpcca analyse <path-to-folder> --sort-by=<field> --sort-order=<order>
13+
```
14+
15+
#### Available Options
16+
17+
- `--sort-by, -s`: Field to sort by (optional)
18+
- `--sort-order`: Sort order - `asc` (ascending) or `desc` (descending), default: `asc`
19+
20+
### Sortable Fields
21+
22+
The following fields are available for sorting:
23+
24+
| Field | Description |
25+
|-------|-------------|
26+
| `score` | Cognitive complexity score |
27+
| `halstead` | Halstead complexity metrics |
28+
| `cyclomatic` | Cyclomatic complexity |
29+
| `class` | Class name (alphabetical) |
30+
| `method` | Method name (alphabetical) |
31+
| `lineCount` | Number of lines of code |
32+
| `argCount` | Number of method arguments |
33+
| `returnCount` | Number of return statements |
34+
| `variableCount` | Number of variables used |
35+
| `propertyCallCount` | Number of property accesses |
36+
| `ifCount` | Number of if statements |
37+
| `ifNestingLevel` | Maximum nesting level of if statements |
38+
| `elseCount` | Number of else statements |
39+
40+
### Examples
41+
42+
Sort by cognitive complexity score (highest first):
43+
```bash
44+
bin/phpcca analyse src/ --sort-by=score --sort-order=desc
45+
```
46+
47+
Sort by method name alphabetically:
48+
```bash
49+
bin/phpcca analyse src/ --sort-by=method --sort-order=asc
50+
```
51+
52+
Sort by cyclomatic complexity:
53+
```bash
54+
bin/phpcca analyse src/ --sort-by=cyclomatic --sort-order=desc
55+
```
56+
57+
## Filtering and Grouping
58+
59+
### Grouping by Class
60+
61+
By default, results are grouped by class to make it easier to understand complexity within specific classes. This behavior can be controlled via configuration:
62+
63+
```yaml
64+
cognitive:
65+
groupByClass: true # Default: true
66+
```
67+
68+
- **`true`**: Results are grouped by class, showing separate tables for each class
69+
- **`false`**: Results are displayed as a flat list without grouping
70+
71+
### Excluding Classes and Methods
72+
73+
You can exclude specific classes and methods from analysis using regex patterns in your configuration file:
74+
75+
```yaml
76+
cognitive:
77+
excludePatterns:
78+
- '(.*)::__construct' # Exclude all constructors
79+
- '(.*)::toArray' # Exclude all toArray methods
80+
- '(.*)Transformer::(.*)' # Exclude all methods in Transformer classes
81+
```
82+
83+
### Excluding Files
84+
85+
You can exclude entire files from analysis:
86+
87+
```yaml
88+
cognitive:
89+
excludeFilePatterns:
90+
- '.*Cognitive.*' # Exclude files with "Cognitive" in the name
91+
- '(.*)Test.php' # Exclude all test files
92+
```
93+
94+
## Error Handling
95+
96+
If you specify an invalid sort field, the tool will display an error message with the list of available fields:
97+
98+
```bash
99+
bin/phpcca analyse src/ --sort-by=invalidField
100+
# Output: Sorting error: Invalid sort field "invalidField". Available fields: score, halstead, cyclomatic, class, method, lineCount, argCount, returnCount, variableCount, propertyCallCount, ifCount, ifNestingLevel, elseCount
101+
```

readme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ bin/phpcca churn <path-to-folder>
5151
* [Metrics Collected](./docs/Cognitive-Complexity-Analysis.md#metrics-collected)
5252
* [Result Interpretation](./docs/Cognitive-Complexity-Analysis.md#result-interpretation)
5353
* [Churn - Finding Hotspots](./docs/Churn-Finding-Hotspots.md)
54+
* [Sorting and Filtering](./docs/Sorting-and-Filtering.md)
5455
* [Configuration](./docs/Configuration.md#configuration)
5556
* [Tuning the calculation](./docs/Configuration.md#tuning-the-calculation)
5657
* [Examples](#examples)

src/Application.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Phauthentic\CognitiveCodeAnalysis\Business\Churn\ChurnCalculator;
99
use Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\Baseline;
1010
use Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\CognitiveMetricsCollector;
11+
use Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\CognitiveMetricsSorter;
1112
use Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\Events\FileProcessed;
1213
use Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\Events\ParserFailed;
1314
use Phauthentic\CognitiveCodeAnalysis\Business\Cognitive\Events\SourceFilesFound;
@@ -97,6 +98,9 @@ private function registerServices(): void
9798
$this->containerBuilder->register(Baseline::class, Baseline::class)
9899
->setPublic(true);
99100

101+
$this->containerBuilder->register(CognitiveMetricsSorter::class, CognitiveMetricsSorter::class)
102+
->setPublic(true);
103+
100104
$this->containerBuilder->register(Processor::class, Processor::class)
101105
->setPublic(true);
102106

@@ -218,6 +222,7 @@ private function registerCommands(): void
218222
new Reference(CognitiveMetricTextRendererInterface::class),
219223
new Reference(Baseline::class),
220224
new Reference(CognitiveMetricsReportHandler::class),
225+
new Reference(CognitiveMetricsSorter::class),
221226
])
222227
->setPublic(true);
223228

src/Business/Cognitive/CognitiveMetricsCollection.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@
1010
use InvalidArgumentException;
1111
use IteratorAggregate;
1212
use JsonSerializable;
13+
use Traversable;
1314

1415
/**
1516
* CognitiveMetricsCollection class
1617
*
17-
* @implements IteratorAggregate<int, CognitiveMetrics>
18+
* @implements IteratorAggregate<string, CognitiveMetrics>
1819
*/
1920
class CognitiveMetricsCollection implements IteratorAggregate, Countable, JsonSerializable
2021
{
@@ -51,9 +52,10 @@ public function filter(Closure $callback): self
5152
/**
5253
* Get an iterator for the collection
5354
*
54-
* @return ArrayIterator<int, CognitiveMetrics>
55+
* @return Traversable<string, CognitiveMetrics>
5556
*/
56-
public function getIterator(): ArrayIterator
57+
#[\ReturnTypeWillChange]
58+
public function getIterator(): Traversable
5759
{
5860
return new ArrayIterator($this->metrics);
5961
}

src/Business/Cognitive/CognitiveMetricsCollector.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@
2121
*/
2222
class CognitiveMetricsCollector
2323
{
24+
/**
25+
* @var array<string, array<string, string>>|null Cached ignored items from the last parsing operation
26+
*/
27+
private ?array $ignoredItems = null;
28+
2429
public function __construct(
2530
protected readonly Parser $parser,
2631
protected readonly DirectoryScanner $directoryScanner,
@@ -82,6 +87,9 @@ private function findMetrics(iterable $files): CognitiveMetricsCollection
8287
$metrics = $this->parser->parse(
8388
$this->getCodeFromFile($file)
8489
);
90+
91+
// Store ignored items from the parser
92+
$this->ignoredItems = $this->parser->getIgnored();
8593
} catch (Throwable $exception) {
8694
$this->messageBus->dispatch(new ParserFailed(
8795
$file,
@@ -162,4 +170,34 @@ private function findSourceFiles(string $path, array $exclude = []): iterable
162170
{
163171
return $this->directoryScanner->scan([$path], ['^(?!.*\.php$).+'] + $exclude);
164172
}
173+
174+
/**
175+
* Get all ignored classes and methods from the last parsing operation.
176+
*
177+
* @return array<string, array<string, string>> Array with 'classes' and 'methods' keys
178+
*/
179+
public function getIgnored(): array
180+
{
181+
return $this->ignoredItems ?? ['classes' => [], 'methods' => []];
182+
}
183+
184+
/**
185+
* Get ignored classes from the last parsing operation.
186+
*
187+
* @return array<string, string> Array of ignored class FQCNs
188+
*/
189+
public function getIgnoredClasses(): array
190+
{
191+
return $this->ignoredItems['classes'] ?? [];
192+
}
193+
194+
/**
195+
* Get ignored methods from the last parsing operation.
196+
*
197+
* @return array<string, string> Array of ignored method keys (ClassName::methodName)
198+
*/
199+
public function getIgnoredMethods(): array
200+
{
201+
return $this->ignoredItems['methods'] ?? [];
202+
}
165203
}

0 commit comments

Comments
 (0)