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

keradus / PHP-CS-Fixer / 17252691116

26 Aug 2025 11:09PM UTC coverage: 94.743% (-0.01%) from 94.755%
17252691116

push

github

keradus
chore: apply phpdoc_tag_no_named_arguments

28313 of 29884 relevant lines covered (94.74%)

45.64 hits per line

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

21.14
/src/Doctrine/Annotation/Tokens.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\Doctrine\Annotation;
16

17
use PhpCsFixer\Preg;
18
use PhpCsFixer\Tokenizer\Token as PhpToken;
19

20
/**
21
 * A list of Doctrine annotation tokens.
22
 *
23
 * @internal
24
 *
25
 * @extends \SplFixedArray<Token>
26
 *
27
 * @no-named-arguments Parameter names are not covered by the backward compatibility promise.
28
 */
29
final class Tokens extends \SplFixedArray
30
{
31
    /**
32
     * @param list<string> $ignoredTags
33
     *
34
     * @throws \InvalidArgumentException
35
     */
36
    public static function createFromDocComment(PhpToken $input, array $ignoredTags = []): self
37
    {
38
        if (!$input->isGivenKind(\T_DOC_COMMENT)) {
3✔
39
            throw new \InvalidArgumentException('Input must be a T_DOC_COMMENT token.');
×
40
        }
41

42
        $tokens = [];
3✔
43

44
        $content = $input->getContent();
3✔
45
        $ignoredTextPosition = 0;
3✔
46
        $currentPosition = 0;
3✔
47
        $token = null;
3✔
48
        while (false !== $nextAtPosition = strpos($content, '@', $currentPosition)) {
3✔
49
            if (0 !== $nextAtPosition && !Preg::match('/\s/', $content[$nextAtPosition - 1])) {
×
50
                $currentPosition = $nextAtPosition + 1;
×
51

52
                continue;
×
53
            }
54

55
            $lexer = new DocLexer();
×
56
            $lexer->setInput(substr($content, $nextAtPosition));
×
57

58
            $scannedTokens = [];
×
59
            $index = 0;
×
60
            $nbScannedTokensToUse = 0;
×
61
            $nbScopes = 0;
×
62
            while (null !== $token = $lexer->peek()) {
×
63
                if (0 === $index && !$token->isType(DocLexer::T_AT)) {
×
64
                    break;
×
65
                }
66

67
                if (1 === $index) {
×
68
                    if (!$token->isType(DocLexer::T_IDENTIFIER) || \in_array($token->getContent(), $ignoredTags, true)) {
×
69
                        break;
×
70
                    }
71

72
                    $nbScannedTokensToUse = 2;
×
73
                }
74

75
                if ($index >= 2 && 0 === $nbScopes && !$token->isType([DocLexer::T_NONE, DocLexer::T_OPEN_PARENTHESIS])) {
×
76
                    break;
×
77
                }
78

79
                $scannedTokens[] = $token;
×
80

81
                if ($token->isType(DocLexer::T_OPEN_PARENTHESIS)) {
×
82
                    ++$nbScopes;
×
83
                } elseif ($token->isType(DocLexer::T_CLOSE_PARENTHESIS)) {
×
84
                    if (0 === --$nbScopes) {
×
85
                        $nbScannedTokensToUse = \count($scannedTokens);
×
86

87
                        break;
×
88
                    }
89
                }
90

91
                ++$index;
×
92
            }
93

94
            if (0 !== $nbScopes) {
×
95
                break;
×
96
            }
97

98
            if (0 !== $nbScannedTokensToUse) {
×
99
                $ignoredTextLength = $nextAtPosition - $ignoredTextPosition;
×
100
                if (0 !== $ignoredTextLength) {
×
101
                    $tokens[] = new Token(DocLexer::T_NONE, substr($content, $ignoredTextPosition, $ignoredTextLength));
×
102
                }
103

104
                $lastTokenEndIndex = 0;
×
105
                foreach (\array_slice($scannedTokens, 0, $nbScannedTokensToUse) as $scannedToken) {
×
106
                    $token = $scannedToken->isType(DocLexer::T_STRING)
×
107
                        ? new Token(
×
108
                            $scannedToken->getType(),
×
109
                            '"'.str_replace('"', '""', $scannedToken->getContent()).'"',
×
110
                            $scannedToken->getPosition()
×
111
                        )
×
112
                        : $scannedToken;
×
113

114
                    $missingTextLength = $token->getPosition() - $lastTokenEndIndex;
×
115
                    if ($missingTextLength > 0) {
×
116
                        $tokens[] = new Token(DocLexer::T_NONE, substr(
×
117
                            $content,
×
118
                            $nextAtPosition + $lastTokenEndIndex,
×
119
                            $missingTextLength
×
120
                        ));
×
121
                    }
122

123
                    $tokens[] = new Token($token->getType(), $token->getContent());
×
124
                    $lastTokenEndIndex = $token->getPosition() + \strlen($token->getContent());
×
125
                }
126

127
                $currentPosition = $ignoredTextPosition = $nextAtPosition + $token->getPosition() + \strlen($token->getContent());
×
128
            } else {
129
                $currentPosition = $nextAtPosition + 1;
×
130
            }
131
        }
132

133
        if ($ignoredTextPosition < \strlen($content)) {
3✔
134
            $tokens[] = new Token(DocLexer::T_NONE, substr($content, $ignoredTextPosition));
3✔
135
        }
136

137
        return self::fromArray($tokens);
3✔
138
    }
139

140
    /**
141
     * Create token collection from array.
142
     *
143
     * @param array<int, Token> $array       the array to import
144
     * @param ?bool             $saveIndices save the numeric indices used in the original array, default is yes
145
     */
146
    public static function fromArray($array, $saveIndices = null): self
147
    {
148
        $tokens = new self(\count($array));
3✔
149

150
        if (null === $saveIndices || $saveIndices) {
3✔
151
            foreach ($array as $key => $val) {
3✔
152
                $tokens[$key] = $val;
3✔
153
            }
154
        } else {
155
            $index = 0;
×
156

157
            foreach ($array as $val) {
×
158
                $tokens[$index++] = $val;
×
159
            }
160
        }
161

162
        return $tokens;
3✔
163
    }
164

165
    /**
166
     * Returns the index of the closest next token that is neither a comment nor a whitespace token.
167
     */
168
    public function getNextMeaningfulToken(int $index): ?int
169
    {
170
        return $this->getMeaningfulTokenSibling($index, 1);
×
171
    }
172

173
    /**
174
     * Returns the index of the last token that is part of the annotation at the given index.
175
     */
176
    public function getAnnotationEnd(int $index): ?int
177
    {
178
        $currentIndex = null;
×
179

180
        if (isset($this[$index + 2])) {
×
181
            if ($this[$index + 2]->isType(DocLexer::T_OPEN_PARENTHESIS)) {
×
182
                $currentIndex = $index + 2;
×
183
            } elseif (
184
                isset($this[$index + 3])
×
185
                && $this[$index + 2]->isType(DocLexer::T_NONE)
×
186
                && $this[$index + 3]->isType(DocLexer::T_OPEN_PARENTHESIS)
×
187
                && Preg::match('/^(\R\s*\*\s*)*\s*$/', $this[$index + 2]->getContent())
×
188
            ) {
189
                $currentIndex = $index + 3;
×
190
            }
191
        }
192

193
        if (null !== $currentIndex) {
×
194
            $level = 0;
×
195
            for ($max = \count($this); $currentIndex < $max; ++$currentIndex) {
×
196
                if ($this[$currentIndex]->isType(DocLexer::T_OPEN_PARENTHESIS)) {
×
197
                    ++$level;
×
198
                } elseif ($this[$currentIndex]->isType(DocLexer::T_CLOSE_PARENTHESIS)) {
×
199
                    --$level;
×
200
                }
201

202
                if (0 === $level) {
×
203
                    return $currentIndex;
×
204
                }
205
            }
206

207
            return null;
×
208
        }
209

210
        return $index + 1;
×
211
    }
212

213
    /**
214
     * Returns the code from the tokens.
215
     */
216
    public function getCode(): string
217
    {
218
        $code = '';
1✔
219
        foreach ($this as $token) {
1✔
220
            $code .= $token->getContent();
1✔
221
        }
222

223
        return $code;
1✔
224
    }
225

226
    /**
227
     * Inserts a token at the given index.
228
     */
229
    public function insertAt(int $index, Token $token): void
230
    {
231
        $this->setSize($this->getSize() + 1);
×
232

233
        for ($i = $this->getSize() - 1; $i > $index; --$i) {
×
234
            $this[$i] = $this[$i - 1] ?? new Token();
×
235
        }
236

237
        $this[$index] = $token;
×
238
    }
239

240
    public function offsetSet($index, $token): void
241
    {
242
        if (null === $token) {
3✔
243
            throw new \InvalidArgumentException('Token must be an instance of PhpCsFixer\Doctrine\Annotation\Token, "null" given.');
1✔
244
        }
245

246
        if (!$token instanceof Token) {
3✔
247
            $type = \gettype($token);
1✔
248

249
            if ('object' === $type) {
1✔
250
                $type = \get_class($token);
×
251
            }
252

253
            throw new \InvalidArgumentException(\sprintf('Token must be an instance of PhpCsFixer\Doctrine\Annotation\Token, "%s" given.', $type));
1✔
254
        }
255

256
        parent::offsetSet($index, $token);
3✔
257
    }
258

259
    /**
260
     * @param mixed $index
261
     *
262
     * @throws \OutOfBoundsException
263
     */
264
    public function offsetUnset($index): void
265
    {
266
        if (!isset($this[$index])) {
×
267
            throw new \OutOfBoundsException(\sprintf('Index "%s" is invalid or does not exist.', $index));
×
268
        }
269

270
        $max = \count($this) - 1;
×
271
        while ($index < $max) {
×
272
            $this[$index] = $this[$index + 1];
×
273
            ++$index;
×
274
        }
275

276
        parent::offsetUnset($index);
×
277

278
        $this->setSize($max);
×
279
    }
280

281
    private function getMeaningfulTokenSibling(int $index, int $direction): ?int
282
    {
283
        while (true) {
×
284
            $index += $direction;
×
285

286
            if (!$this->offsetExists($index)) {
×
287
                break;
×
288
            }
289

290
            if (!$this[$index]->isType(DocLexer::T_NONE)) {
×
291
                return $index;
×
292
            }
293
        }
294

295
        return null;
×
296
    }
297
}
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