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

Breadcrumb

  1. Drupal Core 11.1.x

DeclareStrictTypesSniff.php

Namespace

SlevomatCodingStandard\Sniffs\TypeHints

File

vendor/slevomat/coding-standard/SlevomatCodingStandard/Sniffs/TypeHints/DeclareStrictTypesSniff.php

View source
<?php

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

use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use SlevomatCodingStandard\Helpers\FixerHelper;
use SlevomatCodingStandard\Helpers\SniffSettingsHelper;
use SlevomatCodingStandard\Helpers\TokenHelper;
use function sprintf;
use function str_repeat;
use function strlen;
use function substr;
use function substr_count;
use const T_DECLARE;
use const T_LNUMBER;
use const T_OPEN_TAG;
use const T_STRING;
class DeclareStrictTypesSniff implements Sniff {
    public const CODE_DECLARE_STRICT_TYPES_MISSING = 'DeclareStrictTypesMissing';
    public const CODE_INCORRECT_STRICT_TYPES_FORMAT = 'IncorrectStrictTypesFormat';
    public const CODE_INCORRECT_WHITESPACE_BEFORE_DECLARE = 'IncorrectWhitespaceBeforeDeclare';
    public const CODE_INCORRECT_WHITESPACE_AFTER_DECLARE = 'IncorrectWhitespaceAfterDeclare';
    
    /** @var bool */
    public $declareOnFirstLine = false;
    
    /** @var int */
    public $linesCountBeforeDeclare = 1;
    
    /** @var int */
    public $linesCountAfterDeclare = 1;
    
    /** @var int */
    public $spacesCountAroundEqualsSign = 1;
    
    /**
     * @return array<int, (int|string)>
     */
    public function register() : array {
        return [
            T_OPEN_TAG,
        ];
    }
    
    /**
     * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
     * @param int $openTagPointer
     */
    public function process(File $phpcsFile, $openTagPointer) : void {
        $this->linesCountBeforeDeclare = SniffSettingsHelper::normalizeInteger($this->linesCountBeforeDeclare);
        $this->linesCountAfterDeclare = SniffSettingsHelper::normalizeInteger($this->linesCountAfterDeclare);
        $this->spacesCountAroundEqualsSign = SniffSettingsHelper::normalizeInteger($this->spacesCountAroundEqualsSign);
        if (TokenHelper::findPrevious($phpcsFile, T_OPEN_TAG, $openTagPointer - 1) !== null) {
            return;
        }
        $tokens = $phpcsFile->getTokens();
        $declarePointer = TokenHelper::findNextEffective($phpcsFile, $openTagPointer + 1);
        if ($declarePointer === null || $tokens[$declarePointer]['code'] !== T_DECLARE) {
            $fix = $phpcsFile->addFixableError(sprintf('Missing declare(%s).', $this->getStrictTypeDeclaration()), $openTagPointer, self::CODE_DECLARE_STRICT_TYPES_MISSING);
            if ($fix) {
                $phpcsFile->fixer
                    ->beginChangeset();
                $phpcsFile->fixer
                    ->addContent($openTagPointer, sprintf('declare(%s);%s', $this->getStrictTypeDeclaration(), $phpcsFile->eolChar));
                $phpcsFile->fixer
                    ->endChangeset();
            }
            return;
        }
        $strictTypesPointer = null;
        for ($i = $tokens[$declarePointer]['parenthesis_opener'] + 1; $i < $tokens[$declarePointer]['parenthesis_closer']; $i++) {
            if ($tokens[$i]['code'] !== T_STRING || $tokens[$i]['content'] !== 'strict_types') {
                continue;
            }
            $strictTypesPointer = $i;
            break;
        }
        if ($strictTypesPointer === null) {
            $fix = $phpcsFile->addFixableError(sprintf('Missing declare(%s).', $this->getStrictTypeDeclaration()), $declarePointer, self::CODE_DECLARE_STRICT_TYPES_MISSING);
            if ($fix) {
                $phpcsFile->fixer
                    ->beginChangeset();
                $phpcsFile->fixer
                    ->addContentBefore($tokens[$declarePointer]['parenthesis_closer'], ', ' . $this->getStrictTypeDeclaration());
                $phpcsFile->fixer
                    ->endChangeset();
            }
            return;
        }
        
        /** @var int $numberPointer */
        $numberPointer = TokenHelper::findNext($phpcsFile, T_LNUMBER, $strictTypesPointer + 1);
        if ($tokens[$numberPointer]['content'] !== '1') {
            $fix = $phpcsFile->addFixableError(sprintf('Expected %s, found %s.', $this->getStrictTypeDeclaration(), TokenHelper::getContent($phpcsFile, $strictTypesPointer, $numberPointer)), $declarePointer, self::CODE_DECLARE_STRICT_TYPES_MISSING);
            if ($fix) {
                $phpcsFile->fixer
                    ->beginChangeset();
                $phpcsFile->fixer
                    ->replaceToken($numberPointer, '1');
                $phpcsFile->fixer
                    ->endChangeset();
            }
            return;
        }
        $strictTypesContent = TokenHelper::getContent($phpcsFile, $strictTypesPointer, $numberPointer);
        $format = sprintf('strict_types%1$s=%1$s1', str_repeat(' ', $this->spacesCountAroundEqualsSign));
        if ($strictTypesContent !== $format) {
            $fix = $phpcsFile->addFixableError(sprintf('Expected %s, found %s.', $format, $strictTypesContent), $strictTypesPointer, self::CODE_INCORRECT_STRICT_TYPES_FORMAT);
            if ($fix) {
                $phpcsFile->fixer
                    ->beginChangeset();
                FixerHelper::change($phpcsFile, $strictTypesPointer, $numberPointer, $format);
                $phpcsFile->fixer
                    ->endChangeset();
            }
        }
        $pointerBeforeDeclare = TokenHelper::findPreviousNonWhitespace($phpcsFile, $declarePointer - 1);
        $whitespaceBefore = '';
        if ($pointerBeforeDeclare === $openTagPointer) {
            $whitespaceBefore .= substr($tokens[$openTagPointer]['content'], strlen('<?php'));
        }
        if ($pointerBeforeDeclare + 1 !== $declarePointer) {
            $whitespaceBefore .= TokenHelper::getContent($phpcsFile, $pointerBeforeDeclare + 1, $declarePointer - 1);
        }
        if ($this->declareOnFirstLine) {
            if ($whitespaceBefore !== ' ') {
                $fix = $phpcsFile->addFixableError('There must be a single space between the PHP open tag and declare statement.', $declarePointer, self::CODE_INCORRECT_WHITESPACE_BEFORE_DECLARE);
                if ($fix) {
                    $phpcsFile->fixer
                        ->beginChangeset();
                    FixerHelper::change($phpcsFile, $openTagPointer, $declarePointer - 1, '<?php ');
                    $phpcsFile->fixer
                        ->endChangeset();
                }
            }
        }
        else {
            $declareOnFirstLine = $tokens[$declarePointer]['line'] === $tokens[$openTagPointer]['line'];
            $linesCountBefore = $declareOnFirstLine ? 0 : substr_count($whitespaceBefore, $phpcsFile->eolChar) - 1;
            if ($declareOnFirstLine || $linesCountBefore !== $this->linesCountBeforeDeclare) {
                $fix = $phpcsFile->addFixableError(sprintf('Expected %d line%s before declare statement, found %d.', $this->linesCountBeforeDeclare, $this->linesCountBeforeDeclare === 1 ? '' : 's', $linesCountBefore), $declarePointer, self::CODE_INCORRECT_WHITESPACE_BEFORE_DECLARE);
                if ($fix) {
                    $phpcsFile->fixer
                        ->beginChangeset();
                    if ($pointerBeforeDeclare === $openTagPointer) {
                        $phpcsFile->fixer
                            ->replaceToken($openTagPointer, '<?php');
                    }
                    FixerHelper::removeBetween($phpcsFile, $pointerBeforeDeclare, $declarePointer);
                    for ($i = 0; $i <= $this->linesCountBeforeDeclare; $i++) {
                        $phpcsFile->fixer
                            ->addNewline($pointerBeforeDeclare);
                    }
                    $phpcsFile->fixer
                        ->endChangeset();
                }
            }
        }
        
        /** @var int $declareSemicolonPointer */
        $declareSemicolonPointer = TokenHelper::findNextEffective($phpcsFile, $tokens[$declarePointer]['parenthesis_closer'] + 1);
        $pointerAfterWhitespaceEnd = TokenHelper::findNextNonWhitespace($phpcsFile, $declareSemicolonPointer + 1);
        if ($pointerAfterWhitespaceEnd === null) {
            return;
        }
        $whitespaceAfter = TokenHelper::getContent($phpcsFile, $declareSemicolonPointer + 1, $pointerAfterWhitespaceEnd - 1);
        $newLinesAfter = substr_count($whitespaceAfter, $phpcsFile->eolChar);
        $linesCountAfter = $newLinesAfter > 0 ? $newLinesAfter - 1 : 0;
        if ($linesCountAfter === $this->linesCountAfterDeclare) {
            return;
        }
        $fix = $phpcsFile->addFixableError(sprintf('Expected %d line%s after declare statement, found %d.', $this->linesCountAfterDeclare, $this->linesCountAfterDeclare === 1 ? '' : 's', $linesCountAfter), $declarePointer, self::CODE_INCORRECT_WHITESPACE_AFTER_DECLARE);
        if (!$fix) {
            return;
        }
        $phpcsFile->fixer
            ->beginChangeset();
        FixerHelper::removeBetween($phpcsFile, $declareSemicolonPointer, $pointerAfterWhitespaceEnd);
        for ($i = 0; $i <= $this->linesCountAfterDeclare; $i++) {
            $phpcsFile->fixer
                ->addNewline($declareSemicolonPointer);
        }
        $phpcsFile->fixer
            ->endChangeset();
    }
    protected function getStrictTypeDeclaration() : string {
        return sprintf('strict_types%s=%s1', str_repeat(' ', $this->spacesCountAroundEqualsSign), str_repeat(' ', $this->spacesCountAroundEqualsSign));
    }

}

Classes

Title Deprecated Summary
DeclareStrictTypesSniff
RSS feed
Powered by Drupal