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

keradus / PHP-CS-Fixer / 16303177127

15 Jul 2025 06:22PM UTC coverage: 94.758% (-0.05%) from 94.806%
16303177127

push

github

keradus
bumped version

28199 of 29759 relevant lines covered (94.76%)

45.91 hits per line

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

97.4
/src/Fixer/Phpdoc/PhpdocReturnSelfReferenceFixer.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\Phpdoc;
16

17
use PhpCsFixer\AbstractFixer;
18
use PhpCsFixer\DocBlock\DocBlock;
19
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
20
use PhpCsFixer\Fixer\ConfigurableFixerTrait;
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\Token;
28
use PhpCsFixer\Tokenizer\Tokens;
29
use PhpCsFixer\Tokenizer\TokensAnalyzer;
30
use PhpCsFixer\Utils;
31
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
32
use Symfony\Component\OptionsResolver\Options;
33

34
/**
35
 * @phpstan-type _AutogeneratedInputConfiguration array{
36
 *  replacements?: array<string, string>,
37
 * }
38
 * @phpstan-type _AutogeneratedComputedConfiguration array{
39
 *  replacements: array<string, string>,
40
 * }
41
 *
42
 * @implements ConfigurableFixerInterface<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration>
43
 */
44
final class PhpdocReturnSelfReferenceFixer extends AbstractFixer implements ConfigurableFixerInterface
45
{
46
    /** @use ConfigurableFixerTrait<_AutogeneratedInputConfiguration, _AutogeneratedComputedConfiguration> */
47
    use ConfigurableFixerTrait;
48

49
    /**
50
     * @var list<string>
51
     */
52
    private const TO_TYPES = [
53
        '$this',
54
        'static',
55
        'self',
56
    ];
57

58
    public function getDefinition(): FixerDefinitionInterface
59
    {
60
        return new FixerDefinition(
3✔
61
            'The type of `@return` annotations of methods returning a reference to itself must the configured one.',
3✔
62
            [
3✔
63
                new CodeSample(
3✔
64
                    '<?php
3✔
65
class Sample
66
{
67
    /**
68
     * @return this
69
     */
70
    public function test1()
71
    {
72
        return $this;
73
    }
74

75
    /**
76
     * @return $self
77
     */
78
    public function test2()
79
    {
80
        return $this;
81
    }
82
}
83
'
3✔
84
                ),
3✔
85
                new CodeSample(
3✔
86
                    '<?php
3✔
87
class Sample
88
{
89
    /**
90
     * @return this
91
     */
92
    public function test1()
93
    {
94
        return $this;
95
    }
96

97
    /**
98
     * @return $self
99
     */
100
    public function test2()
101
    {
102
        return $this;
103
    }
104
}
105
',
3✔
106
                    ['replacements' => ['this' => 'self']]
3✔
107
                ),
3✔
108
            ]
3✔
109
        );
3✔
110
    }
111

112
    public function isCandidate(Tokens $tokens): bool
113
    {
114
        return \count($tokens) > 10 && $tokens->isAllTokenKindsFound([\T_DOC_COMMENT, \T_FUNCTION]) && $tokens->isAnyTokenKindsFound(Token::getClassyTokenKinds());
15✔
115
    }
116

117
    /**
118
     * {@inheritdoc}
119
     *
120
     * Must run before NoSuperfluousPhpdocTagsFixer, PhpdocAlignFixer.
121
     * Must run after AlignMultilineCommentFixer, CommentToPhpdocFixer, PhpdocIndentFixer, PhpdocScalarFixer, PhpdocToCommentFixer, PhpdocTypesFixer.
122
     */
123
    public function getPriority(): int
124
    {
125
        return 10;
1✔
126
    }
127

128
    protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
129
    {
130
        $tokensAnalyzer = new TokensAnalyzer($tokens);
13✔
131

132
        foreach ($tokensAnalyzer->getClassyElements() as $index => $element) {
13✔
133
            if ('method' === $element['type']) {
13✔
134
                $this->fixMethod($tokens, $index);
13✔
135
            }
136
        }
137
    }
138

139
    protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
140
    {
141
        $default = [
26✔
142
            'this' => '$this',
26✔
143
            '@this' => '$this',
26✔
144
            '$self' => 'self',
26✔
145
            '@self' => 'self',
26✔
146
            '$static' => 'static',
26✔
147
            '@static' => 'static',
26✔
148
        ];
26✔
149

150
        return new FixerConfigurationResolver([
26✔
151
            (new FixerOptionBuilder('replacements', 'Mapping between replaced return types with new ones.'))
26✔
152
                ->setAllowedTypes(['array<string, string>'])
26✔
153
                ->setNormalizer(static function (Options $options, array $value) use ($default): array {
26✔
154
                    $normalizedValue = [];
26✔
155

156
                    foreach ($value as $from => $to) {
26✔
157
                        if (\is_string($from)) {
26✔
158
                            $from = strtolower($from);
26✔
159
                        }
160

161
                        if (!isset($default[$from])) {
26✔
162
                            throw new InvalidOptionsException(\sprintf(
1✔
163
                                'Unknown key "%s", expected any of %s.',
1✔
164
                                \gettype($from).'#'.$from,
1✔
165
                                Utils::naturalLanguageJoin(array_keys($default))
1✔
166
                            ));
1✔
167
                        }
168

169
                        if (!\in_array($to, self::TO_TYPES, true)) {
26✔
170
                            throw new InvalidOptionsException(\sprintf(
1✔
171
                                'Unknown value "%s", expected any of %s.',
1✔
172
                                \is_object($to) ? \get_class($to) : \gettype($to).(\is_resource($to) ? '' : '#'.$to),
1✔
173
                                Utils::naturalLanguageJoin(self::TO_TYPES)
1✔
174
                            ));
1✔
175
                        }
176

177
                        $normalizedValue[$from] = $to;
26✔
178
                    }
179

180
                    return $normalizedValue;
26✔
181
                })
26✔
182
                ->setDefault($default)
26✔
183
                ->getOption(),
26✔
184
        ]);
26✔
185
    }
186

187
    private function fixMethod(Tokens $tokens, int $index): void
188
    {
189
        // find PHPDoc of method (if any)
190
        while (true) {
13✔
191
            $tokenIndex = $tokens->getPrevMeaningfulToken($index);
13✔
192
            if (!$tokens[$tokenIndex]->isGivenKind([\T_STATIC, \T_FINAL, \T_ABSTRACT, \T_PRIVATE, \T_PROTECTED, \T_PUBLIC])) {
13✔
193
                break;
13✔
194
            }
195

196
            $index = $tokenIndex;
11✔
197
        }
198

199
        $docIndex = $tokens->getPrevNonWhitespace($index);
13✔
200
        if (!$tokens[$docIndex]->isGivenKind(\T_DOC_COMMENT)) {
13✔
201
            return;
1✔
202
        }
203

204
        // find @return
205
        $docBlock = new DocBlock($tokens[$docIndex]->getContent());
13✔
206
        $returnsBlock = $docBlock->getAnnotationsOfType('return');
13✔
207

208
        if (0 === \count($returnsBlock)) {
13✔
209
            return; // no return annotation found
×
210
        }
211

212
        $returnsBlock = $returnsBlock[0];
13✔
213
        $types = $returnsBlock->getTypes();
13✔
214

215
        if (0 === \count($types)) {
13✔
216
            return; // no return type(s) found
×
217
        }
218

219
        $newTypes = [];
13✔
220

221
        foreach ($types as $type) {
13✔
222
            $newTypes[] = $this->configuration['replacements'][strtolower($type)] ?? $type;
13✔
223
        }
224

225
        if ($types === $newTypes) {
13✔
226
            return;
13✔
227
        }
228

229
        $returnsBlock->setTypes($newTypes);
12✔
230
        $tokens[$docIndex] = new Token([\T_DOC_COMMENT, $docBlock->getContent()]);
12✔
231
    }
232
}
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