• 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

98.33
/src/Fixer/LanguageConstruct/GetClassToClassKeywordFixer.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\LanguageConstruct;
16

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

27
/**
28
 * @author John Paul E. Balandan, CPA <paulbalandan@gmail.com>
29
 *
30
 * @no-named-arguments Parameter names are not covered by the backward compatibility promise.
31
 */
32
final class GetClassToClassKeywordFixer extends AbstractFixer
33
{
34
    public function getDefinition(): FixerDefinitionInterface
35
    {
36
        return new FixerDefinition(
3✔
37
            'Replace `get_class` calls on object variables with class keyword syntax.',
3✔
38
            [
3✔
39
                new VersionSpecificCodeSample(
3✔
40
                    "<?php\nget_class(\$a);\n",
3✔
41
                    new VersionSpecification(8_00_00)
3✔
42
                ),
3✔
43
                new VersionSpecificCodeSample(
3✔
44
                    "<?php\n\n\$date = new \\DateTimeImmutable();\n\$class = get_class(\$date);\n",
3✔
45
                    new VersionSpecification(8_00_00)
3✔
46
                ),
3✔
47
            ],
3✔
48
            null,
3✔
49
            'Risky if the `get_class` function is overridden.'
3✔
50
        );
3✔
51
    }
52

53
    /**
54
     * {@inheritdoc}
55
     *
56
     * Must run before MultilineWhitespaceBeforeSemicolonsFixer.
57
     * Must run after NoSpacesAfterFunctionNameFixer, NoSpacesInsideParenthesisFixer, SpacesInsideParenthesesFixer.
58
     */
59
    public function getPriority(): int
60
    {
61
        return 1;
1✔
62
    }
63

64
    public function isCandidate(Tokens $tokens): bool
65
    {
66
        return \PHP_VERSION_ID >= 8_00_00 && $tokens->isAllTokenKindsFound([\T_STRING, \T_VARIABLE]);
26✔
67
    }
68

69
    public function isRisky(): bool
70
    {
71
        return true;
1✔
72
    }
73

74
    protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
75
    {
76
        $functionsAnalyzer = new FunctionsAnalyzer();
22✔
77
        $indicesToClear = [];
22✔
78
        $tokenSlices = [];
22✔
79

80
        for ($index = $tokens->count() - 1; $index > 0; --$index) {
22✔
81
            if (!$tokens[$index]->equals([\T_STRING, 'get_class'], false)) {
22✔
82
                continue;
22✔
83
            }
84

85
            if (!$functionsAnalyzer->isGlobalFunctionCall($tokens, $index)) {
22✔
86
                continue;
2✔
87
            }
88

89
            $braceOpenIndex = $tokens->getNextMeaningfulToken($index);
20✔
90
            $braceCloseIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $braceOpenIndex);
20✔
91

92
            if ($braceCloseIndex === $tokens->getNextMeaningfulToken($braceOpenIndex)) {
20✔
93
                continue; // get_class with no arguments
×
94
            }
95

96
            $meaningfulTokensCount = 0;
20✔
97
            $variableTokensIndices = [];
20✔
98

99
            for ($i = $braceOpenIndex + 1; $i < $braceCloseIndex; ++$i) {
20✔
100
                if (!$tokens[$i]->equalsAny([[\T_WHITESPACE], [\T_COMMENT], [\T_DOC_COMMENT], '(', ')'])) {
20✔
101
                    ++$meaningfulTokensCount;
20✔
102
                }
103

104
                if (!$tokens[$i]->isGivenKind(\T_VARIABLE)) {
20✔
105
                    continue;
14✔
106
                }
107

108
                if ('$this' === strtolower($tokens[$i]->getContent())) {
19✔
109
                    continue 2; // get_class($this)
1✔
110
                }
111

112
                $variableTokensIndices[] = $i;
18✔
113
            }
114

115
            if ($meaningfulTokensCount > 1 || 1 !== \count($variableTokensIndices)) {
19✔
116
                continue; // argument contains more logic, or more arguments, or no variable argument
7✔
117
            }
118

119
            $indicesToClear[$index] = [$braceOpenIndex, current($variableTokensIndices), $braceCloseIndex];
12✔
120
        }
121

122
        foreach ($indicesToClear as $index => $items) {
22✔
123
            $tokenSlices[$index] = $this->getReplacementTokenSlices($tokens, $items[1]);
12✔
124
            $this->clearGetClassCall($tokens, $index, $items[0], $items[2]);
12✔
125
        }
126

127
        $tokens->insertSlices($tokenSlices);
22✔
128
    }
129

130
    /**
131
     * @return non-empty-list<Token>
132
     */
133
    private function getReplacementTokenSlices(Tokens $tokens, int $variableIndex): array
134
    {
135
        return [
12✔
136
            new Token([\T_VARIABLE, $tokens[$variableIndex]->getContent()]),
12✔
137
            new Token([\T_DOUBLE_COLON, '::']),
12✔
138
            new Token([CT::T_CLASS_CONSTANT, 'class']),
12✔
139
        ];
12✔
140
    }
141

142
    private function clearGetClassCall(Tokens $tokens, int $index, int $braceOpenIndex, int $braceCloseIndex): void
143
    {
144
        for ($i = $braceOpenIndex; $i <= $braceCloseIndex; ++$i) {
12✔
145
            if ($tokens[$i]->isGivenKind([\T_WHITESPACE, \T_COMMENT, \T_DOC_COMMENT])) {
12✔
146
                continue;
3✔
147
            }
148

149
            $tokens->clearTokenAndMergeSurroundingWhitespace($i);
12✔
150
        }
151

152
        $prevIndex = $tokens->getPrevMeaningfulToken($index);
12✔
153

154
        if ($tokens[$prevIndex]->isGivenKind(\T_NS_SEPARATOR)) {
12✔
155
            $tokens->clearAt($prevIndex);
2✔
156
        }
157

158
        $tokens->clearAt($index);
12✔
159
    }
160
}
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