diff --git a/doc/list.rst b/doc/list.rst index 44ce877a489..3080f747f6d 100644 --- a/doc/list.rst +++ b/doc/list.rst @@ -196,6 +196,10 @@ List of Available Rules | Whether there should be a single space after the parenthesis of anonymous class (PSR12) or not. | Allowed types: ``bool`` | Default value: ``false`` + - | ``inline_constructor_arguments`` + | Whether constructor argument list in anonymous classes should be single line. + | Allowed types: ``bool`` + | Default value: ``true`` Part of rule sets `@PSR12 <./ruleSets/PSR12.rst>`_ `@PSR2 <./ruleSets/PSR2.rst>`_ `@PhpCsFixer <./ruleSets/PhpCsFixer.rst>`_ `@Symfony <./ruleSets/Symfony.rst>`_ diff --git a/doc/ruleSets/PSR12.rst b/doc/ruleSets/PSR12.rst index 2595a1b0299..a8a6722c2ef 100644 --- a/doc/ruleSets/PSR12.rst +++ b/doc/ruleSets/PSR12.rst @@ -14,7 +14,7 @@ Rules ``['allow_single_line_anonymous_class_with_empty_body' => true]`` - `class_definition <./../rules/class_notation/class_definition.rst>`_ config: - ``['space_before_parenthesis' => true]`` + ``['inline_constructor_arguments' => false, 'space_before_parenthesis' => true]`` - `compact_nullable_typehint <./../rules/whitespace/compact_nullable_typehint.rst>`_ - `declare_equal_normalize <./../rules/language_construct/declare_equal_normalize.rst>`_ - `lowercase_cast <./../rules/cast_notation/lowercase_cast.rst>`_ diff --git a/doc/rules/class_notation/class_definition.rst b/doc/rules/class_notation/class_definition.rst index 58dba01f6b2..cbf0d0ababe 100644 --- a/doc/rules/class_notation/class_definition.rst +++ b/doc/rules/class_notation/class_definition.rst @@ -45,6 +45,15 @@ Allowed types: ``bool`` Default value: ``false`` +``inline_constructor_arguments`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Whether constructor argument list in anonymous classes should be single line. + +Allowed types: ``bool`` + +Default value: ``true`` + Examples -------- @@ -142,6 +151,22 @@ With configuration: ``['space_before_parenthesis' => true]``. -$foo = new class(){}; +$foo = new class () {}; +Example #6 +~~~~~~~~~~ + +With configuration: ``['inline_constructor_arguments' => true]``. + +.. code-block:: diff + + --- Original + +++ New + `_ rule set will enable the ``class_definition`` rule with the config below: - ``['space_before_parenthesis' => true]`` + ``['inline_constructor_arguments' => false, 'space_before_parenthesis' => true]`` @PSR2 Using the `@PSR2 <./../../ruleSets/PSR2.rst>`_ rule set will enable the ``class_definition`` rule with the default config. diff --git a/src/Fixer/ClassNotation/ClassDefinitionFixer.php b/src/Fixer/ClassNotation/ClassDefinitionFixer.php index 6ff89b85df1..9b1394b4128 100644 --- a/src/Fixer/ClassNotation/ClassDefinitionFixer.php +++ b/src/Fixer/ClassNotation/ClassDefinitionFixer.php @@ -93,6 +93,10 @@ interface Bar extends ', ['space_before_parenthesis' => true] ), + new CodeSample( + " true] + ), ] ); } @@ -151,6 +155,10 @@ protected function createConfigurationDefinition(): FixerConfigurationResolverIn ->setAllowedTypes(['bool']) ->setDefault(false) ->getOption(), + (new FixerOptionBuilder('inline_constructor_arguments', 'Whether constructor argument list in anonymous classes should be single line.')) + ->setAllowedTypes(['bool']) + ->setDefault(true) + ->getOption(), ]); } @@ -193,6 +201,18 @@ private function fixClassyDefinition(Tokens $tokens, int $classyIndex): void $end = $tokens->getPrevNonWhitespace($classDefInfo['open']); } + if ($classDefInfo['anonymousClass'] && !$this->configuration['inline_constructor_arguments']) { + if (!$tokens[$end]->equals(')')) { // anonymous class with `extends` and/or `implements` + $start = $tokens->getPrevMeaningfulToken($end); + $this->makeClassyDefinitionSingleLine($tokens, $start, $end); + $end = $start; + } + + if ($tokens[$end]->equals(')')) { // skip constructor arguments of anonymous class + $end = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $end); + } + } + // 4.1 The extends and implements keywords MUST be declared on the same line as the class name. $this->makeClassyDefinitionSingleLine($tokens, $classDefInfo['start'], $end); } diff --git a/src/Fixer/FunctionNotation/MethodArgumentSpaceFixer.php b/src/Fixer/FunctionNotation/MethodArgumentSpaceFixer.php index d09ebf4255b..2579e046e5c 100644 --- a/src/Fixer/FunctionNotation/MethodArgumentSpaceFixer.php +++ b/src/Fixer/FunctionNotation/MethodArgumentSpaceFixer.php @@ -135,7 +135,7 @@ public function getPriority(): int */ protected function applyFix(\SplFileInfo $file, Tokens $tokens): void { - $expectedTokens = [T_LIST, T_FUNCTION, CT::T_USE_LAMBDA, T_FN]; + $expectedTokens = [T_LIST, T_FUNCTION, CT::T_USE_LAMBDA, T_FN, T_CLASS]; for ($index = $tokens->count() - 1; $index > 0; --$index) { $token = $tokens[$index]; diff --git a/src/RuleSet/Sets/PSR12Set.php b/src/RuleSet/Sets/PSR12Set.php index 9a8e4c1bf1e..dd04f4c1e3c 100644 --- a/src/RuleSet/Sets/PSR12Set.php +++ b/src/RuleSet/Sets/PSR12Set.php @@ -29,7 +29,10 @@ public function getRules(): array 'braces' => [ 'allow_single_line_anonymous_class_with_empty_body' => true, ], - 'class_definition' => ['space_before_parenthesis' => true], // defined in PSR12 ¶8. Anonymous Classes + 'class_definition' => [ + 'inline_constructor_arguments' => false, // handled by method_argument_space fixer + 'space_before_parenthesis' => true, // defined in PSR12 ¶8. Anonymous Classes + ], 'compact_nullable_typehint' => true, 'declare_equal_normalize' => true, 'lowercase_cast' => true, diff --git a/tests/Fixer/ClassNotation/ClassDefinitionFixerTest.php b/tests/Fixer/ClassNotation/ClassDefinitionFixerTest.php index 8f8db669a92..035c8ff465b 100644 --- a/tests/Fixer/ClassNotation/ClassDefinitionFixerTest.php +++ b/tests/Fixer/ClassNotation/ClassDefinitionFixerTest.php @@ -34,6 +34,7 @@ public function testConfigureDefaultToFalse(): void 'single_item_single_line' => false, 'single_line' => false, 'space_before_parenthesis' => false, + 'inline_constructor_arguments' => true, ]; $fixer = new ClassDefinitionFixer(); @@ -97,7 +98,7 @@ public function testInvalidConfigurationKey(): void { $this->expectException(InvalidFixerConfigurationException::class); $this->expectExceptionMessageMatches( - '/^\[class_definition\] Invalid configuration: The option "a" does not exist\. Defined options are: "multi_line_extends_each_single_line", "single_item_single_line", "single_line", "space_before_parenthesis"\.$/' + '/^\[class_definition\] Invalid configuration: The option "a" does not exist\. Defined options are: "inline_constructor_arguments", "multi_line_extends_each_single_line", "single_item_single_line", "single_line", "space_before_parenthesis"\.$/' ); $fixer = new ClassDefinitionFixer(); @@ -148,10 +149,30 @@ public function provideAnonymousClassesCases(): array ' false], + ], + [ + ' false], + ], + [ + 'foo() , bar ( $a) ) {};', + "foo() , bar ( \$a) ){};", + ['inline_constructor_arguments' => false], + ], [ ' false], + ], [ 'prop) {};', 'prop ){};', ], + [ + 'prop ) {};', + 'prop ){};', + ['inline_constructor_arguments' => false], + ], + [ + " false], + ], + [ + " false], + ], [ 'prop, $v[3], 4) {};', 'prop,$v[3], 4) {};', @@ -172,7 +208,7 @@ public function provideAnonymousClassesCases(): array \Serializable {};', ' [ @@ -269,6 +305,11 @@ class# ' true], ], + 'space_before_parenthesis and inline_constructor_arguments' => [ + 'bar()) ,baz() ) {};', + 'bar()) ,baz() ) {};', + ['space_before_parenthesis' => true, 'inline_constructor_arguments' => false], + ], ]; } diff --git a/tests/Fixer/FunctionNotation/MethodArgumentSpaceFixerTest.php b/tests/Fixer/FunctionNotation/MethodArgumentSpaceFixerTest.php index 3fa3e5a5497..fc3de20fdaf 100644 --- a/tests/Fixer/FunctionNotation/MethodArgumentSpaceFixerTest.php +++ b/tests/Fixer/FunctionNotation/MethodArgumentSpaceFixerTest.php @@ -169,6 +169,32 @@ public function provideFixCases(): array 'foo() ,$c=30);', ['keep_multiple_spaces_after_comma' => true], ], + 'test named class constructor call' => [ + 'foo(), $c=30);', + 'foo() ,$c=30);', + ], + 'test named class constructor call with multiple spaces' => [ + ' [ + ' true], + ], + 'test anonymous class constructor call' => [ + 'foo(), $c=30) {};', + 'foo() ,$c=30) {};', + ], + 'test anonymous class constructor call with multiple spaces' => [ + ' [ + ' true], + ], 'test receiving data in list context with omitted values' => [ ' [ + ' [ ' 'ensure_single_line'], + ], + 'ensure_single_line_methods_in_anonymous_class' => [ + <<<'EXPECTED' + 'ensure_single_line'],