Skip to content

Commit

Permalink
feature #6081 Allow multiline constructor arguments in an anonymous c…
Browse files Browse the repository at this point in the history
…lasses (jrmajor, SpacePossum)

This PR was squashed before being merged into the master branch (closes #6081).

Discussion
----------

Allow multiline constructor arguments in an anonymous classes

Closes #4289

Commits
-------

8e15162 Allow multiline constructor arguments in an anonymous classes
  • Loading branch information
SpacePossum committed Feb 21, 2022
2 parents ae5701a + 8e15162 commit 0b96918
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 6 deletions.
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
],
'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

0 comments on commit 0b96918

Please sign in to comment.