Skip to content

Commit

Permalink
PSR2/ClassDeclaration: bug fix - space before class keyword is not ch…
Browse files Browse the repository at this point in the history
…ecked correctly

* If there would be a newline + indentation between a modifier keyword and the "class" keyword, the space between them would not be flagged as incorrect (should be one space).
* Along the same lines, if there would be a comment between the modifier keyword and the "class" keyword, the space between them would not be checked, let alone flagged.

Fixed now.

Includes additional tests.
  • Loading branch information
jrfnl committed Jan 31, 2024
1 parent 2fbf9e3 commit f548efa
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 33 deletions.
75 changes: 42 additions & 33 deletions src/Standards/PSR2/Sniffs/Classes/ClassDeclarationSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,42 +65,51 @@ public function processOpen(File $phpcsFile, $stackPtr)
$stackPtrType = strtolower($tokens[$stackPtr]['content']);

// Check alignment of the keyword and braces.
if ($tokens[($stackPtr - 1)]['code'] === T_WHITESPACE) {
$prevContent = $tokens[($stackPtr - 1)]['content'];
if ($prevContent !== $phpcsFile->eolChar) {
$blankSpace = substr($prevContent, strpos($prevContent, $phpcsFile->eolChar));
$spaces = strlen($blankSpace);

if (in_array($tokens[($stackPtr - 2)]['code'], [T_ABSTRACT, T_FINAL, T_READONLY], true) === true
&& $spaces !== 1
) {
$prevContent = strtolower($tokens[($stackPtr - 2)]['content']);
$error = 'Expected 1 space between %s and %s keywords; %s found';
$data = [
$prevContent,
$stackPtrType,
$spaces,
];

$fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceBeforeKeyword', $data);
if ($fix === true) {
$phpcsFile->fixer->replaceToken(($stackPtr - 1), ' ');
}
}
} else if ($tokens[($stackPtr - 2)]['code'] === T_ABSTRACT
|| $tokens[($stackPtr - 2)]['code'] === T_FINAL
|| $tokens[($stackPtr - 2)]['code'] === T_READONLY
) {
$prevContent = strtolower($tokens[($stackPtr - 2)]['content']);
$error = 'Expected 1 space between %s and %s keywords; newline found';
$data = [
$prevContent,
$classModifiers = [
T_ABSTRACT => T_ABSTRACT,
T_FINAL => T_FINAL,
T_READONLY => T_READONLY,
];

$prevNonSpace = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);

if (isset($classModifiers[$tokens[$prevNonEmpty]['code']]) === true) {
$spaces = 0;
$errorCode = 'SpaceBeforeKeyword';
if ($tokens[$prevNonEmpty]['line'] !== $tokens[$stackPtr]['line']) {
$spaces = 'newline';
$errorCode = 'NewlineBeforeKeyword';
} else if ($tokens[($stackPtr - 1)]['code'] === T_WHITESPACE) {
$spaces = $tokens[($stackPtr - 1)]['length'];
}

if ($spaces !== 1) {
$error = 'Expected 1 space between %s and %s keywords; %s found';
$data = [
strtolower($tokens[$prevNonEmpty]['content']),
$stackPtrType,
$spaces,
];

$fix = $phpcsFile->addFixableError($error, $stackPtr, 'NewlineBeforeKeyword', $data);
if ($fix === true) {
$phpcsFile->fixer->replaceToken(($stackPtr - 1), ' ');
if ($prevNonSpace !== $prevNonEmpty) {
// Comment found between modifier and class keyword. Do not auto-fix.
$phpcsFile->addError($error, $stackPtr, $errorCode, $data);
} else {
$fix = $phpcsFile->addFixableError($error, $stackPtr, $errorCode, $data);
if ($fix === true) {
if ($spaces === 0) {
$phpcsFile->fixer->addContentBefore($stackPtr, ' ');
} else {
$phpcsFile->fixer->beginChangeset();
$phpcsFile->fixer->replaceToken(($stackPtr - 1), ' ');
for ($i = ($stackPtr - 2); $i > $prevNonSpace; $i--) {
$phpcsFile->fixer->replaceToken($i, ' ');
}

$phpcsFile->fixer->endChangeset();
}
}
}
}//end if
}//end if
Expand Down
25 changes: 25 additions & 0 deletions src/Standards/PSR2/Tests/Classes/ClassDeclarationUnitTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -257,3 +257,28 @@ if (!class_exists('IndentedDeclaration')) {

}
}

// Space between modifier and class keyword would not be flagged nor fixed if newline + indentation.
final
class FinalClassWithIndentation
{
}

readonly
class ReadonlyClassWithIndentation
{
}

// And would also not be flagged if there was a comment between (not auto-fixable).
final/*comment*/class FinalClassWithComment
{
}
abstract /*comment*/ class AbstractClassWithComment
{
}

readonly
// comment
class ReadonlyClassWithComment
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -247,3 +247,26 @@ if (!class_exists('IndentedDeclaration')) {
function foo() {}
}
}

// Space between modifier and class keyword would not be flagged nor fixed if newline + indentation.
final class FinalClassWithIndentation
{
}

readonly class ReadonlyClassWithIndentation
{
}

// And would also not be flagged if there was a comment between (not auto-fixable).
final/*comment*/class FinalClassWithComment
{
}
abstract /*comment*/ class AbstractClassWithComment
{
}

readonly
// comment
class ReadonlyClassWithComment
{
}
5 changes: 5 additions & 0 deletions src/Standards/PSR2/Tests/Classes/ClassDeclarationUnitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ public function getErrorList()
244 => 1,
248 => 1,
258 => 1,
263 => 1,
268 => 1,
273 => 1,
276 => 1,
282 => 1,
];

}//end getErrorList()
Expand Down

0 comments on commit f548efa

Please sign in to comment.