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

TraitUseDeclarationSniff   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 131
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
dl 0
loc 131
rs 10
c 0
b 0
f 0
wmc 13
lcom 1
cbo 7
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BestIt\Sniffs\Formatting;
6
7
use BestIt\CodeSniffer\Helper\ClassHelper;
8
use BestIt\CodeSniffer\Helper\TokenHelper;
9
use BestIt\Sniffs\AbstractSniff;
10
use BestIt\Sniffs\ClassRegistrationTrait;
11
use const T_COMMA;
12
use const T_OPEN_CURLY_BRACKET;
13
use const T_SEMICOLON;
14
use const T_WHITESPACE;
15
16
/**
17
 * Sniffs if the uses in a class are correct.
18
 *
19
 * @author blange <[email protected]>
20
 * @package BestIt\Sniffs\Formatting
21
 */
22
class TraitUseDeclarationSniff extends AbstractSniff
23
{
24
    use ClassRegistrationTrait;
25
26
    /**
27
     * You MUST provide only one "use" per Line for importing traits etc. in classes.
28
     */
29
    public const CODE_MULTIPLE_TRAITS_PER_DECLARATION = 'MultipleTraitsPerDeclaration';
30
31
    /**
32
     * Readable error message.
33
     */
34
    private const MESSAGE_MULTIPLE_TRAITS_PER_DECLARATION = 'Multiple traits per use statement are forbidden.';
35
36
    /**
37
     * The use declarations positions of this "class".
38
     *
39
     * @var array
40
     */
41
    private array $uses;
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...
42
43
    /**
44
     * Returns false if there are no uses.
45
     *
46
     * @return bool
47
     */
48
    protected function areRequirementsMet(): bool
49
    {
50
        return (bool) $this->uses = ClassHelper::getTraitUsePointers($this->getFile(), $this->getStackPos());
51
    }
52
53
    /**
54
     * Checks the declaration of the given use position and registers and error if needed.
55
     *
56
     * @param int $usePos
57
     *
58
     * @return void
59
     */
60
    private function checkDeclaration(int $usePos): void
61
    {
62
        $file = $this->getFile();
63
64
        if (TokenHelper::findNextLocal($file, T_COMMA, $usePos + 1)) {
65
            $endPos = TokenHelper::findNext($file, [T_OPEN_CURLY_BRACKET, T_SEMICOLON], $usePos + 1);
66
            $tokens = $file->getTokens();
67
68
            if ($tokens[$endPos]['code'] === T_OPEN_CURLY_BRACKET) {
69
                $file->addError(
70
                    self::MESSAGE_MULTIPLE_TRAITS_PER_DECLARATION,
71
                    $usePos,
72
                    static::CODE_MULTIPLE_TRAITS_PER_DECLARATION
73
                );
74
            } else {
75
                $fix = $file->addFixableError(
76
                    self::MESSAGE_MULTIPLE_TRAITS_PER_DECLARATION,
77
                    $usePos,
78
                    static::CODE_MULTIPLE_TRAITS_PER_DECLARATION
79
                );
80
81
                if ($fix) {
82
                    $this->fixeUse($endPos, $usePos);
83
                }
84
            }
85
        }
86
    }
87
88
    /**
89
     * Fixes the given use position.
90
     *
91
     * @param int $endPos The end of the checked position.
92
     * @param int $usePos
93
     *
94
     * @return void
95
     */
96
    protected function fixeUse(int $endPos, int $usePos): void
97
    {
98
        $indentation = $this->getIndentationForFix($usePos);
99
        $file = $this->getFile();
100
        $fixer = $file->fixer;
101
102
        $fixer->beginChangeset();
103
104
        $commaPointers = TokenHelper::findNextAll($file, T_COMMA, $usePos + 1, $endPos);
105
        foreach ($commaPointers as $commaPos) {
106
            $pointerAfterComma = TokenHelper::findNextEffective($file, $commaPos + 1);
107
            $fixer->replaceToken($commaPos, ';' . $file->eolChar . $indentation . 'use ');
108
            for ($i = $commaPos + 1; $i < $pointerAfterComma; $i++) {
109
                $fixer->replaceToken($i, '');
110
            }
111
        }
112
113
        $fixer->endChangeset();
114
    }
115
116
    /**
117
     * Returns the needed indentation whitespace for fhe fixing of the uses.
118
     *
119
     * @param int $usePos
120
     *
121
     * @return string
122
     */
123
    private function getIndentationForFix(int $usePos): string
124
    {
125
        $file = $this->getFile();
126
        $indentation = '';
127
        $currentPointer = $usePos - 1;
128
        $tokens = $file->getTokens();
129
130
        while (
131
            $tokens[$currentPointer]['code'] === T_WHITESPACE &&
132
            $tokens[$currentPointer]['content'] !== $file->eolChar
133
        ) {
134
            $indentation .= $tokens[$currentPointer]['content'];
135
            $currentPointer--;
136
        }
137
138
        return $indentation;
139
    }
140
141
    /**
142
     * Processes the token.
143
     *
144
     * @return void
145
     */
146
    protected function processToken(): void
147
    {
148
        foreach ($this->uses as $use) {
149
            $this->checkDeclaration($use);
150
        }
151
    }
152
}
153