Passed
Push — main ( 5411c5...d846f8 )
by mikhail
03:10
created

CommentDensity::analyzeDirectories()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 44
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 33
CRAP Score 4.0004

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 31
c 2
b 0
f 0
dl 0
loc 44
ccs 33
cts 34
cp 0.9706
rs 9.424
cc 4
nc 4
nop 1
crap 4.0004
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\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...
13
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...
14
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...
15
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...
16
use SavinMikhail\CommentsDensity\Reporters\ReporterInterface;
17
use SplFileInfo;
18
19
use function memory_get_peak_usage;
20
use function microtime;
21
use function round;
22
23
final class CommentDensity
24
{
25
    private bool $exceedThreshold = false;
26
27 3
    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
        private readonly CDS $cds,
33
        private readonly ComToLoc $comToLoc,
34
        private readonly MissingDocBlockAnalyzer $missingDocBlock,
35
    ) {
36 3
    }
37
38 1
    public function analyzeDirectories(array $directories): bool
39
    {
40 1
        $startTime = microtime(true);
41 1
        $comments = [];
42 1
        $commentStatistics = [];
43 1
        $totalLinesOfCode = 0;
44 1
        $cdsSum = 0;
45 1
        $filesAnalyzed = 0;
46
47 1
        foreach ($directories as $directory) {
48 1
            $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory));
49
            /** @var SplFileInfo $file */
50 1
            foreach ($iterator as $file) {
51 1
                if ($this->isInWhitelist($file->getRealPath())) {
52
                    continue;
53
                }
54 1
                $this->fileAnalyzer->analyzeFile(
55 1
                    $file,
56 1
                    $commentStatistics,
57 1
                    $comments,
58 1
                    $totalLinesOfCode,
59 1
                    $cdsSum
60 1
                );
61 1
                $filesAnalyzed++;
62
            }
63
        }
64
65 1
        $endTime = microtime(true);
66 1
        $executionTimeMS = round(($endTime - $startTime) * 1000, 2);
67 1
        $peakMemoryUsage = memory_get_peak_usage(true);
68
69 1
        $outputDTO = $this->createOutputDTO(
70 1
            $comments,
71 1
            $commentStatistics,
72 1
            $totalLinesOfCode,
73 1
            $cdsSum / $totalLinesOfCode,
74 1
            $filesAnalyzed,
75 1
            $executionTimeMS,
76 1
            $peakMemoryUsage
77 1
        );
78
79 1
        $this->reporter->report($outputDTO);
80
81 1
        return $this->exceedThreshold;
82
    }
83
84 1
    private function createOutputDTO(
85
        array $comments,
86
        array $commentStatistics,
87
        int $linesOfCode,
88
        float $cds,
89
        int $filesAnalyzed,
90
        float $executionTime,
91
        float $peakMemoryUsage
92
    ): OutputDTO {
93 1
        $metricsDTO = new PerformanceMetricsDTO(
94 1
            $executionTime,
95 1
            round($peakMemoryUsage / 1024 / 1024, 2)
96 1
        );
97
98 1
        $cdsDTO = $this->cds->prepareCDS($cds);
99 1
        $comToLocDTO = $this->comToLoc->prepareComToLoc($commentStatistics, $linesOfCode);
100
101
102 1
        if ($this->cds->hasExceededThreshold() || $this->comToLoc->hasExceededThreshold()) {
103 1
            $this->exceedThreshold = true;
104
        }
105 1
        return new OutputDTO(
106 1
            $filesAnalyzed,
107 1
            $this->prepareCommentStatistics($commentStatistics),
108 1
            $this->prepareComments($comments),
109 1
            $metricsDTO,
110 1
            $comToLocDTO,
111 1
            $cdsDTO
112 1
        );
113
    }
114
115 1
    private function prepareCommentStatistics(array $commentStatistics): array
116
    {
117 1
        $preparedStatistics = [];
118 1
        foreach ($commentStatistics as $type => $count) {
119 1
            if ($type === 'missingDocblock') {
120 1
                $preparedStatistics[] = new CommentStatisticsDTO(
121 1
                    $this->missingDocBlock->getColor(),
122 1
                    $this->missingDocBlock->getName(),
123 1
                    $count,
124 1
                    $this->missingDocBlock->getStatColor($count, $this->configDTO->thresholds)
125 1
                );
126 1
                $this->exceedThreshold = $this->exceedThreshold ?: $this->missingDocBlock->hasExceededThreshold();
127 1
                continue;
128
            }
129 1
            $commentType = $this->commentFactory->getCommentType($type);
130 1
            if ($commentType) {
131 1
                $preparedStatistics[] = new CommentStatisticsDTO(
132 1
                    $commentType->getColor(),
133 1
                    $commentType->getName(),
134 1
                    $count,
135 1
                    $commentType->getStatColor($count, $this->configDTO->thresholds)
136 1
                );
137 1
                $this->exceedThreshold = $this->exceedThreshold ?: $commentType->hasExceededThreshold();
0 ignored issues
show
Bug introduced by
The method hasExceededThreshold() does not exist on SavinMikhail\CommentsDen...ts\CommentTypeInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to SavinMikhail\CommentsDen...ts\CommentTypeInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

137
                $this->exceedThreshold = $this->exceedThreshold ?: $commentType->/** @scrutinizer ignore-call */ hasExceededThreshold();
Loading history...
138
            }
139
        }
140 1
        return  $preparedStatistics;
141
    }
142
143 1
    private function prepareComments(array $comments): array
144
    {
145 1
        $preparedComments = [];
146 1
        foreach ($comments as $comment) {
147
            /** @var CommentTypeInterface|string $commentType */
148 1
            $commentType = $comment['type'];
149 1
            if ($commentType === 'missingDocblock') {
150
                $preparedComments[] = new CommentDTO(
151
                    'missingDocblock',
152
                    'red',
153
                    $comment['file'],
154
                    $comment['line'],
155
                    $comment['content']
156
                );
157
                continue;
158
            }
159 1
            if ($commentType->getAttitude() === 'good') {
160 1
                continue;
161
            }
162 1
            $preparedComments[] = new CommentDTO(
163 1
                $commentType->getName(),
164 1
                $commentType->getColor(),
165 1
                $comment['file'],
166 1
                $comment['line'],
167 1
                $comment['content']
168 1
            );
169
        }
170 1
        return $preparedComments;
171
    }
172
173
    public function analyzeFile(string $filename): bool
174
    {
175
        $comments = [];
176
        $commentStatistics = [];
177
        $totalLinesOfCode = 0;
178
        $cdsSum = 0;
179
180
        $this->fileAnalyzer->analyzeFile(
181
            new SplFileInfo($filename),
182
            $commentStatistics,
183
            $comments,
184
            $totalLinesOfCode,
185
            $cdsSum
186
        );
187
188
        return $this->exceedThreshold;
189
    }
190
191 1
    private function isInWhitelist(string $filePath): bool
192
    {
193 1
        foreach ($this->configDTO->exclude as $whitelistedDir) {
194
            if (str_contains($filePath, $whitelistedDir)) {
195
                return true;
196
            }
197
        }
198 1
        return false;
199
    }
200
}
201