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

keradus / PHP-CS-Fixer / 17319949156

29 Aug 2025 09:20AM UTC coverage: 94.696% (-0.05%) from 94.744%
17319949156

push

github

keradus
CS

28333 of 29920 relevant lines covered (94.7%)

45.63 hits per line

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

97.78
/src/Fixer/Alias/ArrayPushFixer.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of PHP CS Fixer.
7
 *
8
 * (c) Fabien Potencier <fabien@symfony.com>
9
 *     Dariusz RumiƄski <dariusz.ruminski@gmail.com>
10
 *
11
 * This source file is subject to the MIT license that is bundled
12
 * with this source code in the file LICENSE.
13
 */
14

15
namespace PhpCsFixer\Fixer\Alias;
16

17
use PhpCsFixer\AbstractFixer;
18
use PhpCsFixer\FixerDefinition\CodeSample;
19
use PhpCsFixer\FixerDefinition\FixerDefinition;
20
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
21
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
22
use PhpCsFixer\Tokenizer\CT;
23
use PhpCsFixer\Tokenizer\Token;
24
use PhpCsFixer\Tokenizer\Tokens;
25

26
/**
27
 * @no-named-arguments Parameter names are not covered by the backward compatibility promise.
28
 */
29
final class ArrayPushFixer extends AbstractFixer
30
{
31
    public function getDefinition(): FixerDefinitionInterface
32
    {
33
        return new FixerDefinition(
3✔
34
            'Converts simple usages of `array_push($x, $y);` to `$x[] = $y;`.',
3✔
35
            [new CodeSample("<?php\narray_push(\$x, \$y);\n")],
3✔
36
            null,
3✔
37
            'Risky when the function `array_push` is overridden.'
3✔
38
        );
3✔
39
    }
40

41
    public function isCandidate(Tokens $tokens): bool
42
    {
43
        return $tokens->isTokenKindFound(\T_STRING) && $tokens->count() > 7;
45✔
44
    }
45

46
    public function isRisky(): bool
47
    {
48
        return true;
1✔
49
    }
50

51
    protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
52
    {
53
        $functionsAnalyzer = new FunctionsAnalyzer();
45✔
54

55
        for ($index = $tokens->count() - 7; $index > 0; --$index) {
45✔
56
            if (!$tokens[$index]->equals([\T_STRING, 'array_push'], false)) {
45✔
57
                continue;
44✔
58
            }
59

60
            if (!$functionsAnalyzer->isGlobalFunctionCall($tokens, $index)) {
45✔
61
                continue; // redeclare/override
2✔
62
            }
63

64
            // meaningful before must be `<?php`, `{`, `}` or `;`
65

66
            $callIndex = $index;
43✔
67
            $index = $tokens->getPrevMeaningfulToken($index);
43✔
68
            $namespaceSeparatorIndex = null;
43✔
69

70
            if ($tokens[$index]->isGivenKind(\T_NS_SEPARATOR)) {
43✔
71
                $namespaceSeparatorIndex = $index;
3✔
72
                $index = $tokens->getPrevMeaningfulToken($index);
3✔
73
            }
74

75
            if (!$tokens[$index]->equalsAny([';', '{', '}', ')', [\T_OPEN_TAG]])) {
43✔
76
                continue;
5✔
77
            }
78

79
            // figure out where the arguments list opens
80

81
            $openBraceIndex = $tokens->getNextMeaningfulToken($callIndex);
38✔
82
            $blockType = Tokens::detectBlockType($tokens[$openBraceIndex]);
38✔
83

84
            if (null === $blockType || Tokens::BLOCK_TYPE_PARENTHESIS_BRACE !== $blockType['type']) {
38✔
85
                continue;
×
86
            }
87

88
            // figure out where the arguments list closes
89

90
            $closeBraceIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openBraceIndex);
38✔
91

92
            // meaningful after `)` must be `;`, `? >` or nothing
93

94
            $afterCloseBraceIndex = $tokens->getNextMeaningfulToken($closeBraceIndex);
38✔
95

96
            if (null !== $afterCloseBraceIndex && !$tokens[$afterCloseBraceIndex]->equalsAny([';', [\T_CLOSE_TAG]])) {
38✔
97
                continue;
×
98
            }
99

100
            // must have 2 arguments
101

102
            // first argument must be a variable (with possibly array indexing etc.),
103
            // after that nothing meaningful should be there till the next `,` or `)`
104
            // if `)` than we cannot fix it (it is a single argument call)
105

106
            $firstArgumentStop = $this->getFirstArgumentEnd($tokens, $openBraceIndex);
38✔
107
            $firstArgumentStop = $tokens->getNextMeaningfulToken($firstArgumentStop);
38✔
108

109
            if (!$tokens[$firstArgumentStop]->equals(',')) {
38✔
110
                return;
3✔
111
            }
112

113
            // second argument can be about anything but ellipsis, we must make sure there is not
114
            // a third argument (or more) passed to `array_push`
115

116
            $secondArgumentStart = $tokens->getNextMeaningfulToken($firstArgumentStop);
35✔
117
            $secondArgumentStop = $this->getSecondArgumentEnd($tokens, $secondArgumentStart, $closeBraceIndex);
35✔
118

119
            if (null === $secondArgumentStop) {
35✔
120
                continue;
7✔
121
            }
122

123
            // candidate is valid, replace tokens
124

125
            $tokens->clearTokenAndMergeSurroundingWhitespace($closeBraceIndex);
28✔
126
            $tokens->clearTokenAndMergeSurroundingWhitespace($firstArgumentStop);
28✔
127
            $tokens->insertAt(
28✔
128
                $firstArgumentStop,
28✔
129
                [
28✔
130
                    new Token('['),
28✔
131
                    new Token(']'),
28✔
132
                    new Token([\T_WHITESPACE, ' ']),
28✔
133
                    new Token('='),
28✔
134
                ]
28✔
135
            );
28✔
136
            $tokens->clearTokenAndMergeSurroundingWhitespace($openBraceIndex);
28✔
137
            $tokens->clearTokenAndMergeSurroundingWhitespace($callIndex);
28✔
138

139
            if (null !== $namespaceSeparatorIndex) {
28✔
140
                $tokens->clearTokenAndMergeSurroundingWhitespace($namespaceSeparatorIndex);
3✔
141
            }
142
        }
143
    }
144

145
    private function getFirstArgumentEnd(Tokens $tokens, int $index): int
146
    {
147
        $nextIndex = $tokens->getNextMeaningfulToken($index);
38✔
148
        $nextToken = $tokens[$nextIndex];
38✔
149

150
        while ($nextToken->equalsAny([
38✔
151
            '$',
38✔
152
            '[',
38✔
153
            '(',
38✔
154
            [CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN],
38✔
155
            [CT::T_DYNAMIC_PROP_BRACE_OPEN],
38✔
156
            [CT::T_DYNAMIC_VAR_BRACE_OPEN],
38✔
157
            [CT::T_NAMESPACE_OPERATOR],
38✔
158
            [\T_NS_SEPARATOR],
38✔
159
            [\T_STATIC],
38✔
160
            [\T_STRING],
38✔
161
            [\T_VARIABLE],
38✔
162
        ])) {
38✔
163
            $blockType = Tokens::detectBlockType($nextToken);
37✔
164

165
            if (null !== $blockType) {
37✔
166
                $nextIndex = $tokens->findBlockEnd($blockType['type'], $nextIndex);
5✔
167
            }
168

169
            $index = $nextIndex;
37✔
170
            $nextIndex = $tokens->getNextMeaningfulToken($nextIndex);
37✔
171
            $nextToken = $tokens[$nextIndex];
37✔
172
        }
173

174
        if ($nextToken->isGivenKind(\T_OBJECT_OPERATOR)) {
38✔
175
            return $this->getFirstArgumentEnd($tokens, $nextIndex);
3✔
176
        }
177

178
        if ($nextToken->isGivenKind(\T_PAAMAYIM_NEKUDOTAYIM)) {
38✔
179
            return $this->getFirstArgumentEnd($tokens, $tokens->getNextMeaningfulToken($nextIndex));
5✔
180
        }
181

182
        return $index;
38✔
183
    }
184

185
    /**
186
     * @param int $endIndex boundary, i.e. tokens index of `)`
187
     */
188
    private function getSecondArgumentEnd(Tokens $tokens, int $index, int $endIndex): ?int
189
    {
190
        if ($tokens[$index]->isGivenKind(\T_ELLIPSIS)) {
35✔
191
            return null;
1✔
192
        }
193

194
        for (; $index <= $endIndex; ++$index) {
34✔
195
            $blockType = Tokens::detectBlockType($tokens[$index]);
34✔
196

197
            while (null !== $blockType && $blockType['isStart']) {
34✔
198
                $index = $tokens->findBlockEnd($blockType['type'], $index);
9✔
199
                $index = $tokens->getNextMeaningfulToken($index);
9✔
200
                $blockType = Tokens::detectBlockType($tokens[$index]);
9✔
201
            }
202

203
            if ($tokens[$index]->equals(',') || $tokens[$index]->isGivenKind([\T_YIELD, \T_YIELD_FROM, \T_LOGICAL_AND, \T_LOGICAL_OR, \T_LOGICAL_XOR])) {
34✔
204
                return null;
6✔
205
            }
206
        }
207

208
        return $endIndex;
28✔
209
    }
210
}
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