Passed
Push — master ( 29f8e6...e59da1 )
by Björn
02:58
created

RequiredDocBlockSniff::tearDown()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
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_TRAIT;
20
use const T_VARIABLE;
21
22
/**
23
 * @author blange <[email protected]>
24
 * @package BestIt\Sniffs\Commenting
25
 */
26
class RequiredDocBlockSniff extends AbstractSniff
27
{
28
    use DocPosProviderTrait;
29
30
    /**
31
     * The error code for missing doc blocks.
32
     *
33
     * @var string
34
     */
35
    public const CODE_MISSING_DOC_BLOCK_PREFIX = 'MissingDocBlock';
36
37
    /**
38
     * The error code for the inline block.
39
     *
40
     * @var string
41
     */
42
    public const CODE_NO_MULTI_LINE_DOC_BLOCK_PREFIX = 'NoMultiLineDocBlock';
43
44
    /**
45
     * The message for missing doc blocks.
46
     *
47
     * @var string
48
     */
49
    private const MESSAGE_MISSING_DOC_BLOCK = 'Please provide a doc block for your %s.';
50
51
    /**
52
     * The error message for the inline block.
53
     *
54
     * @var string
55
     */
56
    private const MESSAGE_NO_MULTI_LINE_DOC_BLOCK_PREFIX = 'Please provide a multi line doc block for your %s.';
57
58
    /**
59
     * Maps the registered tokens to a readable key.
60
     *
61
     * @var array
62
     */
63
    private $registeredTokens = [
64
        T_CLASS => 'Class',
65
        T_CONST => 'Constant',
66
        T_INTERFACE => 'Interface',
67
        T_FUNCTION => 'Function',
68
        T_TRAIT => 'Trait',
69
        T_VARIABLE => 'Variable'
70
    ];
71
72
    /**
73
     * Ignore normal variables for this sniff.
74
     *
75
     * @return bool
76
     */
77
    protected function areRequirementsMet(): bool
78
    {
79
        return ($this->token['code'] !== T_VARIABLE) || (new PropertyHelper($this->file))->isProperty($this->stackPos);
0 ignored issues
show
Bug introduced by
It seems like $this->file can be null; however, __construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
80
    }
81
82
    /**
83
     * Checks for a missing doc block and throws an error if the doc is missing.
84
     *
85
     * @throws CodeWarning
86
     *
87
     * @return void
88
     */
89
    private function checkAndRegisterMissingDocBlock(): void
90
    {
91
        if (!$this->getDocCommentPos()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->getDocCommentPos() of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
92
            $tokenIdent = $this->getTokenName();
93
94
            $exception = (new CodeError(
95
                self::CODE_MISSING_DOC_BLOCK_PREFIX . ucfirst($tokenIdent),
96
                self::MESSAGE_MISSING_DOC_BLOCK,
97
                $this->stackPos
98
            ))->setPayload([lcfirst($tokenIdent)]);
99
100
            throw $exception;
101
        }
102
    }
103
104
    /**
105
     * Checks and registers a multi line error.
106
     *
107
     * @throws CodeWarning
108
     *
109
     * @return void
110
     */
111
    private function checkAndRegisterNoMultiLine(): void
112
    {
113
        $docCommentPos = $this->getDocCommentPos();
114
        $openingToken = $this->tokens[$docCommentPos];
115
        $closingToken = $this->tokens[$openingToken['comment_closer']];
116
117
        if ($openingToken['line'] === $closingToken['line']) {
118
            $tokenIdent = $this->getTokenName();
119
120
            $exception = (new CodeError(
121
                self::CODE_NO_MULTI_LINE_DOC_BLOCK_PREFIX . ucfirst($tokenIdent),
122
                self::MESSAGE_NO_MULTI_LINE_DOC_BLOCK_PREFIX,
123
                $docCommentPos
124
            ))->setPayload([lcfirst($tokenIdent)]);
125
126
            throw $exception;
127
        }
128
    }
129
130
    /**
131
     * Returns the name for the token.
132
     *
133
     * @return string
134
     */
135
    private function getTokenName(): string
136
    {
137
        return $this->registeredTokens[$this->token['code']];
138
    }
139
140
    /**
141
     * Processes the token.
142
     *
143
     * @return void
144
     */
145
    protected function processToken(): void
146
    {
147
        try {
148
            $this->checkAndRegisterMissingDocBlock();
149
            $this->checkAndRegisterNoMultiLine();
150
        } catch (CodeWarning $error) {
151
            $this->getExceptionHandler()->handleException($error);
152
        }
153
    }
154
155
    /**
156
     * Register for all tokens which require a php doc block for us.
157
     *
158
     * @return array
159
     */
160
    public function register(): array
161
    {
162
        return array_keys($this->registeredTokens);
163
    }
164
}
165