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

Breadcrumb

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

class SelfMemberReferenceSniff

Hierarchy

  • class \PHP_CodeSniffer\Sniffs\AbstractScopeSniff implements \PHP_CodeSniffer\Sniffs\Sniff
    • class \PHP_CodeSniffer\Standards\Squiz\Sniffs\Classes\SelfMemberReferenceSniff extends \PHP_CodeSniffer\Sniffs\AbstractScopeSniff

Expanded class hierarchy of SelfMemberReferenceSniff

File

vendor/squizlabs/php_codesniffer/src/Standards/Squiz/Sniffs/Classes/SelfMemberReferenceSniff.php, line 21

Namespace

PHP_CodeSniffer\Standards\Squiz\Sniffs\Classes
View source
class SelfMemberReferenceSniff extends AbstractScopeSniff {
    
    /**
     * Constructs a Squiz_Sniffs_Classes_SelfMemberReferenceSniff.
     */
    public function __construct() {
        parent::__construct([
            T_CLASS,
        ], [
            T_DOUBLE_COLON,
        ]);
    }
    
    //end __construct()
    
    /**
     * Processes the function tokens within the class.
     *
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
     * @param int                         $stackPtr  The position where the token was found.
     * @param int                         $currScope The current scope opener token.
     *
     * @return void
     */
    protected function processTokenWithinScope(File $phpcsFile, $stackPtr, $currScope) {
        $tokens = $phpcsFile->getTokens();
        // Determine if this is a double colon which needs to be examined.
        $conditions = $tokens[$stackPtr]['conditions'];
        $conditions = array_reverse($conditions, true);
        foreach ($conditions as $conditionToken => $tokenCode) {
            if ($tokenCode === T_CLASS || $tokenCode === T_ANON_CLASS || $tokenCode === T_CLOSURE) {
                break;
            }
        }
        if ($conditionToken !== $currScope) {
            return;
        }
        $calledClassName = $phpcsFile->findPrevious(Tokens::$emptyTokens, $stackPtr - 1, null, true);
        if ($calledClassName === false) {
            // Parse error.
            return;
        }
        if ($tokens[$calledClassName]['code'] === T_SELF) {
            if ($tokens[$calledClassName]['content'] !== 'self') {
                $error = 'Must use "self::" for local static member reference; found "%s::"';
                $data = [
                    $tokens[$calledClassName]['content'],
                ];
                $fix = $phpcsFile->addFixableError($error, $calledClassName, 'IncorrectCase', $data);
                if ($fix === true) {
                    $phpcsFile->fixer
                        ->replaceToken($calledClassName, 'self');
                }
                return;
            }
        }
        else {
            if ($tokens[$calledClassName]['code'] === T_STRING) {
                // If the class is called with a namespace prefix, build fully qualified
                // namespace calls for both current scope class and requested class.
                $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, $calledClassName - 1, null, true);
                if ($prevNonEmpty !== false && $tokens[$prevNonEmpty]['code'] === T_NS_SEPARATOR) {
                    $declarationName = $this->getDeclarationNameWithNamespace($tokens, $calledClassName);
                    $declarationName = ltrim($declarationName, '\\');
                    $fullQualifiedClassName = $this->getNamespaceOfScope($phpcsFile, $currScope);
                    if ($fullQualifiedClassName === '\\') {
                        $fullQualifiedClassName = '';
                    }
                    else {
                        $fullQualifiedClassName .= '\\';
                    }
                    $fullQualifiedClassName .= $phpcsFile->getDeclarationName($currScope);
                }
                else {
                    $declarationName = $phpcsFile->getDeclarationName($currScope);
                    $fullQualifiedClassName = $tokens[$calledClassName]['content'];
                }
                if ($declarationName === $fullQualifiedClassName) {
                    // Class name is the same as the current class, which is not allowed.
                    $error = 'Must use "self::" for local static member reference';
                    $fix = $phpcsFile->addFixableError($error, $calledClassName, 'NotUsed');
                    if ($fix === true) {
                        $phpcsFile->fixer
                            ->beginChangeset();
                        $currentPointer = $stackPtr - 1;
                        while ($tokens[$currentPointer]['code'] === T_NS_SEPARATOR || $tokens[$currentPointer]['code'] === T_STRING || isset(Tokens::$emptyTokens[$tokens[$currentPointer]['code']]) === true) {
                            if (isset(Tokens::$emptyTokens[$tokens[$currentPointer]['code']]) === true) {
                                --$currentPointer;
                                continue;
                            }
                            $phpcsFile->fixer
                                ->replaceToken($currentPointer, '');
                            --$currentPointer;
                        }
                        $phpcsFile->fixer
                            ->replaceToken($stackPtr, 'self::');
                        $phpcsFile->fixer
                            ->endChangeset();
                        // Fix potential whitespace issues in the next loop.
                        return;
                    }
                    
                    //end if
                }
                
                //end if
            }
        }
        
        //end if
        if ($tokens[$stackPtr - 1]['code'] === T_WHITESPACE) {
            $found = $tokens[$stackPtr - 1]['length'];
            $error = 'Expected 0 spaces before double colon; %s found';
            $data = [
                $found,
            ];
            $fix = $phpcsFile->addFixableError($error, $stackPtr - 1, 'SpaceBefore', $data);
            if ($fix === true) {
                $phpcsFile->fixer
                    ->beginChangeset();
                for ($i = $stackPtr - 1; $tokens[$i]['code'] === T_WHITESPACE; $i--) {
                    $phpcsFile->fixer
                        ->replaceToken($i, '');
                }
                $phpcsFile->fixer
                    ->endChangeset();
            }
        }
        if ($tokens[$stackPtr + 1]['code'] === T_WHITESPACE) {
            $found = $tokens[$stackPtr + 1]['length'];
            $error = 'Expected 0 spaces after double colon; %s found';
            $data = [
                $found,
            ];
            $fix = $phpcsFile->addFixableError($error, $stackPtr - 1, 'SpaceAfter', $data);
            if ($fix === true) {
                $phpcsFile->fixer
                    ->beginChangeset();
                for ($i = $stackPtr + 1; $tokens[$i]['code'] === T_WHITESPACE; $i++) {
                    $phpcsFile->fixer
                        ->replaceToken($i, '');
                }
                $phpcsFile->fixer
                    ->endChangeset();
            }
        }
    }
    
    //end processTokenWithinScope()
    
    /**
     * Processes a token that is found within the scope that this test is
     * listening to.
     *
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
     * @param int                         $stackPtr  The position in the stack where this
     *                                               token was found.
     *
     * @return void
     */
    protected function processTokenOutsideScope(File $phpcsFile, $stackPtr) {
    }
    
    //end processTokenOutsideScope()
    
    /**
     * Returns the declaration names for classes/interfaces/functions with a namespace.
     *
     * @param array $tokens   Token stack for this file.
     * @param int   $stackPtr The position where the namespace building will start.
     *
     * @return string
     */
    protected function getDeclarationNameWithNamespace(array $tokens, $stackPtr) {
        $nameParts = [];
        $currentPointer = $stackPtr;
        while ($tokens[$currentPointer]['code'] === T_NS_SEPARATOR || $tokens[$currentPointer]['code'] === T_STRING || isset(Tokens::$emptyTokens[$tokens[$currentPointer]['code']]) === true) {
            if (isset(Tokens::$emptyTokens[$tokens[$currentPointer]['code']]) === true) {
                --$currentPointer;
                continue;
            }
            $nameParts[] = $tokens[$currentPointer]['content'];
            --$currentPointer;
        }
        $nameParts = array_reverse($nameParts);
        return implode('', $nameParts);
    }
    
    //end getDeclarationNameWithNamespace()
    
    /**
     * Returns the namespace declaration of a file.
     *
     * @param \PHP_CodeSniffer\Files\File $phpcsFile The file where this token was found.
     * @param int                         $stackPtr  The position where the search for the
     *                                               namespace declaration will start.
     *
     * @return string
     */
    protected function getNamespaceOfScope(File $phpcsFile, $stackPtr) {
        $namespace = '\\';
        $tokens = $phpcsFile->getTokens();
        while (($namespaceDeclaration = $phpcsFile->findPrevious(T_NAMESPACE, $stackPtr)) !== false) {
            $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $namespaceDeclaration + 1, null, true);
            if ($tokens[$nextNonEmpty]['code'] === T_NS_SEPARATOR) {
                // Namespace operator. Ignore.
                $stackPtr = $namespaceDeclaration - 1;
                continue;
            }
            $endOfNamespaceDeclaration = $phpcsFile->findNext([
                T_SEMICOLON,
                T_OPEN_CURLY_BRACKET,
                T_CLOSE_TAG,
            ], $namespaceDeclaration);
            $namespace = $this->getDeclarationNameWithNamespace($phpcsFile->getTokens(), $endOfNamespaceDeclaration - 1);
            break;
        }
        return $namespace;
    }
    
    //end getNamespaceOfScope()

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title
AbstractScopeSniff::$listenOutside private property True if this test should fire on tokens outside of the scope.
AbstractScopeSniff::$scopeTokens private property The type of scope opener tokens that this test wishes to listen to.
AbstractScopeSniff::$tokens private property The token types that this test wishes to listen to within the scope.
AbstractScopeSniff::process final public function Processes the tokens that this test is listening for. Overrides Sniff::process
AbstractScopeSniff::register final public function The method that is called to register the tokens this test wishes to
listen to.
Overrides Sniff::register
SelfMemberReferenceSniff::getDeclarationNameWithNamespace protected function Returns the declaration names for classes/interfaces/functions with a namespace.
SelfMemberReferenceSniff::getNamespaceOfScope protected function Returns the namespace declaration of a file.
SelfMemberReferenceSniff::processTokenOutsideScope protected function Processes a token that is found within the scope that this test is
listening to.
Overrides AbstractScopeSniff::processTokenOutsideScope
SelfMemberReferenceSniff::processTokenWithinScope protected function Processes the function tokens within the class. Overrides AbstractScopeSniff::processTokenWithinScope
SelfMemberReferenceSniff::__construct public function Constructs a Squiz_Sniffs_Classes_SelfMemberReferenceSniff. Overrides AbstractScopeSniff::__construct
RSS feed
Powered by Drupal