Passed
Push — main ( 395368...b13408 )
by mikhail
02:58
created

CommentDensity::createOutputDTO()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 10
c 1
b 0
f 0
nc 1
nop 7
dl 0
loc 20
ccs 0
cts 12
cp 0
crap 2
rs 9.9332
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SavinMikhail\CommentsDensity;
6
7
use RecursiveDirectoryIterator;
8
use RecursiveIteratorIterator;
9
use SavinMikhail\CommentsDensity\Comments\CommentFactory;
10
use SavinMikhail\CommentsDensity\Comments\CommentTypeInterface;
11
use SavinMikhail\CommentsDensity\DTO\Input\ConfigDTO;
0 ignored issues
show
Bug introduced by
The type SavinMikhail\CommentsDensity\DTO\Input\ConfigDTO was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
12
use SavinMikhail\CommentsDensity\DTO\Output\CdsDTO;
0 ignored issues
show
Bug introduced by
The type SavinMikhail\CommentsDensity\DTO\Output\CdsDTO was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
use SavinMikhail\CommentsDensity\DTO\Output\CommentDTO;
0 ignored issues
show
Bug introduced by
The type SavinMikhail\CommentsDensity\DTO\Output\CommentDTO was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
14
use SavinMikhail\CommentsDensity\DTO\Output\CommentStatisticsDTO;
0 ignored issues
show
Bug introduced by
The type SavinMikhail\CommentsDen...ut\CommentStatisticsDTO was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
use SavinMikhail\CommentsDensity\DTO\Output\ComToLocDTO;
0 ignored issues
show
Bug introduced by
The type SavinMikhail\CommentsDen...\DTO\Output\ComToLocDTO was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
use SavinMikhail\CommentsDensity\DTO\Output\OutputDTO;
0 ignored issues
show
Bug introduced by
The type SavinMikhail\CommentsDensity\DTO\Output\OutputDTO was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
17
use SavinMikhail\CommentsDensity\DTO\Output\PerformanceMetricsDTO;
0 ignored issues
show
Bug introduced by
The type SavinMikhail\CommentsDen...t\PerformanceMetricsDTO was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use SavinMikhail\CommentsDensity\Reporters\ReporterInterface;
19
use SplFileInfo;
20
21
use function round;
22
23
final class CommentDensity
24
{
25
    private bool $exceedThreshold = false;
26
27
    public function __construct(
28
        private readonly ConfigDTO $configDTO,
29
        private readonly CommentFactory $commentFactory,
30
        private readonly FileAnalyzer $fileAnalyzer,
0 ignored issues
show
Bug introduced by
The type SavinMikhail\CommentsDensity\FileAnalyzer was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
31
        private readonly ReporterInterface $reporter,
32
    ) {
33
    }
34
35
    public function analyzeDirectories(array $directories): bool
36
    {
37
        $startTime = microtime(true);
38
        $comments = [];
39
        $commentStatistics = [];
40
        $totalLinesOfCode = 0;
41
        $cdsSum = 0;
42
        $filesAnalyzed = 0;
43
44
        foreach ($directories as $directory) {
45
            $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory));
46
            /** @var SplFileInfo $file */
47
            foreach ($iterator as $file) {
48
                if ($this->isInWhitelist($file->getRealPath())) {
49
                    continue;
50
                }
51
                $this->fileAnalyzer->analyzeFile(
52
                    $file,
53
                    $commentStatistics,
54
                    $comments,
55
                    $totalLinesOfCode,
56
                    $cdsSum
57
                );
58
                $filesAnalyzed++;
59
            }
60
        }
61
62
        $endTime = microtime(true);
63
        $executionTimeMS = round(($endTime - $startTime) * 1000, 2);
64
        $peakMemoryUsage = memory_get_peak_usage(true);
65
66
        $outputDTO = $this->createOutputDTO(
67
            $comments,
68
            $commentStatistics,
69
            $totalLinesOfCode,
70
            $cdsSum / $totalLinesOfCode,
71
            $filesAnalyzed,
72
            $executionTimeMS,
73
            $peakMemoryUsage
74
        );
75
76
        $this->reporter->report($outputDTO);
77
78
        return $this->exceedThreshold;
79
    }
80
81
    private function createOutputDTO(
82
        array $comments,
83
        array $commentStatistics,
84
        int $linesOfCode,
85
        float $cds,
86
        int $filesAnalyzed,
87
        float $executionTime,
88
        float $peakMemoryUsage
89
    ): OutputDTO {
90
        $performanceMetricsDTO = new PerformanceMetricsDTO(
91
            $executionTime,
92
            round($peakMemoryUsage / 1024 / 1024, 2)
93
        );
94
        return new OutputDTO(
95
            $filesAnalyzed,
96
            $this->prepareCommentStatistics($commentStatistics),
97
            $this->prepareComments($comments),
98
            $performanceMetricsDTO,
99
            $this->prepareComToLoc($commentStatistics, $linesOfCode),
100
            $this->prepareCDS($cds)
101
        );
102
    }
103
104
    private function prepareCDS(float $cds): CdsDTO
105
    {
106
        $cds = round($cds, 2);
107
        return new CdsDTO(
108
            $cds,
109
            $this->getColorForCDS($cds),
110
        );
111
    }
112
113
    private function getColorForCDS(float $cds): string
114
    {
115
        if (! isset($this->thresholds['CDS'])) {
0 ignored issues
show
Bug Best Practice introduced by
The property thresholds does not exist on SavinMikhail\CommentsDensity\CommentDensity. Did you maybe forget to declare it?
Loading history...
116
            return 'white';
117
        }
118
        if ($cds >= $this->thresholds['CDS']) {
119
            return 'green';
120
        }
121
        $this->exceedThreshold = true;
122
        return 'red';
123
    }
124
125
    private function prepareComToLoc(array $commentStatistics, int $linesOfCode): ComToLocDTO
126
    {
127
        $ratio = $this->getRatio($commentStatistics, $linesOfCode);
128
        return new ComToLocDTO(
129
            $ratio,
130
            $this->getColorForRatio($ratio)
131
        );
132
    }
133
134
    private function getRatio(array $commentStatistics, int $linesOfCode): float
135
    {
136
        $totalComments = array_sum($commentStatistics);
137
        return round($totalComments / $linesOfCode, 2);
138
    }
139
140
    private function getColorForRatio(float $ratio): string
141
    {
142
        if (! isset($this->thresholds['Com/LoC'])) {
0 ignored issues
show
Bug Best Practice introduced by
The property thresholds does not exist on SavinMikhail\CommentsDensity\CommentDensity. Did you maybe forget to declare it?
Loading history...
143
            return 'white';
144
        }
145
        if ($ratio >= $this->thresholds['Com/LoC']) {
146
            return 'green';
147
        }
148
        $this->exceedThreshold = true;
149
        return 'red';
150
    }
151
152
    private function prepareCommentStatistics(array $commentStatistics): array
153
    {
154
        $preparedCommentStatistics = [];
155
        foreach ($commentStatistics as $type => $count) {
156
            if ($type === 'missingDocblock') {
157
                $preparedCommentStatistics[] = new CommentStatisticsDTO(
158
                    $this->getMissingDocBlockColor(),
159
                    'missingDocblock',
160
                    $count,
161
                    $this->getMissingDocBlockStatColor($count)
162
                );
163
                continue;
164
            }
165
            $commentType = $this->commentFactory->getCommentType($type);
166
            if ($commentType) {
167
                $preparedCommentStatistics[] = new CommentStatisticsDTO(
168
                    $commentType->getColor(),
169
                    $commentType->getName(),
170
                    $count,
171
                    $commentType->getStatColor($count, $this->configDTO->thresholds)
172
                );
173
            }
174
        }
175
        return  $preparedCommentStatistics;
176
    }
177
178
    private function getMissingDocBlockColor(): string
179
    {
180
        return 'red';
181
    }
182
183
    private function getMissingDocBlockStatColor(float $count): string
184
    {
185
        if (! isset($this->thresholds['missingDocBlock'])) {
0 ignored issues
show
Bug Best Practice introduced by
The property thresholds does not exist on SavinMikhail\CommentsDensity\CommentDensity. Did you maybe forget to declare it?
Loading history...
186
            return 'white';
187
        }
188
        if ($count <= $this->thresholds['missingDocBlock']) {
189
            return 'green';
190
        }
191
        $this->exceedThreshold = true;
192
        return 'red';
193
    }
194
195
    private function prepareComments(array $comments): array
196
    {
197
        $preparedComments = [];
198
        foreach ($comments as $comment) {
199
            /** @var CommentTypeInterface|string $commentType */
200
            $commentType = $comment['type'];
201
            if ($commentType === 'missingDocblock') {
202
                $preparedComments[] = new CommentDTO(
203
                    'missingDocblock',
204
                    'red',
205
                    $comment['file'],
206
                    $comment['line'],
207
                    $comment['content']
208
                );
209
                continue;
210
            }
211
            if ($commentType->getAttitude() === 'good') {
212
                continue;
213
            }
214
            $preparedComments[] = new CommentDTO(
215
                $commentType->getName(),
216
                $commentType->getColor(),
217
                $comment['file'],
218
                $comment['line'],
219
                $comment['content']
220
            );
221
        }
222
        return $preparedComments;
223
    }
224
225
    public function analyzeFile(string $filename): bool
226
    {
227
        $comments = [];
228
        $commentStatistics = [];
229
        $totalLinesOfCode = 0;
230
        $cdsSum = 0;
231
232
        $this->fileAnalyzer->analyzeFile(
233
            new SplFileInfo($filename),
234
            $commentStatistics,
235
            $comments,
236
            $totalLinesOfCode,
237
            $cdsSum
238
        );
239
240
241
        return $this->exceedThreshold;
242
    }
243
244
    private function isInWhitelist(string $filePath): bool
245
    {
246
        foreach ($this->configDTO->exclude as $whitelistedDir) {
247
            if (str_contains($filePath, $whitelistedDir)) {
248
                return true;
249
            }
250
        }
251
        return false;
252
    }
253
}
254