DeclareStrictTypesSniff.php
Namespace
SlevomatCodingStandard\Sniffs\TypeHintsFile
-
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 |