Skip to content
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

Allow multiline constructor arguments in an anonymous classes #6081

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/list.rst
Expand Up @@ -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>`_
Expand Down
2 changes: 1 addition & 1 deletion doc/ruleSets/PSR12.rst
Expand Up @@ -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>`_
Expand Down
27 changes: 26 additions & 1 deletion doc/rules/class_notation/class_definition.rst
Expand Up @@ -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
--------

Expand Down Expand Up @@ -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
<?php
-$foo = new class(
- $bar,
- $baz
-) {};
+$foo = new class($bar, $baz) {};

Rule sets
---------

Expand All @@ -150,7 +175,7 @@ The rule is part of the following rule sets:
@PSR12
Using the `@PSR12 <./../../ruleSets/PSR12.rst>`_ 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.
Expand Down
20 changes: 20 additions & 0 deletions src/Fixer/ClassNotation/ClassDefinitionFixer.php
Expand Up @@ -93,6 +93,10 @@ interface Bar extends
',
['space_before_parenthesis' => true]
),
new CodeSample(
"<?php\n\$foo = new class(\n \$bar,\n \$baz\n) {};\n",
['inline_constructor_arguments' => true]
),
]
);
}
Expand Down Expand Up @@ -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(),
]);
}

Expand Down Expand Up @@ -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);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Fixer/FunctionNotation/MethodArgumentSpaceFixer.php
Expand Up @@ -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];
Expand Down
5 changes: 4 additions & 1 deletion src/RuleSet/Sets/PSR12Set.php
Expand Up @@ -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
SpacePossum marked this conversation as resolved.
Show resolved Hide resolved
],
'compact_nullable_typehint' => true,
'declare_equal_normalize' => true,
'lowercase_cast' => true,
Expand Down
45 changes: 43 additions & 2 deletions tests/Fixer/ClassNotation/ClassDefinitionFixerTest.php
Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -148,10 +149,30 @@ public function provideAnonymousClassesCases(): array
'<?php $a = new class() {};',
"<?php \$a = new\n class ( ){};",
],
[
'<?php $a = new class( ) {};',
"<?php \$a = new\n class ( ){};",
['inline_constructor_arguments' => false],
],
[
'<?php $a = new class implements Foo {};',
"<?php \$a = new\n class implements Foo {};",
['inline_constructor_arguments' => false],
],
[
'<?php $a = new class( $this->foo() , bar ( $a) ) {};',
"<?php \$a = new\n class ( \$this->foo() , bar ( \$a) ){};",
['inline_constructor_arguments' => false],
],
[
'<?php $a = new class(10, 1, /**/ 2) {};',
'<?php $a = new class( 10, 1,/**/2 ){};',
],
[
'<?php $a = new class( 10, 1,/**/2 ) {};',
'<?php $a = new class( 10, 1,/**/2 ){};',
['inline_constructor_arguments' => false],
],
[
'<?php $a = new class(2) {};',
'<?php $a = new class(2){};',
Expand All @@ -160,6 +181,21 @@ public function provideAnonymousClassesCases(): array
'<?php $a = new class($this->prop) {};',
'<?php $a = new class( $this->prop ){};',
],
[
'<?php $a = new class( $this->prop ) {};',
'<?php $a = new class( $this->prop ){};',
['inline_constructor_arguments' => false],
],
[
"<?php \$a = new class(\n\t\$a,\n\t\$b,\n\t\$c,\n\t\$d) implements A, B {};",
"<?php \$a = new class(\n\t\$a,\n\t\$b,\n\t\$c,\n\t\$d) implements A, \t B{};",
['inline_constructor_arguments' => false],
],
[
"<?php \$a = new class(\n\t\$a,\n\t\$b,\n\t\$c,\n\t\$d) implements A, B {};",
"<?php \$a = new class (\n\t\$a,\n\t\$b,\n\t\$c,\n\t\$d) implements A, \t B{};",
['inline_constructor_arguments' => false],
],
[
'<?php $a = new class($this->prop, $v[3], 4) {};',
'<?php $a = new class( $this->prop,$v[3], 4) {};',
Expand All @@ -172,7 +208,7 @@ public function provideAnonymousClassesCases(): array
\Serializable
{};',
'<?php
$instance = new class extends \Foo implements
$instance = new class extends \Foo implements
\ArrayAccess,\Countable,\Serializable{};',
],
'PSR-12 Implements Parenthesis on the next line.' => [
Expand Down Expand Up @@ -269,6 +305,11 @@ class#
'<?php $z = new class () {};',
['space_before_parenthesis' => true],
],
'space_before_parenthesis and inline_constructor_arguments' => [
'<?php $z = new class ( static::foo($this->bar()) ,baz() ) {};',
'<?php $z = new class ( static::foo($this->bar()) ,baz() ) {};',
['space_before_parenthesis' => true, 'inline_constructor_arguments' => false],
],
];
}

Expand Down
67 changes: 67 additions & 0 deletions tests/Fixer/FunctionNotation/MethodArgumentSpaceFixerTest.php
Expand Up @@ -169,6 +169,32 @@ public function provideFixCases(): array
'<?php xyz($a=10,$b=20 , $this->foo() ,$c=30);',
['keep_multiple_spaces_after_comma' => true],
],
'test named class constructor call' => [
'<?php new Foo($a=10, $b=20, $this->foo(), $c=30);',
'<?php new Foo($a=10,$b=20 ,$this->foo() ,$c=30);',
],
'test named class constructor call with multiple spaces' => [
'<?php new Foo($a=10, $b=20, $c=30);',
'<?php new Foo($a=10 , $b=20 , $c=30);',
],
'test named class constructor call with multiple spaces (kmsac)' => [
'<?php new Foo($a=10, $b=20, $c=30);',
'<?php new Foo($a=10 , $b=20 , $c=30);',
['keep_multiple_spaces_after_comma' => true],
],
'test anonymous class constructor call' => [
'<?php new class ($a=10, $b=20, $this->foo(), $c=30) {};',
'<?php new class ($a=10,$b=20 ,$this->foo() ,$c=30) {};',
],
'test anonymous class constructor call with multiple spaces' => [
'<?php new class ($a=10, $b=20, $c=30) extends Foo {};',
'<?php new class ($a=10 , $b=20 , $c=30) extends Foo {};',
],
'test anonymous class constructor call with multiple spaces (kmsac)' => [
'<?php new class ($a=10, $b=20, $c=30) {};',
'<?php new class ($a=10 , $b=20 , $c=30) {};',
['keep_multiple_spaces_after_comma' => true],
],
'test receiving data in list context with omitted values' => [
'<?php list($a, $b, , , $c) = foo();',
'<?php list($a, $b,, ,$c) = foo();',
Expand Down Expand Up @@ -228,6 +254,20 @@ public function provideFixCases(): array
);
}',
],
'multi line anonymous class constructor call' => [
'<?php if (1) {
new class (
$a=10,
$b=20,
$c=30
) {};
}',
'<?php if (1) {
new class (
$a=10 ,
$b=20,$c=30) {};
}',
],
'skip arrays but replace arg methods' => [
'<?php fnc(1, array(2, func2(6, 7) ,4), 5);',
'<?php fnc(1,array(2, func2(6, 7) ,4), 5);',
Expand Down Expand Up @@ -779,6 +819,33 @@ private function foo2(
$c
) {}
}
INPUT
,
['on_multiline' => 'ensure_single_line'],
],
'ensure_single_line_methods_in_anonymous_class' => [
<<<'EXPECTED'
<?php
new class {
public static function foo1($a, $b, $c) {}
private function foo2($a, $b, $c) {}
};
EXPECTED
,
<<<'INPUT'
<?php
new class {
public static function foo1(
$a,
$b,
$c
) {}
private function foo2(
$a,
$b,
$c
) {}
};
INPUT
,
['on_multiline' => 'ensure_single_line'],
Expand Down