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

kubawerlos / php-cs-fixer-custom-fixers / 13229950121

09 Feb 2025 09:55PM UTC coverage: 99.962% (-0.04%) from 100.0%
13229950121

Pull #1000

github

web-flow
Merge 02237b74f into 692267233
Pull Request #1000: Add ClassConstantUsageFixer

56 of 57 new or added lines in 1 file covered. (98.25%)

2636 of 2637 relevant lines covered (99.96%)

26.32 hits per line

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

98.25
/src/Fixer/ClassConstantUsageFixer.php
1
<?php declare(strict_types=1);
2

3
/*
4
 * This file is part of PHP CS Fixer: custom fixers.
5
 *
6
 * (c) 2018 Kuba Werłos
7
 *
8
 * For the full copyright and license information, please view
9
 * the LICENSE file that was distributed with this source code.
10
 */
11

12
namespace PhpCsFixerCustomFixers\Fixer;
13

14
use PhpCsFixer\FixerDefinition\CodeSample;
15
use PhpCsFixer\FixerDefinition\FixerDefinition;
16
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
17
use PhpCsFixer\Tokenizer\Token;
18
use PhpCsFixer\Tokenizer\Tokens;
19

20
final class ClassConstantUsageFixer extends AbstractFixer
21
{
22
    public function getDefinition(): FixerDefinitionInterface
23
    {
24
        return new FixerDefinition(
3✔
25
            'Class constant must be used instead of a copy of string.',
3✔
26
            [new CodeSample(
3✔
27
                <<<'PHP'
3✔
28
                <?php
29
                class Foo
30
                {
31
                    public const BAR = 'bar';
32
                    public function bar()
33
                    {
34
                        return 'bar';
35
                    }
36
                }
37

38
                PHP,
3✔
39
            )],
3✔
40
            '',
3✔
41
        );
3✔
42
    }
43

44
    public function getPriority(): int
45
    {
46
        return 0;
1✔
47
    }
48

49
    public function isCandidate(Tokens $tokens): bool
50
    {
51
        return $tokens->isAllTokenKindsFound([\T_CLASS, \T_CONSTANT_ENCAPSED_STRING]);
1✔
52
    }
53

54
    public function isRisky(): bool
55
    {
56
        return false;
3✔
57
    }
58

59
    public function fix(\SplFileInfo $file, Tokens $tokens): void
60
    {
61
        for ($index = $tokens->count() - 1; $index > 0; $index--) {
2✔
62
            if (!$tokens[$index]->isGivenKind(\T_CLASS)) {
2✔
63
                continue;
2✔
64
            }
65

66
            $openParenthesisIndex = $tokens->getNextTokenOfKind($index, ['{']);
2✔
67
            \assert(\is_int($openParenthesisIndex));
2✔
68

69
            $closeParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $openParenthesisIndex);
2✔
70

71
            $this->fixClass($tokens, $openParenthesisIndex, $closeParenthesisIndex);
2✔
72
        }
73
    }
74

75
    private function fixClass(Tokens $tokens, int $openParenthesisIndex, int $closeParenthesisIndex): void
76
    {
77
        [$constantsMap, $constantsIndices] = $this->getClassConstants($tokens, $openParenthesisIndex, $closeParenthesisIndex);
2✔
78

79
        for ($index = $closeParenthesisIndex; $index > $openParenthesisIndex; $index--) {
2✔
80
            if (!$tokens[$index]->isGivenKind(\T_CONSTANT_ENCAPSED_STRING)) {
2✔
81
                continue;
2✔
82
            }
83

84
            if (!isset($constantsMap[$tokens[$index]->getContent()])) {
2✔
85
                continue;
1✔
86
            }
87

88
            if (isset($constantsIndices[$index])) {
1✔
89
                continue;
1✔
90
            }
91

92
            $tokens->overrideRange(
1✔
93
                $index,
1✔
94
                $index,
1✔
95
                [
1✔
96
                    new Token([\T_STRING, 'self']),
1✔
97
                    new Token([\T_DOUBLE_COLON, '::']),
1✔
98
                    new Token([\T_STRING, $constantsMap[$tokens[$index]->getContent()]]),
1✔
99
                ],
1✔
100
            );
1✔
101
        }
102
    }
103

104
    private function getClassConstants(Tokens $tokens, int $openParenthesisIndex, int $closeParenthesisIndex): array
105
    {
106
        $constants = [];
2✔
107
        $constantsIndices = [];
2✔
108
        for ($index = $openParenthesisIndex; $index < $closeParenthesisIndex; $index++) {
2✔
109
            if (!$tokens[$index]->isGivenKind(\T_CONST)) {
2✔
110
                continue;
2✔
111
            }
112

113
            $assignTokenIndex = $tokens->getNextTokenOfKind($index, ['=']);
2✔
114

115
            $constantNameIndex = $tokens->getPrevMeaningfulToken($assignTokenIndex);
2✔
116
            $constantValueIndex = $tokens->getNextMeaningfulToken($assignTokenIndex);
2✔
117

118
            $constantsIndices[$constantValueIndex] = true;
2✔
119

120
            if (!$tokens[$constantValueIndex]->isGivenKind(\T_CONSTANT_ENCAPSED_STRING)) {
2✔
NEW
121
                continue;
×
122
            }
123

124
            $constants[$tokens[$constantNameIndex]->getContent()] = $tokens[$constantValueIndex]->getContent();
2✔
125
        }
126

127
        return [$this->getClassConstantsMap($constants), $constantsIndices];
2✔
128
    }
129

130
    private function getClassConstantsMap(array $constants): array
131
    {
132
        $map = [];
2✔
133
        $valuesCount = [];
2✔
134

135
        foreach ($constants as $name => $value) {
2✔
136
            $map[$value] = $name;
2✔
137
            $valuesCount[$value] = $valuesCount[$value] ?? 0;
2✔
138
            $valuesCount[$value]++;
2✔
139

140
            if (($valuesCount[$value] ?? 0) > 1) {
2✔
141
                unset($map[$value]);
1✔
142
            }
143
        }
144

145
        return $map;
2✔
146
    }
147
}
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