Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x
  2. AnnotationNameSniff.php

class AnnotationNameSniff

Hierarchy

  • class \SlevomatCodingStandard\Sniffs\Commenting\AnnotationNameSniff implements \PHP_CodeSniffer\Sniffs\Sniff

Expanded class hierarchy of AnnotationNameSniff

File

vendor/slevomat/coding-standard/SlevomatCodingStandard/Sniffs/Commenting/AnnotationNameSniff.php, line 28

Namespace

SlevomatCodingStandard\Sniffs\Commenting
View source
class AnnotationNameSniff implements Sniff {
    public const CODE_ANNOTATION_NAME_INCORRECT = 'AnnotationNameIncorrect';
    private const STANDARD_ANNOTATIONS = [
        'api',
        'author',
        'category',
        'copyright',
        'deprecated',
        'example',
        'filesource',
        'global',
        'ignore',
        'inheritDoc',
        'internal',
        'license',
        'link',
        'method',
        'package',
        'param',
        'property',
        'property-read',
        'property-write',
        'return',
        'see',
        'since',
        'source',
        'subpackage',
        'throws',
        'todo',
        'uses',
        'used-by',
        'var',
        'version',
    ];
    private const STATIC_ANALYSIS_ANNOTATIONS = [
        'api',
        'allow-private-mutation',
        'assert',
        'assert-if-true',
        'assert-if-false',
        'consistent-constructor',
        'consistent-templates',
        'extends',
        'external-mutation-free',
        'implements',
        'mixin',
        'ignore-falsable-return',
        'ignore-nullable-return',
        'ignore-var',
        'ignore-variable-method',
        'ignore-variable-property',
        'immutable',
        'import-type',
        'internal',
        'method',
        'mutation-free',
        'no-named-arguments',
        'param',
        'param-out',
        'property',
        'property-read',
        'property-write',
        'psalm-check-type',
        'psalm-check-type-exact',
        'psalm-suppress',
        'psalm-trace',
        'pure',
        'readonly',
        'readonly-allow-private-mutation',
        'require-extends',
        'require-implements',
        'return',
        'seal-properties',
        'self-out',
        'template',
        'template-covariant',
        'template-extends',
        'template-implements',
        'template-use',
        'this-out',
        'type',
        'var',
        'yield',
    ];
    private const PHPUNIT_ANNOTATIONS = [
        'author',
        'after',
        'afterClass',
        'backupGlobals',
        'backupStaticAttributes',
        'before',
        'beforeClass',
        'codeCoverageIgnore',
        'codeCoverageIgnoreStart',
        'codeCoverageIgnoreEnd',
        'covers',
        'coversDefaultClass',
        'coversNothing',
        'dataProvider',
        'depends',
        'doesNotPerformAssertions',
        'group',
        'large',
        'medium',
        'preserveGlobalState',
        'requires',
        'runTestsInSeparateProcesses',
        'runInSeparateProcess',
        'small',
        'test',
        'testdox',
        'testWith',
        'ticket',
        'uses',
    ];
    
    /** @var list<string>|null */
    public $annotations;
    
    /** @var array<string, string>|null */
    private $normalizedAnnotations;
    
    /**
     * @return array<int, (int|string)>
     */
    public function register() : array {
        return [
            T_DOC_COMMENT_OPEN_TAG,
        ];
    }
    
    /**
     * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
     * @param int $docCommentOpenPointer
     */
    public function process(File $phpcsFile, $docCommentOpenPointer) : void {
        $annotations = AnnotationHelper::getAnnotations($phpcsFile, $docCommentOpenPointer);
        $correctAnnotationNames = $this->getNormalizedAnnotationNames();
        foreach ($annotations as $annotation) {
            $lowerCasedAnnotationName = strtolower($annotation->getName());
            if (!array_key_exists($lowerCasedAnnotationName, $correctAnnotationNames)) {
                continue;
            }
            $correctAnnotationName = $correctAnnotationNames[$lowerCasedAnnotationName];
            if ($correctAnnotationName === $annotation->getName()) {
                continue;
            }
            $annotationNameWithoutAtSign = ltrim($annotation->getName(), '@');
            $fullyQualifiedAnnotationName = NamespaceHelper::resolveClassName($phpcsFile, $annotationNameWithoutAtSign, $annotation->getStartPointer());
            if (NamespaceHelper::normalizeToCanonicalName($fullyQualifiedAnnotationName) !== $annotationNameWithoutAtSign) {
                continue;
            }
            $fix = $phpcsFile->addFixableError(sprintf('Annotation name is incorrect. Expected %s, found %s.', $correctAnnotationName, $annotation->getName()), $annotation->getStartPointer(), self::CODE_ANNOTATION_NAME_INCORRECT);
            if (!$fix) {
                continue;
            }
            $phpcsFile->fixer
                ->beginChangeset();
            $phpcsFile->fixer
                ->replaceToken($annotation->getStartPointer(), $correctAnnotationName);
            $phpcsFile->fixer
                ->endChangeset();
        }
        $tokens = $phpcsFile->getTokens();
        $docCommentContent = TokenHelper::getContent($phpcsFile, $docCommentOpenPointer, $tokens[$docCommentOpenPointer]['comment_closer']);
        if (preg_match_all('~\\{(' . implode('|', $correctAnnotationNames) . ')\\}~i', $docCommentContent, $matches, PREG_OFFSET_CAPTURE) === 0) {
            return;
        }
        foreach ($matches[1] as $match) {
            $correctAnnotationName = $correctAnnotationNames[strtolower($match[0])];
            if ($correctAnnotationName === $match[0]) {
                continue;
            }
            $fix = $phpcsFile->addFixableError(sprintf('Annotation name is incorrect. Expected %s, found %s.', $correctAnnotationName, $match[0]), $docCommentOpenPointer, self::CODE_ANNOTATION_NAME_INCORRECT);
            if (!$fix) {
                continue;
            }
            $phpcsFile->fixer
                ->beginChangeset();
            $fixedDocCommentContent = substr($docCommentContent, 0, $match[1]) . $correctAnnotationName . substr($docCommentContent, $match[1] + strlen($match[0]));
            FixerHelper::change($phpcsFile, $docCommentOpenPointer, $tokens[$docCommentOpenPointer]['comment_closer'], $fixedDocCommentContent);
            $phpcsFile->fixer
                ->endChangeset();
        }
    }
    
    /**
     * @return array<string, string>
     */
    private function getNormalizedAnnotationNames() : array {
        if ($this->normalizedAnnotations !== null) {
            return $this->normalizedAnnotations;
        }
        if ($this->annotations !== null) {
            $annotationNames = array_map(static function (string $annotationName) : string {
                return ltrim($annotationName, '@');
            }, SniffSettingsHelper::normalizeArray($this->annotations));
        }
        else {
            $annotationNames = array_merge(self::STANDARD_ANNOTATIONS, self::PHPUNIT_ANNOTATIONS, self::STATIC_ANALYSIS_ANNOTATIONS);
            foreach (self::STATIC_ANALYSIS_ANNOTATIONS as $annotationName) {
                if (strpos($annotationName, 'psalm') === 0) {
                    continue;
                }
                foreach (AnnotationHelper::STATIC_ANALYSIS_PREFIXES as $prefix) {
                    $annotationNames[] = sprintf('%s-%s', $prefix, $annotationName);
                }
            }
        }
        $annotationNames = array_map(static function (string $annotationName) : string {
            return '@' . $annotationName;
        }, array_unique($annotationNames));
        $this->normalizedAnnotations = array_combine(array_map(static function (string $annotationName) : string {
            return strtolower($annotationName);
        }, $annotationNames), $annotationNames);
        return $this->normalizedAnnotations;
    }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title
AnnotationNameSniff::$annotations public property @var list&lt;string&gt;|null
AnnotationNameSniff::$normalizedAnnotations private property @var array&lt;string, string&gt;|null
AnnotationNameSniff::CODE_ANNOTATION_NAME_INCORRECT public constant
AnnotationNameSniff::getNormalizedAnnotationNames private function *
AnnotationNameSniff::PHPUNIT_ANNOTATIONS private constant
AnnotationNameSniff::process public function * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
*
Overrides Sniff::process
AnnotationNameSniff::register public function * Overrides Sniff::register
AnnotationNameSniff::STANDARD_ANNOTATIONS private constant
AnnotationNameSniff::STATIC_ANALYSIS_ANNOTATIONS private constant

API Navigation

  • Drupal Core 11.1.x
  • Topics
  • Classes
  • Functions
  • Constants
  • Globals
  • Files
  • Namespaces
  • Deprecated
  • Services
RSS feed
Powered by Drupal