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

Breadcrumb

  1. Drupal Core 11.1.x

ModernClassNameReferenceSniff.php

Namespace

SlevomatCodingStandard\Sniffs\Classes

File

vendor/slevomat/coding-standard/SlevomatCodingStandard/Sniffs/Classes/ModernClassNameReferenceSniff.php

View source
<?php

declare (strict_types=1);
namespace SlevomatCodingStandard\Sniffs\Classes;

use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use SlevomatCodingStandard\Helpers\ClassHelper;
use SlevomatCodingStandard\Helpers\FixerHelper;
use SlevomatCodingStandard\Helpers\FunctionHelper;
use SlevomatCodingStandard\Helpers\SniffSettingsHelper;
use SlevomatCodingStandard\Helpers\TokenHelper;
use function in_array;
use function ltrim;
use function sprintf;
use function strtolower;
use const T_CLASS_C;
use const T_DOUBLE_COLON;
use const T_FUNCTION;
use const T_NS_SEPARATOR;
use const T_OBJECT_OPERATOR;
use const T_OPEN_PARENTHESIS;
use const T_VARIABLE;
class ModernClassNameReferenceSniff implements Sniff {
    public const CODE_CLASS_NAME_REFERENCED_VIA_MAGIC_CONSTANT = 'ClassNameReferencedViaMagicConstant';
    public const CODE_CLASS_NAME_REFERENCED_VIA_FUNCTION_CALL = 'ClassNameReferencedViaFunctionCall';
    
    /** @var bool|null */
    public $enableOnObjects = null;
    
    /**
     * @return array<int, (int|string)>
     */
    public function register() : array {
        $tokens = TokenHelper::getOnlyNameTokenCodes();
        $tokens[] = T_CLASS_C;
        return $tokens;
    }
    
    /**
     * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
     * @param int $pointer
     */
    public function process(File $phpcsFile, $pointer) : void {
        $this->enableOnObjects = SniffSettingsHelper::isEnabledByPhpVersion($this->enableOnObjects, 80000);
        $tokens = $phpcsFile->getTokens();
        if ($tokens[$pointer]['code'] === T_CLASS_C) {
            $this->checkMagicConstant($phpcsFile, $pointer);
            return;
        }
        $this->checkFunctionCall($phpcsFile, $pointer);
    }
    private function checkMagicConstant(File $phpcsFile, int $pointer) : void {
        $fix = $phpcsFile->addFixableError('Class name referenced via magic constant.', $pointer, self::CODE_CLASS_NAME_REFERENCED_VIA_MAGIC_CONSTANT);
        if (!$fix) {
            return;
        }
        $phpcsFile->fixer
            ->beginChangeset();
        $phpcsFile->fixer
            ->replaceToken($pointer, 'self::class');
        $phpcsFile->fixer
            ->endChangeset();
    }
    private function checkFunctionCall(File $phpcsFile, int $functionPointer) : void {
        $tokens = $phpcsFile->getTokens();
        $functionName = ltrim(strtolower($tokens[$functionPointer]['content']), '\\');
        $functionNames = [
            'get_class',
            'get_parent_class',
            'get_called_class',
        ];
        if (!in_array($functionName, $functionNames, true)) {
            return;
        }
        $openParenthesisPointer = TokenHelper::findNextEffective($phpcsFile, $functionPointer + 1);
        if ($tokens[$openParenthesisPointer]['code'] !== T_OPEN_PARENTHESIS) {
            return;
        }
        $previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $functionPointer - 1);
        if (in_array($tokens[$previousPointer]['code'], [
            T_OBJECT_OPERATOR,
            T_DOUBLE_COLON,
            T_FUNCTION,
        ], true)) {
            return;
        }
        $parameterPointer = TokenHelper::findNextEffective($phpcsFile, $openParenthesisPointer + 1, $tokens[$openParenthesisPointer]['parenthesis_closer']);
        $isObjectParameter = static function () use ($phpcsFile, $tokens, $openParenthesisPointer, $parameterPointer) : bool {
            if ($tokens[$parameterPointer]['code'] !== T_VARIABLE) {
                return false;
            }
            $pointerAfterParameterPointer = TokenHelper::findNextEffective($phpcsFile, $parameterPointer + 1);
            return $pointerAfterParameterPointer === $tokens[$openParenthesisPointer]['parenthesis_closer'];
        };
        $isThisParameter = static function () use ($tokens, $parameterPointer, $isObjectParameter) : bool {
            if (!$isObjectParameter()) {
                return false;
            }
            $parameterName = strtolower($tokens[$parameterPointer]['content']);
            return $parameterName === '$this';
        };
        if ($functionName === 'get_class') {
            if ($parameterPointer === null) {
                $fixedContent = 'self::class';
            }
            elseif ($isThisParameter()) {
                $fixedContent = 'static::class';
            }
            elseif ($this->enableOnObjects && $isObjectParameter()) {
                $fixedContent = sprintf('%s::class', $tokens[$parameterPointer]['content']);
            }
            else {
                return;
            }
        }
        elseif ($functionName === 'get_parent_class') {
            if ($parameterPointer !== null) {
                if (!$isThisParameter()) {
                    return;
                }
                
                /** @var int $classPointer */
                $classPointer = FunctionHelper::findClassPointer($phpcsFile, $functionPointer);
                if (!ClassHelper::isFinal($phpcsFile, $classPointer)) {
                    return;
                }
            }
            $fixedContent = 'parent::class';
        }
        else {
            $fixedContent = 'static::class';
        }
        $fix = $phpcsFile->addFixableError(sprintf('Class name referenced via call of function %s().', $functionName), $functionPointer, self::CODE_CLASS_NAME_REFERENCED_VIA_FUNCTION_CALL);
        if (!$fix) {
            return;
        }
        $phpcsFile->fixer
            ->beginChangeset();
        if ($tokens[$functionPointer - 1]['code'] === T_NS_SEPARATOR) {
            $phpcsFile->fixer
                ->replaceToken($functionPointer - 1, '');
        }
        FixerHelper::change($phpcsFile, $functionPointer, $tokens[$openParenthesisPointer]['parenthesis_closer'], $fixedContent);
        $phpcsFile->fixer
            ->endChangeset();
    }

}

Classes

Title Deprecated Summary
ModernClassNameReferenceSniff

API Navigation

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