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

PHP-CS-Fixer / PHP-CS-Fixer / 3721300657

pending completion
3721300657

push

github

GitHub
minor: Follow PSR12 ordered imports in Symfony ruleset (#6712)

9 of 9 new or added lines in 2 files covered. (100.0%)

22674 of 24281 relevant lines covered (93.38%)

39.08 hits per line

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

0.0
/src/AbstractPhpdocToTypeDeclarationFixer.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;
16

17
use PhpCsFixer\DocBlock\Annotation;
18
use PhpCsFixer\DocBlock\DocBlock;
19
use PhpCsFixer\Fixer\ConfigurableFixerInterface;
20
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
21
use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
22
use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
23
use PhpCsFixer\Tokenizer\Analyzer\NamespacesAnalyzer;
24
use PhpCsFixer\Tokenizer\Analyzer\NamespaceUsesAnalyzer;
25
use PhpCsFixer\Tokenizer\CT;
26
use PhpCsFixer\Tokenizer\Token;
27
use PhpCsFixer\Tokenizer\Tokens;
28

29
/**
30
 * @internal
31
 */
32
abstract class AbstractPhpdocToTypeDeclarationFixer extends AbstractFixer implements ConfigurableFixerInterface
33
{
34
    private const CLASS_REGEX = '/^\\\\?[a-zA-Z_\\x7f-\\xff](?:\\\\?[a-zA-Z0-9_\\x7f-\\xff]+)*$/';
35

36
    /**
37
     * @var array<string, int>
38
     */
39
    private array $versionSpecificTypes = [
40
        'void' => 70100,
41
        'iterable' => 70100,
42
        'object' => 70200,
43
        'mixed' => 80000,
44
    ];
45

46
    /**
47
     * @var array<string, bool>
48
     */
49
    private array $scalarTypes = [
50
        'bool' => true,
51
        'float' => true,
52
        'int' => true,
53
        'string' => true,
54
    ];
55

56
    /**
57
     * @var array<string, bool>
58
     */
59
    private static array $syntaxValidationCache = [];
60

61
    /**
62
     * {@inheritdoc}
63
     */
64
    public function isRisky(): bool
65
    {
66
        return true;
×
67
    }
68

69
    abstract protected function isSkippedType(string $type): bool;
70

71
    /**
72
     * {@inheritdoc}
73
     */
74
    protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
75
    {
76
        return new FixerConfigurationResolver([
×
77
            (new FixerOptionBuilder('scalar_types', 'Fix also scalar types; may have unexpected behaviour due to PHP bad type coercion system.'))
×
78
                ->setAllowedTypes(['bool'])
×
79
                ->setDefault(true)
×
80
                ->getOption(),
×
81
        ]);
×
82
    }
83

84
    /**
85
     * @param int $index The index of the function token
86
     */
87
    protected function findFunctionDocComment(Tokens $tokens, int $index): ?int
88
    {
89
        do {
90
            $index = $tokens->getPrevNonWhitespace($index);
×
91
        } while ($tokens[$index]->isGivenKind([
×
92
            T_COMMENT,
×
93
            T_ABSTRACT,
×
94
            T_FINAL,
×
95
            T_PRIVATE,
×
96
            T_PROTECTED,
×
97
            T_PUBLIC,
×
98
            T_STATIC,
×
99
        ]));
×
100

101
        if ($tokens[$index]->isGivenKind(T_DOC_COMMENT)) {
×
102
            return $index;
×
103
        }
104

105
        return null;
×
106
    }
107

108
    /**
109
     * @return Annotation[]
110
     */
111
    protected function getAnnotationsFromDocComment(string $name, Tokens $tokens, int $docCommentIndex): array
112
    {
113
        $namespacesAnalyzer = new NamespacesAnalyzer();
×
114
        $namespace = $namespacesAnalyzer->getNamespaceAt($tokens, $docCommentIndex);
×
115

116
        $namespaceUsesAnalyzer = new NamespaceUsesAnalyzer();
×
117
        $namespaceUses = $namespaceUsesAnalyzer->getDeclarationsInNamespace($tokens, $namespace);
×
118

119
        $doc = new DocBlock(
×
120
            $tokens[$docCommentIndex]->getContent(),
×
121
            $namespace,
×
122
            $namespaceUses
×
123
        );
×
124

125
        return $doc->getAnnotationsOfType($name);
×
126
    }
127

128
    /**
129
     * @return Token[]
130
     */
131
    protected function createTypeDeclarationTokens(string $type, bool $isNullable): array
132
    {
133
        static $specialTypes = [
×
134
            'array' => [CT::T_ARRAY_TYPEHINT, 'array'],
×
135
            'callable' => [T_CALLABLE, 'callable'],
×
136
            'static' => [T_STATIC, 'static'],
×
137
        ];
×
138

139
        $newTokens = [];
×
140

141
        if (true === $isNullable && 'mixed' !== $type) {
×
142
            $newTokens[] = new Token([CT::T_NULLABLE_TYPE, '?']);
×
143
        }
144

145
        if (isset($specialTypes[$type])) {
×
146
            $newTokens[] = new Token($specialTypes[$type]);
×
147
        } else {
148
            $typeUnqualified = ltrim($type, '\\');
×
149

150
            if (isset($this->scalarTypes[$typeUnqualified]) || isset($this->versionSpecificTypes[$typeUnqualified])) {
×
151
                // 'scalar's, 'void', 'iterable' and 'object' must be unqualified
152
                $newTokens[] = new Token([T_STRING, $typeUnqualified]);
×
153
            } else {
154
                foreach (explode('\\', $type) as $nsIndex => $value) {
×
155
                    if (0 === $nsIndex && '' === $value) {
×
156
                        continue;
×
157
                    }
158

159
                    if (0 < $nsIndex) {
×
160
                        $newTokens[] = new Token([T_NS_SEPARATOR, '\\']);
×
161
                    }
162

163
                    $newTokens[] = new Token([T_STRING, $value]);
×
164
                }
165
            }
166
        }
167

168
        return $newTokens;
×
169
    }
170

171
    /**
172
     * @return null|array{string, bool}
173
     */
174
    protected function getCommonTypeFromAnnotation(Annotation $annotation, bool $isReturnType): ?array
175
    {
176
        $typesExpression = $annotation->getTypeExpression();
×
177

178
        $commonType = $typesExpression->getCommonType();
×
179
        $isNullable = $typesExpression->allowsNull();
×
180

181
        if (null === $commonType) {
×
182
            return null;
×
183
        }
184

185
        if ($isNullable && 'void' === $commonType) {
×
186
            return null;
×
187
        }
188

189
        if ('static' === $commonType && (!$isReturnType || \PHP_VERSION_ID < 80000)) {
×
190
            $commonType = 'self';
×
191
        }
192

193
        if ($this->isSkippedType($commonType)) {
×
194
            return null;
×
195
        }
196

197
        if (isset($this->versionSpecificTypes[$commonType]) && \PHP_VERSION_ID < $this->versionSpecificTypes[$commonType]) {
×
198
            return null;
×
199
        }
200

201
        if (isset($this->scalarTypes[$commonType])) {
×
202
            if (false === $this->configuration['scalar_types']) {
×
203
                return null;
×
204
            }
205
        } elseif (1 !== Preg::match(self::CLASS_REGEX, $commonType)) {
×
206
            return null;
×
207
        }
208

209
        return [$commonType, $isNullable];
×
210
    }
211

212
    final protected function isValidSyntax(string $code): bool
213
    {
214
        if (!isset(self::$syntaxValidationCache[$code])) {
×
215
            try {
216
                Tokens::fromCode($code);
×
217
                self::$syntaxValidationCache[$code] = true;
×
218
            } catch (\ParseError $e) {
×
219
                self::$syntaxValidationCache[$code] = false;
×
220
            }
221
        }
222

223
        return self::$syntaxValidationCache[$code];
×
224
    }
225
}
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

© 2025 Coveralls, Inc