From 4c5ace491871218ce908851236bf9e87e790d4b7 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Sat, 23 Jul 2022 13:28:20 +0200 Subject: [PATCH 1/2] ComparisonOperatorUsage: add test with anonymous class .. to document that the sniff also handles comparisons passed in the instantiation of an anonymous class. --- .../Tests/Operators/ComparisonOperatorUsageUnitTest.inc | 7 +++++++ .../Tests/Operators/ComparisonOperatorUsageUnitTest.php | 1 + 2 files changed, 8 insertions(+) diff --git a/src/Standards/Squiz/Tests/Operators/ComparisonOperatorUsageUnitTest.inc b/src/Standards/Squiz/Tests/Operators/ComparisonOperatorUsageUnitTest.inc index 1e18526738..6bef5f4e5b 100644 --- a/src/Standards/Squiz/Tests/Operators/ComparisonOperatorUsageUnitTest.inc +++ b/src/Standards/Squiz/Tests/Operators/ComparisonOperatorUsageUnitTest.inc @@ -152,3 +152,10 @@ if (true) {} if (\true) {} for ($var1 = 10; FALSE; $var1--) {} for ($var1 = 10; \FALSE; $var1--) {} + +$anon = new class(!$foo ? 0 : 1, ($bar == true) ? 1 : 0) { + function __construct($a, $b) {} +}; +$anon = new class($foo === false ? 0 : 1, ($bar === true) ? 1 : 0) { + function __construct($a, $b) {} +}; diff --git a/src/Standards/Squiz/Tests/Operators/ComparisonOperatorUsageUnitTest.php b/src/Standards/Squiz/Tests/Operators/ComparisonOperatorUsageUnitTest.php index ca5864fdde..607d3d852a 100644 --- a/src/Standards/Squiz/Tests/Operators/ComparisonOperatorUsageUnitTest.php +++ b/src/Standards/Squiz/Tests/Operators/ComparisonOperatorUsageUnitTest.php @@ -63,6 +63,7 @@ public function getErrorList() 146 => 1, 147 => 1, 148 => 1, + 156 => 2, ]; }//end getErrorList() From 4700653b529ca98f9f56bf5ac599db07a6752ce6 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Sat, 23 Jul 2022 15:20:32 +0200 Subject: [PATCH 2/2] PEAR/PSR2/FunctionCallSignature: support anonymous classes The function call spacing for anonymous class instantiations was so far not checked by these or any other PHPCS native sniffs. In my opinion, object instantiations of anonymous classes should be treated the same an object instantiations of non-anonymous classes. The `PEAR.Functions.FunctionCallSignature` and the `PSR2.Methods.FunctionCallSignature` sniffs check the object instantiation spacing for non-anonymous classes, so seem like the logical place to also check the spacing for anonymous class object instantiations. To add this support, the `T_ANON_CLASS` token has been added to the `Tokens::$functionNameTokens` array. Notes: * As PSR12 does not specify the spacing between the `class` keyword and the open parenthesis (or rather is unclear about it), I am explicitly excluding anonymous classes from the "space before open parenthesis" check. Related: squizlabs/PHP_CodeSniffer 3200 * I have verified all other uses of the `Tokens::$functionNameTokens` array within PHPCS. - The `Generic.WhiteSpace.ArbitraryParenthesesSpacing` sniff is not affected by the change and already contains a test to verify this. - The `Squiz.Operators.ComparisonOperatorUsage` sniff also is not affected by the change. I have added tests to confirm this in a separate commit. * Obviously external standards using the token array _may_ be affected by the change, but a scan of the most popular external standards showed me that the token array is rarely used and when it is used, is mostly used incorrectly. The only sniff using the array, which looks to be using it correctly and which may be affected, is the `WebImpressCodingStandard.WhiteSpace.ScopeIndent` sniff. Whether this is positive or negative is up to michalbundyra to determine. Includes unit tests for both the `PEAR.Functions.FunctionCallSignature` and the `PSR2.Methods.FunctionCallSignature` sniffs . --- .../Functions/FunctionCallSignatureSniff.php | 4 +++- .../FunctionCallSignatureUnitTest.inc | 21 +++++++++++++++++ .../FunctionCallSignatureUnitTest.inc.fixed | 22 ++++++++++++++++++ .../FunctionCallSignatureUnitTest.php | 6 +++++ .../Methods/FunctionCallSignatureUnitTest.inc | 21 +++++++++++++++++ .../FunctionCallSignatureUnitTest.inc.fixed | 23 +++++++++++++++++++ .../Methods/FunctionCallSignatureUnitTest.php | 5 ++++ src/Util/Tokens.php | 17 +++++++------- 8 files changed, 110 insertions(+), 9 deletions(-) diff --git a/src/Standards/PEAR/Sniffs/Functions/FunctionCallSignatureSniff.php b/src/Standards/PEAR/Sniffs/Functions/FunctionCallSignatureSniff.php index 29655d1dd1..24fb56ac1c 100644 --- a/src/Standards/PEAR/Sniffs/Functions/FunctionCallSignatureSniff.php +++ b/src/Standards/PEAR/Sniffs/Functions/FunctionCallSignatureSniff.php @@ -110,7 +110,9 @@ public function process(File $phpcsFile, $stackPtr) $closeBracket = $tokens[$openBracket]['parenthesis_closer']; - if (($stackPtr + 1) !== $openBracket) { + if ($tokens[$stackPtr]['code'] !== T_ANON_CLASS + && ($stackPtr + 1) !== $openBracket + ) { // Checking this: $value = my_function[*](...). $error = 'Space before opening parenthesis of function call prohibited'; $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceBeforeOpenBracket'); diff --git a/src/Standards/PEAR/Tests/Functions/FunctionCallSignatureUnitTest.inc b/src/Standards/PEAR/Tests/Functions/FunctionCallSignatureUnitTest.inc index d73725bfc6..3006081105 100644 --- a/src/Standards/PEAR/Tests/Functions/FunctionCallSignatureUnitTest.inc +++ b/src/Standards/PEAR/Tests/Functions/FunctionCallSignatureUnitTest.inc @@ -588,3 +588,24 @@ $val = namespace\functionCall( $arg, $arg2 ); + +// Anonymous object instantiations are treated the same as a normal call. +$anon = new class() {}; +$anon = new class($foo, true) {}; +$anon = new class( + $foo, + true, + 10 +) {}; + +$anon = new class( ) {}; +$anon = new class( $foo, true ) {}; +$anon = new class($foo, + + true, 10) {}; + +// ... though do not enforce no space between the class keyword and the open parenthesis. +$anon = new class () {}; + +// And anonymous object instantiations without parentheses are ignored. +$anon = new class {}; diff --git a/src/Standards/PEAR/Tests/Functions/FunctionCallSignatureUnitTest.inc.fixed b/src/Standards/PEAR/Tests/Functions/FunctionCallSignatureUnitTest.inc.fixed index d5cc0136fe..ca29fc1a65 100644 --- a/src/Standards/PEAR/Tests/Functions/FunctionCallSignatureUnitTest.inc.fixed +++ b/src/Standards/PEAR/Tests/Functions/FunctionCallSignatureUnitTest.inc.fixed @@ -606,3 +606,25 @@ $val = namespace\functionCall( $arg, $arg2 ); + +// Anonymous object instantiations are treated the same as a normal call. +$anon = new class() {}; +$anon = new class($foo, true) {}; +$anon = new class( + $foo, + true, + 10 +) {}; + +$anon = new class() {}; +$anon = new class($foo, true) {}; +$anon = new class( + $foo, + true, 10 +) {}; + +// ... though do not enforce no space between the class keyword and the open parenthesis. +$anon = new class () {}; + +// And anonymous object instantiations without parentheses are ignored. +$anon = new class {}; diff --git a/src/Standards/PEAR/Tests/Functions/FunctionCallSignatureUnitTest.php b/src/Standards/PEAR/Tests/Functions/FunctionCallSignatureUnitTest.php index 4f36d9d1db..2def5fe808 100644 --- a/src/Standards/PEAR/Tests/Functions/FunctionCallSignatureUnitTest.php +++ b/src/Standards/PEAR/Tests/Functions/FunctionCallSignatureUnitTest.php @@ -122,6 +122,12 @@ public function getErrorList() 583 => 2, 584 => 1, 586 => 2, + + 601 => 2, + 602 => 2, + 603 => 1, + 604 => 1, + 605 => 2, ]; }//end getErrorList() diff --git a/src/Standards/PSR2/Tests/Methods/FunctionCallSignatureUnitTest.inc b/src/Standards/PSR2/Tests/Methods/FunctionCallSignatureUnitTest.inc index 1ca477d054..57ed98c301 100644 --- a/src/Standards/PSR2/Tests/Methods/FunctionCallSignatureUnitTest.inc +++ b/src/Standards/PSR2/Tests/Methods/FunctionCallSignatureUnitTest.inc @@ -265,3 +265,24 @@ array_fill_keys( ), value: true, ); // phpcs:set PSR2.Methods.FunctionCallSignature allowMultipleArguments false + +// Anonymous object instantiations are treated the same as a normal call. +$anon = new class() {}; +$anon = new class($foo, true) {}; +$anon = new class( + $foo, + true, + 10 +) {}; + +$anon = new class( ) {}; +$anon = new class( $foo, true ) {}; +$anon = new class($foo, + + true, 10) {}; + +// ... though do not enforce no space between the class keyword and the open parenthesis. +$anon = new class () {}; + +// And anonymous object instantiations without parentheses are ignored. +$anon = new class {}; diff --git a/src/Standards/PSR2/Tests/Methods/FunctionCallSignatureUnitTest.inc.fixed b/src/Standards/PSR2/Tests/Methods/FunctionCallSignatureUnitTest.inc.fixed index dc383ed2a7..ef4630874b 100644 --- a/src/Standards/PSR2/Tests/Methods/FunctionCallSignatureUnitTest.inc.fixed +++ b/src/Standards/PSR2/Tests/Methods/FunctionCallSignatureUnitTest.inc.fixed @@ -281,3 +281,26 @@ array_fill_keys( ), value: true, ); // phpcs:set PSR2.Methods.FunctionCallSignature allowMultipleArguments false + +// Anonymous object instantiations are treated the same as a normal call. +$anon = new class() {}; +$anon = new class($foo, true) {}; +$anon = new class( + $foo, + true, + 10 +) {}; + +$anon = new class() {}; +$anon = new class($foo, true) {}; +$anon = new class( + $foo, + true, + 10 +) {}; + +// ... though do not enforce no space between the class keyword and the open parenthesis. +$anon = new class () {}; + +// And anonymous object instantiations without parentheses are ignored. +$anon = new class {}; diff --git a/src/Standards/PSR2/Tests/Methods/FunctionCallSignatureUnitTest.php b/src/Standards/PSR2/Tests/Methods/FunctionCallSignatureUnitTest.php index 6799cad080..40e082047b 100644 --- a/src/Standards/PSR2/Tests/Methods/FunctionCallSignatureUnitTest.php +++ b/src/Standards/PSR2/Tests/Methods/FunctionCallSignatureUnitTest.php @@ -76,6 +76,11 @@ public function getErrorList() 258 => 1, 263 => 1, 264 => 1, + 278 => 2, + 279 => 2, + 280 => 1, + 281 => 1, + 282 => 3, ]; }//end getErrorList() diff --git a/src/Util/Tokens.php b/src/Util/Tokens.php index 408ba2ac52..7a525d4452 100644 --- a/src/Util/Tokens.php +++ b/src/Util/Tokens.php @@ -464,14 +464,15 @@ final class Tokens * @var array */ public const FUNCTION_NAME_TOKENS = (self::INCLUDE_TOKENS + self::NAME_TOKENS + [ - T_EVAL => T_EVAL, - T_EXIT => T_EXIT, - T_ISSET => T_ISSET, - T_UNSET => T_UNSET, - T_EMPTY => T_EMPTY, - T_SELF => T_SELF, - T_PARENT => T_PARENT, - T_STATIC => T_STATIC, + T_EVAL => T_EVAL, + T_EXIT => T_EXIT, + T_ISSET => T_ISSET, + T_UNSET => T_UNSET, + T_EMPTY => T_EMPTY, + T_SELF => T_SELF, + T_PARENT => T_PARENT, + T_STATIC => T_STATIC, + T_ANON_CLASS => T_ANON_CLASS, ]); /**