229 lines
6.7 KiB
PHP
229 lines
6.7 KiB
PHP
<?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, ' ');
|
|
}
|
|
|
|
}
|