Completed
Push — master ( 14c836...906b18 )
by Björn
06:09 queued 03:52
created

RequiredDocBlockSniff   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 148
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
dl 0
loc 148
rs 10
c 0
b 0
f 0
wmc 11
lcom 1
cbo 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BestIt\Sniffs\Commenting;
6
7
use BestIt\CodeSniffer\CodeError;
8
use BestIt\CodeSniffer\CodeWarning;
9
use BestIt\CodeSniffer\Helper\PropertyHelper;
10
use BestIt\Sniffs\AbstractSniff;
11
use BestIt\Sniffs\DocPosProviderTrait;
12
use function array_keys;
13
use function lcfirst;
14
use function ucfirst;
15
use const T_CLASS;
16
use const T_CONST;
17
use const T_FUNCTION;
18
use const T_INTERFACE;
19
use const T_PRIVATE;
20
use const T_PROTECTED;
21
use const T_PUBLIC;
22
use const T_TRAIT;
23
use const T_VARIABLE;
24
25
/**
26
 * @author blange <[email protected]>
27
 * @package BestIt\Sniffs\Commenting
28
 */
29
class RequiredDocBlockSniff extends AbstractSniff
30
{
31
    use DocPosProviderTrait;
32
33
    /**
34
     * There MUST be a doc block before a Class, Constant, Interface, Function, Trait, Variable.
35
     *
36
     * It will get suffixed like MissingDocBlockClass.
37
     */
38
    public const CODE_MISSING_DOC_BLOCK_PREFIX = 'MissingDocBlock';
39
40
    /**
41
     * The doc block before a Class, Constant, Interface, Function, Trait, Variable must be multi-line.
42
     *
43
     * It will be suffixed with the name of the structure like NoMultiLineDocBlockClass.
44
     */
45
    public const CODE_NO_MULTI_LINE_DOC_BLOCK_PREFIX = 'NoMultiLineDocBlock';
46
47
    /**
48
     * The message for missing doc blocks.
49
     */
50
    private const MESSAGE_MISSING_DOC_BLOCK = 'Please provide a doc block for your %s.';
51
52
    /**
53
     * The message for missing doc blocks in variable declarations.
54
     */
55
    private const MESSAGE_MISSING_DOC_BLOCK_VAR = 'Please provide a doc block for your %s or define a property type.';
56
57
    /**
58
     * The error message for the inline block.
59
     */
60
    private const MESSAGE_NO_MULTI_LINE_DOC_BLOCK_PREFIX = 'Please provide a multi line doc block for your %s.';
61
62
    /**
63
     * Maps the registered tokens to a readable key.
64
     *
65
     * @var array
66
     */
67
    private array $registeredTokens = [
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_ARRAY, expecting T_FUNCTION or T_CONST
Loading history...
68
        T_CLASS => 'Class',
69
        T_CONST => 'Constant',
70
        T_INTERFACE => 'Interface',
71
        T_FUNCTION => 'Function',
72
        T_PRIVATE => 'MethodOrProperty',
73
        T_PROTECTED => 'MethodOrProperty',
74
        T_PUBLIC => 'MethodOrProperty',
75
        T_TRAIT => 'Trait',
76
        T_VARIABLE => 'Variable',
77
    ];
78
79
    /**
80
     * Ignore normal variables for this sniff.
81
     *
82
     * @return bool
83
     */
84
    protected function areRequirementsMet(): bool
85
    {
86
        return ($this->token['code'] !== T_VARIABLE) || (new PropertyHelper($this->file))->isProperty($this->stackPos);
87
    }
88
89
    /**
90
     * Checks for a missing doc block and throws an error if the doc is missing.
91
     *
92
     * @throws CodeWarning
93
     *
94
     * @return void
95
     */
96
    private function checkAndRegisterMissingDocBlock(): void
97
    {
98
        if (!$this->getDocCommentPos()) {
99
            $tokenIdent = $this->getTokenName();
100
101
            throw (new CodeError(
102
                static::CODE_MISSING_DOC_BLOCK_PREFIX . ucfirst($tokenIdent),
103
                self::MESSAGE_MISSING_DOC_BLOCK,
104
                $this->stackPos
105
            ))->setPayload([lcfirst($tokenIdent)]);
106
        }
107
    }
108
109
    /**
110
     * Checks and registers a multi line error.
111
     *
112
     * @throws CodeWarning
113
     *
114
     * @return void
115
     */
116
    private function checkAndRegisterNoMultiLine(): void
117
    {
118
        $docCommentPos = $this->getDocCommentPos();
119
        $openingToken = $this->tokens[$docCommentPos];
120
        $closingToken = $this->tokens[$openingToken['comment_closer']];
121
122
        if ($openingToken['line'] === $closingToken['line']) {
123
            $tokenIdent = $this->getTokenName();
124
125
            $exception = (new CodeError(
126
                static::CODE_NO_MULTI_LINE_DOC_BLOCK_PREFIX . ucfirst($tokenIdent),
127
                self::MESSAGE_NO_MULTI_LINE_DOC_BLOCK_PREFIX,
128
                $docCommentPos
129
            ))->setPayload([lcfirst($tokenIdent)]);
130
131
            throw $exception;
132
        }
133
    }
134
135
    /**
136
     * Returns the name for the token.
137
     *
138
     * @return string
139
     */
140
    private function getTokenName(): string
141
    {
142
        return $this->registeredTokens[$this->token['code']];
143
    }
144
145
    /**
146
     * Processes the token.
147
     *
148
     * @return void
149
     */
150
    protected function processToken(): void
151
    {
152
        try {
153
            $this->checkAndRegisterMissingDocBlock();
154
            $this->checkAndRegisterNoMultiLine();
155
        } catch (CodeWarning $error) {
156
            $this->getExceptionHandler()->handleException($error);
157
        }
158
    }
159
160
    /**
161
     * Register for all tokens which require a php doc block for us.
162
     *
163
     * @return array
164
     */
165
    public function register(): array
166
    {
167
        return array_keys($this->registeredTokens);
168
    }
169
}
170