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

keradus / PHP-CS-Fixer / 17253322895

26 Aug 2025 11:52PM UTC coverage: 94.753% (+0.008%) from 94.745%
17253322895

push

github

keradus
add to git-blame-ignore-revs

28316 of 29884 relevant lines covered (94.75%)

45.64 hits per line

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

98.67
/src/Fixer/ClassNotation/SelfStaticAccessorFixer.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\ClassNotation;
16

17
use PhpCsFixer\AbstractFixer;
18
use PhpCsFixer\FixerDefinition\CodeSample;
19
use PhpCsFixer\FixerDefinition\FixerDefinition;
20
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
21
use PhpCsFixer\FixerDefinition\VersionSpecification;
22
use PhpCsFixer\FixerDefinition\VersionSpecificCodeSample;
23
use PhpCsFixer\Tokenizer\FCT;
24
use PhpCsFixer\Tokenizer\Token;
25
use PhpCsFixer\Tokenizer\Tokens;
26
use PhpCsFixer\Tokenizer\TokensAnalyzer;
27

28
/**
29
 * @no-named-arguments Parameter names are not covered by the backward compatibility promise.
30
 */
31
final class SelfStaticAccessorFixer extends AbstractFixer
32
{
33
    private const CLASSY_TYPES = [\T_CLASS, FCT::T_ENUM];
34
    private const CLASSY_TOKENS_OF_INTEREST = [[\T_CLASS], [FCT::T_ENUM]];
35
    private TokensAnalyzer $tokensAnalyzer;
36

37
    public function getDefinition(): FixerDefinitionInterface
38
    {
39
        return new FixerDefinition(
3✔
40
            'Inside an enum or `final`/anonymous class, `self` should be preferred over `static`.',
3✔
41
            [
3✔
42
                new CodeSample(
3✔
43
                    '<?php
3✔
44
final class Sample
45
{
46
    private static $A = 1;
47

48
    public function getBar()
49
    {
50
        return static::class.static::test().static::$A;
51
    }
52

53
    private static function test()
54
    {
55
        return \'test\';
56
    }
57
}
58
'
3✔
59
                ),
3✔
60
                new CodeSample(
3✔
61
                    '<?php
3✔
62
final class Foo
63
{
64
    public function bar()
65
    {
66
        return new static();
67
    }
68
}
69
'
3✔
70
                ),
3✔
71
                new CodeSample(
3✔
72
                    '<?php
3✔
73
final class Foo
74
{
75
    public function isBar()
76
    {
77
        return $foo instanceof static;
78
    }
79
}
80
'
3✔
81
                ),
3✔
82
                new CodeSample(
3✔
83
                    '<?php
3✔
84
$a = new class() {
85
    public function getBar()
86
    {
87
        return static::class;
88
    }
89
};
90
'
3✔
91
                ),
3✔
92
                new VersionSpecificCodeSample(
3✔
93
                    '<?php
3✔
94
enum Foo
95
{
96
    public const A = 123;
97

98
    public static function bar(): void
99
    {
100
        echo static::A;
101
    }
102
}
103
',
3✔
104
                    new VersionSpecification(8_01_00)
3✔
105
                ),
3✔
106
            ]
3✔
107
        );
3✔
108
    }
109

110
    public function isCandidate(Tokens $tokens): bool
111
    {
112
        return $tokens->isTokenKindFound(\T_STATIC)
19✔
113
            && $tokens->isAnyTokenKindsFound(self::CLASSY_TYPES)
19✔
114
            && $tokens->isAnyTokenKindsFound([\T_DOUBLE_COLON, \T_NEW, \T_INSTANCEOF]);
19✔
115
    }
116

117
    /**
118
     * {@inheritdoc}
119
     *
120
     * Must run after FinalClassFixer, FinalInternalClassFixer, FunctionToConstantFixer, PhpUnitTestCaseStaticMethodCallsFixer.
121
     */
122
    public function getPriority(): int
123
    {
124
        return -10;
1✔
125
    }
126

127
    protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
128
    {
129
        $this->tokensAnalyzer = new TokensAnalyzer($tokens);
18✔
130
        $classyIndex = $tokens->getNextTokenOfKind(0, self::CLASSY_TOKENS_OF_INTEREST);
18✔
131

132
        while (null !== $classyIndex) {
18✔
133
            if ($tokens[$classyIndex]->isGivenKind(\T_CLASS)) {
18✔
134
                $modifiers = $this->tokensAnalyzer->getClassyModifiers($classyIndex);
16✔
135

136
                if (
137
                    isset($modifiers['final'])
16✔
138
                    || $this->tokensAnalyzer->isAnonymousClass($classyIndex)
16✔
139
                ) {
140
                    $classyIndex = $this->fixClassy($tokens, $classyIndex);
14✔
141
                }
142
            } else {
143
                $classyIndex = $this->fixClassy($tokens, $classyIndex);
3✔
144
            }
145

146
            $classyIndex = $tokens->getNextTokenOfKind($classyIndex, self::CLASSY_TOKENS_OF_INTEREST);
18✔
147
        }
148
    }
149

150
    private function fixClassy(Tokens $tokens, int $index): int
151
    {
152
        $index = $tokens->getNextTokenOfKind($index, ['{']);
16✔
153
        $classOpenCount = 1;
16✔
154

155
        while ($classOpenCount > 0) {
16✔
156
            ++$index;
16✔
157

158
            if ($tokens[$index]->equals('{')) {
16✔
159
                ++$classOpenCount;
16✔
160

161
                continue;
16✔
162
            }
163

164
            if ($tokens[$index]->equals('}')) {
16✔
165
                --$classOpenCount;
16✔
166

167
                continue;
16✔
168
            }
169

170
            if ($tokens[$index]->isGivenKind(\T_FUNCTION)) {
16✔
171
                // do not fix inside lambda
172
                if ($this->tokensAnalyzer->isLambda($index)) {
12✔
173
                    // figure out where the lambda starts
174
                    $index = $tokens->getNextTokenOfKind($index, ['{']);
2✔
175
                    $openCount = 1;
2✔
176

177
                    do {
178
                        $index = $tokens->getNextTokenOfKind($index, ['}', '{', [\T_CLASS]]);
2✔
179
                        if ($tokens[$index]->equals('}')) {
2✔
180
                            --$openCount;
2✔
181
                        } elseif ($tokens[$index]->equals('{')) {
1✔
182
                            ++$openCount;
×
183
                        } else {
184
                            $index = $this->fixClassy($tokens, $index);
1✔
185
                        }
186
                    } while ($openCount > 0);
2✔
187
                }
188

189
                continue;
12✔
190
            }
191

192
            if ($tokens[$index]->isGivenKind([\T_NEW, \T_INSTANCEOF])) {
16✔
193
                $index = $tokens->getNextMeaningfulToken($index);
9✔
194

195
                if ($tokens[$index]->isGivenKind(\T_STATIC)) {
9✔
196
                    $tokens[$index] = new Token([\T_STRING, 'self']);
7✔
197
                }
198

199
                continue;
9✔
200
            }
201

202
            if (!$tokens[$index]->isGivenKind(\T_STATIC)) {
16✔
203
                continue;
16✔
204
            }
205

206
            $staticIndex = $index;
13✔
207
            $index = $tokens->getNextMeaningfulToken($index);
13✔
208

209
            if (!$tokens[$index]->isGivenKind(\T_DOUBLE_COLON)) {
13✔
210
                continue;
7✔
211
            }
212

213
            $tokens[$staticIndex] = new Token([\T_STRING, 'self']);
10✔
214
        }
215

216
        return $index;
16✔
217
    }
218
}
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