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

keradus / PHP-CS-Fixer / 17678835382

12 Sep 2025 03:24PM UTC coverage: 94.69% (-0.06%) from 94.75%
17678835382

push

github

keradus
fix typo

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

1042 existing lines in 177 files now uncovered.

28424 of 30018 relevant lines covered (94.69%)

45.5 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
                        <?php
38
                        settype($foo, "integer");
39
                        settype($bar, "string");
40
                        settype($bar, "null");
41

42
                        PHP
3✔
43
                ),
3✔
44
            ],
3✔
45
            null,
3✔
46
            'Risky when the `settype` function is overridden or when used as the 2nd or 3rd expression in a `for` loop .'
3✔
47
        );
3✔
48
    }
49

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

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

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

80
        $argumentsAnalyzer = new ArgumentsAnalyzer();
36✔
81

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

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

90
            $prev = $tokens->getPrevMeaningfulToken($functionNameIndex);
27✔
91

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

96
            reset($arguments);
22✔
97

98
            // --- Test first argument --------------------
99

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

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

109
            $commaIndex = $tokens->getNextMeaningfulToken($firstArgumentStart);
22✔
110

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

115
            // --- Test second argument -------------------
116

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

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

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

132
            // --- Test type ------------------------------
133

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

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

140
            // --- Fixing ---------------------------------
141

142
            $argumentToken = $tokens[$firstArgumentStart];
19✔
143

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

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

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

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

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

180
        return $candidates;
36✔
181
    }
182

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

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

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

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

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