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

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

09 Feb 2025 10:01PM UTC coverage: 99.962% (-0.04%) from 100.0%
13229986895

Pull #1000

github

web-flow
Merge 86f3a9a95 into 692267233
Pull Request #1000: Add ClassConstantUsageFixer

58 of 59 new or added lines in 1 file covered. (98.31%)

2638 of 2639 relevant lines covered (99.96%)

26.3 hits per line

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

98.31
/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
    /**
105
     * @return array{array<string, string>, array<int, true>}
106
     */
107
    private function getClassConstants(Tokens $tokens, int $openParenthesisIndex, int $closeParenthesisIndex): array
108
    {
109
        $constants = [];
2✔
110
        $constantsIndices = [];
2✔
111
        for ($index = $openParenthesisIndex; $index < $closeParenthesisIndex; $index++) {
2✔
112
            if (!$tokens[$index]->isGivenKind(\T_CONST)) {
2✔
113
                continue;
2✔
114
            }
115

116
            $assignTokenIndex = $tokens->getNextTokenOfKind($index, ['=']);
2✔
117
            \assert(\is_int($assignTokenIndex));
2✔
118

119
            $constantNameIndex = $tokens->getPrevMeaningfulToken($assignTokenIndex);
2✔
120
            \assert(\is_int($constantNameIndex));
2✔
121

122
            $constantValueIndex = $tokens->getNextMeaningfulToken($assignTokenIndex);
2✔
123
            \assert(\is_int($constantValueIndex));
2✔
124

125
            $constantsIndices[$constantValueIndex] = true;
2✔
126

127
            if (!$tokens[$constantValueIndex]->isGivenKind(\T_CONSTANT_ENCAPSED_STRING)) {
2✔
NEW
128
                continue;
×
129
            }
130

131
            $constants[$tokens[$constantNameIndex]->getContent()] = $tokens[$constantValueIndex]->getContent();
2✔
132
        }
133

134
        return [$this->getClassConstantsMap($constants), $constantsIndices];
2✔
135
    }
136

137
    /**
138
     * @param array<string, string> $constants
139
     *
140
     * @return array<string, string>
141
     */
142
    private function getClassConstantsMap(array $constants): array
143
    {
144
        $map = [];
2✔
145
        $valuesCount = [];
2✔
146

147
        foreach ($constants as $name => $value) {
2✔
148
            $map[$value] = $name;
2✔
149
            $valuesCount[$value] = ($valuesCount[$value] ?? 0) + 1;
2✔
150

151
            if ($valuesCount[$value] > 1) {
2✔
152
                unset($map[$value]);
1✔
153
            }
154
        }
155

156
        return $map;
2✔
157
    }
158
}
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