Skip to content

Commit

Permalink
Allow multiline constructor arguments in an anonymous classes
Browse files Browse the repository at this point in the history
  • Loading branch information
jrmajor authored and SpacePossum committed Feb 21, 2022
1 parent ae5701a commit 8e15162
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 8e15162

Please sign in to comment.