Passed
Push — main ( dfc5c9...2b32e1 )
by mikhail
03:37
created

MissingDocBlockAnalyzer::getStatColor()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

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