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

Breadcrumb

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

function ReferenceUsedNamesOnlySniff::process

* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint *

Parameters

int $openTagPointer:

Overrides Sniff::process

File

vendor/slevomat/coding-standard/SlevomatCodingStandard/Sniffs/Namespaces/ReferenceUsedNamesOnlySniff.php, line 137

Class

ReferenceUsedNamesOnlySniff

Namespace

SlevomatCodingStandard\Sniffs\Namespaces

Code

public function process(File $phpcsFile, $openTagPointer) : void {
    if (TokenHelper::findPrevious($phpcsFile, T_OPEN_TAG, $openTagPointer - 1) !== null) {
        return;
    }
    $tokens = $phpcsFile->getTokens();
    $references = $this->getReferences($phpcsFile, $openTagPointer);
    $definedClassesIndex = [];
    foreach (ClassHelper::getAllNames($phpcsFile) as $definedClassPointer => $definedClassName) {
        $definedClassesIndex[strtolower($definedClassName)] = NamespaceHelper::resolveClassName($phpcsFile, $definedClassName, $definedClassPointer);
    }
    $definedFunctionsIndex = array_flip(array_map(static function (string $functionName) : string {
        return strtolower($functionName);
    }, FunctionHelper::getAllFunctionNames($phpcsFile)));
    $definedConstantsIndex = array_flip(ConstantHelper::getAllNames($phpcsFile));
    $classReferencesIndex = [];
    $classReferences = array_filter($references, static function (stdClass $reference) : bool {
        return $reference->source === self::SOURCE_CODE && $reference->isClass;
    });
    foreach ($classReferences as $classReference) {
        $classReferencesIndex[strtolower($classReference->name)] = NamespaceHelper::resolveName($phpcsFile, $classReference->name, $classReference->type, $classReference->startPointer);
    }
    $namespacePointers = NamespaceHelper::getAllNamespacesPointers($phpcsFile);
    $referenceErrors = [];
    foreach ($references as $reference) {
        $useStatements = UseStatementHelper::getUseStatementsForPointer($phpcsFile, $reference->startPointer);
        $name = $reference->name;
        
        /** @var int $startPointer */
        $startPointer = $reference->startPointer;
        $canonicalName = NamespaceHelper::normalizeToCanonicalName($name);
        $unqualifiedName = NamespaceHelper::getUnqualifiedNameFromFullyQualifiedName($name);
        if (in_array(strtolower($unqualifiedName), [
            'true',
            'false',
            'null',
        ], true)) {
            continue;
        }
        $collidingUseStatementUniqueId = UseStatement::getUniqueId($reference->type, $unqualifiedName);
        $isPartialUse = false;
        foreach ($useStatements as $useStatement) {
            $useStatementName = $useStatement->getAlias() ?? $useStatement->getNameAsReferencedInFile();
            if (strpos($name, $useStatementName . '\\') === 0) {
                $isPartialUse = true;
                break;
            }
        }
        $isFullyQualified = NamespaceHelper::isFullyQualifiedName($name) || $namespacePointers === [] && NamespaceHelper::hasNamespace($name) && !$isPartialUse;
        $isGlobalFallback = !$isFullyQualified && !NamespaceHelper::hasNamespace($name) && $namespacePointers !== [] && !array_key_exists(UseStatement::getUniqueId($reference->type, $name), $useStatements);
        $isGlobalFunctionFallback = false;
        if ($reference->isFunction && $isGlobalFallback) {
            $isGlobalFunctionFallback = !array_key_exists(strtolower($reference->name), $definedFunctionsIndex) && function_exists($reference->name);
        }
        $isGlobalConstantFallback = false;
        if ($reference->isConstant && $isGlobalFallback) {
            $isGlobalConstantFallback = !array_key_exists($reference->name, $definedConstantsIndex) && defined($reference->name);
        }
        if ($isFullyQualified) {
            if ($reference->isClass && $this->allowFullyQualifiedNameForCollidingClasses) {
                $lowerCasedUnqualifiedClassName = strtolower($unqualifiedName);
                if (array_key_exists($lowerCasedUnqualifiedClassName, $definedClassesIndex) && $canonicalName !== NamespaceHelper::normalizeToCanonicalName($definedClassesIndex[$lowerCasedUnqualifiedClassName])) {
                    continue;
                }
                if (array_key_exists($lowerCasedUnqualifiedClassName, $classReferencesIndex) && $name !== $classReferencesIndex[$lowerCasedUnqualifiedClassName]) {
                    continue;
                }
                if (array_key_exists($collidingUseStatementUniqueId, $useStatements) && $canonicalName !== NamespaceHelper::normalizeToCanonicalName($useStatements[$collidingUseStatementUniqueId]->getFullyQualifiedTypeName())) {
                    continue;
                }
            }
            elseif ($reference->isFunction && $this->allowFullyQualifiedNameForCollidingFunctions) {
                $lowerCasedUnqualifiedFunctionName = strtolower($unqualifiedName);
                if (array_key_exists($lowerCasedUnqualifiedFunctionName, $definedFunctionsIndex)) {
                    continue;
                }
                if (array_key_exists($collidingUseStatementUniqueId, $useStatements) && $canonicalName !== NamespaceHelper::normalizeToCanonicalName($useStatements[$collidingUseStatementUniqueId]->getFullyQualifiedTypeName())) {
                    continue;
                }
            }
            elseif ($reference->isConstant && $this->allowFullyQualifiedNameForCollidingConstants) {
                if (array_key_exists($unqualifiedName, $definedConstantsIndex)) {
                    continue;
                }
                if (array_key_exists($collidingUseStatementUniqueId, $useStatements) && $canonicalName !== NamespaceHelper::normalizeToCanonicalName($useStatements[$collidingUseStatementUniqueId]->getFullyQualifiedTypeName())) {
                    continue;
                }
            }
        }
        if ($isFullyQualified || $isGlobalFunctionFallback || $isGlobalConstantFallback) {
            if ($isFullyQualified && !$this->isRequiredToBeUsed($name)) {
                continue;
            }
            $isExceptionByName = StringHelper::endsWith($name, 'Exception') || $name === '\\Throwable' || StringHelper::endsWith($name, 'Error') && !NamespaceHelper::hasNamespace($name) || in_array($canonicalName, $this->getSpecialExceptionNames(), true);
            $inIgnoredNames = in_array($canonicalName, $this->getIgnoredNames(), true);
            if ($isExceptionByName && !$inIgnoredNames && $this->allowFullyQualifiedExceptions) {
                continue;
            }
            if ($isFullyQualified && !NamespaceHelper::hasNamespace($name) && $namespacePointers === []) {
                $label = sprintf($reference->isConstant ? 'Constant %s' : ($reference->isFunction ? 'Function %s()' : 'Class %s'), $name);
                $fix = $phpcsFile->addFixableError(sprintf('%s should not be referenced via a fully qualified name, but via an unqualified name without the leading \\, because the file does not have a namespace and the type cannot be put in a use statement.', $label), $startPointer, self::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME_WITHOUT_NAMESPACE);
                if ($fix) {
                    $phpcsFile->fixer
                        ->beginChangeset();
                    if ($reference->source === self::SOURCE_ANNOTATION) {
                        $fixedDocComment = AnnotationHelper::fixAnnotation($reference->parsedDocComment, $reference->annotation, $reference->nameNode, new IdentifierTypeNode(substr($reference->name, 1)));
                        FixerHelper::change($phpcsFile, $reference->parsedDocComment
                            ->getOpenPointer(), $reference->parsedDocComment
                            ->getClosePointer(), $fixedDocComment);
                    }
                    elseif ($reference->source === self::SOURCE_ANNOTATION_CONSTANT_FETCH) {
                        $fixedDocComment = AnnotationHelper::fixAnnotation($reference->parsedDocComment, $reference->annotation, $reference->constantFetchNode, new ConstFetchNode(substr($reference->name, 1), $reference->constantFetchNode->name));
                        FixerHelper::change($phpcsFile, $reference->parsedDocComment
                            ->getOpenPointer(), $reference->parsedDocComment
                            ->getClosePointer(), $fixedDocComment);
                    }
                    else {
                        $phpcsFile->fixer
                            ->replaceToken($startPointer, substr($tokens[$startPointer]['content'], 1));
                    }
                    $phpcsFile->fixer
                        ->endChangeset();
                }
            }
            else {
                $shouldBeUsed = NamespaceHelper::hasNamespace($name);
                if (!$shouldBeUsed) {
                    if ($reference->isFunction) {
                        $shouldBeUsed = $isFullyQualified ? !$this->allowFullyQualifiedGlobalFunctions : !$this->allowFallbackGlobalFunctions;
                    }
                    elseif ($reference->isConstant) {
                        $shouldBeUsed = $isFullyQualified ? !$this->allowFullyQualifiedGlobalConstants : !$this->allowFallbackGlobalConstants;
                    }
                    else {
                        $shouldBeUsed = !$this->allowFullyQualifiedGlobalClasses;
                    }
                }
                if (!$shouldBeUsed) {
                    continue;
                }
                $referenceErrors[] = (object) [
                    'reference' => $reference,
                    'canonicalName' => $canonicalName,
                    'isGlobalConstantFallback' => $isGlobalConstantFallback,
                    'isGlobalFunctionFallback' => $isGlobalFunctionFallback,
                ];
            }
        }
        elseif (!$this->allowPartialUses) {
            if (NamespaceHelper::isQualifiedName($name)) {
                $phpcsFile->addError(sprintf('Partial use statements are not allowed, but referencing %s found.', $name), $startPointer, self::CODE_PARTIAL_USE);
            }
        }
    }
    if (count($referenceErrors) === 0) {
        return;
    }
    $alreadyAddedUses = [
        UseStatement::TYPE_CLASS => [],
        UseStatement::TYPE_FUNCTION => [],
        UseStatement::TYPE_CONSTANT => [],
    ];
    $phpcsFile->fixer
        ->beginChangeset();
    foreach ($referenceErrors as $referenceData) {
        $reference = $referenceData->reference;
        
        /** @var int $startPointer */
        $startPointer = $reference->startPointer;
        $canonicalName = $referenceData->canonicalName;
        $nameToReference = NamespaceHelper::getUnqualifiedNameFromFullyQualifiedName($reference->name);
        $canonicalNameToReference = $reference->isConstant ? $nameToReference : strtolower($nameToReference);
        $isGlobalConstantFallback = $referenceData->isGlobalConstantFallback;
        $isGlobalFunctionFallback = $referenceData->isGlobalFunctionFallback;
        $useStatements = UseStatementHelper::getUseStatementsForPointer($phpcsFile, $reference->startPointer);
        $canBeFixed = array_reduce($alreadyAddedUses[$reference->type], static function (bool $carry, string $use) use ($canonicalName) : bool {
            $useLastName = strtolower(NamespaceHelper::getLastNamePart($use));
            $canonicalLastName = strtolower(NamespaceHelper::getLastNamePart($canonicalName));
            return $useLastName === $canonicalLastName ? false : $carry;
        }, true);
        if ($reference->isClass && array_key_exists($canonicalNameToReference, $definedClassesIndex) && $canonicalName !== NamespaceHelper::normalizeToCanonicalName($definedClassesIndex[$canonicalNameToReference]) || $reference->isClass && array_key_exists($canonicalNameToReference, $classReferencesIndex) && $canonicalName !== NamespaceHelper::normalizeToCanonicalName($classReferencesIndex[$canonicalNameToReference]) || $reference->isFunction && array_key_exists($canonicalNameToReference, $definedFunctionsIndex) || $reference->isConstant && array_key_exists($canonicalNameToReference, $definedConstantsIndex)) {
            $canBeFixed = false;
        }
        foreach ($useStatements as $useStatement) {
            if ($useStatement->getType() !== $reference->type) {
                continue;
            }
            if ($useStatement->getFullyQualifiedTypeName() === $canonicalName) {
                continue;
            }
            if ($useStatement->getCanonicalNameAsReferencedInFile() !== $canonicalNameToReference) {
                continue;
            }
            $canBeFixed = false;
            break;
        }
        $label = sprintf($reference->isConstant ? 'Constant %s' : ($reference->isFunction ? 'Function %s()' : 'Class %s'), $reference->name);
        $errorCode = $isGlobalConstantFallback || $isGlobalFunctionFallback ? self::CODE_REFERENCE_VIA_FALLBACK_GLOBAL_NAME : self::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME;
        $errorMessage = $isGlobalConstantFallback || $isGlobalFunctionFallback ? sprintf('%s should not be referenced via a fallback global name, but via a use statement.', $label) : sprintf('%s should not be referenced via a fully qualified name, but via a use statement.', $label);
        if (!$canBeFixed) {
            $phpcsFile->addError($errorMessage, $startPointer, $errorCode);
            continue;
        }
        $fix = $phpcsFile->addFixableError($errorMessage, $startPointer, $errorCode);
        if (!$fix) {
            continue;
        }
        $addUse = !in_array($canonicalName, $alreadyAddedUses[$reference->type], true);
        if ($reference->isClass && array_key_exists($canonicalNameToReference, $definedClassesIndex)) {
            $addUse = false;
        }
        foreach ($useStatements as $useStatement) {
            if ($useStatement->getType() !== $reference->type || $useStatement->getFullyQualifiedTypeName() !== $canonicalName) {
                continue;
            }
            $nameToReference = $useStatement->getNameAsReferencedInFile();
            $addUse = false;
            // Lock the use statement, so it is not modified by other sniffs
            $phpcsFile->fixer
                ->replaceToken($useStatement->getPointer(), $phpcsFile->fixer
                ->getTokenContent($useStatement->getPointer()));
            break;
        }
        if ($addUse) {
            $useStatementPlacePointer = $this->getUseStatementPlacePointer($phpcsFile, $openTagPointer, $useStatements);
            $useTypeName = UseStatement::getTypeName($reference->type);
            $useTypeFormatted = $useTypeName !== null ? sprintf('%s ', $useTypeName) : '';
            $phpcsFile->fixer
                ->addNewline($useStatementPlacePointer);
            $phpcsFile->fixer
                ->addContent($useStatementPlacePointer, sprintf('use %s%s;', $useTypeFormatted, $canonicalName));
            $alreadyAddedUses[$reference->type][] = $canonicalName;
        }
        if ($reference->source === self::SOURCE_ANNOTATION) {
            $fixedDocComment = AnnotationHelper::fixAnnotation($reference->parsedDocComment, $reference->annotation, $reference->nameNode, new IdentifierTypeNode($nameToReference));
            FixerHelper::change($phpcsFile, $reference->parsedDocComment
                ->getOpenPointer(), $reference->parsedDocComment
                ->getClosePointer(), $fixedDocComment);
        }
        elseif ($reference->source === self::SOURCE_ANNOTATION_CONSTANT_FETCH) {
            $fixedDocComment = AnnotationHelper::fixAnnotation($reference->parsedDocComment, $reference->annotation, $reference->constantFetchNode, new ConstFetchNode($nameToReference, $reference->constantFetchNode->name));
            FixerHelper::change($phpcsFile, $reference->parsedDocComment
                ->getOpenPointer(), $reference->parsedDocComment
                ->getClosePointer(), $fixedDocComment);
        }
        elseif ($reference->source === self::SOURCE_ATTRIBUTE) {
            $attributeContent = TokenHelper::getContent($phpcsFile, $startPointer, $reference->endPointer);
            $fixedAttributeContent = preg_replace('~(?<=\\W)' . preg_quote($reference->name, '~') . '(?=\\W)~', $nameToReference, $attributeContent);
            FixerHelper::change($phpcsFile, $startPointer, $reference->endPointer, $fixedAttributeContent);
        }
        else {
            FixerHelper::change($phpcsFile, $startPointer, $reference->endPointer, $nameToReference);
        }
    }
    $phpcsFile->fixer
        ->endChangeset();
}

API Navigation

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