I am attempting to write a little simple phpcs (phpcbf) sniff to remove whitespace within empty curly braces. I’ve written quite a few small fixers, and this seemed like it should be very easy.
Basically with the move to promoted parameters, we’re ending up with various whitespace setups inside the empty curly braces following the construct like
public function __construct( protected string $cdnPath ) {
}
public function __construct( protected string $cdnPath ) { }
public function __construct( protected string $cdnPath ) {}
I just want to unify these on {}
The sniff as I have it so far
<?php
use PHP_CodeSnifferFilesFile;
use PHP_CodeSnifferSniffsSniff;
class CurlyBraceInnerWhiteSpaceSniff implements Sniff {
public function register() : array {
return [
T_OPEN_CURLY_BRACKET,
];
}
/**
* @param int $stackPtr
*/
public function process( File $phpcsFile, $stackPtr ) : void {
$tokens = $phpcsFile->getTokens();
if( $tokens[$stackPtr + 1]['code'] !== T_WHITESPACE ) {
return;
}
$closePtr = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, null, true);
if( $tokens[$closePtr]['code'] !== T_CLOSE_CURLY_BRACKET ) {
return;
}
$fix = $phpcsFile->addFixableError('There must be no whitespace inside empty curly braces', $stackPtr + 1, 'CurlyBraceInnerWhiteSpace');
if( $fix ) {
$phpcsFile->fixer->beginChangeset();
for( $i = $stackPtr + 1; $i < $closePtr; $i++ ) {
$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->endChangeset();
}
}
}
It takes a VERY long time before giving up
running phpcbf
with the -v
reveals many many entries like this with [made 50 passes]... ERROR
Processing TransformCommand.php [PHP => 636 tokens in 89 lines]... DONE in 11ms (1 fixable violations)
=> Fixing file: 1/1 violations remaining [made 50 passes]... ERROR in 585ms
I found in the documentation somewhere that adding return $phpcsFile->numTokens;
to process
could help, by preventing the sniff from running against the same file twice, but it has not seemed to help at all.
3
Answers
The problem was that I had another sniff from another collection fighting my sniff.
It wants to replace
{}
with{ }
. Having the two sniffs fighting caused the back and forth which is why it would try 50 times before giving up.The
SpacesInsideParenthesesFixer
could be converted into aSpacesInsideCurlyBracketsFixer
.Not exactly sure how it would behave with new-line, but there’s also example patterns for that.
Your implementation doesn’t catch empty lines or Allman-style bracing. I’d start by using the
Generic.Functions.OpeningFunctionBraceKernighanRitchie
sniff to make sure your open brace is on the same line. Then, note that the tokens array contains the scope level and the position of the matching closing brace:So your sniff would basically become, "if this is a class method (i.e., level 1) and the only thing between the braces is whitespace, raise an error."
You might also want to apply this only to class methods named
__construct
. You could find the start of the function declaration with$tokens[$stackPtr]['scope_condition']
and then work forward to find the name.Before:
Command:
After: