From 6213153c2079407d9d81234887acfc0de3052a47 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Thu, 30 Mar 2023 14:21:44 +0200 Subject: [PATCH] PEAR/Squiz/[MultiLine]FunctionDeclaration: allow for PHP 8.1 new in initializers Since PHP 8.1, `new ClassName()` expressions can be used as the default value of a parameter in a function declaration. `new ClassName()` expressions should be treated as function _calls_, which have their own indentation rules, so the `PEAR.Functions.FunctionDeclaration` and the `Squiz.Functions.MultiLineFunctionDeclaration` sniffs should skip over these and not attempt to change the indentation of parameters in those function calls. This commit implements this change. Includes unit tests. Refs: * https://wiki.php.net/rfc/new_in_initializers * https://www.php.net/manual/en/migration81.new-features.php#migration81.new-features.core.new-in-initializer * https://www.php.net/manual/en/language.oop5.decon.php#language.oop5.decon.constructor.new Fixes 3786 --- .../Functions/FunctionDeclarationSniff.php | 13 +++++ .../Functions/FunctionDeclarationUnitTest.inc | 47 +++++++++++++++++++ .../FunctionDeclarationUnitTest.inc.fixed | 47 +++++++++++++++++++ .../MultiLineFunctionDeclarationUnitTest.inc | 47 +++++++++++++++++++ ...iLineFunctionDeclarationUnitTest.inc.fixed | 47 +++++++++++++++++++ 5 files changed, 201 insertions(+) diff --git a/src/Standards/PEAR/Sniffs/Functions/FunctionDeclarationSniff.php b/src/Standards/PEAR/Sniffs/Functions/FunctionDeclarationSniff.php index bd59a7cd90..14479e2c0a 100644 --- a/src/Standards/PEAR/Sniffs/Functions/FunctionDeclarationSniff.php +++ b/src/Standards/PEAR/Sniffs/Functions/FunctionDeclarationSniff.php @@ -496,6 +496,19 @@ public function processArgumentList($phpcsFile, $stackPtr, $indent, $type='funct $lastLine = $tokens[$i]['line']; }//end if + if ($tokens[$i]['code'] === T_OPEN_PARENTHESIS + && isset($tokens[$i]['parenthesis_closer']) === true + ) { + $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($i - 1), null, true); + if ($tokens[$prevNonEmpty]['code'] !== T_USE) { + // Since PHP 8.1, a default value can contain a class instantiation. + // Skip over these "function calls" as they have their own indentation rules. + $i = $tokens[$i]['parenthesis_closer']; + $lastLine = $tokens[$i]['line']; + continue; + } + } + if ($tokens[$i]['code'] === T_ARRAY || $tokens[$i]['code'] === T_OPEN_SHORT_ARRAY) { // Skip arrays as they have their own indentation rules. if ($tokens[$i]['code'] === T_OPEN_SHORT_ARRAY) { diff --git a/src/Standards/PEAR/Tests/Functions/FunctionDeclarationUnitTest.inc b/src/Standards/PEAR/Tests/Functions/FunctionDeclarationUnitTest.inc index 02e0a20dbf..fb7cb52f4d 100644 --- a/src/Standards/PEAR/Tests/Functions/FunctionDeclarationUnitTest.inc +++ b/src/Standards/PEAR/Tests/Functions/FunctionDeclarationUnitTest.inc @@ -418,3 +418,50 @@ class ConstructorPropertyPromotionMultiLineAttributesIncorrectIndent // Do something. } } + +// PHP 8.1: new in initializers means that class instantiations with parameters can occur in a function declaration. +function usingNewInInitializersCallParamsIndented( + int $paramA, + string $paramB, + object $paramC = new SomeClass( + new InjectedDependencyA(), + new InjectedDependencyB + ) +) {} + +function usingNewInInitializersCallParamsNotIndented( + int $paramA, + string $paramB, + object $paramC = new SomeClass( + new InjectedDependencyA, + new InjectedDependencyB() + ) +) {} + +function usingNewInInitializersCallParamsIncorrectlyIndentedShouldNotBeFlaggedNorFixed( + int $paramA, + string $paramB, + object $paramC = new SomeClass( +new InjectedDependencyA(), new InjectedDependencyB() +) +) {} + +class UsingNewInInitializers { + public function doSomething( + object $paramA, + stdClass $paramB = new stdClass(), + Exception $paramC = new Exception( + new ExceptionMessage(), + new ExceptionCode(), + ), + ) { + } + + public function callParamsIncorrectlyIndentedShouldNotBeFlaggedNorFixed( + Exception $param = new Exception( +new ExceptionMessage(), + new ExceptionCode(), + ), + ) { + } +} diff --git a/src/Standards/PEAR/Tests/Functions/FunctionDeclarationUnitTest.inc.fixed b/src/Standards/PEAR/Tests/Functions/FunctionDeclarationUnitTest.inc.fixed index 0d67e9f758..f82b5665a5 100644 --- a/src/Standards/PEAR/Tests/Functions/FunctionDeclarationUnitTest.inc.fixed +++ b/src/Standards/PEAR/Tests/Functions/FunctionDeclarationUnitTest.inc.fixed @@ -416,3 +416,50 @@ class ConstructorPropertyPromotionMultiLineAttributesIncorrectIndent // Do something. } } + +// PHP 8.1: new in initializers means that class instantiations with parameters can occur in a function declaration. +function usingNewInInitializersCallParamsIndented( + int $paramA, + string $paramB, + object $paramC = new SomeClass( + new InjectedDependencyA(), + new InjectedDependencyB + ) +) {} + +function usingNewInInitializersCallParamsNotIndented( + int $paramA, + string $paramB, + object $paramC = new SomeClass( + new InjectedDependencyA, + new InjectedDependencyB() + ) +) {} + +function usingNewInInitializersCallParamsIncorrectlyIndentedShouldNotBeFlaggedNorFixed( + int $paramA, + string $paramB, + object $paramC = new SomeClass( +new InjectedDependencyA(), new InjectedDependencyB() +) +) {} + +class UsingNewInInitializers { + public function doSomething( + object $paramA, + stdClass $paramB = new stdClass(), + Exception $paramC = new Exception( + new ExceptionMessage(), + new ExceptionCode(), + ), + ) { + } + + public function callParamsIncorrectlyIndentedShouldNotBeFlaggedNorFixed( + Exception $param = new Exception( +new ExceptionMessage(), + new ExceptionCode(), + ), + ) { + } +} diff --git a/src/Standards/Squiz/Tests/Functions/MultiLineFunctionDeclarationUnitTest.inc b/src/Standards/Squiz/Tests/Functions/MultiLineFunctionDeclarationUnitTest.inc index fce0b237ba..811c56ec14 100644 --- a/src/Standards/Squiz/Tests/Functions/MultiLineFunctionDeclarationUnitTest.inc +++ b/src/Standards/Squiz/Tests/Functions/MultiLineFunctionDeclarationUnitTest.inc @@ -255,3 +255,50 @@ private string $private, ) { } } + +// PHP 8.1: new in initializers means that class instantiations with parameters can occur in a function declaration. +function usingNewInInitializersCallParamsIndented( + int $paramA, + string $paramB, + object $paramC = new SomeClass( + new InjectedDependencyA(), + new InjectedDependencyB + ) +) {} + +function usingNewInInitializersCallParamsNotIndented( + int $paramA, + string $paramB, + object $paramC = new SomeClass( + new InjectedDependencyA, + new InjectedDependencyB() + ) +) {} + +function usingNewInInitializersCallParamsIncorrectlyIndentedShouldNotBeFlaggedNorFixed( + int $paramA, + string $paramB, + object $paramC = new SomeClass( +new InjectedDependencyA(), new InjectedDependencyB() +) +) {} + +class UsingNewInInitializers { + public function doSomething( + object $paramA, + stdClass $paramB = new stdClass(), + Exception $paramC = new Exception( + new ExceptionMessage(), + new ExceptionCode(), + ), + ) { + } + + public function callParamsIncorrectlyIndentedShouldNotBeFlaggedNorFixed( + Exception $param = new Exception( +new ExceptionMessage(), + new ExceptionCode(), + ), + ) { + } +} diff --git a/src/Standards/Squiz/Tests/Functions/MultiLineFunctionDeclarationUnitTest.inc.fixed b/src/Standards/Squiz/Tests/Functions/MultiLineFunctionDeclarationUnitTest.inc.fixed index b927a001b3..c38e3ecc0a 100644 --- a/src/Standards/Squiz/Tests/Functions/MultiLineFunctionDeclarationUnitTest.inc.fixed +++ b/src/Standards/Squiz/Tests/Functions/MultiLineFunctionDeclarationUnitTest.inc.fixed @@ -267,3 +267,50 @@ class ConstructorPropertyPromotionMultiLineDocblockAndAttributeIncorrectIndent ) { } } + +// PHP 8.1: new in initializers means that class instantiations with parameters can occur in a function declaration. +function usingNewInInitializersCallParamsIndented( + int $paramA, + string $paramB, + object $paramC = new SomeClass( + new InjectedDependencyA(), + new InjectedDependencyB + ) +) {} + +function usingNewInInitializersCallParamsNotIndented( + int $paramA, + string $paramB, + object $paramC = new SomeClass( + new InjectedDependencyA, + new InjectedDependencyB() + ) +) {} + +function usingNewInInitializersCallParamsIncorrectlyIndentedShouldNotBeFlaggedNorFixed( + int $paramA, + string $paramB, + object $paramC = new SomeClass( +new InjectedDependencyA(), new InjectedDependencyB() +) +) {} + +class UsingNewInInitializers { + public function doSomething( + object $paramA, + stdClass $paramB = new stdClass(), + Exception $paramC = new Exception( + new ExceptionMessage(), + new ExceptionCode(), + ), + ) { + } + + public function callParamsIncorrectlyIndentedShouldNotBeFlaggedNorFixed( + Exception $param = new Exception( +new ExceptionMessage(), + new ExceptionCode(), + ), + ) { + } +}