Passed
Push — main ( c6e244...97b3cf )
by mikhail
03:30
created

MissingDocBlockAnalyzer   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 123
Duplicated Lines 0 %

Test Coverage

Coverage 79.69%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 52
c 3
b 0
f 0
dl 0
loc 123
ccs 51
cts 64
cp 0.7969
rs 10
wmc 24

10 Methods

Rating   Name   Duplication   Size   Complexity  
A getStatColor() 0 10 3
A shouldAnalyzeToken() 0 11 1
A getColor() 0 3 1
A __construct() 0 4 1
A createMissingDocBlockStat() 0 7 1
A hasExceededThreshold() 0 3 1
A getMissingDocblocks() 0 3 1
A analyzeTokens() 0 24 6
A getName() 0 3 1
B isDocBlockRequired() 0 25 8
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SavinMikhail\CommentsDensity\MissingDocblock;
6
7
use SavinMikhail\CommentsDensity\DTO\Input\MissingDocblockConfigDTO;
0 ignored issues
show
Bug introduced by
The type SavinMikhail\CommentsDen...issingDocblockConfigDTO 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...
8
9
use function is_array;
10
11
use const T_CLASS;
12
use const T_CONST;
13
use const T_DOC_COMMENT;
14
use const T_ENUM;
15
use const T_FUNCTION;
16
use const T_INTERFACE;
17
use const T_TRAIT;
18
use const T_VARIABLE;
19
20
final class MissingDocBlockAnalyzer
21
{
22
    private bool $exceedThreshold = false;
23
24 23
    public function __construct(
25
        private readonly Tokenizer $tokenizer,
0 ignored issues
show
Bug introduced by
The type SavinMikhail\CommentsDen...ssingDocblock\Tokenizer 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...
26
        private readonly MissingDocblockConfigDTO $docblockConfigDTO,
27
    ) {
28 23
    }
29
30
    /**
31
     * Analyzes the tokens of a file for docblocks.
32
     *
33
     * @param array $tokens The tokens to analyze.
34
     * @return array The analysis results.
35
     */
36 23
    private function analyzeTokens(array $tokens, string $filename): array
37
    {
38 23
        $lastDocBlock = null;
39 23
        $missingDocBlocks = [];
40 23
        $tokenCount = count($tokens);
41
42 23
        for ($i = 0; $i < $tokenCount; $i++) {
43 23
            $token = $tokens[$i];
44
45 23
            if (! is_array($token)) {
46 23
                continue;
47
            }
48
49 23
            if ($token[0] === T_DOC_COMMENT) {
50 9
                $lastDocBlock = $token[1];
51 23
            } elseif ($this->isDocBlockRequired($token, $tokens, $i)) {
52 14
                if (empty($lastDocBlock)) {
53 12
                    $missingDocBlocks[] = $this->createMissingDocBlockStat($token, $filename);
54
                }
55 14
                $lastDocBlock = null;
56
            }
57
        }
58
59 23
        return $missingDocBlocks;
60
    }
61
62 23
    private function shouldAnalyzeToken(array $token): bool
63
    {
64 23
        return match ($token[0]) {
65 23
            T_CLASS => $this->docblockConfigDTO->class,
66 23
            T_TRAIT => $this->docblockConfigDTO->trait,
67 23
            T_INTERFACE => $this->docblockConfigDTO->interface,
68 23
            T_ENUM => $this->docblockConfigDTO->enum,
69 23
            T_FUNCTION => $this->docblockConfigDTO->function,
70 23
            T_CONST => $this->docblockConfigDTO->constant,
71 23
            T_VARIABLE => $this->docblockConfigDTO->property,
72 23
            default => false,
73 23
        };
74
    }
75
76 23
    private function isDocBlockRequired(array $token, array $tokens, int $index): bool
77
    {
78 23
        if (!$this->shouldAnalyzeToken($token)) {
79 23
            return false;
80
        }
81
82 23
        if ($this->tokenizer->isClass($token)) {
83 15
               return ! $this->tokenizer->isAnonymousClass($tokens, $index)
84 15
                && !$this->tokenizer->isClassNameResolution($tokens, $index);
85
        }
86
87 22
        if ($this->tokenizer->isFunction($token)) {
88 7
             return  ! $this->tokenizer->isAnonymousFunction($tokens, $index)
89 7
                && ! $this->tokenizer->isFunctionImport($tokens, $index);
90
        }
91
92 19
        if ($this->tokenizer->isVariable($token)) {
93 15
            return $this->tokenizer->isPropertyOrConstant($tokens, $index);
94
        }
95
96 4
        if ($this->tokenizer->isConst($token)) {
97 1
            return $this->tokenizer->isPropertyOrConstant($tokens, $index);
98
        }
99
100 3
        return true;
101
    }
102
103 12
    private function createMissingDocBlockStat(array $token, string $filename): array
104
    {
105 12
        return [
106 12
            'type' => 'missingDocblock',
107 12
            'content' => '',
108 12
            'file' => $filename,
109 12
            'line' => $token[2]
110 12
        ];
111
    }
112
113 23
    public function getMissingDocblocks(array $tokens, string $filename): array
114
    {
115 23
        return $this->analyzeTokens($tokens, $filename);
116
    }
117
118
    public function getColor(): string
119
    {
120
        return 'red';
121
    }
122
123
    public function getStatColor(float $count, array $thresholds): string
124
    {
125
        if (! isset($thresholds['missingDocBlock'])) {
126
            return 'white';
127
        }
128
        if ($count <= $thresholds['missingDocBlock']) {
129
            return 'green';
130
        }
131
        $this->exceedThreshold = true;
132
        return 'red';
133
    }
134
135
    public function hasExceededThreshold(): bool
136
    {
137
        return $this->exceedThreshold;
138
    }
139
140
    public function getName(): string
141
    {
142
        return 'missingDocblock';
143
    }
144
}
145