Completed
Branch development (b1b115)
by Johannes
10:28
created

OperatorSpacingSniff::process()   F

Complexity

Conditions 58
Paths 2333

Size

Total Lines 238

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 238
rs 0
cc 58
nc 2333
nop 2

How to fix   Long Method    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
 * Verifies that operators have valid spacing surrounding them.
4
 *
5
 * @author    Greg Sherwood <[email protected]>
6
 * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
7
 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
8
 */
9
10
namespace PHP_CodeSniffer\Standards\Squiz\Sniffs\WhiteSpace;
11
12
use PHP_CodeSniffer\Sniffs\Sniff;
13
use PHP_CodeSniffer\Files\File;
14
use PHP_CodeSniffer\Util\Tokens;
15
16
class OperatorSpacingSniff implements Sniff
17
{
18
19
    /**
20
     * A list of tokenizers this sniff supports.
21
     *
22
     * @var array
23
     */
24
    public $supportedTokenizers = [
25
        'PHP',
26
        'JS',
27
    ];
28
29
    /**
30
     * Allow newlines instead of spaces.
31
     *
32
     * @var boolean
33
     */
34
    public $ignoreNewlines = false;
35
36
37
    /**
38
     * Returns an array of tokens this test wants to listen for.
39
     *
40
     * @return array
41
     */
42
    public function register()
43
    {
44
        $comparison = Tokens::$comparisonTokens;
45
        $operators  = Tokens::$operators;
46
        $assignment = Tokens::$assignmentTokens;
47
        $inlineIf   = [
48
            T_INLINE_THEN,
49
            T_INLINE_ELSE,
50
        ];
51
52
        return array_unique(
53
            array_merge($comparison, $operators, $assignment, $inlineIf)
54
        );
55
56
    }//end register()
57
58
59
    /**
60
     * Processes this sniff, when one of its tokens is encountered.
61
     *
62
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being checked.
63
     * @param int                         $stackPtr  The position of the current token in
64
     *                                               the stack passed in $tokens.
65
     *
66
     * @return void
67
     */
68
    public function process(File $phpcsFile, $stackPtr)
69
    {
70
        $tokens = $phpcsFile->getTokens();
71
72
        // Skip default values in function declarations.
73
        // Skip declare statements.
74
        if ($tokens[$stackPtr]['code'] === T_EQUAL
75
            || $tokens[$stackPtr]['code'] === T_MINUS
76
        ) {
77
            if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
78
                $parenthesis = array_keys($tokens[$stackPtr]['nested_parenthesis']);
79
                $bracket     = array_pop($parenthesis);
80
                if (isset($tokens[$bracket]['parenthesis_owner']) === true) {
81
                    $function = $tokens[$bracket]['parenthesis_owner'];
82
                    if ($tokens[$function]['code'] === T_FUNCTION
83
                        || $tokens[$function]['code'] === T_CLOSURE
84
                        || $tokens[$function]['code'] === T_DECLARE
85
                    ) {
86
                        return;
87
                    }
88
                }
89
            }
90
        }
91
92
        if ($tokens[$stackPtr]['code'] === T_EQUAL) {
93
            // Skip for '=&' case.
94
            if (isset($tokens[($stackPtr + 1)]) === true
95
                && $tokens[($stackPtr + 1)]['code'] === T_BITWISE_AND
96
            ) {
97
                return;
98
            }
99
        }
100
101
        if ($tokens[$stackPtr]['code'] === T_BITWISE_AND) {
102
            // If it's not a reference, then we expect one space either side of the
103
            // bitwise operator.
104
            if ($phpcsFile->isReference($stackPtr) === true) {
105
                return;
106
            }
107
108
            // Check there is one space before the & operator.
109
            if ($tokens[($stackPtr - 1)]['code'] !== T_WHITESPACE) {
110
                $error = 'Expected 1 space before "&" operator; 0 found';
111
                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceBeforeAmp');
112
                if ($fix === true) {
113
                    $phpcsFile->fixer->addContentBefore($stackPtr, ' ');
114
                }
115
116
                $phpcsFile->recordMetric($stackPtr, 'Space before operator', 0);
117
            } else {
118
                if ($tokens[($stackPtr - 2)]['line'] !== $tokens[$stackPtr]['line']) {
119
                    $found = 'newline';
120
                } else {
121
                    $found = $tokens[($stackPtr - 1)]['length'];
122
                }
123
124
                $phpcsFile->recordMetric($stackPtr, 'Space before operator', $found);
125
                if ($found !== 1
126
                    && ($found !== 'newline' || $this->ignoreNewlines === false)
127
                ) {
128
                    $error = 'Expected 1 space before "&" operator; %s found';
129
                    $data  = [$found];
130
                    $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBeforeAmp', $data);
131
                    if ($fix === true) {
132
                        $phpcsFile->fixer->replaceToken(($stackPtr - 1), ' ');
133
                    }
134
                }
135
            }//end if
136
137
            // Check there is one space after the & operator.
138
            if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) {
139
                $error = 'Expected 1 space after "&" operator; 0 found';
140
                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceAfterAmp');
141
                if ($fix === true) {
142
                    $phpcsFile->fixer->addContent($stackPtr, ' ');
143
                }
144
145
                $phpcsFile->recordMetric($stackPtr, 'Space after operator', 0);
146
            } else {
147
                if ($tokens[($stackPtr + 2)]['line'] !== $tokens[$stackPtr]['line']) {
148
                    $found = 'newline';
149
                } else {
150
                    $found = $tokens[($stackPtr + 1)]['length'];
151
                }
152
153
                $phpcsFile->recordMetric($stackPtr, 'Space after operator', $found);
154
                if ($found !== 1
155
                    && ($found !== 'newline' || $this->ignoreNewlines === false)
156
                ) {
157
                    $error = 'Expected 1 space after "&" operator; %s found';
158
                    $data  = [$found];
159
                    $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterAmp', $data);
160
                    if ($fix === true) {
161
                        $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
162
                    }
163
                }
164
            }//end if
165
166
            return;
167
        }//end if
168
169
        if ($tokens[$stackPtr]['code'] === T_MINUS || $tokens[$stackPtr]['code'] === T_PLUS) {
170
            // Check minus spacing, but make sure we aren't just assigning
171
            // a minus value or returning one.
172
            $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
173
            if ($tokens[$prev]['code'] === T_RETURN) {
174
                // Just returning a negative value; eg. (return -1).
175
                return;
176
            }
177
178
            if (isset(Tokens::$operators[$tokens[$prev]['code']]) === true) {
179
                // Just trying to operate on a negative value; eg. ($var * -1).
180
                return;
181
            }
182
183
            if (isset(Tokens::$comparisonTokens[$tokens[$prev]['code']]) === true) {
184
                // Just trying to compare a negative value; eg. ($var === -1).
185
                return;
186
            }
187
188
            if (isset(Tokens::$booleanOperators[$tokens[$prev]['code']]) === true) {
189
                // Just trying to compare a negative value; eg. ($var || -1 === $b).
190
                return;
191
            }
192
193
            if (isset(Tokens::$assignmentTokens[$tokens[$prev]['code']]) === true) {
194
                // Just trying to assign a negative value; eg. ($var = -1).
195
                return;
196
            }
197
198
            // A list of tokens that indicate that the token is not
199
            // part of an arithmetic operation.
200
            $invalidTokens = [
201
                T_COMMA               => true,
202
                T_OPEN_PARENTHESIS    => true,
203
                T_OPEN_SQUARE_BRACKET => true,
204
                T_OPEN_SHORT_ARRAY    => true,
205
                T_DOUBLE_ARROW        => true,
206
                T_COLON               => true,
207
                T_INLINE_THEN         => true,
208
                T_INLINE_ELSE         => true,
209
                T_CASE                => true,
210
            ];
211
212
            if (isset($invalidTokens[$tokens[$prev]['code']]) === true) {
213
                // Just trying to use a negative value; eg. myFunction($var, -2).
214
                return;
215
            }
216
        }//end if
217
218
        $operator = $tokens[$stackPtr]['content'];
219
220
        if ($tokens[($stackPtr - 1)]['code'] !== T_WHITESPACE
221
            && (($tokens[($stackPtr - 1)]['code'] === T_INLINE_THEN
222
            && $tokens[($stackPtr )]['code'] === T_INLINE_ELSE) === false)
223
        ) {
224
            $error = "Expected 1 space before \"$operator\"; 0 found";
225
            $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceBefore');
226
            if ($fix === true) {
227
                $phpcsFile->fixer->addContentBefore($stackPtr, ' ');
228
            }
229
230
            $phpcsFile->recordMetric($stackPtr, 'Space before operator', 0);
231
        } else if (isset(Tokens::$assignmentTokens[$tokens[$stackPtr]['code']]) === false) {
232
            // Don't throw an error for assignments, because other standards allow
233
            // multiple spaces there to align multiple assignments.
234
            if ($tokens[($stackPtr - 2)]['line'] !== $tokens[$stackPtr]['line']) {
235
                $found = 'newline';
236
            } else {
237
                $found = $tokens[($stackPtr - 1)]['length'];
238
            }
239
240
            $phpcsFile->recordMetric($stackPtr, 'Space before operator', $found);
241
            if ($found !== 1
242
                && ($found !== 'newline' || $this->ignoreNewlines === false)
243
            ) {
244
                $error = 'Expected 1 space before "%s"; %s found';
245
                $data  = [
246
                    $operator,
247
                    $found,
248
                ];
249
                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBefore', $data);
250
                if ($fix === true) {
251
                    $phpcsFile->fixer->beginChangeset();
252
                    if ($found === 'newline') {
253
                        $i = ($stackPtr - 2);
254
                        while ($tokens[$i]['code'] === T_WHITESPACE) {
255
                            $phpcsFile->fixer->replaceToken($i, '');
256
                            $i--;
257
                        }
258
                    }
259
260
                    $phpcsFile->fixer->replaceToken(($stackPtr - 1), ' ');
261
                    $phpcsFile->fixer->endChangeset();
262
                }
263
            }//end if
264
        }//end if
265
266
        if (isset($tokens[($stackPtr + 1)]) === false) {
267
            return;
268
        }
269
270
        if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) {
271
            // Skip short ternary such as: "$foo = $bar ?: true;".
272
            if (($tokens[$stackPtr]['code'] === T_INLINE_THEN
273
                && $tokens[($stackPtr + 1)]['code'] === T_INLINE_ELSE)
274
            ) {
275
                return;
276
            }
277
278
            $error = "Expected 1 space after \"$operator\"; 0 found";
279
            $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceAfter');
280
            if ($fix === true) {
281
                $phpcsFile->fixer->addContent($stackPtr, ' ');
282
            }
283
284
            $phpcsFile->recordMetric($stackPtr, 'Space after operator', 0);
285
        } else {
286
            if (isset($tokens[($stackPtr + 2)]) === true
287
                && $tokens[($stackPtr + 2)]['line'] !== $tokens[$stackPtr]['line']
288
            ) {
289
                $found = 'newline';
290
            } else {
291
                $found = $tokens[($stackPtr + 1)]['length'];
292
            }
293
294
            $phpcsFile->recordMetric($stackPtr, 'Space after operator', $found);
295
            if ($found !== 1
296
                && ($found !== 'newline' || $this->ignoreNewlines === false)
297
            ) {
298
                $error = 'Expected 1 space after "%s"; %s found';
299
                $data  = [
300
                    $operator,
301
                    $found,
302
                ];
303
                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfter', $data);
304
                if ($fix === true) {
305
                    $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
306
                }
307
            }
308
        }//end if
309
310
    }//end process()
311
312
313
}//end class
314