-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add new sniff to ban the use of compact() function #3795
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've done a cursory review of the sniff without an opinion on whether it should be included in PHPCS.
I would note that I'm not sure it's a good idea to include a fixer in this sniff unless you are certain that every possible code situation has been covered. As things are, I would consider this a pretty risky fixer.
Not sure if this should be added to
package.xml
?
Yes, all files need to be listed in package.xml
.
And I'm also not sure if
Arrays
is good namespace for it.
See my suggestion inline.
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence | ||
*/ | ||
|
||
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Arrays; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think Arrays
is the correct category as that only contains sniffs which are looking for array syntax.
I'd suggest using the PHP
category.
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true); | ||
|
||
// Make sure its not method of an object or class. | ||
if ($tokens[$prev]['code'] === T_OBJECT_OPERATOR || $tokens[$prev]['code'] === T_DOUBLE_COLON) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this needs fixing to allow for modern PHP:
$obj?->compact('a');
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, thanks. I've added that, plus few other things.
I'm now questioning if I should look back for things that indicates it's not actually builtin function call, or maybe should change approach and list things that indicates it is function call.
So what I mean by that is if its better to check (as it is now) :
if ($tokens[$prev]['code'] === T_OBJECT_OPERATOR ... ) {
return;
}
Or maybe would be better something like:
if ($tokens[$prev]['code'] !== T_EQUAL ...) {
return;
}
It might be more future proof if changed, but not sure if I wont end up with long list of allowed tokens.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my experience the current approach of bowing out of specific tokens is the correct one.
Changing the logic around would make the sniff much less stable as new tokens get added regularly (either via PHP itself or in PHPCS to account for changes in PHP) and this sniff would need to be updated each time that happens.
// Not a builtin function call. | ||
return; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about namespaced functions ?
MyNamespace\compact('a');
namespace\compact('a');
Also what about these ?
class Foo {
public function compact( $param = 'a' ) {}
public function &compact( $param = 'a' ) {}
}
$obj = new Compact('a');
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added them to the test case, they should not produce sniff errors.
I've also added \compact()
as this is valid function call and should produce error.
$toExpand = []; | ||
$openPtr = $next; | ||
$closePtr = null; | ||
$validVarNameRegExp = '/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$/'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe declare this regex as a class constant ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did that, thanks for suggestion.
)); | ||
|
||
$var = compact('aa', 'invalid-var.name'); | ||
$var = Bazz::compact('a', 'b'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see quite some code in this test case file which doesn't seem to be testing anything in the sniff... ?
Here are some more test cases to take into account:
compact(...$names);
compact( 'prefix' . $name, '$name' . 'suffix', "some$name");
compact(...get_names('category1', 'category2'));
// Last test in the file.
// Live coding/parse error.
compact( 'a', 'b'
See this as an invitation to get a little more creative with the tests and to make sure the sniff handles those situations correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most of these were already covered as sniff requires only strings and commas within parenthesis. I've added them to test case. Last one was actually causing error so I fixed that as well.
// This is not a merge conflict - it is a valid test case to test handling of | ||
// compact function call without associated closer. | ||
// Please do not remove. | ||
function test() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure why this test is included ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough, will remove. (I've copied some testes from different sniff as I started to work on it).
|
||
$content = $tokens[$stackPtr]['content']; | ||
|
||
if ($content !== 'compact') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest adding tests with the below (function names in PHP are case-INsensitive):
COMPACT('a');
Compact('a');
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added and fixed.
Thank you for review, will add more tests for cases you listed and check code suggestions. As for the fixer, it is checking if there is:
If any of the above is not true then error would not be fixable. |
a48958f
to
02aadff
Compare
So that means that only plain strings would be taken into account, while per the documentation of
Source: https://www.php.net/manual/en/function.compact.php#refsect1-function.compact-parameters |
For fixing it yes. It will still be reported if its compact containing an array, but it would not be fixed automatically. My reasoning was that simpler fixer the better, and most cases in the wild I've seen were just using simple string arguments. Otherwise would have to check for arrays (short and long ones) and array values (that may have keys specified). And also nested arrays. For example this: Do you think its wrong way of looking at it? |
No, I concur that is probably the sensible way of doing it to prevent the fixer from getting overly complicated/making incorrect fixes. |
Adds new generic sniffer for banning usage of
compact()
function.Using this function often is not playing nice with static analysers and LSPs and makes refactoring harder.
I've added sniffer to detect if
compact()
is used to create array. If function call contains only strings that could be valid variable names then it can be replace automatically by array usecompact('a')
will be fixed as['a' => $a]
.Not sure if this should be added to
package.xml
? And I'm also not sure ifArrays
is good namespace for it. It is function after all, but it is just used to build array. I'm happy to change whatever is needed.