123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- <?php
- declare(strict_types=1);
- /*
- * This file is part of PHP CS Fixer.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- * Dariusz Rumiński <dariusz.ruminski@gmail.com>
- *
- * This source file is subject to the MIT license that is bundled
- * with this source code in the file LICENSE.
- */
- namespace PhpCsFixer;
- use PhpCsFixer\Doctrine\Annotation\Tokens as DoctrineAnnotationTokens;
- use PhpCsFixer\Fixer\ConfigurableFixerInterface;
- use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
- use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
- use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
- use PhpCsFixer\Tokenizer\CT;
- use PhpCsFixer\Tokenizer\Token;
- use PhpCsFixer\Tokenizer\Tokens;
- use PhpCsFixer\Tokenizer\TokensAnalyzer;
- /**
- * @internal
- */
- abstract class AbstractDoctrineAnnotationFixer extends AbstractFixer implements ConfigurableFixerInterface
- {
- /**
- * @var array<int, array{classIndex: int, token: Token, type: string}>
- */
- private array $classyElements;
- public function isCandidate(Tokens $tokens): bool
- {
- return $tokens->isTokenKindFound(T_DOC_COMMENT);
- }
- protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
- {
- // fetch indices one time, this is safe as we never add or remove a token during fixing
- $analyzer = new TokensAnalyzer($tokens);
- $this->classyElements = $analyzer->getClassyElements();
- /** @var Token $docCommentToken */
- foreach ($tokens->findGivenKind(T_DOC_COMMENT) as $index => $docCommentToken) {
- if (!$this->nextElementAcceptsDoctrineAnnotations($tokens, $index)) {
- continue;
- }
- $doctrineAnnotationTokens = DoctrineAnnotationTokens::createFromDocComment(
- $docCommentToken,
- $this->configuration['ignored_tags']
- );
- $this->fixAnnotations($doctrineAnnotationTokens);
- $tokens[$index] = new Token([T_DOC_COMMENT, $doctrineAnnotationTokens->getCode()]);
- }
- }
- /**
- * Fixes Doctrine annotations from the given PHPDoc style comment.
- */
- abstract protected function fixAnnotations(DoctrineAnnotationTokens $doctrineAnnotationTokens): void;
- protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
- {
- return new FixerConfigurationResolver([
- (new FixerOptionBuilder('ignored_tags', 'List of tags that must not be treated as Doctrine Annotations.'))
- ->setAllowedTypes(['string[]'])
- ->setAllowedValues([static function (array $values): bool {
- foreach ($values as $value) {
- if (!\is_string($value)) {
- return false;
- }
- }
- return true;
- }])
- ->setDefault([
- // PHPDocumentor 1
- 'abstract',
- 'access',
- 'code',
- 'deprec',
- 'encode',
- 'exception',
- 'final',
- 'ingroup',
- 'inheritdoc',
- 'inheritDoc',
- 'magic',
- 'name',
- 'toc',
- 'tutorial',
- 'private',
- 'static',
- 'staticvar',
- 'staticVar',
- 'throw',
- // PHPDocumentor 2
- 'api',
- 'author',
- 'category',
- 'copyright',
- 'deprecated',
- 'example',
- 'filesource',
- 'global',
- 'ignore',
- 'internal',
- 'license',
- 'link',
- 'method',
- 'package',
- 'param',
- 'property',
- 'property-read',
- 'property-write',
- 'return',
- 'see',
- 'since',
- 'source',
- 'subpackage',
- 'throws',
- 'todo',
- 'TODO',
- 'usedBy',
- 'uses',
- 'var',
- 'version',
- // PHPUnit
- 'after',
- 'afterClass',
- 'backupGlobals',
- 'backupStaticAttributes',
- 'before',
- 'beforeClass',
- 'codeCoverageIgnore',
- 'codeCoverageIgnoreStart',
- 'codeCoverageIgnoreEnd',
- 'covers',
- 'coversDefaultClass',
- 'coversNothing',
- 'dataProvider',
- 'depends',
- 'expectedException',
- 'expectedExceptionCode',
- 'expectedExceptionMessage',
- 'expectedExceptionMessageRegExp',
- 'group',
- 'large',
- 'medium',
- 'preserveGlobalState',
- 'requires',
- 'runTestsInSeparateProcesses',
- 'runInSeparateProcess',
- 'small',
- 'test',
- 'testdox',
- 'ticket',
- 'uses',
- // PHPCheckStyle
- 'SuppressWarnings',
- // PHPStorm
- 'noinspection',
- // PEAR
- 'package_version',
- // PlantUML
- 'enduml',
- 'startuml',
- // Psalm
- 'psalm',
- // PHPStan
- 'phpstan',
- 'template',
- // other
- 'fix',
- 'FIXME',
- 'fixme',
- 'override',
- ])
- ->getOption(),
- ]);
- }
- private function nextElementAcceptsDoctrineAnnotations(Tokens $tokens, int $index): bool
- {
- $classModifiers = [T_ABSTRACT, T_FINAL];
- if (\defined('T_READONLY')) { // @TODO: drop condition when PHP 8.2+ is required
- $classModifiers[] = T_READONLY;
- }
- do {
- $index = $tokens->getNextMeaningfulToken($index);
- if (null === $index) {
- return false;
- }
- } while ($tokens[$index]->isGivenKind($classModifiers));
- if ($tokens[$index]->isGivenKind(T_CLASS)) {
- return true;
- }
- $modifierKinds = [T_PUBLIC, T_PROTECTED, T_PRIVATE, T_FINAL, T_ABSTRACT, T_NS_SEPARATOR, T_STRING, CT::T_NULLABLE_TYPE];
- if (\defined('T_READONLY')) { // @TODO: drop condition when PHP 8.1+ is required
- $modifierKinds[] = T_READONLY;
- }
- while ($tokens[$index]->isGivenKind($modifierKinds)) {
- $index = $tokens->getNextMeaningfulToken($index);
- }
- if (!isset($this->classyElements[$index])) {
- return false;
- }
- return $tokens[$this->classyElements[$index]['classIndex']]->isGivenKind(T_CLASS); // interface, enums and traits cannot have doctrine annotations
- }
- }
|