Passed
Push — master ( 00bc3e...3edb00 )
by Björn
02:16
created

EmptyArrayForComparisonSniff::setUp()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.9666
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\Comparisons;
6
7
use BestIt\CodeSniffer\CodeError;
8
use BestIt\Sniffs\AbstractSniff;
9
use SlevomatCodingStandard\Helpers\TokenHelper;
10
use function array_key_exists;
11
use const T_ARRAY;
12
use const T_CLOSE_PARENTHESIS;
13
use const T_CLOSE_SHORT_ARRAY;
14
use const T_IS_EQUAL;
15
use const T_IS_IDENTICAL;
16
use const T_IS_NOT_EQUAL;
17
use const T_IS_NOT_IDENTICAL;
18
use const T_OPEN_PARENTHESIS;
19
use const T_OPEN_SHORT_ARRAY;
20
21
/**
22
 * Prevents that you compare against an empty array.
23
 *
24
 * @author blange <[email protected]>
25
 * @package BestIt\Sniffs\Comparisons
26
 */
27
class EmptyArrayForComparisonSniff extends AbstractSniff
28
{
29
    /**
30
     * You MUST not create an empty array, to check for an empty array.
31
     */
32
    public const CODE_EMPTY_ARRAY = 'EmptyArray';
33
34
    /**
35
     * Used message to display the error.
36
     */
37
    private const MESSAGE_EMPTY_ARRAY = 'Please do not initalize an empty array to check for an empty array!';
38
39
    /**
40
     * The check must not contain just an empty array.
41
     *
42
     * @var array|null Is filled by the setup method.
43
     */
44
    private $invalidStructure;
45
46
    /**
47
     * Search starting with the given search pos for the invalid codes in consecutive order.
48
     *
49
     * @throws CodeError Contains the error message if there is an invalid array check.
50
     *
51
     * @param array $invalidCodes
52
     * @param int $searchPos
53
     *
54
     * @return void
55
     */
56
    private function checkArrayStructure(array $invalidCodes, int $searchPos): void
57
    {
58
        // Rename the var to get more readable code.
59
        $remainingInvalidCodes = $invalidCodes;
60
        unset($invalidCodes);
61
62
        foreach ($remainingInvalidCodes as $nextInvalidCodeIndex => $nextInvalidCode) {
63
            $foundTokenPos = TokenHelper::findNextEffective($this->getFile(), $searchPos);
64
            $foundToken = $this->tokens[$foundTokenPos];
65
66
            // We can stop the search, if there is no invalid code.
67
            if ($foundToken['code'] !== $nextInvalidCode) {
68
                break;
69
            }
70
71
            // Check the next possible token
72
            $searchPos = $foundTokenPos + 1;
73
            unset($remainingInvalidCodes[$nextInvalidCodeIndex]);
74
        }
75
76
        $matchedEveryInvalidCode = !$remainingInvalidCodes;
77
78
        $this->file->recordMetric(
79
            $searchPos,
80
            'Invalid array comparison',
81
            $matchedEveryInvalidCode ? 'yes' : 'no'
82
        );
83
84
        if ($matchedEveryInvalidCode) {
85
            throw (new CodeError(static::CODE_EMPTY_ARRAY, self::MESSAGE_EMPTY_ARRAY, $searchPos));
86
        }
87
    }
88
89
    /**
90
     * Processes the token.
91
     *
92
     * @throws CodeError Contains the error message if there is an invalid array check.
93
     *
94
     * @return void
95
     */
96
    protected function processToken(): void
97
    {
98
        $this->file->recordMetric($this->getStackPos(), 'Used comparison (for array checks)', $this->token['type']);
99
100
        $nextTokenPos = TokenHelper::findNextEffective($this->getFile(), $startPos = $this->getStackPos() + 1);
101
        $nextToken = $this->tokens[$nextTokenPos];
102
103
        // Add in Array check
104
        if (array_key_exists($nextToken['code'], $this->invalidStructure)) {
105
            $this->checkArrayStructure($this->invalidStructure[$nextToken['code']], $nextTokenPos + 1);
106
        } else {
107
            $this->file->recordMetric(
108
                $startPos,
109
                'Invalid array comparison',
110
                'no'
111
            );
112
        }
113
    }
114
115
    /**
116
     * Registers the tokens that this sniff wants to listen for.
117
     *
118
     * An example return value for a sniff that wants to listen for whitespace
119
     * and any comments would be:
120
     *
121
     * <code>
122
     *    return array(
123
     *            T_WHITESPACE,
124
     *            T_DOC_COMMENT,
125
     *            T_COMMENT,
126
     *           );
127
     * </code>
128
     *
129
     * @return int[]
130
     * @see    Tokens.php
131
     */
132
    public function register(): array
133
    {
134
        return [T_IS_EQUAL, T_IS_NOT_EQUAL, T_IS_IDENTICAL, T_IS_NOT_IDENTICAL];
135
    }
136
137
    /**
138
     * Declares the forbidden array structure.
139
     *
140
     * @return void
141
     */
142
    protected function setUp(): void
143
    {
144
        parent::setUp();
145
146
        $this->invalidStructure = [
147
            T_ARRAY => [T_OPEN_PARENTHESIS, T_CLOSE_PARENTHESIS],
148
            T_OPEN_SHORT_ARRAY => [T_CLOSE_SHORT_ARRAY]
149
        ];
150
    }
151
}
152