clean install

This commit is contained in:
2024-12-10 15:08:16 +01:00
commit e14eb2d8fd
31193 changed files with 3555714 additions and 0 deletions

View File

@@ -0,0 +1,85 @@
<?php declare(strict_types = 1);
namespace SlevomatCodingStandard\Sniffs\Arrays;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use SlevomatCodingStandard\Helpers\ArrayHelper;
use SlevomatCodingStandard\Helpers\ArrayKeyValue;
use SlevomatCodingStandard\Helpers\FixerHelper;
use SlevomatCodingStandard\Helpers\TokenHelper;
use function array_map;
use function count;
use function implode;
use function strnatcasecmp;
use function usort;
class AlphabeticallySortedByKeysSniff implements Sniff
{
public const CODE_INCORRECT_KEY_ORDER = 'IncorrectKeyOrder';
/**
* @return array<int, (int|string)>
*/
public function register(): array
{
return TokenHelper::$arrayTokenCodes;
}
/**
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
* @param int $stackPointer
*/
public function process(File $phpcsFile, $stackPointer): void
{
if (ArrayHelper::isMultiLine($phpcsFile, $stackPointer) === false) {
return;
}
// "Parse" the array... get info for each key/value pair
$keyValues = ArrayHelper::parse($phpcsFile, $stackPointer);
if (ArrayHelper::isKeyedAll($keyValues) === false) {
return;
}
if (ArrayHelper::isSortedByKey($keyValues)) {
return;
}
$fix = $phpcsFile->addFixableError(
'Keyed multi-line arrays must be sorted alphabetically.',
$stackPointer,
self::CODE_INCORRECT_KEY_ORDER
);
if ($fix) {
$this->fix($phpcsFile, $keyValues);
}
}
/**
* @param list<ArrayKeyValue> $keyValues
*/
private function fix(File $phpcsFile, array $keyValues): void
{
$pointerStart = $keyValues[0]->getPointerStart();
$pointerEnd = $keyValues[count($keyValues) - 1]->getPointerEnd();
// determine indent to use
$indent = ArrayHelper::getIndentation($keyValues);
usort($keyValues, static function ($a1, $a2) {
return strnatcasecmp((string) $a1->getKey(), (string) $a2->getKey());
});
$content = implode('', array_map(static function (ArrayKeyValue $keyValue) use ($phpcsFile, $indent) {
return $keyValue->getContent($phpcsFile, true, $indent) . $phpcsFile->eolChar;
}, $keyValues));
$phpcsFile->fixer->beginChangeset();
FixerHelper::change($phpcsFile, $pointerStart, $pointerEnd, $content);
$phpcsFile->fixer->endChangeset();
}
}

View File

@@ -0,0 +1,76 @@
<?php declare(strict_types = 1);
namespace SlevomatCodingStandard\Sniffs\Arrays;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use SlevomatCodingStandard\Helpers\TokenHelper;
use const T_CLOSE_SQUARE_BRACKET;
use const T_OPEN_SQUARE_BRACKET;
use const T_VARIABLE;
class ArrayAccessSniff implements Sniff
{
public const CODE_NO_SPACE_BEFORE_BRACKETS = 'NoSpaceBeforeBrackets';
public const CODE_NO_SPACE_BETWEEN_BRACKETS = 'NoSpaceBetweenBrackets';
/**
* @return list<string>
*/
public function register(): array
{
return [T_OPEN_SQUARE_BRACKET];
}
/**
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
* @param int $stackPointer
*/
public function process(File $phpcsFile, $stackPointer): void
{
$tokens = $phpcsFile->getTokens();
$previousToken = TokenHelper::findPreviousNonWhitespace($phpcsFile, $stackPointer - 1);
if (
$previousToken === null
|| $previousToken === $stackPointer - 1) {
return;
}
if ($tokens[$previousToken]['code'] === T_VARIABLE) {
$this->addError(
$phpcsFile,
$stackPointer,
'There should be no space between array variable and array access operator.',
self::CODE_NO_SPACE_BEFORE_BRACKETS
);
}
if ($tokens[$previousToken]['code'] !== T_CLOSE_SQUARE_BRACKET) {
return;
}
$this->addError(
$phpcsFile,
$stackPointer,
'There should be no space between array access operators.',
self::CODE_NO_SPACE_BETWEEN_BRACKETS
);
}
private function addError(File $phpcsFile, int $stackPointer, string $error, string $code): void
{
$fix = $phpcsFile->addFixableError($error, $stackPointer, $code);
if (!$fix) {
return;
}
$phpcsFile->fixer->beginChangeset();
$phpcsFile->fixer->replaceToken($stackPointer - 1, '');
$phpcsFile->fixer->endChangeset();
}
}

View File

@@ -0,0 +1,280 @@
<?php declare(strict_types = 1);
namespace SlevomatCodingStandard\Sniffs\Arrays;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use SlevomatCodingStandard\Helpers\ScopeHelper;
use SlevomatCodingStandard\Helpers\TokenHelper;
use function array_key_exists;
use function array_reverse;
use function count;
use function in_array;
use const T_CLOSE_PARENTHESIS;
use const T_CLOSURE;
use const T_COMMA;
use const T_DOUBLE_COLON;
use const T_EQUAL;
use const T_FOREACH;
use const T_GLOBAL;
use const T_LIST;
use const T_OBJECT_OPERATOR;
use const T_OPEN_PARENTHESIS;
use const T_OPEN_SHORT_ARRAY;
use const T_OPEN_SQUARE_BRACKET;
use const T_OPEN_TAG;
use const T_STATIC;
use const T_STRING;
use const T_USE;
use const T_VARIABLE;
class DisallowImplicitArrayCreationSniff implements Sniff
{
public const CODE_IMPLICIT_ARRAY_CREATION_USED = 'ImplicitArrayCreationUsed';
/**
* @return array<int, (int|string)>
*/
public function register(): array
{
return [
T_OPEN_SQUARE_BRACKET,
];
}
/**
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
* @param int $bracketOpenerPointer
*/
public function process(File $phpcsFile, $bracketOpenerPointer): void
{
$tokens = $phpcsFile->getTokens();
$assignmentPointer = TokenHelper::findNextEffective($phpcsFile, $tokens[$bracketOpenerPointer]['bracket_closer'] + 1);
if ($tokens[$assignmentPointer]['code'] !== T_EQUAL) {
return;
}
/** @var int $variablePointer */
$variablePointer = TokenHelper::findPreviousEffective($phpcsFile, $bracketOpenerPointer - 1);
if ($tokens[$variablePointer]['code'] !== T_VARIABLE) {
return;
}
if (in_array($tokens[$variablePointer]['content'], [
'$GLOBALS',
'$_SERVER',
'$_REQUEST',
'$_POST',
'$_GET',
'$_FILES',
'$_ENV',
'$_COOKIE',
'$_SESSION',
'$this',
], true)) {
return;
}
$pointerBeforeVariable = TokenHelper::findPreviousEffective($phpcsFile, $variablePointer - 1);
if (in_array($tokens[$pointerBeforeVariable]['code'], [T_OBJECT_OPERATOR, T_DOUBLE_COLON], true)) {
return;
}
$scopeOwnerPointer = null;
foreach (array_reverse($tokens[$variablePointer]['conditions'], true) as $conditionPointer => $conditionTokenCode) {
if (!in_array($conditionTokenCode, TokenHelper::$functionTokenCodes, true)) {
continue;
}
$scopeOwnerPointer = $conditionPointer;
break;
}
if ($scopeOwnerPointer === null) {
$scopeOwnerPointer = TokenHelper::findPrevious($phpcsFile, T_OPEN_TAG, $variablePointer - 1);
}
$scopeOpenerPointer = $tokens[$scopeOwnerPointer]['code'] === T_OPEN_TAG
? $scopeOwnerPointer
: $tokens[$scopeOwnerPointer]['scope_opener'];
$scopeCloserPointer = $tokens[$scopeOwnerPointer]['code'] === T_OPEN_TAG
? count($tokens) - 1
: $tokens[$scopeOwnerPointer]['scope_closer'];
if (in_array($tokens[$scopeOwnerPointer]['code'], TokenHelper::$functionTokenCodes, true)) {
if ($this->isParameter($phpcsFile, $scopeOwnerPointer, $variablePointer)) {
return;
}
if (
$tokens[$scopeOwnerPointer]['code'] === T_CLOSURE
&& $this->isInheritedVariable($phpcsFile, $scopeOwnerPointer, $variablePointer)
) {
return;
}
}
if ($this->hasExplicitCreation($phpcsFile, $scopeOpenerPointer, $scopeCloserPointer, $variablePointer)) {
return;
}
$phpcsFile->addError('Implicit array creation is disallowed.', $variablePointer, self::CODE_IMPLICIT_ARRAY_CREATION_USED);
}
private function isParameter(File $phpcsFile, int $functionPointer, int $variablePointer): bool
{
$tokens = $phpcsFile->getTokens();
$variableName = $tokens[$variablePointer]['content'];
$parameterPointer = TokenHelper::findNextContent(
$phpcsFile,
T_VARIABLE,
$variableName,
$tokens[$functionPointer]['parenthesis_opener'] + 1,
$tokens[$functionPointer]['parenthesis_closer']
);
return $parameterPointer !== null;
}
private function isInheritedVariable(File $phpcsFile, int $closurePointer, int $variablePointer): bool
{
$tokens = $phpcsFile->getTokens();
$variableName = $tokens[$variablePointer]['content'];
$usePointer = TokenHelper::findNext(
$phpcsFile,
T_USE,
$tokens[$closurePointer]['parenthesis_closer'] + 1,
$tokens[$closurePointer]['scope_opener']
);
if ($usePointer === null) {
return false;
}
$parenthesisOpenerPointer = TokenHelper::findNextEffective($phpcsFile, $usePointer + 1);
$inheritedVariablePointer = TokenHelper::findNextContent(
$phpcsFile,
T_VARIABLE,
$variableName,
$parenthesisOpenerPointer + 1,
$tokens[$parenthesisOpenerPointer]['parenthesis_closer']
);
return $inheritedVariablePointer !== null;
}
private function hasExplicitCreation(File $phpcsFile, int $scopeOpenerPointer, int $scopeCloserPointer, int $variablePointer): bool
{
$tokens = $phpcsFile->getTokens();
$variableName = $tokens[$variablePointer]['content'];
for ($i = $scopeOpenerPointer + 1; $i < $variablePointer; $i++) {
if ($tokens[$i]['code'] !== T_VARIABLE) {
continue;
}
if ($tokens[$i]['content'] !== $variableName) {
continue;
}
if (!ScopeHelper::isInSameScope($phpcsFile, $variablePointer, $i)) {
continue;
}
$assignmentPointer = TokenHelper::findNextEffective($phpcsFile, $i + 1);
if ($tokens[$assignmentPointer]['code'] === T_EQUAL) {
return true;
}
$staticPointer = TokenHelper::findPreviousEffective($phpcsFile, $i - 1);
if ($tokens[$staticPointer]['code'] === T_STATIC) {
return true;
}
if ($this->isCreatedInForeach($phpcsFile, $i, $scopeCloserPointer)) {
return true;
}
if ($this->isCreatedInList($phpcsFile, $i, $scopeOpenerPointer)) {
return true;
}
if ($this->isCreatedByReferencedParameterInFunctionCall($phpcsFile, $i, $scopeOpenerPointer)) {
return true;
}
if ($this->isImportedUsingGlobalStatement($phpcsFile, $i)) {
return true;
}
}
return false;
}
private function isCreatedInList(File $phpcsFile, int $variablePointer, int $scopeOpenerPointer): bool
{
$tokens = $phpcsFile->getTokens();
$parenthesisOpenerPointer = TokenHelper::findPrevious(
$phpcsFile,
[T_OPEN_PARENTHESIS, T_OPEN_SHORT_ARRAY, T_OPEN_SQUARE_BRACKET],
$variablePointer - 1,
$scopeOpenerPointer
);
if ($parenthesisOpenerPointer === null) {
return false;
}
if ($tokens[$parenthesisOpenerPointer]['code'] === T_OPEN_PARENTHESIS) {
if ($tokens[$parenthesisOpenerPointer]['parenthesis_closer'] < $variablePointer) {
return false;
}
$pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1);
return $tokens[$pointerBeforeParenthesisOpener]['code'] === T_LIST;
}
return $tokens[$parenthesisOpenerPointer]['bracket_closer'] > $variablePointer;
}
private function isCreatedInForeach(File $phpcsFile, int $variablePointer, int $scopeCloserPointer): bool
{
$tokens = $phpcsFile->getTokens();
$parenthesisCloserPointer = TokenHelper::findNext($phpcsFile, T_CLOSE_PARENTHESIS, $variablePointer + 1, $scopeCloserPointer);
return $parenthesisCloserPointer !== null
&& array_key_exists('parenthesis_owner', $tokens[$parenthesisCloserPointer])
&& $tokens[$tokens[$parenthesisCloserPointer]['parenthesis_owner']]['code'] === T_FOREACH
&& $tokens[$parenthesisCloserPointer]['parenthesis_opener'] < $variablePointer;
}
private function isCreatedByReferencedParameterInFunctionCall(File $phpcsFile, int $variablePointer, int $scopeOpenerPointer): bool
{
$tokens = $phpcsFile->getTokens();
$parenthesisOpenerPointer = TokenHelper::findPrevious($phpcsFile, T_OPEN_PARENTHESIS, $variablePointer - 1, $scopeOpenerPointer);
if (
$parenthesisOpenerPointer === null
|| $tokens[$parenthesisOpenerPointer]['parenthesis_closer'] < $variablePointer
) {
return false;
}
$pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1);
return $tokens[$pointerBeforeParenthesisOpener]['code'] === T_STRING;
}
private function isImportedUsingGlobalStatement(File $phpcsFile, int $variablePointer): bool
{
$tokens = $phpcsFile->getTokens();
$startOfStatement = $phpcsFile->findStartOfStatement($variablePointer, T_COMMA);
return $tokens[$startOfStatement]['code'] === T_GLOBAL;
}
}

View File

@@ -0,0 +1,42 @@
<?php declare(strict_types = 1);
namespace SlevomatCodingStandard\Sniffs\Arrays;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use SlevomatCodingStandard\Helpers\ArrayHelper;
use SlevomatCodingStandard\Helpers\TokenHelper;
class DisallowPartiallyKeyedSniff implements Sniff
{
public const CODE_DISALLOWED_PARTIALLY_KEYED = 'DisallowedPartiallyKeyed';
/**
* @return array<int, (int|string)>
*/
public function register(): array
{
return TokenHelper::$arrayTokenCodes;
}
/**
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
* @param int $stackPointer
*/
public function process(File $phpcsFile, $stackPointer): void
{
$keyValues = ArrayHelper::parse($phpcsFile, $stackPointer);
if (!ArrayHelper::isKeyed($keyValues)) {
return;
}
if (ArrayHelper::isKeyedAll($keyValues)) {
return;
}
$phpcsFile->addError('Partially keyed array disallowed.', $stackPointer, self::CODE_DISALLOWED_PARTIALLY_KEYED);
}
}

View File

@@ -0,0 +1,60 @@
<?php declare(strict_types = 1);
namespace SlevomatCodingStandard\Sniffs\Arrays;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use SlevomatCodingStandard\Helpers\ArrayHelper;
use SlevomatCodingStandard\Helpers\TokenHelper;
use function in_array;
class MultiLineArrayEndBracketPlacementSniff implements Sniff
{
public const CODE_ARRAY_END_WRONG_PLACEMENT = 'ArrayEndWrongPlacement';
/**
* @return array<int, (int|string)>
*/
public function register(): array
{
return TokenHelper::$arrayTokenCodes;
}
/**
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
* @param int $stackPointer
*/
public function process(File $phpcsFile, $stackPointer): void
{
$tokens = $phpcsFile->getTokens();
if (ArrayHelper::isMultiLine($phpcsFile, $stackPointer) === false) {
return;
}
[$arrayOpenerPointer, $arrayCloserPointer] = ArrayHelper::openClosePointers($tokens[$stackPointer]);
$nextEffective = TokenHelper::findNextEffective($phpcsFile, $arrayOpenerPointer + 1, $arrayCloserPointer);
if ($nextEffective === null || in_array($tokens[$nextEffective]['code'], TokenHelper::$arrayTokenCodes, true) === false) {
return;
}
[$nextPointerOpener, $nextPointerCloser] = ArrayHelper::openClosePointers($tokens[$nextEffective]);
$arraysStartAtSameLine = $tokens[$arrayOpenerPointer]['line'] === $tokens[$nextPointerOpener]['line'];
$arraysEndAtSameLine = $tokens[$arrayCloserPointer]['line'] === $tokens[$nextPointerCloser]['line'];
if (!$arraysStartAtSameLine || $arraysEndAtSameLine) {
return;
}
$error = "Expected nested array to end at the same line as it's parent. Either put the nested array's end at the same line as the parent's end, or put the nested array start on it's own line.";
$fix = $phpcsFile->addFixableError($error, $arrayOpenerPointer, self::CODE_ARRAY_END_WRONG_PLACEMENT);
if (!$fix) {
return;
}
$phpcsFile->fixer->addContent($arrayOpenerPointer, $phpcsFile->eolChar);
}
}

View File

@@ -0,0 +1,228 @@
<?php declare(strict_types = 1);
namespace SlevomatCodingStandard\Sniffs\Arrays;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use SlevomatCodingStandard\Helpers\ArrayHelper;
use SlevomatCodingStandard\Helpers\SniffSettingsHelper;
use SlevomatCodingStandard\Helpers\TokenHelper;
use function in_array;
use function sprintf;
use function str_repeat;
use const T_COMMA;
use const T_OPEN_PARENTHESIS;
use const T_WHITESPACE;
class SingleLineArrayWhitespaceSniff implements Sniff
{
public const CODE_SPACE_BEFORE_COMMA = 'SpaceBeforeComma';
public const CODE_SPACE_AFTER_COMMA = 'SpaceAfterComma';
public const CODE_SPACE_AFTER_ARRAY_OPEN = 'SpaceAfterArrayOpen';
public const CODE_SPACE_BEFORE_ARRAY_CLOSE = 'SpaceBeforeArrayClose';
public const CODE_SPACE_IN_EMPTY_ARRAY = 'SpaceInEmptyArray';
/** @var int */
public $spacesAroundBrackets = 0;
/** @var bool */
public $enableEmptyArrayCheck = false;
/**
* @return array<int, (int|string)>
*/
public function register(): array
{
return TokenHelper::$arrayTokenCodes;
}
/**
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
* @param int $stackPointer
*/
public function process(File $phpcsFile, $stackPointer): int
{
$this->spacesAroundBrackets = SniffSettingsHelper::normalizeInteger($this->spacesAroundBrackets);
$tokens = $phpcsFile->getTokens();
[$arrayOpenerPointer, $arrayCloserPointer] = ArrayHelper::openClosePointers($tokens[$stackPointer]);
// Check only single-line arrays.
if ($tokens[$arrayOpenerPointer]['line'] !== $tokens[$arrayCloserPointer]['line']) {
return $arrayCloserPointer;
}
$pointerContent = TokenHelper::findNextNonWhitespace($phpcsFile, $arrayOpenerPointer + 1, $arrayCloserPointer + 1);
if ($pointerContent === $arrayCloserPointer) {
// Empty array, but if the brackets aren't together, there's a problem.
if ($this->enableEmptyArrayCheck) {
$this->checkWhitespaceInEmptyArray($phpcsFile, $arrayOpenerPointer, $arrayCloserPointer);
}
// We can return here because there is nothing else to check.
// All code below can assume that the array is not empty.
return $arrayCloserPointer + 1;
}
$this->checkWhitespaceAfterOpeningBracket($phpcsFile, $arrayOpenerPointer);
$this->checkWhitespaceBeforeClosingBracket($phpcsFile, $arrayCloserPointer);
for ($i = $arrayOpenerPointer + 1; $i < $arrayCloserPointer; $i++) {
// Skip bracketed statements, like function calls.
if ($tokens[$i]['code'] === T_OPEN_PARENTHESIS) {
$i = $tokens[$i]['parenthesis_closer'];
continue;
}
// Skip nested arrays as they will be processed separately
if (in_array($tokens[$i]['code'], TokenHelper::$arrayTokenCodes, true)) {
$i = ArrayHelper::openClosePointers($tokens[$i])[1];
continue;
}
if ($tokens[$i]['code'] !== T_COMMA) {
continue;
}
// Before checking this comma, make sure we are not at the end of the array.
$next = TokenHelper::findNextNonWhitespace($phpcsFile, $i + 1, $arrayCloserPointer);
if ($next === null) {
return $arrayOpenerPointer + 1;
}
$this->checkWhitespaceBeforeComma($phpcsFile, $i);
$this->checkWhitespaceAfterComma($phpcsFile, $i);
}
return $arrayOpenerPointer + 1;
}
private function checkWhitespaceInEmptyArray(File $phpcsFile, int $arrayStart, int $arrayEnd): void
{
if ($arrayEnd - $arrayStart === 1) {
return;
}
$error = 'Empty array declaration must have no space between the parentheses.';
$fix = $phpcsFile->addFixableError($error, $arrayStart, self::CODE_SPACE_IN_EMPTY_ARRAY);
if (!$fix) {
return;
}
$phpcsFile->fixer->replaceToken($arrayStart + 1, '');
}
private function checkWhitespaceAfterOpeningBracket(File $phpcsFile, int $arrayStart): void
{
$tokens = $phpcsFile->getTokens();
$whitespacePointer = $arrayStart + 1;
$spaceLength = 0;
if ($tokens[$whitespacePointer]['code'] === T_WHITESPACE) {
$spaceLength = $tokens[$whitespacePointer]['length'];
}
if ($spaceLength === $this->spacesAroundBrackets) {
return;
}
$error = sprintf('Expected %d spaces after array opening bracket, %d found.', $this->spacesAroundBrackets, $spaceLength);
$fix = $phpcsFile->addFixableError($error, $arrayStart, self::CODE_SPACE_AFTER_ARRAY_OPEN);
if (!$fix) {
return;
}
if ($spaceLength === 0) {
$phpcsFile->fixer->addContent($arrayStart, str_repeat(' ', $this->spacesAroundBrackets));
} else {
$phpcsFile->fixer->replaceToken($whitespacePointer, str_repeat(' ', $this->spacesAroundBrackets));
}
}
private function checkWhitespaceBeforeClosingBracket(File $phpcsFile, int $arrayEnd): void
{
$tokens = $phpcsFile->getTokens();
$whitespacePointer = $arrayEnd - 1;
$spaceLength = 0;
if ($tokens[$whitespacePointer]['code'] === T_WHITESPACE) {
$spaceLength = $tokens[$whitespacePointer]['length'];
}
if ($spaceLength === $this->spacesAroundBrackets) {
return;
}
$error = sprintf('Expected %d spaces before array closing bracket, %d found.', $this->spacesAroundBrackets, $spaceLength);
$fix = $phpcsFile->addFixableError($error, $arrayEnd, self::CODE_SPACE_BEFORE_ARRAY_CLOSE);
if (!$fix) {
return;
}
if ($spaceLength === 0) {
$phpcsFile->fixer->addContentBefore($arrayEnd, str_repeat(' ', $this->spacesAroundBrackets));
} else {
$phpcsFile->fixer->replaceToken($whitespacePointer, str_repeat(' ', $this->spacesAroundBrackets));
}
}
private function checkWhitespaceBeforeComma(File $phpcsFile, int $comma): void
{
$tokens = $phpcsFile->getTokens();
if ($tokens[$comma - 1]['code'] !== T_WHITESPACE) {
return;
}
if ($tokens[$comma - 2]['code'] === T_COMMA) {
return;
}
$error = sprintf(
'Expected 0 spaces between "%s" and comma, %d found.',
$tokens[$comma - 2]['content'],
$tokens[$comma - 1]['length']
);
$fix = $phpcsFile->addFixableError($error, $comma, self::CODE_SPACE_BEFORE_COMMA);
if (!$fix) {
return;
}
$phpcsFile->fixer->replaceToken($comma - 1, '');
}
private function checkWhitespaceAfterComma(File $phpcsFile, int $comma): void
{
$tokens = $phpcsFile->getTokens();
if ($tokens[$comma + 1]['code'] !== T_WHITESPACE) {
$error = sprintf('Expected 1 space between comma and "%s", 0 found.', $tokens[$comma + 1]['content']);
$fix = $phpcsFile->addFixableError($error, $comma, self::CODE_SPACE_AFTER_COMMA);
if ($fix) {
$phpcsFile->fixer->addContent($comma, ' ');
}
return;
}
$spaceLength = $tokens[$comma + 1]['length'];
if ($spaceLength === 1) {
return;
}
$error = sprintf('Expected 1 space between comma and "%s", %d found.', $tokens[$comma + 2]['content'], $spaceLength);
$fix = $phpcsFile->addFixableError($error, $comma, self::CODE_SPACE_AFTER_COMMA);
if (!$fix) {
return;
}
$phpcsFile->fixer->replaceToken($comma + 1, ' ');
}
}

View File

@@ -0,0 +1,80 @@
<?php declare(strict_types = 1);
namespace SlevomatCodingStandard\Sniffs\Arrays;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use SlevomatCodingStandard\Helpers\ArrayHelper;
use SlevomatCodingStandard\Helpers\SniffSettingsHelper;
use SlevomatCodingStandard\Helpers\TokenHelper;
use function in_array;
use const T_COMMA;
use const T_END_HEREDOC;
use const T_END_NOWDOC;
class TrailingArrayCommaSniff implements Sniff
{
public const CODE_MISSING_TRAILING_COMMA = 'MissingTrailingComma';
/** @var bool|null */
public $enableAfterHeredoc = null;
/**
* @return array<int, (int|string)>
*/
public function register(): array
{
return TokenHelper::$arrayTokenCodes;
}
/**
* @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint
* @param int $stackPointer
*/
public function process(File $phpcsFile, $stackPointer): void
{
$this->enableAfterHeredoc = SniffSettingsHelper::isEnabledByPhpVersion($this->enableAfterHeredoc, 70300);
$tokens = $phpcsFile->getTokens();
[$arrayOpenerPointer, $arrayCloserPointer] = ArrayHelper::openClosePointers($tokens[$stackPointer]);
if ($tokens[$arrayOpenerPointer]['line'] === $tokens[$arrayCloserPointer]['line']) {
return;
}
/** @var int $pointerPreviousToClose */
$pointerPreviousToClose = TokenHelper::findPreviousEffective($phpcsFile, $arrayCloserPointer - 1);
$tokenPreviousToClose = $tokens[$pointerPreviousToClose];
if (
$pointerPreviousToClose === $arrayOpenerPointer
|| $tokenPreviousToClose['code'] === T_COMMA
|| $tokens[$arrayCloserPointer]['line'] === $tokenPreviousToClose['line']
) {
return;
}
if (
!$this->enableAfterHeredoc
&& in_array($tokenPreviousToClose['code'], [T_END_HEREDOC, T_END_NOWDOC], true)
) {
return;
}
$fix = $phpcsFile->addFixableError(
'Multi-line arrays must have a trailing comma after the last element.',
$pointerPreviousToClose,
self::CODE_MISSING_TRAILING_COMMA
);
if (!$fix) {
return;
}
$phpcsFile->fixer->beginChangeset();
$phpcsFile->fixer->addContent($pointerPreviousToClose, ',');
$phpcsFile->fixer->endChangeset();
}
}