• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PHPCSStandards / PHP_CodeSniffer / 12658687769

07 Jan 2025 08:19PM UTC coverage: 78.321% (+0.2%) from 78.074%
12658687769

Pull #620

github

web-flow
Merge 4e97c930c into b43e1d8e8
Pull Request #620: Fix fixer conflict: `PSR12` / `Squiz.Functions.FunctionDeclarationArgumentSpacing`

17 of 17 new or added lines in 1 file covered. (100.0%)

21 existing lines in 3 files now uncovered.

24477 of 31252 relevant lines covered (78.32%)

65.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.54
/src/Standards/Squiz/Sniffs/Arrays/ArrayDeclarationSniff.php
1
<?php
2
/**
3
 * Ensures that arrays conform to the array coding standard.
4
 *
5
 * @author    Greg Sherwood <gsherwood@squiz.net>
6
 * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
7
 * @license   https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
8
 */
9

10
namespace PHP_CodeSniffer\Standards\Squiz\Sniffs\Arrays;
11

12
use PHP_CodeSniffer\Files\File;
13
use PHP_CodeSniffer\Sniffs\Sniff;
14
use PHP_CodeSniffer\Util\Tokens;
15

16
class ArrayDeclarationSniff implements Sniff
17
{
18

19

20
    /**
21
     * Returns an array of tokens this test wants to listen for.
22
     *
23
     * @return array<int|string>
24
     */
25
    public function register()
3✔
26
    {
27
        return [
1✔
28
            T_ARRAY,
3✔
29
            T_OPEN_SHORT_ARRAY,
3✔
30
        ];
2✔
31

32
    }//end register()
33

34

35
    /**
36
     * Processes this sniff, when one of its tokens is encountered.
37
     *
38
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being checked.
39
     * @param int                         $stackPtr  The position of the current token in
40
     *                                               the stack passed in $tokens.
41
     *
42
     * @return void
43
     */
44
    public function process(File $phpcsFile, $stackPtr)
3✔
45
    {
46
        $tokens = $phpcsFile->getTokens();
3✔
47

48
        // Prevent acting on short lists inside a foreach (see
49
        // https://github.com/PHPCSStandards/PHP_CodeSniffer/issues/527).
50
        if ($tokens[$stackPtr]['code'] === T_OPEN_SHORT_ARRAY
3✔
51
            && isset($tokens[$stackPtr]['nested_parenthesis']) === true
3✔
52
        ) {
1✔
53
            $nestedParens          = $tokens[$stackPtr]['nested_parenthesis'];
3✔
54
            $lastParenthesisCloser = end($nestedParens);
3✔
55
            $lastParenthesisOpener = key($nestedParens);
3✔
56

57
            if (isset($tokens[$lastParenthesisCloser]['parenthesis_owner']) === true
3✔
58
                && $tokens[$tokens[$lastParenthesisCloser]['parenthesis_owner']]['code'] === T_FOREACH
3✔
59
            ) {
1✔
60
                $asKeyword = $phpcsFile->findNext(T_AS, ($lastParenthesisOpener + 1), $lastParenthesisCloser);
3✔
61

62
                if ($asKeyword !== false && $asKeyword < $stackPtr) {
3✔
63
                    return;
3✔
64
                }
65
            }
1✔
66
        }
1✔
67

68
        if ($tokens[$stackPtr]['code'] === T_ARRAY) {
3✔
69
            $phpcsFile->recordMetric($stackPtr, 'Short array syntax used', 'no');
3✔
70

71
            // Array keyword should be lower case.
72
            if ($tokens[$stackPtr]['content'] !== strtolower($tokens[$stackPtr]['content'])) {
3✔
73
                if ($tokens[$stackPtr]['content'] === strtoupper($tokens[$stackPtr]['content'])) {
3✔
74
                    $phpcsFile->recordMetric($stackPtr, 'Array keyword case', 'upper');
3✔
75
                } else {
1✔
76
                    $phpcsFile->recordMetric($stackPtr, 'Array keyword case', 'mixed');
3✔
77
                }
78

79
                $error = 'Array keyword should be lower case; expected "array" but found "%s"';
3✔
80
                $data  = [$tokens[$stackPtr]['content']];
3✔
81
                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'NotLowerCase', $data);
3✔
82
                if ($fix === true) {
3✔
83
                    $phpcsFile->fixer->replaceToken($stackPtr, 'array');
3✔
84
                }
1✔
85
            } else {
1✔
86
                $phpcsFile->recordMetric($stackPtr, 'Array keyword case', 'lower');
3✔
87
            }
88

89
            $arrayStart = $tokens[$stackPtr]['parenthesis_opener'];
3✔
90
            if (isset($tokens[$arrayStart]['parenthesis_closer']) === false) {
3✔
91
                return;
3✔
92
            }
93

94
            $arrayEnd = $tokens[$arrayStart]['parenthesis_closer'];
3✔
95

96
            if ($arrayStart !== ($stackPtr + 1)) {
3✔
97
                $error = 'There must be no space between the "array" keyword and the opening parenthesis';
3✔
98

99
                $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), $arrayStart, true);
3✔
100
                if (isset(Tokens::$commentTokens[$tokens[$next]['code']]) === true) {
3✔
101
                    // We don't have anywhere to put the comment, so don't attempt to fix it.
102
                    $phpcsFile->addError($error, $stackPtr, 'SpaceAfterKeyword');
3✔
103
                } else {
1✔
104
                    $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceAfterKeyword');
3✔
105
                    if ($fix === true) {
3✔
106
                        $phpcsFile->fixer->beginChangeset();
3✔
107
                        for ($i = ($stackPtr + 1); $i < $arrayStart; $i++) {
3✔
108
                            $phpcsFile->fixer->replaceToken($i, '');
3✔
109
                        }
1✔
110

111
                        $phpcsFile->fixer->endChangeset();
3✔
112
                    }
1✔
113
                }
114
            }
1✔
115
        } else {
1✔
116
            $phpcsFile->recordMetric($stackPtr, 'Short array syntax used', 'yes');
3✔
117
            $arrayStart = $stackPtr;
3✔
118
            $arrayEnd   = $tokens[$stackPtr]['bracket_closer'];
3✔
119
        }//end if
120

121
        // Check for empty arrays.
122
        $content = $phpcsFile->findNext(T_WHITESPACE, ($arrayStart + 1), ($arrayEnd + 1), true);
3✔
123
        if ($content === $arrayEnd) {
3✔
124
            // Empty array, but if the brackets aren't together, there's a problem.
125
            if (($arrayEnd - $arrayStart) !== 1) {
3✔
126
                $error = 'Empty array declaration must have no space between the parentheses';
3✔
127
                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceInEmptyArray');
3✔
128

129
                if ($fix === true) {
3✔
130
                    $phpcsFile->fixer->beginChangeset();
3✔
131
                    for ($i = ($arrayStart + 1); $i < $arrayEnd; $i++) {
3✔
132
                        $phpcsFile->fixer->replaceToken($i, '');
3✔
133
                    }
1✔
134

135
                    $phpcsFile->fixer->endChangeset();
3✔
136
                }
1✔
137
            }
1✔
138

139
            // We can return here because there is nothing else to check. All code
140
            // below can assume that the array is not empty.
141
            return;
3✔
142
        }
143

144
        if ($tokens[$arrayStart]['line'] === $tokens[$arrayEnd]['line']) {
3✔
145
            $this->processSingleLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd);
3✔
146
        } else {
1✔
147
            $this->processMultiLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd);
3✔
148
        }
149

150
    }//end process()
2✔
151

152

153
    /**
154
     * Processes a single-line array definition.
155
     *
156
     * @param \PHP_CodeSniffer\Files\File $phpcsFile  The current file being checked.
157
     * @param int                         $stackPtr   The position of the current token
158
     *                                                in the stack passed in $tokens.
159
     * @param int                         $arrayStart The token that starts the array definition.
160
     * @param int                         $arrayEnd   The token that ends the array definition.
161
     *
162
     * @return void
163
     */
164
    public function processSingleLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd)
3✔
165
    {
166
        $tokens = $phpcsFile->getTokens();
3✔
167

168
        // Check if there are multiple values. If so, then it has to be multiple lines
169
        // unless it is contained inside a function call or condition.
170
        $valueCount = 0;
3✔
171
        $commas     = [];
3✔
172
        for ($i = ($arrayStart + 1); $i < $arrayEnd; $i++) {
3✔
173
            // Skip bracketed statements, like function calls.
174
            if ($tokens[$i]['code'] === T_OPEN_PARENTHESIS) {
3✔
175
                $i = $tokens[$i]['parenthesis_closer'];
3✔
176
                continue;
3✔
177
            }
178

179
            if ($tokens[$i]['code'] === T_COMMA) {
3✔
180
                // Before counting this comma, make sure we are not
181
                // at the end of the array.
182
                $next = $phpcsFile->findNext(T_WHITESPACE, ($i + 1), $arrayEnd, true);
3✔
183
                if ($next !== false) {
3✔
184
                    $valueCount++;
3✔
185
                    $commas[] = $i;
3✔
186
                } else {
1✔
187
                    // There is a comma at the end of a single line array.
188
                    $error = 'Comma not allowed after last value in single-line array declaration';
3✔
189
                    $fix   = $phpcsFile->addFixableError($error, $i, 'CommaAfterLast');
3✔
190
                    if ($fix === true) {
3✔
191
                        $phpcsFile->fixer->replaceToken($i, '');
3✔
192
                    }
1✔
193
                }
194
            }
1✔
195
        }//end for
1✔
196

197
        // Now check each of the double arrows (if any).
198
        $nextArrow = $arrayStart;
3✔
199
        while (($nextArrow = $phpcsFile->findNext(T_DOUBLE_ARROW, ($nextArrow + 1), $arrayEnd)) !== false) {
3✔
200
            if ($tokens[($nextArrow - 1)]['code'] !== T_WHITESPACE) {
3✔
201
                $content = $tokens[($nextArrow - 1)]['content'];
3✔
202
                $error   = 'Expected 1 space between "%s" and double arrow; 0 found';
3✔
203
                $data    = [$content];
3✔
204
                $fix     = $phpcsFile->addFixableError($error, $nextArrow, 'NoSpaceBeforeDoubleArrow', $data);
3✔
205
                if ($fix === true) {
3✔
206
                    $phpcsFile->fixer->addContentBefore($nextArrow, ' ');
3✔
207
                }
1✔
208
            } else {
1✔
209
                $spaceLength = $tokens[($nextArrow - 1)]['length'];
3✔
210
                if ($spaceLength !== 1) {
3✔
211
                    $content = $tokens[($nextArrow - 2)]['content'];
3✔
212
                    $error   = 'Expected 1 space between "%s" and double arrow; %s found';
3✔
213
                    $data    = [
1✔
214
                        $content,
3✔
215
                        $spaceLength,
3✔
216
                    ];
2✔
217

218
                    $fix = $phpcsFile->addFixableError($error, $nextArrow, 'SpaceBeforeDoubleArrow', $data);
3✔
219
                    if ($fix === true) {
3✔
220
                        $phpcsFile->fixer->replaceToken(($nextArrow - 1), ' ');
3✔
221
                    }
1✔
222
                }
1✔
223
            }//end if
224

225
            if ($tokens[($nextArrow + 1)]['code'] !== T_WHITESPACE) {
3✔
226
                $content = $tokens[($nextArrow + 1)]['content'];
3✔
227
                $error   = 'Expected 1 space between double arrow and "%s"; 0 found';
3✔
228
                $data    = [$content];
3✔
229
                $fix     = $phpcsFile->addFixableError($error, $nextArrow, 'NoSpaceAfterDoubleArrow', $data);
3✔
230
                if ($fix === true) {
3✔
231
                    $phpcsFile->fixer->addContent($nextArrow, ' ');
3✔
232
                }
1✔
233
            } else {
1✔
234
                $spaceLength = $tokens[($nextArrow + 1)]['length'];
3✔
235
                if ($spaceLength !== 1) {
3✔
236
                    $content = $tokens[($nextArrow + 2)]['content'];
3✔
237
                    $error   = 'Expected 1 space between double arrow and "%s"; %s found';
3✔
238
                    $data    = [
1✔
239
                        $content,
3✔
240
                        $spaceLength,
3✔
241
                    ];
2✔
242

243
                    $fix = $phpcsFile->addFixableError($error, $nextArrow, 'SpaceAfterDoubleArrow', $data);
3✔
244
                    if ($fix === true) {
3✔
245
                        $phpcsFile->fixer->replaceToken(($nextArrow + 1), ' ');
3✔
246
                    }
1✔
247
                }
1✔
248
            }//end if
249
        }//end while
1✔
250

251
        if ($valueCount > 0) {
3✔
252
            $nestedParenthesis = false;
3✔
253
            if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
3✔
254
                $nested            = $tokens[$stackPtr]['nested_parenthesis'];
3✔
255
                $nestedParenthesis = array_pop($nested);
3✔
256
            }
1✔
257

258
            if ($nestedParenthesis === false
2✔
259
                || $tokens[$nestedParenthesis]['line'] !== $tokens[$stackPtr]['line']
3✔
260
            ) {
1✔
261
                $error = 'Array with multiple values cannot be declared on a single line';
3✔
262
                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SingleLineNotAllowed');
3✔
263
                if ($fix === true) {
3✔
264
                    $phpcsFile->fixer->beginChangeset();
3✔
265
                    $phpcsFile->fixer->addNewline($arrayStart);
3✔
266

267
                    if ($tokens[($arrayEnd - 1)]['code'] === T_WHITESPACE) {
3✔
268
                        $phpcsFile->fixer->replaceToken(($arrayEnd - 1), $phpcsFile->eolChar);
3✔
269
                    } else {
1✔
270
                        $phpcsFile->fixer->addNewlineBefore($arrayEnd);
3✔
271
                    }
272

273
                    $phpcsFile->fixer->endChangeset();
3✔
274
                }
1✔
275

276
                return;
3✔
277
            }
278

279
            // We have a multiple value array that is inside a condition or
280
            // function. Check its spacing is correct.
281
            foreach ($commas as $comma) {
3✔
282
                if ($tokens[($comma + 1)]['code'] !== T_WHITESPACE) {
3✔
283
                    $content = $tokens[($comma + 1)]['content'];
3✔
284
                    $error   = 'Expected 1 space between comma and "%s"; 0 found';
3✔
285
                    $data    = [$content];
3✔
286
                    $fix     = $phpcsFile->addFixableError($error, $comma, 'NoSpaceAfterComma', $data);
3✔
287
                    if ($fix === true) {
3✔
288
                        $phpcsFile->fixer->addContent($comma, ' ');
3✔
289
                    }
1✔
290
                } else {
1✔
291
                    $spaceLength = $tokens[($comma + 1)]['length'];
3✔
292
                    if ($spaceLength !== 1) {
3✔
293
                        $content = $tokens[($comma + 2)]['content'];
3✔
294
                        $error   = 'Expected 1 space between comma and "%s"; %s found';
3✔
295
                        $data    = [
1✔
296
                            $content,
3✔
297
                            $spaceLength,
3✔
298
                        ];
2✔
299

300
                        $fix = $phpcsFile->addFixableError($error, $comma, 'SpaceAfterComma', $data);
3✔
301
                        if ($fix === true) {
3✔
302
                            $phpcsFile->fixer->replaceToken(($comma + 1), ' ');
3✔
303
                        }
1✔
304
                    }
1✔
305
                }//end if
306

307
                if ($tokens[($comma - 1)]['code'] === T_WHITESPACE) {
3✔
308
                    $content     = $tokens[($comma - 2)]['content'];
3✔
309
                    $spaceLength = $tokens[($comma - 1)]['length'];
3✔
310
                    $error       = 'Expected 0 spaces between "%s" and comma; %s found';
3✔
311
                    $data        = [
1✔
312
                        $content,
3✔
313
                        $spaceLength,
3✔
314
                    ];
2✔
315

316
                    $fix = $phpcsFile->addFixableError($error, $comma, 'SpaceBeforeComma', $data);
3✔
317
                    if ($fix === true) {
3✔
318
                        $phpcsFile->fixer->replaceToken(($comma - 1), '');
3✔
319
                    }
1✔
320
                }
1✔
321
            }//end foreach
1✔
322
        }//end if
1✔
323

324
    }//end processSingleLineArray()
2✔
325

326

327
    /**
328
     * Processes a multi-line array definition.
329
     *
330
     * @param \PHP_CodeSniffer\Files\File $phpcsFile  The current file being checked.
331
     * @param int                         $stackPtr   The position of the current token
332
     *                                                in the stack passed in $tokens.
333
     * @param int                         $arrayStart The token that starts the array definition.
334
     * @param int                         $arrayEnd   The token that ends the array definition.
335
     *
336
     * @return void
337
     */
338
    public function processMultiLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd)
3✔
339
    {
340
        $tokens       = $phpcsFile->getTokens();
3✔
341
        $keywordStart = $tokens[$stackPtr]['column'];
3✔
342

343
        // Check the closing bracket is on a new line.
344
        $lastContent = $phpcsFile->findPrevious(T_WHITESPACE, ($arrayEnd - 1), $arrayStart, true);
3✔
345
        if ($tokens[$lastContent]['line'] === $tokens[$arrayEnd]['line']) {
3✔
346
            $error = 'Closing parenthesis of array declaration must be on a new line';
3✔
347
            $fix   = $phpcsFile->addFixableError($error, $arrayEnd, 'CloseBraceNewLine');
3✔
348
            if ($fix === true) {
3✔
349
                $phpcsFile->fixer->addNewlineBefore($arrayEnd);
3✔
350
            }
1✔
351
        } else if ($tokens[$arrayEnd]['column'] !== $keywordStart) {
3✔
352
            // Check the closing bracket is lined up under the "a" in array.
353
            $expected       = ($keywordStart - 1);
3✔
354
            $found          = ($tokens[$arrayEnd]['column'] - 1);
3✔
355
            $pluralizeSpace = 's';
3✔
356
            if ($expected === 1) {
3✔
357
                $pluralizeSpace = '';
3✔
358
            }
1✔
359

360
            $error = 'Closing parenthesis not aligned correctly; expected %s space%s but found %s';
3✔
361
            $data  = [
1✔
362
                $expected,
3✔
363
                $pluralizeSpace,
3✔
364
                $found,
3✔
365
            ];
2✔
366

367
            $fix = $phpcsFile->addFixableError($error, $arrayEnd, 'CloseBraceNotAligned', $data);
3✔
368
            if ($fix === true) {
3✔
369
                if ($found === 0) {
3✔
370
                    $phpcsFile->fixer->addContent(($arrayEnd - 1), str_repeat(' ', $expected));
3✔
371
                } else {
1✔
372
                    $phpcsFile->fixer->replaceToken(($arrayEnd - 1), str_repeat(' ', $expected));
3✔
373
                }
374
            }
1✔
375
        }//end if
1✔
376

377
        $keyUsed    = false;
3✔
378
        $singleUsed = false;
3✔
379
        $indices    = [];
3✔
380
        $maxLength  = 0;
3✔
381

382
        if ($tokens[$stackPtr]['code'] === T_ARRAY) {
3✔
383
            $lastToken = $tokens[$stackPtr]['parenthesis_opener'];
3✔
384
        } else {
1✔
385
            $lastToken = $stackPtr;
3✔
386
        }
387

388
        // Find all the double arrows that reside in this scope.
389
        for ($nextToken = ($stackPtr + 1); $nextToken < $arrayEnd; $nextToken++) {
3✔
390
            // Skip bracketed statements, like function calls.
391
            if ($tokens[$nextToken]['code'] === T_OPEN_PARENTHESIS
3✔
392
                && (isset($tokens[$nextToken]['parenthesis_owner']) === false
3✔
393
                || $tokens[$nextToken]['parenthesis_owner'] !== $stackPtr)
3✔
394
            ) {
1✔
395
                $nextToken = $tokens[$nextToken]['parenthesis_closer'];
3✔
396
                continue;
3✔
397
            }
398

399
            if ($tokens[$nextToken]['code'] === T_ARRAY
3✔
400
                || $tokens[$nextToken]['code'] === T_OPEN_SHORT_ARRAY
3✔
401
                || $tokens[$nextToken]['code'] === T_CLOSURE
3✔
402
                || $tokens[$nextToken]['code'] === T_FN
3✔
403
                || $tokens[$nextToken]['code'] === T_MATCH
3✔
404
            ) {
1✔
405
                // Let subsequent calls of this test handle nested arrays.
406
                if ($tokens[$lastToken]['code'] !== T_DOUBLE_ARROW) {
3✔
407
                    $indices[] = ['value' => $nextToken];
3✔
408
                    $lastToken = $nextToken;
3✔
409
                }
1✔
410

411
                if ($tokens[$nextToken]['code'] === T_ARRAY) {
3✔
412
                    $nextToken = $tokens[$tokens[$nextToken]['parenthesis_opener']]['parenthesis_closer'];
3✔
413
                } else if ($tokens[$nextToken]['code'] === T_OPEN_SHORT_ARRAY) {
3✔
414
                    $nextToken = $tokens[$nextToken]['bracket_closer'];
3✔
415
                } else {
1✔
416
                    // T_CLOSURE.
417
                    $nextToken = $tokens[$nextToken]['scope_closer'];
3✔
418
                }
419

420
                $nextToken = $phpcsFile->findNext(T_WHITESPACE, ($nextToken + 1), null, true);
3✔
421
                if ($tokens[$nextToken]['code'] !== T_COMMA) {
3✔
422
                    $nextToken--;
3✔
423
                } else {
1✔
424
                    $lastToken = $nextToken;
3✔
425
                }
426

427
                continue;
3✔
428
            }//end if
429

430
            if ($tokens[$nextToken]['code'] !== T_DOUBLE_ARROW && $tokens[$nextToken]['code'] !== T_COMMA) {
3✔
431
                continue;
3✔
432
            }
433

434
            $currentEntry = [];
3✔
435

436
            if ($tokens[$nextToken]['code'] === T_COMMA) {
3✔
437
                $stackPtrCount = 0;
3✔
438
                if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
3✔
439
                    $stackPtrCount = count($tokens[$stackPtr]['nested_parenthesis']);
3✔
440
                }
1✔
441

442
                $commaCount = 0;
3✔
443
                if (isset($tokens[$nextToken]['nested_parenthesis']) === true) {
3✔
444
                    $commaCount = count($tokens[$nextToken]['nested_parenthesis']);
3✔
445
                    if ($tokens[$stackPtr]['code'] === T_ARRAY) {
3✔
446
                        // Remove parenthesis that are used to define the array.
447
                        $commaCount--;
3✔
448
                    }
1✔
449
                }
1✔
450

451
                if ($commaCount > $stackPtrCount) {
3✔
452
                    // This comma is inside more parenthesis than the ARRAY keyword,
453
                    // then there it is actually a comma used to separate arguments
454
                    // in a function call.
UNCOV
455
                    continue;
×
456
                }
457

458
                if ($keyUsed === true && $tokens[$lastToken]['code'] === T_COMMA) {
3✔
459
                    $nextToken = $phpcsFile->findNext(Tokens::$emptyTokens, ($lastToken + 1), null, true);
3✔
460
                    // Allow for PHP 7.4+ array unpacking within an array declaration.
461
                    if ($tokens[$nextToken]['code'] !== T_ELLIPSIS) {
3✔
462
                        $error = 'No key specified for array entry; first entry specifies key';
3✔
463
                        $phpcsFile->addError($error, $nextToken, 'NoKeySpecified');
3✔
464
                        return;
3✔
465
                    }
466
                }
1✔
467

468
                if ($keyUsed === false) {
3✔
469
                    if ($tokens[($nextToken - 1)]['code'] === T_WHITESPACE) {
3✔
470
                        $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($nextToken - 1), null, true);
3✔
471
                        if (($tokens[$prev]['code'] !== T_END_HEREDOC
3✔
472
                            && $tokens[$prev]['code'] !== T_END_NOWDOC)
3✔
473
                            || $tokens[($nextToken - 1)]['line'] === $tokens[$nextToken]['line']
3✔
474
                        ) {
1✔
475
                            if ($tokens[($nextToken - 1)]['content'] === $phpcsFile->eolChar) {
3✔
476
                                $spaceLength = 'newline';
3✔
477
                            } else {
1✔
478
                                $spaceLength = $tokens[($nextToken - 1)]['length'];
3✔
479
                            }
480

481
                            $error = 'Expected 0 spaces before comma; %s found';
3✔
482
                            $data  = [$spaceLength];
3✔
483

484
                            // The error is only fixable if there is only whitespace between the tokens.
485
                            if ($prev === $phpcsFile->findPrevious(T_WHITESPACE, ($nextToken - 1), null, true)) {
3✔
486
                                $fix = $phpcsFile->addFixableError($error, $nextToken, 'SpaceBeforeComma', $data);
3✔
487
                                if ($fix === true) {
3✔
488
                                    $phpcsFile->fixer->replaceToken(($nextToken - 1), '');
3✔
489
                                }
1✔
490
                            } else {
1✔
491
                                $phpcsFile->addError($error, $nextToken, 'SpaceBeforeComma', $data);
3✔
492
                            }
493
                        }
1✔
494
                    }//end if
1✔
495

496
                    $valueContent = $phpcsFile->findNext(
3✔
497
                        Tokens::$emptyTokens,
3✔
498
                        ($lastToken + 1),
3✔
499
                        $nextToken,
3✔
500
                        true
2✔
501
                    );
2✔
502

503
                    $indices[]          = ['value' => $valueContent];
3✔
504
                    $usesArrayUnpacking = $phpcsFile->findPrevious(
3✔
505
                        Tokens::$emptyTokens,
3✔
506
                        ($nextToken - 2),
3✔
507
                        null,
3✔
508
                        true
2✔
509
                    );
2✔
510
                    if ($tokens[$usesArrayUnpacking]['code'] !== T_ELLIPSIS) {
3✔
511
                        // Don't decide if an array is key => value indexed or not when PHP 7.4+ array unpacking is used.
512
                        $singleUsed = true;
3✔
513
                    }
1✔
514
                }//end if
1✔
515

516
                $lastToken = $nextToken;
3✔
517
                continue;
3✔
518
            }//end if
519

520
            if ($tokens[$nextToken]['code'] === T_DOUBLE_ARROW) {
3✔
521
                if ($singleUsed === true) {
3✔
522
                    $error = 'Key specified for array entry; first entry has no key';
3✔
523
                    $phpcsFile->addError($error, $nextToken, 'KeySpecified');
3✔
524
                    return;
3✔
525
                }
526

527
                $currentEntry['arrow'] = $nextToken;
3✔
528
                $keyUsed = true;
3✔
529

530
                // Find the start of index that uses this double arrow.
531
                $indexEnd   = $phpcsFile->findPrevious(T_WHITESPACE, ($nextToken - 1), $arrayStart, true);
3✔
532
                $indexStart = $phpcsFile->findStartOfStatement($indexEnd);
3✔
533

534
                if ($indexStart === $indexEnd) {
3✔
535
                    $currentEntry['index']         = $indexEnd;
3✔
536
                    $currentEntry['index_content'] = $tokens[$indexEnd]['content'];
3✔
537
                    $currentEntry['index_length']  = $tokens[$indexEnd]['length'];
3✔
538
                } else {
1✔
539
                    $currentEntry['index']         = $indexStart;
3✔
540
                    $currentEntry['index_content'] = '';
3✔
541
                    $currentEntry['index_length']  = 0;
3✔
542
                    for ($i = $indexStart; $i <= $indexEnd; $i++) {
3✔
543
                        $currentEntry['index_content'] .= $tokens[$i]['content'];
3✔
544
                        $currentEntry['index_length']  += $tokens[$i]['length'];
3✔
545
                    }
1✔
546
                }
547

548
                if ($maxLength < $currentEntry['index_length']) {
3✔
549
                    $maxLength = $currentEntry['index_length'];
3✔
550
                }
1✔
551

552
                // Find the value of this index.
553
                $nextContent = $phpcsFile->findNext(
3✔
554
                    Tokens::$emptyTokens,
3✔
555
                    ($nextToken + 1),
3✔
556
                    $arrayEnd,
3✔
557
                    true
2✔
558
                );
2✔
559

560
                $currentEntry['value'] = $nextContent;
3✔
561
                $indices[] = $currentEntry;
3✔
562
                $lastToken = $nextToken;
3✔
563
            }//end if
1✔
564
        }//end for
1✔
565

566
        // Check for multi-line arrays that should be single-line.
567
        $singleValue = false;
3✔
568

569
        if (empty($indices) === true) {
3✔
570
            $singleValue = true;
3✔
571
        } else if (count($indices) === 1 && $tokens[$lastToken]['code'] === T_COMMA) {
3✔
572
            // There may be another array value without a comma.
573
            $exclude     = Tokens::$emptyTokens;
3✔
574
            $exclude[]   = T_COMMA;
3✔
575
            $nextContent = $phpcsFile->findNext($exclude, ($indices[0]['value'] + 1), $arrayEnd, true);
3✔
576
            if ($nextContent === false) {
3✔
577
                $singleValue = true;
3✔
578
            }
1✔
579
        }
1✔
580

581
        if ($singleValue === true) {
3✔
582
            // Before we complain, make sure the single value isn't a here/nowdoc.
583
            $next = $phpcsFile->findNext(Tokens::$heredocTokens, ($arrayStart + 1), ($arrayEnd - 1));
3✔
584
            if ($next === false) {
3✔
585
                // Array cannot be empty, so this is a multi-line array with
586
                // a single value. It should be defined on single line.
587
                $error     = 'Multi-line array contains a single value; use single-line array instead';
3✔
588
                $errorCode = 'MultiLineNotAllowed';
3✔
589

590
                $find    = Tokens::$phpcsCommentTokens;
3✔
591
                $find[]  = T_COMMENT;
3✔
592
                $comment = $phpcsFile->findNext($find, ($arrayStart + 1), $arrayEnd);
3✔
593
                if ($comment === false) {
3✔
594
                    $fix = $phpcsFile->addFixableError($error, $stackPtr, $errorCode);
3✔
595
                } else {
1✔
596
                    $fix = false;
3✔
597
                    $phpcsFile->addError($error, $stackPtr, $errorCode);
3✔
598
                }
599

600
                if ($fix === true) {
3✔
601
                    $phpcsFile->fixer->beginChangeset();
3✔
602
                    for ($i = ($arrayStart + 1); $i < $arrayEnd; $i++) {
3✔
603
                        if ($tokens[$i]['code'] !== T_WHITESPACE) {
3✔
604
                            break;
3✔
605
                        }
606

607
                        $phpcsFile->fixer->replaceToken($i, '');
3✔
608
                    }
1✔
609

610
                    for ($i = ($arrayEnd - 1); $i > $arrayStart; $i--) {
3✔
611
                        if ($tokens[$i]['code'] !== T_WHITESPACE) {
3✔
612
                            break;
3✔
613
                        }
614

615
                        $phpcsFile->fixer->replaceToken($i, '');
3✔
616
                    }
1✔
617

618
                    $phpcsFile->fixer->endChangeset();
3✔
619
                }
1✔
620

621
                return;
3✔
622
            }//end if
623
        }//end if
1✔
624

625
        /*
626
            This section checks for arrays that don't specify keys.
627

628
            Arrays such as:
629
               array(
630
                'aaa',
631
                'bbb',
632
                'd',
633
               );
634
        */
635

636
        if ($keyUsed === false && empty($indices) === false) {
3✔
637
            $count     = count($indices);
3✔
638
            $lastIndex = $indices[($count - 1)]['value'];
3✔
639

640
            $trailingContent = $phpcsFile->findPrevious(
3✔
641
                Tokens::$emptyTokens,
3✔
642
                ($arrayEnd - 1),
3✔
643
                $lastIndex,
3✔
644
                true
2✔
645
            );
2✔
646

647
            if ($tokens[$trailingContent]['code'] !== T_COMMA) {
3✔
648
                $phpcsFile->recordMetric($stackPtr, 'Array end comma', 'no');
3✔
649
                $error = 'Comma required after last value in array declaration';
3✔
650
                $fix   = $phpcsFile->addFixableError($error, $trailingContent, 'NoCommaAfterLast');
3✔
651
                if ($fix === true) {
3✔
652
                    $phpcsFile->fixer->addContent($trailingContent, ',');
3✔
653
                }
1✔
654
            } else {
1✔
655
                $phpcsFile->recordMetric($stackPtr, 'Array end comma', 'yes');
3✔
656
            }
657

658
            foreach ($indices as $valuePosition => $value) {
3✔
659
                if (empty($value['value']) === true) {
3✔
660
                    // Array was malformed and we couldn't figure out
661
                    // the array value correctly, so we have to ignore it.
662
                    // Other parts of this sniff will correct the error.
UNCOV
663
                    continue;
×
664
                }
665

666
                $valuePointer = $value['value'];
3✔
667

668
                $ignoreTokens  = [
1✔
669
                    T_WHITESPACE => T_WHITESPACE,
3✔
670
                    T_COMMA      => T_COMMA,
3✔
671
                ];
2✔
672
                $ignoreTokens += Tokens::$castTokens;
3✔
673

674
                if ($tokens[$valuePointer]['code'] === T_CLOSURE
3✔
675
                    || $tokens[$valuePointer]['code'] === T_FN
3✔
676
                ) {
1✔
677
                    // Check if the closure is static, if it is, override the value pointer as indices before skip static.
678
                    $staticPointer = $phpcsFile->findPrevious($ignoreTokens, ($valuePointer - 1), ($arrayStart + 1), true);
3✔
679
                    if ($staticPointer !== false && $tokens[$staticPointer]['code'] === T_STATIC) {
3✔
680
                        $valuePointer = $staticPointer;
3✔
681
                    }
1✔
682
                }
1✔
683

684
                $previous = $phpcsFile->findPrevious($ignoreTokens, ($valuePointer - 1), ($arrayStart + 1), true);
3✔
685
                if ($previous === false) {
3✔
686
                    $previous = $stackPtr;
3✔
687
                }
1✔
688

689
                $previousIsWhitespace = $tokens[($valuePointer - 1)]['code'] === T_WHITESPACE;
3✔
690
                if ($tokens[$previous]['line'] === $tokens[$valuePointer]['line']) {
3✔
691
                    $error = 'Each value in a multi-line array must be on a new line';
3✔
692
                    if ($valuePosition === 0) {
3✔
693
                        $error = 'The first value in a multi-value array must be on a new line';
3✔
694
                    }
1✔
695

696
                    $fix = $phpcsFile->addFixableError($error, $valuePointer, 'ValueNoNewline');
3✔
697
                    if ($fix === true) {
3✔
698
                        if ($previousIsWhitespace === true) {
3✔
699
                            $phpcsFile->fixer->replaceToken(($valuePointer - 1), $phpcsFile->eolChar);
3✔
700
                        } else {
1✔
701
                            $phpcsFile->fixer->addNewlineBefore($valuePointer);
3✔
702
                        }
703
                    }
1✔
704
                } else if ($previousIsWhitespace === true) {
3✔
705
                    $expected = $keywordStart;
3✔
706

707
                    $first          = $phpcsFile->findFirstOnLine(T_WHITESPACE, $valuePointer, true);
3✔
708
                    $found          = ($tokens[$first]['column'] - 1);
3✔
709
                    $pluralizeSpace = 's';
3✔
710
                    if ($expected === 1) {
3✔
711
                        $pluralizeSpace = '';
3✔
712
                    }
1✔
713

714
                    if ($found !== $expected) {
3✔
715
                        $error = 'Array value not aligned correctly; expected %s space%s but found %s';
3✔
716
                        $data  = [
1✔
717
                            $expected,
3✔
718
                            $pluralizeSpace,
3✔
719
                            $found,
3✔
720
                        ];
2✔
721

722
                        $fix = $phpcsFile->addFixableError($error, $first, 'ValueNotAligned', $data);
3✔
723
                        if ($fix === true) {
3✔
724
                            if ($found === 0) {
3✔
725
                                $phpcsFile->fixer->addContent(($first - 1), str_repeat(' ', $expected));
3✔
726
                            } else {
1✔
727
                                $phpcsFile->fixer->replaceToken(($first - 1), str_repeat(' ', $expected));
3✔
728
                            }
729
                        }
1✔
730
                    }
1✔
731
                }//end if
1✔
732
            }//end foreach
1✔
733
        }//end if
1✔
734

735
        /*
736
            Below the actual indentation of the array is checked.
737
            Errors will be thrown when a key is not aligned, when
738
            a double arrow is not aligned, and when a value is not
739
            aligned correctly.
740
            If an error is found in one of the above areas, then errors
741
            are not reported for the rest of the line to avoid reporting
742
            spaces and columns incorrectly. Often fixing the first
743
            problem will fix the other 2 anyway.
744

745
            For example:
746

747
            $a = array(
748
                  'index'  => '2',
749
                 );
750

751
            or
752

753
            $a = [
754
                  'index'  => '2',
755
                 ];
756

757
            In this array, the double arrow is indented too far, but this
758
            will also cause an error in the value's alignment. If the arrow were
759
            to be moved back one space however, then both errors would be fixed.
760
        */
761

762
        $indicesStart = ($keywordStart + 1);
3✔
763
        foreach ($indices as $valuePosition => $index) {
3✔
764
            $valuePointer = $index['value'];
3✔
765
            if ($valuePointer === false) {
3✔
766
                // Syntax error or live coding.
767
                continue;
3✔
768
            }
769

770
            if (isset($index['index']) === false) {
3✔
771
                // Array value only.
772
                continue;
3✔
773
            }
774

775
            $indexPointer = $index['index'];
3✔
776
            $indexLine    = $tokens[$indexPointer]['line'];
3✔
777

778
            $previous = $phpcsFile->findPrevious([T_WHITESPACE, T_COMMA], ($indexPointer - 1), ($arrayStart + 1), true);
3✔
779
            if ($previous === false) {
3✔
780
                $previous = $stackPtr;
3✔
781
            }
1✔
782

783
            if ($tokens[$previous]['line'] === $indexLine) {
3✔
784
                $error = 'Each index in a multi-line array must be on a new line';
3✔
785
                if ($valuePosition === 0) {
3✔
786
                    $error = 'The first index in a multi-value array must be on a new line';
3✔
787
                }
1✔
788

789
                $fix = $phpcsFile->addFixableError($error, $indexPointer, 'IndexNoNewline');
3✔
790
                if ($fix === true) {
3✔
791
                    if ($tokens[($indexPointer - 1)]['code'] === T_WHITESPACE) {
3✔
792
                        $phpcsFile->fixer->replaceToken(($indexPointer - 1), $phpcsFile->eolChar);
3✔
793
                    } else {
1✔
794
                        $phpcsFile->fixer->addNewlineBefore($indexPointer);
3✔
795
                    }
796
                }
1✔
797

798
                continue;
3✔
799
            }
800

801
            if ($tokens[$indexPointer]['column'] !== $indicesStart && ($indexPointer - 1) !== $arrayStart) {
3✔
802
                $expected       = ($indicesStart - 1);
3✔
803
                $found          = ($tokens[$indexPointer]['column'] - 1);
3✔
804
                $pluralizeSpace = 's';
3✔
805
                if ($expected === 1) {
3✔
806
                    $pluralizeSpace = '';
3✔
807
                }
1✔
808

809
                $error = 'Array key not aligned correctly; expected %s space%s but found %s';
3✔
810
                $data  = [
1✔
811
                    $expected,
3✔
812
                    $pluralizeSpace,
3✔
813
                    $found,
3✔
814
                ];
2✔
815

816
                $fix = $phpcsFile->addFixableError($error, $indexPointer, 'KeyNotAligned', $data);
3✔
817
                if ($fix === true) {
3✔
818
                    if ($found === 0 || $tokens[($indexPointer - 1)]['code'] !== T_WHITESPACE) {
3✔
819
                        $phpcsFile->fixer->addContent(($indexPointer - 1), str_repeat(' ', $expected));
3✔
820
                    } else {
1✔
821
                        $phpcsFile->fixer->replaceToken(($indexPointer - 1), str_repeat(' ', $expected));
3✔
822
                    }
823
                }
1✔
824
            }//end if
1✔
825

826
            $arrowStart = ($tokens[$indexPointer]['column'] + $maxLength + 1);
3✔
827
            if ($tokens[$index['arrow']]['column'] !== $arrowStart) {
3✔
828
                $expected       = ($arrowStart - ($index['index_length'] + $tokens[$indexPointer]['column']));
3✔
829
                $found          = ($tokens[$index['arrow']]['column'] - ($index['index_length'] + $tokens[$indexPointer]['column']));
3✔
830
                $pluralizeSpace = 's';
3✔
831
                if ($expected === 1) {
3✔
832
                    $pluralizeSpace = '';
3✔
833
                }
1✔
834

835
                $error = 'Array double arrow not aligned correctly; expected %s space%s but found %s';
3✔
836
                $data  = [
1✔
837
                    $expected,
3✔
838
                    $pluralizeSpace,
3✔
839
                    $found,
3✔
840
                ];
2✔
841

842
                $fix = $phpcsFile->addFixableError($error, $index['arrow'], 'DoubleArrowNotAligned', $data);
3✔
843
                if ($fix === true) {
3✔
844
                    if ($found === 0) {
3✔
845
                        $phpcsFile->fixer->addContent(($index['arrow'] - 1), str_repeat(' ', $expected));
3✔
846
                    } else {
1✔
847
                        $phpcsFile->fixer->replaceToken(($index['arrow'] - 1), str_repeat(' ', $expected));
3✔
848
                    }
849
                }
1✔
850

851
                continue;
3✔
852
            }//end if
853

854
            $valueStart = ($arrowStart + 3);
3✔
855
            if ($tokens[$valuePointer]['column'] !== $valueStart) {
3✔
856
                $expected = ($valueStart - ($tokens[$index['arrow']]['length'] + $tokens[$index['arrow']]['column']));
3✔
857
                $found    = ($tokens[$valuePointer]['column'] - ($tokens[$index['arrow']]['length'] + $tokens[$index['arrow']]['column']));
3✔
858
                if ($found < 0) {
3✔
859
                    $found = 'newline';
3✔
860
                }
1✔
861

862
                $pluralizeSpace = 's';
3✔
863
                if ($expected === 1) {
3✔
864
                    $pluralizeSpace = '';
3✔
865
                }
1✔
866

867
                $error = 'Array value not aligned correctly; expected %s space%s but found %s';
3✔
868
                $data  = [
1✔
869
                    $expected,
3✔
870
                    $pluralizeSpace,
3✔
871
                    $found,
3✔
872
                ];
2✔
873

874
                $fix = $phpcsFile->addFixableError($error, $index['arrow'], 'ValueNotAligned', $data);
3✔
875
                if ($fix === true) {
3✔
876
                    if ($found === 'newline') {
3✔
877
                        $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($valuePointer - 1), null, true);
3✔
878
                        $phpcsFile->fixer->beginChangeset();
3✔
879
                        for ($i = ($prev + 1); $i < $valuePointer; $i++) {
3✔
880
                            $phpcsFile->fixer->replaceToken($i, '');
3✔
881
                        }
1✔
882

883
                        $phpcsFile->fixer->replaceToken(($valuePointer - 1), str_repeat(' ', $expected));
3✔
884
                        $phpcsFile->fixer->endChangeset();
3✔
885
                    } else if ($found === 0) {
3✔
886
                        $phpcsFile->fixer->addContent(($valuePointer - 1), str_repeat(' ', $expected));
3✔
887
                    } else {
1✔
888
                        $phpcsFile->fixer->replaceToken(($valuePointer - 1), str_repeat(' ', $expected));
3✔
889
                    }
890
                }
1✔
891
            }//end if
1✔
892

893
            // Check each line ends in a comma.
894
            $valueStart = $valuePointer;
3✔
895
            $nextComma  = false;
3✔
896

897
            $end = $phpcsFile->findEndOfStatement($valueStart);
3✔
898
            if ($end === false) {
3✔
UNCOV
899
                $valueEnd = $valueStart;
×
900
            } else if ($tokens[$end]['code'] === T_COMMA) {
3✔
901
                $valueEnd  = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($end - 1), $valueStart, true);
3✔
902
                $nextComma = $end;
3✔
903
            } else {
1✔
904
                $valueEnd = $end;
3✔
905
                $next     = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), $arrayEnd, true);
3✔
906
                if ($next !== false && $tokens[$next]['code'] === T_COMMA) {
3✔
907
                    $nextComma = $next;
3✔
908
                }
1✔
909
            }
910

911
            $valueLine = $tokens[$valueEnd]['line'];
3✔
912
            if ($tokens[$valueEnd]['code'] === T_END_HEREDOC || $tokens[$valueEnd]['code'] === T_END_NOWDOC) {
3✔
913
                $valueLine++;
3✔
914
            }
1✔
915

916
            if ($nextComma === false || ($tokens[$nextComma]['line'] !== $valueLine)) {
3✔
917
                $error = 'Each line in an array declaration must end in a comma';
3✔
918
                $fix   = $phpcsFile->addFixableError($error, $valuePointer, 'NoComma');
3✔
919

920
                if ($fix === true) {
3✔
921
                    // Find the end of the line and put a comma there.
922
                    for ($i = ($valuePointer + 1); $i <= $arrayEnd; $i++) {
3✔
923
                        if ($tokens[$i]['line'] > $valueLine) {
3✔
924
                            break;
3✔
925
                        }
926
                    }
1✔
927

928
                    $phpcsFile->fixer->beginChangeset();
3✔
929
                    $phpcsFile->fixer->addContentBefore(($i - 1), ',');
3✔
930
                    if ($nextComma !== false) {
3✔
931
                        $phpcsFile->fixer->replaceToken($nextComma, '');
3✔
932
                    }
1✔
933

934
                    $phpcsFile->fixer->endChangeset();
3✔
935
                }
1✔
936
            }//end if
1✔
937

938
            // Check that there is no space before the comma.
939
            if ($nextComma !== false && $tokens[($nextComma - 1)]['code'] === T_WHITESPACE) {
3✔
940
                // Here/nowdoc closing tags must have the comma on the next line.
941
                $prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($nextComma - 1), null, true);
3✔
942
                if ($tokens[$prev]['code'] !== T_END_HEREDOC && $tokens[$prev]['code'] !== T_END_NOWDOC) {
3✔
943
                    $content     = $tokens[($nextComma - 2)]['content'];
3✔
944
                    $spaceLength = $tokens[($nextComma - 1)]['length'];
3✔
945
                    $error       = 'Expected 0 spaces between "%s" and comma; %s found';
3✔
946
                    $data        = [
1✔
947
                        $content,
3✔
948
                        $spaceLength,
3✔
949
                    ];
2✔
950

951
                    $fix = $phpcsFile->addFixableError($error, $nextComma, 'SpaceBeforeComma', $data);
3✔
952
                    if ($fix === true) {
3✔
953
                        $phpcsFile->fixer->replaceToken(($nextComma - 1), '');
3✔
954
                    }
1✔
955
                }
1✔
956
            }
1✔
957
        }//end foreach
1✔
958

959
    }//end processMultiLineArray()
2✔
960

961

962
}//end class
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc