Skip to content

Commit 276b848

Browse files
Refactoring the report generation
1 parent 5e1e85b commit 276b848

File tree

6 files changed

+230
-70
lines changed

6 files changed

+230
-70
lines changed

composer.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@
6060
"analyze": [
6161
"phpstan analyse src/"
6262
],
63+
"analyse": [
64+
"phpstan analyse src/"
65+
],
6366
"phpmd": [
6467
"bin/phpmd ./src text cleancode,codesize,controversial,design"
6568
],

src/Business/Cognitive/Exporter/JsonExporter.php

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,26 @@ public function export(CognitiveMetricsCollection $metricsCollection, string $fi
2424
'methods' => [
2525
$metrics->getMethod() => [
2626
'name' => $metrics->getMethod(),
27-
'line_count' => $metrics->getLineCount(),
28-
'arg_count' => $metrics->getArgCount(),
29-
'return_count' => $metrics->getReturnCount(),
30-
'variable_count' => $metrics->getVariableCount(),
31-
'property_call_count' => $metrics->getPropertyCallCount(),
32-
'if_nesting_level' => $metrics->getIfNestingLevel(),
33-
'else_count' => $metrics->getElseCount(),
27+
'lineCount' => $metrics->getLineCount(),
28+
'lineCountWeight' => $metrics->getLineCountWeight(),
29+
'argCount' => $metrics->getArgCount(),
30+
'argCountWeight' => $metrics->getArgCountWeight(),
31+
'returnCount' => $metrics->getReturnCount(),
32+
'returnCountWeight' => $metrics->getReturnCountWeight(),
33+
'variableCount' => $metrics->getVariableCount(),
34+
'variableCountWeight' => $metrics->getVariableCountWeight(),
35+
'propertyCallCount' => $metrics->getPropertyCallCount(),
36+
'propertyCallCountWeight' => $metrics->getPropertyCallCountWeight(),
37+
'ifCount' => $metrics->getIfCount(),
38+
'ifCountWeight' => $metrics->getIfCountWeight(),
39+
'ifNestingLevel' => $metrics->getIfNestingLevel(),
40+
'ifNestingLevelWeight' => $metrics->getIfNestingLevelWeight(),
41+
'elseCount' => $metrics->getElseCount(),
42+
'elseCountWeight' => $metrics->getElseCountWeight(),
3443
'score' => $metrics->getScore()
3544
]
3645
]
3746
];
38-
39-
$metrics->setScore(
40-
$metrics->getPropertyCallCount() + $metrics->getVariableCount() + $metrics->getArgCount()
41-
);
4247
}
4348
}
4449

src/Business/MetricsFacade.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
use Phauthentic\CodeQualityMetrics\Business\Cognitive\CognitiveMetricsCollection;
88
use Phauthentic\CodeQualityMetrics\Business\Cognitive\CognitiveMetricsCollector;
9+
use Phauthentic\CodeQualityMetrics\Business\Cognitive\Exporter\CsvExporter;
10+
use Phauthentic\CodeQualityMetrics\Business\Cognitive\Exporter\HtmlExporter;
11+
use Phauthentic\CodeQualityMetrics\Business\Cognitive\Exporter\JsonExporter;
912
use Phauthentic\CodeQualityMetrics\Business\Cognitive\ScoreCalculator;
1013
use Phauthentic\CodeQualityMetrics\Business\Halstead\HalsteadMetricsCollection;
1114
use Phauthentic\CodeQualityMetrics\Business\Halstead\HalsteadMetricsCollector;
@@ -72,4 +75,19 @@ public function loadConfig(string $configFilePath): void
7275
{
7376
$this->configService->loadConfig($configFilePath);
7477
}
78+
79+
public function metricsCollectionToCsv(CognitiveMetricsCollection $metricsCollection, string $filename): void
80+
{
81+
(new CsvExporter())->export($metricsCollection, $filename);
82+
}
83+
84+
public function metricsCollectionToJson(CognitiveMetricsCollection $metricsCollection, string $filename): void
85+
{
86+
(new JsonExporter())->export($metricsCollection, $filename);
87+
}
88+
89+
public function metricsCollectionToHtml(CognitiveMetricsCollection $metricsCollection, string $filename): void
90+
{
91+
(new HtmlExporter())->export($metricsCollection, $filename);
92+
}
7593
}

src/Command/CognitiveMetricsCommand.php

Lines changed: 87 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424
class CognitiveMetricsCommand extends Command
2525
{
2626
// Option names for exporting metrics in different formats and loading a configuration file.
27-
private const OPTION_EXPORT_JSON = 'export-json';
28-
private const OPTION_EXPORT_CSV = 'export-csv';
29-
private const OPTION_EXPORT_HTML = 'export-html';
3027
private const OPTION_CONFIG_FILE = 'config';
28+
private const OPTION_BASELINE = 'baseline';
29+
private const OPTION_REPORT_TYPE = 'report-type';
30+
private const OPTION_REPORT_FILE = 'report-file';
3131

3232
// Argument name for the path to the PHP files or directories.
3333
private const ARGUMENT_PATH = 'path';
@@ -52,10 +52,10 @@ protected function configure(): void
5252
{
5353
$this
5454
->addArgument(self::ARGUMENT_PATH, InputArgument::REQUIRED, 'Path to PHP files or directories to parse.')
55-
->addOption(self::OPTION_EXPORT_CSV, null, InputArgument::OPTIONAL, 'Writes metrics to a CSV file', null)
56-
->addOption(self::OPTION_EXPORT_JSON, null, InputArgument::OPTIONAL, 'Writes metrics to a JSON file', null)
57-
->addOption(self::OPTION_EXPORT_HTML, null, InputArgument::OPTIONAL, 'Writes metrics to an HTML file', null)
58-
->addOption(self::OPTION_CONFIG_FILE, null, InputArgument::OPTIONAL, 'Path to a configuration file', null);
55+
->addOption(self::OPTION_CONFIG_FILE, 'c', InputArgument::OPTIONAL, 'Path to a configuration file', null)
56+
->addOption(self::OPTION_BASELINE, 'b', InputArgument::OPTIONAL, 'Baseline file to get the delta.', null)
57+
->addOption(self::OPTION_REPORT_TYPE, 'r', InputArgument::OPTIONAL, 'Type of report to generate (json, csv, html).', null, ['json', 'csv', 'html'])
58+
->addOption(self::OPTION_REPORT_FILE, 'f', InputArgument::OPTIONAL, 'File to write the report to.');
5959
}
6060

6161
/**
@@ -76,18 +76,62 @@ protected function execute(InputInterface $input, OutputInterface $output): int
7676
return Command::FAILURE;
7777
}
7878

79+
// Load baseline if the option is provided.
80+
$baseline = $this->handleBaseLine($input);
81+
7982
// Generate metrics for the provided path.
8083
$metricsCollection = $this->metricsFacade->getCognitiveMetrics($path);
8184

82-
// Render the metrics to the console.
83-
$this->metricTextRenderer->render($metricsCollection, $output);
84-
8585
// Handle different export options.
86-
$this->handleExportOptions($input, $metricsCollection);
86+
if (!$this->handleExportOptions($input, $output, $metricsCollection)) {
87+
return Command::FAILURE;
88+
}
89+
90+
// Render the metrics to the console.
91+
$this->metricTextRenderer->render($metricsCollection, $baseline, $output);
8792

8893
return Command::SUCCESS;
8994
}
9095

96+
/**
97+
* Handles the baseline option and loads the baseline file if provided.
98+
*
99+
* @param InputInterface $input
100+
* @return array<int, array<string, mixed>>
101+
* @throws Exception
102+
*/
103+
private function handleBaseLine(InputInterface $input): array
104+
{
105+
$baseline = [];
106+
$baselineFile = $input->getOption(self::OPTION_BASELINE);
107+
if ($baselineFile) {
108+
$baseline = $this->loadBaseline($baselineFile);
109+
}
110+
111+
return $baseline;
112+
}
113+
114+
/**
115+
* Loads the baseline file and returns the data as an array.
116+
*
117+
* @param string $baselineFile
118+
* @return array<int, array<string, mixed>> $baseline
119+
* @throws \JsonException
120+
*/
121+
private function loadBaseline(string $baselineFile): array
122+
{
123+
if (!file_exists($baselineFile)) {
124+
throw new Exception('Baseline file does not exist.');
125+
}
126+
127+
$baseline = file_get_contents($baselineFile);
128+
if ($baseline === false) {
129+
throw new Exception('Failed to read baseline file.');
130+
}
131+
132+
return json_decode($baseline, true, 512, JSON_THROW_ON_ERROR);
133+
}
134+
91135
/**
92136
* Loads configuration and handles errors.
93137
*
@@ -110,22 +154,41 @@ private function loadConfiguration(string $configFile, OutputInterface $output):
110154
* Handles exporting metrics to different formats based on user input.
111155
*
112156
* @param InputInterface $input
157+
* @param OutputInterface $output
113158
* @param CognitiveMetricsCollection $metricsCollection
114-
* @return void
159+
* @return bool
115160
*/
116-
private function handleExportOptions(InputInterface $input, CognitiveMetricsCollection $metricsCollection): void
161+
private function handleExportOptions(InputInterface $input, OutputInterface $output, CognitiveMetricsCollection $metricsCollection): bool
117162
{
118-
$options = [
119-
self::OPTION_EXPORT_CSV => 'metricsCollectionToCsv',
120-
self::OPTION_EXPORT_JSON => 'metricsCollectionToJson',
121-
self::OPTION_EXPORT_HTML => 'metricsCollectionToHtml',
122-
];
123-
124-
foreach ($options as $option => $method) {
125-
$file = $input->getOption($option);
126-
if ($file) {
127-
$this->metricsFacade->$method($metricsCollection, $file);
128-
}
163+
$reportType = $input->getOption(self::OPTION_REPORT_TYPE);
164+
$reportFile = $input->getOption(self::OPTION_REPORT_FILE);
165+
166+
// If both options are missing, return success
167+
if ($reportType === null && $reportFile === null) {
168+
return true;
129169
}
170+
171+
// If one of the options is provided but the other is missing, return false
172+
if (($reportType !== null && $reportFile === null) || ($reportType === null && $reportFile !== null)) {
173+
$output->writeln('<error>Both report type and file must be provided.</error>');
174+
return false;
175+
}
176+
177+
// Validate the report type option
178+
if (!in_array($reportType, ['json', 'csv', 'html'])) {
179+
$message = sprintf('Invalid report type `%s` provided. Only `json`, `csv`, and `html` are accepted.', $reportType);
180+
$output->writeln('<error>' . $message . '</error>');
181+
return false;
182+
}
183+
184+
// Proceed with export based on the report type
185+
match ($reportType) {
186+
'json' => $this->metricsFacade->metricsCollectionToJson($metricsCollection, $reportFile),
187+
'csv' => $this->metricsFacade->metricsCollectionToCsv($metricsCollection, $reportFile),
188+
'html' => $this->metricsFacade->metricsCollectionToHtml($metricsCollection, $reportFile),
189+
default => null,
190+
};
191+
192+
return true;
130193
}
131194
}

src/Command/Presentation/CognitiveMetricTextRenderer.php

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@
1414
*/
1515
class CognitiveMetricTextRenderer
1616
{
17-
public function render(CognitiveMetricsCollection $metricsCollection, OutputInterface $output): void
17+
/**
18+
* @param CognitiveMetricsCollection $metricsCollection
19+
* @param array<int, array<string, mixed>> $baseline
20+
* @param OutputInterface $output
21+
*/
22+
public function render(CognitiveMetricsCollection $metricsCollection, array $baseline, OutputInterface $output): void
1823
{
1924
$groupedByClass = $metricsCollection->groupBy('class');
2025

@@ -27,7 +32,7 @@ public function render(CognitiveMetricsCollection $metricsCollection, OutputInte
2732

2833
$rows = [];
2934
foreach ($metrics as $metric) {
30-
$row = $this->prepareTableRow($metric);
35+
$row = $this->prepareTableRow($metric, $baseline);
3136
$rows[] = $row;
3237
}
3338

@@ -58,9 +63,10 @@ protected function getTableHeaders(): array
5863

5964
/**
6065
* @param CognitiveMetrics $metrics
66+
* @param array<int, array<string, mixed>> $baseline
6167
* @return array<string, mixed>
6268
*/
63-
protected function prepareTableRow(CognitiveMetrics $metrics): array
69+
protected function prepareTableRow(CognitiveMetrics $metrics, array $baseline): array
6470
{
6571
$row = [
6672
'methodName' => $metrics->getMethod(),
@@ -91,6 +97,53 @@ protected function prepareTableRow(CognitiveMetrics $metrics): array
9197
$getMethodWeight = 'get' . $key . 'Weight';
9298
$weight = $metrics->{$getMethodWeight}();
9399
$row[$key] = $metrics->{$getMethod}() . ' (' . round($weight, 3) . ')';
100+
$row = $this->addDelta($row, $metrics, $baseline, $key, $weight);
101+
}
102+
103+
return $row;
104+
}
105+
106+
/**
107+
* @param array<string, mixed> $row
108+
* @param CognitiveMetrics $metrics
109+
* @param array<int, array<string, mixed>> $baseline
110+
* @param string $key
111+
* @param float $weight
112+
* @return array<string, mixed>
113+
*/
114+
private function addDelta(
115+
array $row,
116+
CognitiveMetrics $metrics,
117+
array $baseline,
118+
string $key,
119+
float $weight
120+
): array {
121+
foreach ($baseline as $classMetrics) {
122+
if (!isset($classMetrics['class']) || $classMetrics['class'] !== $metrics->getClass()) {
123+
continue;
124+
}
125+
126+
if (!isset($classMetrics['methods'][$metrics->getMethod()])) {
127+
continue;
128+
}
129+
130+
$method = $key . 'Weight';
131+
if (!isset($classMetrics['methods'][$metrics->getMethod()][$method])) {
132+
continue;
133+
}
134+
135+
$baselineWeight = (float)$classMetrics['methods'][$metrics->getMethod()][$method];
136+
if ($baselineWeight === $weight) {
137+
return $row;
138+
}
139+
140+
if ($baselineWeight > $weight) {
141+
$row[$key] .= PHP_EOL . '<info>Δ -' . round($baselineWeight - $weight, 3) . '</info>';
142+
}
143+
144+
if ($baselineWeight < $weight) {
145+
$row[$key] .= PHP_EOL . '<error>Δ +' . round($weight - $baselineWeight, 3) . '</error>';
146+
}
94147
}
95148

96149
return $row;

0 commit comments

Comments
 (0)