Test Failed
Push — main ( 2b32e1...14d6a2 )
by mikhail
03:00
created

MissingDocBlockAnalyzer::isDocBlockRequired()   C

Complexity

Conditions 12
Paths 32

Size

Total Lines 37
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 12
eloc 19
nc 32
nop 3
dl 0
loc 37
ccs 18
cts 18
cp 1
crap 12
rs 6.9666
c 1
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 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...
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->isVariable($token)) {
94 12
            if (! $this->tokenizer->isPropertyOrConstant($tokens, $index)) {
95 7
                return false;
96
            }
97
        }
98
99 13
        if ($this->tokenizer->isConst($token)) {
100
            if (! $this->tokenizer->isPropertyOrConstant($tokens, $index)) {
101
                return false;
102 12
            }
103
        }
104 12
105 12
        return true;
106 12
    }
107 12
108 12
    private function createMissingDocBlockStat(array $token, string $filename): array
109 12
    {
110
        return [
111
            'type' => 'missingDocblock',
112 19
            'content' => '',
113
            'file' => $filename,
114 19
            'line' => $token[2]
115
        ];
116
    }
117
118
    public function getMissingDocblocks(array $tokens, string $filename): array
119
    {
120
        return $this->analyzeTokens($tokens, $filename);
121
    }
122
123
    public function getColor(): string
124
    {
125
        return 'red';
126
    }
127
128
    public function getStatColor(float $count, array $thresholds): string
129
    {
130
        if (! isset($thresholds['missingDocBlock'])) {
131
            return 'white';
132
        }
133
        if ($count <= $thresholds['missingDocBlock']) {
134
            return 'green';
135
        }
136
        $this->exceedThreshold = true;
137
        return 'red';
138
    }
139
140
    public function hasExceededThreshold(): bool
141
    {
142
        return $this->exceedThreshold;
143
    }
144
145
    public function getName(): string
146
    {
147
        return 'missingDocblock';
148
    }
149
}
150