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

PHPCSStandards / PHP_CodeSniffer / 17663621563

12 Sep 2025 03:25AM UTC coverage: 78.786%. Remained the same
17663621563

push

github

web-flow
Merge pull request #1243 from PHPCSStandards/phpcs-4.x/feature/155-normalize-some-code-style-rules-5

CS: normalize code style rules [5]

294 of 308 new or added lines in 191 files covered. (95.45%)

2354 existing lines in 130 files now uncovered.

19732 of 25045 relevant lines covered (78.79%)

96.47 hits per line

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

99.43
/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 [
2✔
28
            T_ARRAY,
3✔
29
            T_OPEN_SHORT_ARRAY,
3✔
30
        ];
2✔
31
    }
32

33

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

150

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

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

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

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

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

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

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

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

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

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

271
                    $phpcsFile->fixer->endChangeset();
3✔
272
                }
273

274
                return;
3✔
275
            }
276

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

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

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

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

323

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

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

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

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

374
        $keyUsed    = false;
3✔
375
        $singleUsed = false;
3✔
376
        $indices    = [];
3✔
377
        $maxLength  = 0;
3✔
378

379
        if ($tokens[$stackPtr]['code'] === T_ARRAY) {
3✔
380
            $lastToken = $tokens[$stackPtr]['parenthesis_opener'];
3✔
381
        } else {
382
            $lastToken = $stackPtr;
3✔
383
        }
384

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

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

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

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

424
                continue;
3✔
425
            }//end if
426

427
            if ($tokens[$nextToken]['code'] !== T_DOUBLE_ARROW && $tokens[$nextToken]['code'] !== T_COMMA) {
3✔
428
                continue;
3✔
429
            }
430

431
            $currentEntry = [];
3✔
432

433
            if ($tokens[$nextToken]['code'] === T_COMMA) {
3✔
434
                $stackPtrCount = 0;
3✔
435
                if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
3✔
436
                    $stackPtrCount = count($tokens[$stackPtr]['nested_parenthesis']);
3✔
437
                }
438

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

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

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

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

478
                            $error = 'Expected 0 spaces before comma; %s found';
3✔
479
                            $data  = [$spaceLength];
3✔
480

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

493
                    $valueContent = $phpcsFile->findNext(
3✔
494
                        Tokens::EMPTY_TOKENS,
3✔
495
                        ($lastToken + 1),
3✔
496
                        $nextToken,
3✔
497
                        true
3✔
498
                    );
2✔
499

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

513
                $lastToken = $nextToken;
3✔
514
                continue;
3✔
515
            }//end if
516

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

524
                $currentEntry['arrow'] = $nextToken;
3✔
525
                $keyUsed = true;
3✔
526

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

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

545
                if ($maxLength < $currentEntry['index_length']) {
3✔
546
                    $maxLength = $currentEntry['index_length'];
3✔
547
                }
548

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

557
                $currentEntry['value'] = $nextContent;
3✔
558
                $indices[] = $currentEntry;
3✔
559
                $lastToken = $nextToken;
3✔
560
            }//end if
561
        }//end for
562

563
        // Check for multi-line arrays that should be single-line.
564
        $singleValue = false;
3✔
565

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

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

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

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

604
                        $phpcsFile->fixer->replaceToken($i, '');
3✔
605
                    }
606

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

612
                        $phpcsFile->fixer->replaceToken($i, '');
3✔
613
                    }
614

615
                    $phpcsFile->fixer->endChangeset();
3✔
616
                }
617

618
                return;
3✔
619
            }//end if
620
        }//end if
621

622
        /*
623
            This section checks for arrays that don't specify keys.
624

625
            Arrays such as:
626
               array(
627
                'aaa',
628
                'bbb',
629
                'd',
630
               );
631
        */
632

633
        if ($keyUsed === false && empty($indices) === false) {
3✔
634
            $count     = count($indices);
3✔
635
            $lastIndex = $indices[($count - 1)]['value'];
3✔
636

637
            $trailingContent = $phpcsFile->findPrevious(
3✔
638
                Tokens::EMPTY_TOKENS,
3✔
639
                ($arrayEnd - 1),
3✔
640
                $lastIndex,
3✔
641
                true
3✔
642
            );
2✔
643

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

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

663
                $valuePointer = $value['value'];
3✔
664

665
                $ignoreTokens  = [
2✔
666
                    T_WHITESPACE => T_WHITESPACE,
3✔
667
                    T_COMMA      => T_COMMA,
3✔
668
                ];
2✔
669
                $ignoreTokens += Tokens::CAST_TOKENS;
3✔
670

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

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

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

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

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

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

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

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

742
            For example:
743

744
            $a = array(
745
                  'index'  => '2',
746
                 );
747

748
            or
749

750
            $a = [
751
                  'index'  => '2',
752
                 ];
753

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

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

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

772
            $indexPointer = $index['index'];
3✔
773
            $indexLine    = $tokens[$indexPointer]['line'];
3✔
774

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

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

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

795
                continue;
3✔
796
            }
797

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

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

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

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

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

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

848
                continue;
3✔
849
            }//end if
850

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

859
                $pluralizeSpace = 's';
3✔
860
                if ($expected === 1) {
3✔
861
                    $pluralizeSpace = '';
3✔
862
                }
863

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

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

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

890
            // Check each line ends in a comma.
891
            $valueStart = $valuePointer;
3✔
892
            $nextComma  = false;
3✔
893

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

908
            $valueLine = $tokens[$valueEnd]['line'];
3✔
909
            if ($tokens[$valueEnd]['code'] === T_END_HEREDOC || $tokens[$valueEnd]['code'] === T_END_NOWDOC) {
3✔
910
                $valueLine++;
3✔
911
            }
912

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

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

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

931
                    $phpcsFile->fixer->endChangeset();
3✔
932
                }
933
            }//end if
934

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

948
                    $fix = $phpcsFile->addFixableError($error, $nextComma, 'SpaceBeforeComma', $data);
3✔
949
                    if ($fix === true) {
3✔
950
                        $phpcsFile->fixer->replaceToken(($nextComma - 1), '');
3✔
951
                    }
952
                }
953
            }
954
        }//end foreach
955
    }
1✔
956
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc