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

keradus / PHP-CS-Fixer / 17279562118

27 Aug 2025 09:47PM UTC coverage: 94.693%. Remained the same
17279562118

push

github

keradus
CS

28316 of 29903 relevant lines covered (94.69%)

45.61 hits per line

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

99.09
/src/Fixer/Alias/SetTypeToCastFixer.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\AbstractFunctionReferenceFixer;
18
use PhpCsFixer\FixerDefinition\CodeSample;
19
use PhpCsFixer\FixerDefinition\FixerDefinition;
20
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
21
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
22
use PhpCsFixer\Tokenizer\Token;
23
use PhpCsFixer\Tokenizer\Tokens;
24

25
/**
26
 * @no-named-arguments Parameter names are not covered by the backward compatibility promise.
27
 */
28
final class SetTypeToCastFixer extends AbstractFunctionReferenceFixer
29
{
30
    public function getDefinition(): FixerDefinitionInterface
31
    {
32
        return new FixerDefinition(
3✔
33
            'Cast shall be used, not `settype`.',
3✔
34
            [
3✔
35
                new CodeSample(
3✔
36
                    '<?php
3✔
37
settype($foo, "integer");
38
settype($bar, "string");
39
settype($bar, "null");
40
'
3✔
41
                ),
3✔
42
            ],
3✔
43
            null,
3✔
44
            'Risky when the `settype` function is overridden or when used as the 2nd or 3rd expression in a `for` loop .'
3✔
45
        );
3✔
46
    }
47

48
    /**
49
     * {@inheritdoc}
50
     *
51
     * Must run after NoBinaryStringFixer, NoUselessConcatOperatorFixer.
52
     */
53
    public function getPriority(): int
54
    {
55
        return 0;
1✔
56
    }
57

58
    public function isCandidate(Tokens $tokens): bool
59
    {
60
        return $tokens->isAllTokenKindsFound([\T_CONSTANT_ENCAPSED_STRING, \T_STRING, \T_VARIABLE]);
36✔
61
    }
62

63
    protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
64
    {
65
        $map = [
36✔
66
            'array' => [\T_ARRAY_CAST, '(array)'],
36✔
67
            'bool' => [\T_BOOL_CAST, '(bool)'],
36✔
68
            'boolean' => [\T_BOOL_CAST, '(bool)'],
36✔
69
            'double' => [\T_DOUBLE_CAST, '(float)'],
36✔
70
            'float' => [\T_DOUBLE_CAST, '(float)'],
36✔
71
            'int' => [\T_INT_CAST, '(int)'],
36✔
72
            'integer' => [\T_INT_CAST, '(int)'],
36✔
73
            'object' => [\T_OBJECT_CAST, '(object)'],
36✔
74
            'string' => [\T_STRING_CAST, '(string)'],
36✔
75
            // note: `'null' is dealt with later on
36✔
76
        ];
36✔
77

78
        $argumentsAnalyzer = new ArgumentsAnalyzer();
36✔
79

80
        foreach (array_reverse($this->findSettypeCalls($tokens)) as $candidate) {
36✔
81
            $functionNameIndex = $candidate[0];
28✔
82

83
            $arguments = $argumentsAnalyzer->getArguments($tokens, $candidate[1], $candidate[2]);
28✔
84
            if (2 !== \count($arguments)) {
28✔
85
                continue; // function must be overridden or used incorrectly
1✔
86
            }
87

88
            $prev = $tokens->getPrevMeaningfulToken($functionNameIndex);
27✔
89

90
            if (!$tokens[$prev]->equalsAny([';', '{', '}', [\T_OPEN_TAG]])) {
27✔
91
                continue; // return value of the function is used
6✔
92
            }
93

94
            reset($arguments);
22✔
95

96
            // --- Test first argument --------------------
97

98
            $firstArgumentStart = key($arguments);
22✔
99
            if ($tokens[$firstArgumentStart]->isComment() || $tokens[$firstArgumentStart]->isWhitespace()) {
22✔
100
                $firstArgumentStart = $tokens->getNextMeaningfulToken($firstArgumentStart);
8✔
101
            }
102

103
            if (!$tokens[$firstArgumentStart]->isGivenKind(\T_VARIABLE)) {
22✔
104
                continue; // settype only works with variables pass by reference, function must be overridden
2✔
105
            }
106

107
            $commaIndex = $tokens->getNextMeaningfulToken($firstArgumentStart);
22✔
108

109
            if (null === $commaIndex || !$tokens[$commaIndex]->equals(',')) {
22✔
110
                continue; // first argument is complex statement; function must be overridden
×
111
            }
112

113
            // --- Test second argument -------------------
114

115
            next($arguments);
22✔
116
            $secondArgumentStart = key($arguments);
22✔
117
            $secondArgumentEnd = $arguments[$secondArgumentStart];
22✔
118

119
            if ($tokens[$secondArgumentStart]->isComment() || $tokens[$secondArgumentStart]->isWhitespace()) {
22✔
120
                $secondArgumentStart = $tokens->getNextMeaningfulToken($secondArgumentStart);
21✔
121
            }
122

123
            if (
124
                !$tokens[$secondArgumentStart]->isGivenKind(\T_CONSTANT_ENCAPSED_STRING)
22✔
125
                || $tokens->getNextMeaningfulToken($secondArgumentStart) < $secondArgumentEnd
22✔
126
            ) {
127
                continue; // second argument is of the wrong type or is a (complex) statement of some sort (function is overridden)
2✔
128
            }
129

130
            // --- Test type ------------------------------
131

132
            $type = strtolower(trim($tokens[$secondArgumentStart]->getContent(), '"\''));
20✔
133

134
            if ('null' !== $type && !isset($map[$type])) {
20✔
135
                continue; // we don't know how to map
1✔
136
            }
137

138
            // --- Fixing ---------------------------------
139

140
            $argumentToken = $tokens[$firstArgumentStart];
19✔
141

142
            $this->removeSettypeCall(
19✔
143
                $tokens,
19✔
144
                $functionNameIndex,
19✔
145
                $candidate[1],
19✔
146
                $firstArgumentStart,
19✔
147
                $commaIndex,
19✔
148
                $secondArgumentStart,
19✔
149
                $candidate[2]
19✔
150
            );
19✔
151

152
            if ('null' === $type) {
19✔
153
                $this->fixSettypeNullCall($tokens, $functionNameIndex, $argumentToken);
3✔
154
            } else {
155
                $this->fixSettypeCall($tokens, $functionNameIndex, $argumentToken, new Token($map[$type]));
17✔
156
            }
157
        }
158
    }
159

160
    /**
161
     * @return list<array{int, int, int}>
162
     */
163
    private function findSettypeCalls(Tokens $tokens): array
164
    {
165
        $candidates = [];
36✔
166

167
        $end = \count($tokens);
36✔
168
        for ($i = 1; $i < $end; ++$i) {
36✔
169
            $candidate = $this->find('settype', $tokens, $i, $end);
36✔
170
            if (null === $candidate) {
36✔
171
                break;
36✔
172
            }
173

174
            $i = $candidate[1]; // proceed to openParenthesisIndex
28✔
175
            $candidates[] = $candidate;
28✔
176
        }
177

178
        return $candidates;
36✔
179
    }
180

181
    private function removeSettypeCall(
182
        Tokens $tokens,
183
        int $functionNameIndex,
184
        int $openParenthesisIndex,
185
        int $firstArgumentStart,
186
        int $commaIndex,
187
        int $secondArgumentStart,
188
        int $closeParenthesisIndex
189
    ): void {
190
        $tokens->clearTokenAndMergeSurroundingWhitespace($closeParenthesisIndex);
19✔
191
        $prevIndex = $tokens->getPrevMeaningfulToken($closeParenthesisIndex);
19✔
192
        if ($tokens[$prevIndex]->equals(',')) {
19✔
193
            $tokens->clearTokenAndMergeSurroundingWhitespace($prevIndex);
2✔
194
        }
195
        $tokens->clearTokenAndMergeSurroundingWhitespace($secondArgumentStart);
19✔
196
        $tokens->clearTokenAndMergeSurroundingWhitespace($commaIndex);
19✔
197
        $tokens->clearTokenAndMergeSurroundingWhitespace($firstArgumentStart);
19✔
198
        $tokens->clearTokenAndMergeSurroundingWhitespace($openParenthesisIndex);
19✔
199
        $tokens->clearAt($functionNameIndex); // we'll be inserting here so no need to merge the space tokens
19✔
200
        $tokens->clearEmptyTokens();
19✔
201
    }
202

203
    private function fixSettypeCall(
204
        Tokens $tokens,
205
        int $functionNameIndex,
206
        Token $argumentToken,
207
        Token $castToken
208
    ): void {
209
        $tokens->insertAt(
17✔
210
            $functionNameIndex,
17✔
211
            [
17✔
212
                clone $argumentToken,
17✔
213
                new Token([\T_WHITESPACE, ' ']),
17✔
214
                new Token('='),
17✔
215
                new Token([\T_WHITESPACE, ' ']),
17✔
216
                $castToken,
17✔
217
                new Token([\T_WHITESPACE, ' ']),
17✔
218
                clone $argumentToken,
17✔
219
            ]
17✔
220
        );
17✔
221

222
        $tokens->removeTrailingWhitespace($functionNameIndex + 6); // 6 = number of inserted tokens -1 for offset correction
17✔
223
    }
224

225
    private function fixSettypeNullCall(
226
        Tokens $tokens,
227
        int $functionNameIndex,
228
        Token $argumentToken
229
    ): void {
230
        $tokens->insertAt(
3✔
231
            $functionNameIndex,
3✔
232
            [
3✔
233
                clone $argumentToken,
3✔
234
                new Token([\T_WHITESPACE, ' ']),
3✔
235
                new Token('='),
3✔
236
                new Token([\T_WHITESPACE, ' ']),
3✔
237
                new Token([\T_STRING, 'null']),
3✔
238
            ]
3✔
239
        );
3✔
240

241
        $tokens->removeTrailingWhitespace($functionNameIndex + 4); // 4 = number of inserted tokens -1 for offset correction
3✔
242
    }
243
}
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