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

keradus / PHP-CS-Fixer / 17377459942

01 Sep 2025 12:19PM UTC coverage: 94.684% (-0.009%) from 94.693%
17377459942

push

github

web-flow
chore: `Tokens::offsetSet` - explicit validation of input (#9004)

1 of 5 new or added lines in 1 file covered. (20.0%)

306 existing lines in 60 files now uncovered.

28390 of 29984 relevant lines covered (94.68%)

45.5 hits per line

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

98.67
/src/Fixer/Alias/NoAliasFunctionsFixer.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\Fixer\ConfigurableFixerInterface;
19
use PhpCsFixer\Fixer\ConfigurableFixerTrait;
20
use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
21
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
22
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
23
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
24
use PhpCsFixer\FixerDefinition\CodeSample;
25
use PhpCsFixer\FixerDefinition\FixerDefinition;
26
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
27
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
28
use PhpCsFixer\Tokenizer\Analyzer\FunctionsAnalyzer;
29
use PhpCsFixer\Tokenizer\Token;
30
use PhpCsFixer\Tokenizer\Tokens;
31

32
/**
33
 * @phpstan-type _AutogeneratedInputConfiguration array{
34
 *  sets?: list<'@all'|'@exif'|'@ftp'|'@IMAP'|'@internal'|'@ldap'|'@mbreg'|'@mysqli'|'@oci'|'@odbc'|'@openssl'|'@pcntl'|'@pg'|'@posix'|'@snmp'|'@sodium'|'@time'>,
35
 * }
36
 * @phpstan-type _AutogeneratedComputedConfiguration array{
37
 *  sets: list<'@all'|'@exif'|'@ftp'|'@IMAP'|'@internal'|'@ldap'|'@mbreg'|'@mysqli'|'@oci'|'@odbc'|'@openssl'|'@pcntl'|'@pg'|'@posix'|'@snmp'|'@sodium'|'@time'>,
38
 * }
39
 *
40
 * @implements ConfigurableFixerInterface<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration>
41
 *
42
 * @author Vladimir Reznichenko <kalessil@gmail.com>
43
 * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
44
 *
45
 * @no-named-arguments Parameter names are not covered by the backward compatibility promise.
46
 */
47
final class NoAliasFunctionsFixer extends AbstractFixer implements ConfigurableFixerInterface
48
{
49
    /** @use ConfigurableFixerTrait<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration> */
50
    use ConfigurableFixerTrait;
51

52
    private const SETS = [
53
        '@internal' => [
54
            'diskfreespace' => 'disk_free_space',
55

56
            'dns_check_record' => 'checkdnsrr',
57
            'dns_get_mx' => 'getmxrr',
58

59
            'session_commit' => 'session_write_close',
60

61
            'stream_register_wrapper' => 'stream_wrapper_register',
62
            'set_file_buffer' => 'stream_set_write_buffer',
63
            'socket_set_blocking' => 'stream_set_blocking',
64
            'socket_get_status' => 'stream_get_meta_data',
65
            'socket_set_timeout' => 'stream_set_timeout',
66
            'socket_getopt' => 'socket_get_option',
67
            'socket_setopt' => 'socket_set_option',
68

69
            'chop' => 'rtrim',
70
            'close' => 'closedir',
71
            'doubleval' => 'floatval',
72
            'fputs' => 'fwrite',
73
            'get_required_files' => 'get_included_files',
74
            'ini_alter' => 'ini_set',
75
            'is_double' => 'is_float',
76
            'is_integer' => 'is_int',
77
            'is_long' => 'is_int',
78
            'is_real' => 'is_float',
79
            'is_writeable' => 'is_writable',
80
            'join' => 'implode',
81
            'key_exists' => 'array_key_exists',
82
            'magic_quotes_runtime' => 'set_magic_quotes_runtime',
83
            'pos' => 'current',
84
            'show_source' => 'highlight_file',
85
            'sizeof' => 'count',
86
            'strchr' => 'strstr',
87
            'user_error' => 'trigger_error',
88
        ],
89

90
        '@IMAP' => [
91
            'imap_create' => 'imap_createmailbox',
92
            'imap_fetchtext' => 'imap_body',
93
            'imap_header' => 'imap_headerinfo',
94
            'imap_listmailbox' => 'imap_list',
95
            'imap_listsubscribed' => 'imap_lsub',
96
            'imap_rename' => 'imap_renamemailbox',
97
            'imap_scan' => 'imap_listscan',
98
            'imap_scanmailbox' => 'imap_listscan',
99
        ],
100

101
        '@ldap' => [
102
            'ldap_close' => 'ldap_unbind',
103
            'ldap_modify' => 'ldap_mod_replace',
104
        ],
105

106
        '@mysqli' => [
107
            'mysqli_execute' => 'mysqli_stmt_execute',
108
            'mysqli_set_opt' => 'mysqli_options',
109
            'mysqli_escape_string' => 'mysqli_real_escape_string',
110
        ],
111

112
        '@pg' => [
113
            'pg_exec' => 'pg_query',
114
        ],
115

116
        '@oci' => [
117
            'oci_free_cursor' => 'oci_free_statement',
118
        ],
119

120
        '@odbc' => [
121
            'odbc_do' => 'odbc_exec',
122
            'odbc_field_precision' => 'odbc_field_len',
123
        ],
124

125
        '@mbreg' => [
126
            'mbereg' => 'mb_ereg',
127
            'mbereg_match' => 'mb_ereg_match',
128
            'mbereg_replace' => 'mb_ereg_replace',
129
            'mbereg_search' => 'mb_ereg_search',
130
            'mbereg_search_getpos' => 'mb_ereg_search_getpos',
131
            'mbereg_search_getregs' => 'mb_ereg_search_getregs',
132
            'mbereg_search_init' => 'mb_ereg_search_init',
133
            'mbereg_search_pos' => 'mb_ereg_search_pos',
134
            'mbereg_search_regs' => 'mb_ereg_search_regs',
135
            'mbereg_search_setpos' => 'mb_ereg_search_setpos',
136
            'mberegi' => 'mb_eregi',
137
            'mberegi_replace' => 'mb_eregi_replace',
138
            'mbregex_encoding' => 'mb_regex_encoding',
139
            'mbsplit' => 'mb_split',
140
        ],
141

142
        '@openssl' => [
143
            'openssl_get_publickey' => 'openssl_pkey_get_public',
144
            'openssl_get_privatekey' => 'openssl_pkey_get_private',
145
        ],
146

147
        '@sodium' => [
148
            'sodium_crypto_scalarmult_base' => 'sodium_crypto_box_publickey_from_secretkey',
149
        ],
150

151
        '@exif' => [
152
            'read_exif_data' => 'exif_read_data',
153
        ],
154

155
        '@ftp' => [
156
            'ftp_quit' => 'ftp_close',
157
        ],
158

159
        '@posix' => [
160
            'posix_errno' => 'posix_get_last_error',
161
        ],
162

163
        '@pcntl' => [
164
            'pcntl_errno' => 'pcntl_get_last_error',
165
        ],
166

167
        '@time' => [
168
            'mktime' => ['time', 0],
169
            'gmmktime' => ['time', 0],
170
        ],
171
    ];
172

173
    /**
174
     * @var array<string, array{string, int}|string> stores alias (key) - master (value) functions mapping
175
     */
176
    private array $aliases = [];
177

178
    public function getDefinition(): FixerDefinitionInterface
179
    {
180
        return new FixerDefinition(
3✔
181
            'Master functions shall be used instead of aliases.',
3✔
182
            [
3✔
183
                new CodeSample(
3✔
184
                    <<<'PHP'
3✔
185
                        <?php
186
                        $a = chop($b);
187
                        close($b);
188
                        $a = doubleval($b);
189
                        $a = fputs($b, $c);
190
                        $a = get_required_files();
191
                        ini_alter($b, $c);
192
                        $a = is_double($b);
193
                        $a = is_integer($b);
194
                        $a = is_long($b);
195
                        $a = is_real($b);
196
                        $a = is_writeable($b);
197
                        $a = join($glue, $pieces);
198
                        $a = key_exists($key, $array);
199
                        magic_quotes_runtime($new_setting);
200
                        $a = pos($array);
201
                        $a = show_source($filename, true);
202
                        $a = sizeof($b);
203
                        $a = strchr($haystack, $needle);
204
                        $a = imap_header($imap_stream, 1);
205
                        user_error($message);
206
                        mbereg_search_getregs();
207

208
                        PHP
3✔
209
                ),
3✔
210
                new CodeSample(
3✔
211
                    <<<'PHP'
3✔
212
                        <?php
213
                        $a = is_double($b);
214
                        mbereg_search_getregs();
215

216
                        PHP,
3✔
217
                    ['sets' => ['@mbreg']]
3✔
218
                ),
3✔
219
            ],
3✔
220
            null,
3✔
221
            'Risky when any of the alias functions are overridden.'
3✔
222
        );
3✔
223
    }
224

225
    /**
226
     * {@inheritdoc}
227
     *
228
     * Must run before ImplodeCallFixer, PhpUnitDedicateAssertFixer.
229
     */
230
    public function getPriority(): int
231
    {
232
        return 40;
1✔
233
    }
234

235
    public function isCandidate(Tokens $tokens): bool
236
    {
237
        return $tokens->isTokenKindFound(\T_STRING);
3,006✔
238
    }
239

240
    public function isRisky(): bool
241
    {
242
        return true;
1✔
243
    }
244

245
    protected function configurePostNormalisation(): void
246
    {
247
        $this->aliases = [];
3,015✔
248

249
        foreach ($this->configuration['sets'] as $set) {
3,015✔
250
            if ('@all' === $set) {
3,015✔
251
                $this->aliases = array_merge(...array_values(self::SETS));
1✔
252

253
                break;
1✔
254
            }
255

256
            if (!isset(self::SETS[$set])) {
3,015✔
UNCOV
257
                throw new \LogicException(\sprintf('Set %s passed option validation, but not part of ::SETS.', $set));
×
258
            }
259

260
            $this->aliases = array_merge($this->aliases, self::SETS[$set]);
3,015✔
261
        }
262
    }
263

264
    protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
265
    {
266
        $functionsAnalyzer = new FunctionsAnalyzer();
2,462✔
267
        $argumentsAnalyzer = new ArgumentsAnalyzer();
2,462✔
268

269
        foreach ($tokens->findGivenKind(\T_STRING) as $index => $token) {
2,462✔
270
            // check mapping hit
271
            $tokenContent = strtolower($token->getContent());
2,462✔
272

273
            if (!isset($this->aliases[$tokenContent])) {
2,462✔
274
                continue;
2,140✔
275
            }
276

277
            // skip expressions without parameters list
278
            $openParenthesis = $tokens->getNextMeaningfulToken($index);
1,726✔
279

280
            if (!$tokens[$openParenthesis]->equals('(')) {
1,726✔
281
                continue;
428✔
282
            }
283

284
            if (!$functionsAnalyzer->isGlobalFunctionCall($tokens, $index)) {
1,405✔
285
                continue;
751✔
286
            }
287

288
            if (\is_array($this->aliases[$tokenContent])) {
654✔
289
                [$alias, $numberOfArguments] = $this->aliases[$tokenContent];
2✔
290

291
                $count = $argumentsAnalyzer->countArguments($tokens, $openParenthesis, $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesis));
2✔
292

293
                if ($numberOfArguments !== $count) {
2✔
294
                    continue;
2✔
295
                }
296
            } else {
297
                $alias = $this->aliases[$tokenContent];
653✔
298
            }
299

300
            $tokens[$index] = new Token([\T_STRING, $alias]);
654✔
301
        }
302
    }
303

304
    protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
305
    {
306
        $sets = [
3,015✔
307
            '@all' => 'all listed sets',
3,015✔
308
            '@internal' => 'native functions',
3,015✔
309
            '@exif' => 'EXIF functions',
3,015✔
310
            '@ftp' => 'FTP functions',
3,015✔
311
            '@IMAP' => 'IMAP functions',
3,015✔
312
            '@ldap' => 'LDAP functions',
3,015✔
313
            '@mbreg' => 'from `ext-mbstring`',
3,015✔
314
            '@mysqli' => 'mysqli functions',
3,015✔
315
            '@oci' => 'oci functions',
3,015✔
316
            '@odbc' => 'odbc functions',
3,015✔
317
            '@openssl' => 'openssl functions',
3,015✔
318
            '@pcntl' => 'PCNTL functions',
3,015✔
319
            '@pg' => 'pg functions',
3,015✔
320
            '@posix' => 'POSIX functions',
3,015✔
321
            '@snmp' => 'SNMP functions', // @TODO Remove on next major 4.0 as this set is now empty
3,015✔
322
            '@sodium' => 'libsodium functions',
3,015✔
323
            '@time' => 'time functions',
3,015✔
324
        ];
3,015✔
325

326
        $list = "List of sets to fix. Defined sets are:\n\n";
3,015✔
327

328
        foreach ($sets as $set => $description) {
3,015✔
329
            $list .= \sprintf("* `%s` (%s);\n", $set, $description);
3,015✔
330
        }
331

332
        $list = rtrim($list, ";\n").'.';
3,015✔
333

334
        return new FixerConfigurationResolver([
3,015✔
335
            (new FixerOptionBuilder('sets', $list))
3,015✔
336
                ->setAllowedTypes(['string[]'])
3,015✔
337
                ->setAllowedValues([new AllowedValueSubset(array_keys($sets))])
3,015✔
338
                ->setDefault(['@internal', '@IMAP', '@pg'])
3,015✔
339
                ->getOption(),
3,015✔
340
        ]);
3,015✔
341
    }
342
}
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