* Dariusz Rumiński * * This source file is subject to the MIT license that is bundled * with this source code in the file LICENSE. */ namespace PhpCsFixer\Tests\Fixer\Basic; use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException; use PhpCsFixer\Fixer\Basic\BracesFixer; use PhpCsFixer\Tests\Test\AbstractFixerTestCase; use PhpCsFixer\WhitespacesFixerConfig; /** * @author Dariusz Rumiński * * @internal * * @covers \PhpCsFixer\Fixer\Basic\BracesFixer * * @extends AbstractFixerTestCase<\PhpCsFixer\Fixer\Basic\BracesFixer> * * @phpstan-import-type _AutogeneratedInputConfiguration from \PhpCsFixer\Fixer\Basic\BracesFixer */ final class BracesFixerTest extends AbstractFixerTestCase { private const CONFIGURATION_OOP_POSITION_SAME_LINE = ['position_after_functions_and_oop_constructs' => BracesFixer::LINE_SAME]; private const CONFIGURATION_CTRL_STRUCT_POSITION_NEXT_LINE = ['position_after_control_structures' => BracesFixer::LINE_NEXT]; private const CONFIGURATION_ANONYMOUS_POSITION_NEXT_LINE = ['position_after_anonymous_constructs' => BracesFixer::LINE_NEXT]; public function testInvalidConfigurationClassyConstructs(): void { $this->expectException(InvalidFixerConfigurationException::class); $this->expectExceptionMessageMatches('#^\[braces\] Invalid configuration: The option "position_after_functions_and_oop_constructs" with value "neither" is invalid\. Accepted values are: "next", "same"\.$#'); $this->fixer->configure(['position_after_functions_and_oop_constructs' => 'neither']); // @phpstan-ignore-line } /** * @param _AutogeneratedInputConfiguration $configuration * * @dataProvider provideFixControlContinuationBracesCases */ public function testFixControlContinuationBraces(string $expected, ?string $input = null, array $configuration = []): void { $this->fixer->configure($configuration); $this->doTest($expected, $input); } public static function provideFixControlContinuationBracesCases(): iterable { yield [ ' TestTest foo; echo $collection->items[1]->property; } ', ]; yield [ ' TestTest TestTest foo; echo $collection->items[1]->property; } ', null, self::CONFIGURATION_OOP_POSITION_SAME_LINE, ]; yield [ ' echo 1; echo 2; ', ]; yield [ ' $item) { if ($item): ?> ', ]; yield [ '

fixer->configure($configuration); $this->doTest($expected, $input); } public static function provideFixMissingBracesAndIndentCases(): iterable { yield [ 'next());', ]; yield [ 'getTest());', 'getTest());', ]; yield [ 'getTest());', 'getTest());', ]; yield [ 'c()}d"; }', ]; yield [ '{$c->d}($e); $f->{$g} = $h; $i->{$j}[$k] = $l; $m = $n->{$o}; $p = array($q->{$r}, $s->{$t}); $u->{$v}->w = 1; }', ]; yield [ '{"a{$c}d"}(); }', ]; yield [ '{"a{$c->{\'foo-bar\'}()}d"}(); }', ]; yield [ '{"a{$c->{\'foo-bar\'}()}d"}(); }', ]; yield [ 'x', 'x', ]; yield [ 'phoneNumbers->filter(function ($phone) { $a = 1; $b = 1; $c = 1; return ($phone->getType() === 1) ? true : false; }); } } }', 'phoneNumbers->filter(function ($phone) { $a = 1; $b = 1; $c = 1; return ($phone->getType() === 1) ? true : false; }); } }', ]; yield [ 'next());', null, self::CONFIGURATION_OOP_POSITION_SAME_LINE, ]; yield [ 'getTest());', 'getTest());', self::CONFIGURATION_OOP_POSITION_SAME_LINE, ]; yield [ 'getTest());', 'getTest());', self::CONFIGURATION_OOP_POSITION_SAME_LINE, ]; yield [ 'c()}d"; }', 'c()}d"; }', self::CONFIGURATION_OOP_POSITION_SAME_LINE, ]; yield [ 'c()}d"; }', 'c()}d"; }', self::CONFIGURATION_OOP_POSITION_SAME_LINE + self::CONFIGURATION_CTRL_STRUCT_POSITION_NEXT_LINE, ]; yield [ '{$c->d}($e); $f->{$g} = $h; $i->{$j}[$k] = $l; $m = $n->{$o}; $p = array($q->{$r}, $s->{$t}); $u->{$v}->w = 1; }', '{$c->d}($e); $f->{$g} = $h; $i->{$j}[$k] = $l; $m = $n->{$o}; $p = array($q->{$r}, $s->{$t}); $u->{$v}->w = 1; }', self::CONFIGURATION_OOP_POSITION_SAME_LINE, ]; yield [ '{$c->d}($e); $f->{$g} = $h; $i->{$j}[$k] = $l; $m = $n->{$o}; $p = array($q->{$r}, $s->{$t}); $u->{$v}->w = 1; }', '{$c->d}($e); $f->{$g} = $h; $i->{$j}[$k] = $l; $m = $n->{$o}; $p = array($q->{$r}, $s->{$t}); $u->{$v}->w = 1; }', self::CONFIGURATION_OOP_POSITION_SAME_LINE + self::CONFIGURATION_CTRL_STRUCT_POSITION_NEXT_LINE, ]; yield [ '{"a{$c}d"}(); }', '{"a{$c}d"}(); }', self::CONFIGURATION_OOP_POSITION_SAME_LINE, ]; yield [ '{"a{$c->{\'foo-bar\'}()}d"}(); }', '{"a{$c->{\'foo-bar\'}()}d"}(); }', self::CONFIGURATION_OOP_POSITION_SAME_LINE, ]; yield [ '{"a{$c->{\'foo-bar\'}()}d"}(); }', '{"a{$c->{\'foo-bar\'}()}d"}(); }', self::CONFIGURATION_OOP_POSITION_SAME_LINE, ]; yield [ 'x', 'x', self::CONFIGURATION_OOP_POSITION_SAME_LINE, ]; yield [ 'phoneNumbers->filter(function ($phone) { $a = 1; $b = 1; $c = 1; return ($phone->getType() === 1) ? true : false; }); } } }', 'phoneNumbers->filter(function ($phone) { $a = 1; $b = 1; $c = 1; return ($phone->getType() === 1) ? true : false; }); } }', self::CONFIGURATION_OOP_POSITION_SAME_LINE, ]; yield [ 'phoneNumbers->filter(function ($phone) { $a = 1; $b = 1; $c = 1; return ($phone->getType() === 1) ? true : false; }); } } }', 'phoneNumbers->filter(function ($phone) { $a = 1; $b = 1; $c = 1; return ($phone->getType() === 1) ? true : false; }); } }', self::CONFIGURATION_OOP_POSITION_SAME_LINE + self::CONFIGURATION_CTRL_STRUCT_POSITION_NEXT_LINE, ]; yield [ 'phoneNumbers->filter(function ($phone) { $a = 1; $b = 1; $c = 1; return ($phone->getType() === 1) ? true : false; }); } } }', 'phoneNumbers->filter(function ($phone) { $a = 1; $b = 1; $c = 1; return ($phone->getType() === 1) ? true : false; }); } }', self::CONFIGURATION_OOP_POSITION_SAME_LINE + self::CONFIGURATION_CTRL_STRUCT_POSITION_NEXT_LINE + self::CONFIGURATION_ANONYMOUS_POSITION_NEXT_LINE, ]; yield [ 'fixer->configure($configuration); $this->doTest($expected, $input); } public static function provideFixClassyBracesCases(): iterable { yield [ 'fixer->configure($configuration); $this->doTest($expected, $input); } public static function provideFixAnonFunctionInShortArraySyntaxCases(): iterable { yield [ ' function ($data) { return true; } ], [ "callback" => function ($data) { return true; }, ], ]; }', ' function ($data) { return true; } ], [ "callback" => function ($data) { return true; }, ], ]; }', ]; yield [ ' function ($data) { return true; } ], [ "callback" => function ($data) { return true; }, ], ]; }', ' function ($data) { return true; } ], [ "callback" => function ($data) { return true; }, ], ]; }', self::CONFIGURATION_OOP_POSITION_SAME_LINE, ]; yield [ ' function ($data) { return true; } ], [ "callback" => function ($data) { return true; }, ], ]; }', ' function ($data) { return true; } ], [ "callback" => function ($data) { return true; }, ], ]; }', self::CONFIGURATION_OOP_POSITION_SAME_LINE + self::CONFIGURATION_ANONYMOUS_POSITION_NEXT_LINE, ]; } /** * @param _AutogeneratedInputConfiguration $configuration * * @dataProvider provideFixCommentBeforeBraceCases */ public function testFixCommentBeforeBrace(string $expected, ?string $input = null, array $configuration = []): void { $this->fixer->configure($configuration); $this->doTest($expected, $input); } public static function provideFixCommentBeforeBraceCases(): iterable { yield [ '', '', ]; yield [ '', ]; yield [ 'fixer->configure($configuration); $this->doTest($expected, $input); } public static function provideFixWhitespaceBeforeBraceCases(): iterable { yield [ 'getFile()) { }', 'getFile()) { }', ]; yield [ 'getFile()) { }', 'getFile()) { }', self::CONFIGURATION_OOP_POSITION_SAME_LINE, ]; yield [ 'getFile()) { }', 'getFile()) { }', self::CONFIGURATION_ANONYMOUS_POSITION_NEXT_LINE, ]; yield [ 'fixer->configure($configuration); $this->doTest($expected, $input); } public static function provideFixFunctionsCases(): iterable { yield [ 'bar(), function ($o) { return $o->isBaz(); }); }, $collection));', 'bar(), function ($o) { return $o->isBaz(); }); }, $collection));', ]; yield [ 'fixers, function &($a, $b) use ($selfName) { return 1; });', ]; yield [ 'fixers, function &($a, $b) use ($selfName) { return 1; } );', ]; yield [ 'bar(), function ($o) { return $o->isBaz(); }); }, $collection));', 'bar(), function ($o) { return $o->isBaz(); }); }, $collection));', self::CONFIGURATION_OOP_POSITION_SAME_LINE, ]; yield [ 'bar(), function ($o) { return $o->isBaz(); }); }, $collection));', 'bar(), function ($o) { return $o->isBaz(); }); }, $collection));', self::CONFIGURATION_OOP_POSITION_SAME_LINE + self::CONFIGURATION_ANONYMOUS_POSITION_NEXT_LINE, ]; yield [ 'fixers, function &($a, $b) use ($selfName) { return 1; });', null, self::CONFIGURATION_OOP_POSITION_SAME_LINE, ]; yield [ 'fixers, function &($a, $b) use ($selfName) { return 1; } );', null, self::CONFIGURATION_OOP_POSITION_SAME_LINE, ]; yield [ 'fixer->configure($configuration); $this->doTest($expected, $input); } public static function provideFixMultiLineStructuresCases(): iterable { yield [ ' $fooBarBazBuzz ) { }', ' $fooBarBazBuzz ) { }', self::CONFIGURATION_CTRL_STRUCT_POSITION_NEXT_LINE, ]; yield [ 'fixer->configure($configuration); $this->doTest($expected, $input); } public static function provideFixSpaceAroundTokenCases(): iterable { yield [ 'tesT ($test)) { }', 'tesT ($test)) { }', ]; yield [ 'tesT ($test)) { }', 'tesT ($test)) { }', self::CONFIGURATION_OOP_POSITION_SAME_LINE, ]; yield [ 'fixer->configure($configuration); $this->doTest($expected, $input); } public static function provideFinallyCases(): iterable { yield [ 'fixer->configure($configuration); $this->doTest($expected, $input); } public static function provideFunctionImportCases(): iterable { yield [ 'fixer->configure($configuration); $this->doTest($expected, $input); } public static function provideFixCases(): iterable { yield [ ' true], ]; yield [ ' true], ]; yield [ ' true], ]; yield [ ' true], ]; } /** * @param _AutogeneratedInputConfiguration $configuration * * @dataProvider providePreserveLineAfterControlBraceCases */ public function testPreserveLineAfterControlBrace(string $expected, ?string $input = null, array $configuration = []): void { $this->fixer->configure($configuration); $this->doTest($expected, $input); } public static function providePreserveLineAfterControlBraceCases(): iterable { yield [ 'fixer->configure([ 'allow_single_line_closure' => true, ]); $this->doTest($expected, $input); } /** * @return iterable */ public static function provideFixWithAllowSingleLineClosureCases(): iterable { yield [ 'doTest($expected, $input); } /** * @return iterable */ public static function provideDoWhileLoopInsideAnIfWithoutBracketsCases(): iterable { yield [ 'fixer->configure($configuration); $this->fixer->setWhitespacesConfig(new WhitespacesFixerConfig("\t", "\r\n")); $this->doTest($expected, $input); } public static function provideMessyWhitespacesCases(): iterable { yield [ 'doTest($expected, $input); } /** * @return iterable */ public static function provideNowdocInTemplatesCases(): iterable { yield [ <<<'EOT' doTest($expected, $input); $this->doTest(str_replace('//', '#', $expected), null === $input ? null : str_replace('//', '#', $input)); } /** * @return iterable */ public static function provideFixCommentsCases(): iterable { yield [ 'doTest( 'fixer->setWhitespacesConfig($config); } $this->doTest($expected, $input); } /** * @return iterable */ public static function provideIndentCommentCases(): iterable { yield [ "doTest($expected, $input); } /** * @return iterable */ public static function provideFixAlternativeSyntaxCases(): iterable { yield [ ' X ', ' X ', ]; yield [ ' X ', ]; yield [ ' X ', ]; yield [ ' X ', ]; yield [ ' X ', ]; yield [ ' X ,', ]; yield [ ' ', ' ', ]; yield [ 'doTest($expected, $input); } /** * @return iterable */ public static function provideFix80Cases(): iterable { yield 'match' => [ ' "Same for 1 and 2", };', ' "Same for 1 and 2", };', ]; } /** * @requires PHP 8.1 * * @dataProvider provideFix81Cases */ public function testFix81(string $expected, string $input): void { $this->doTest($expected, $input); } /** * @return iterable */ public static function provideFix81Cases(): iterable { yield 'enum' => [ ' [ 'fixer->configure($configuration); $this->doTest($expected, $input); } /** * @return iterable */ public static function provideFixPre84Cases(): iterable { yield [ 'foo; echo $collection->items{1}->property; } ', ]; yield [ 'foo; echo $collection->items{1}->property; } ', null, self::CONFIGURATION_OOP_POSITION_SAME_LINE, ]; } }