* 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\ControlStructure; use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException; use PhpCsFixer\FixerDefinition\FixerDefinitionInterface; use PhpCsFixer\Tests\Test\AbstractFixerTestCase; /** * @author Dariusz Rumiński * * @internal * * @covers \PhpCsFixer\Fixer\ControlStructure\YodaStyleFixer * * @extends AbstractFixerTestCase<\PhpCsFixer\Fixer\ControlStructure\YodaStyleFixer> * * @phpstan-import-type _AutogeneratedInputConfiguration from \PhpCsFixer\Fixer\ControlStructure\YodaStyleFixer */ final class YodaStyleFixerTest extends AbstractFixerTestCase { /** * @param array $extraConfig * * @dataProvider provideFixCases */ public function testFix(string $expected, ?string $input = null, array $extraConfig = []): void { $this->fixer->configure(['equal' => true, 'identical' => true] + $extraConfig); $this->doTest($expected, $input); } /** * Test with the inverse config. * * @param array $extraConfig * * @dataProvider provideFixCases */ public function testFixInverse(string $expected, ?string $input = null, array $extraConfig = []): void { $this->fixer->configure(['equal' => false, 'identical' => false] + $extraConfig); if (null === $input) { $this->doTest($expected); } else { $this->doTest($input, $expected); } } public static function provideFixCases(): iterable { yield [ ' true], ]; yield [ ' true], ]; yield [ ' false], ]; yield [ ' true], ]; yield [ 'array[$var]) === $a;']; yield ['array[$var]);']; yield ['getStuff() === $myVariable;']; yield ['getStuff();']; yield [' 2;']; yield ['myObject1->{$index}+$b === "";']; yield ['$a === $foo->$b->$c;']; yield [' [ ' [ '1&&$c<=10;', '1&&$c<=10;', ]; yield 'Comments.' => [ '', ]; yield [ '', ]; yield [ '', '', ['always_move_variable' => true], ]; yield [ 'myArray[$index];', 'myArray[$index] === "";', ]; yield [ 'myArray[$index]->/*1*//*2*//*3*/a;', 'myArray[$index]->/*1*//*2*//*3*/a === "";', ]; yield [ 'myArray[$index]->a;', 'myArray[$index]->a === "";', ]; yield [ 'myObject2-> {$index};', 'myObject2-> {$index} === "";', ]; yield [ 'myObject3->{$index}->a;', 'myObject3->{$index}->a === "";', ]; yield [ 'myObject4->{$index}->{$index}->a;', 'myObject4->{$index}->{$index}->a === "";', ]; yield [ 'myObject4->$index->a;', 'myObject4->$index->a === "";', ]; yield [ '', '', ]; yield 'Nested case' => [ '{null === $a ? "a" : "b"};', '{$a === null ? "a" : "b"} === null;', ]; yield 'Complex code sample.' => [ 'b) : 0 === $b->a; } else { if ($c === (null === $b)) { return false === $d; } }', 'b === 0) : $b->a === 0; } else { if ($c === ($b === null)) { return $d === false; } }', ]; yield [ 'getStuff();', 'getStuff() === 2;', ]; yield [ 'myObject5->{$index}->/*1*//*2*/b;', 'myObject5->{$index}->/*1*//*2*/b === "";', ]; yield [ ' $value) { false !== uniqid() ? 1 : 2; } false !== uniqid() ? 1 : 2; }', ' $value) { uniqid() !== false ? 1 : 2; } uniqid() !== false ? 1 : 2; }', ]; yield [ 'array[$var]) === $a;', 'array[$var]);', ['always_move_variable' => true], ]; yield [ 'getStuff() === $myVariable;', 'getStuff();', ['always_move_variable' => true], ]; yield [ ' true], ]; yield [ ' true], ]; yield [ ' true], ]; yield [ ' true], ]; yield [ ' true], ]; yield [ ' true], ]; yield [ ' true], ]; yield [ ' true], ]; yield [ ' true], ]; yield [ ' 2 === $k;', ' 2;', ['always_move_variable' => true], ]; yield [ ' true], ]; yield [ '= 2 === $k;', '= 2;', ['always_move_variable' => true], ]; yield [ ' true], ]; yield [ ' true], ]; yield [ '> 2 === $k;', '> 2;', ['always_move_variable' => true], ]; yield [ ' true], ]; yield [ ' true], ]; yield [ ' true], ]; yield [ ' true], ]; yield [ ' true], ]; yield [ ' true], ]; yield [ ' true], ]; yield [ ' true], ]; yield [ ' true], ]; } $assignmentOperators = ['=', '**=', '*=', '|=', '+=', '-=', '^=', '<<=', '>>=', '&=', '.=', '/=', '%=', '??=']; $logicalOperators = ['xor', 'or', 'and', '||', '&&', '??']; foreach ([...$assignmentOperators, ...$logicalOperators] as $operator) { yield [ \sprintf(' $d;']; yield [ ' [ ' [ ' [ ' [ ' [ ' $c === array(1) ? $b : $d;', null, [ 'less_and_greater' => false, ], ]; } /** * @dataProvider provideLessGreaterCases */ public function testFixLessGreater(string $expected, string $input): void { $this->fixer->configure(['less_and_greater' => true]); $this->doTest($expected, $input); } /** * Test with the inverse config. * * @dataProvider provideLessGreaterCases */ public function testFixLessGreaterInverse(string $expected, string $input): void { $this->fixer->configure(['less_and_greater' => false]); $this->doTest($input, $expected); } /** * @return iterable */ public static function provideLessGreaterCases(): iterable { yield [ '= 3;', ]; yield [ ' $b;', ' $b) || $d;', 'fixer->configure([ 'equal' => null, 'identical' => true, 'less_and_greater' => false, ]); $this->doTest( ' 3; ', ' 3; ' ); } /** * @param _AutogeneratedInputConfiguration $config * * @dataProvider provideInvalidConfigCases */ public function testInvalidConfig(array $config, string $expectedMessage): void { $this->expectException(InvalidFixerConfigurationException::class); $this->expectExceptionMessageMatches("#^\\[{$this->fixer->getName()}\\] {$expectedMessage}$#"); $this->fixer->configure($config); } public static function provideInvalidConfigCases(): iterable { yield [['equal' => 2], 'Invalid configuration: The option "equal" with value 2 is expected to be of type "bool" or "null", but is of type "(int|integer)"\.']; yield [['_invalid_' => true], 'Invalid configuration: The option "_invalid_" does not exist\. Defined options are: "always_move_variable", "equal", "identical", "less_and_greater"\.']; } public function testDefinition(): void { self::assertInstanceOf(FixerDefinitionInterface::class, $this->fixer->getDefinition()); } /** * @dataProvider providePHP71Cases */ public function testPHP71(string $expected, ?string $input = null): void { $this->fixer->configure(['equal' => true, 'identical' => true]); $this->doTest($expected, $input); } /** * Test with the inverse config. * * @dataProvider providePHP71Cases */ public function testPHP71Inverse(string $expected, ?string $input = null): void { $this->fixer->configure(['equal' => false, 'identical' => false]); if (null === $input) { $this->doTest($expected); } else { $this->doTest($input, $expected); } } /** * @return iterable */ public static function providePHP71Cases(): iterable { // no fix cases yield [' $a, "b" => $b, "c" => $c) = $c === array(1) ? $b : $d;']; yield [' $x1, "y" => $y1), list("x" => $x2, "y" => $y2)) = $points;']; yield [' list($x1, $y1), "second" => list($x2, $y2)) = $points;']; yield [' $a, "b" => $b, "c" => $c] = $a[0];']; yield [' $a, "b" => $b, "c" => $c) = 1 === $c ? $b : $d;', ' $a, "b" => $b, "c" => $c) = $c === 1 ? $b : $d;', ]; yield [ ' $a, "b" => $b, "c" => $c) = A::B === $c ? $b : $d;', ' $a, "b" => $b, "c" => $c) = $c === A::B ? $b : $d;', ]; yield [ ' $b) = ["a" => 7 === $c ? 5 : 1, "b" => 7];', ' $b) = ["a" => $c === 7 ? 5 : 1, "b" => 7];', ]; yield [ ' $b] = ["a" => 7 === $c ? 5 : 1, "b" => 7];', ' $b] = ["a" => $c === 7 ? 5 : 1, "b" => 7];', ]; yield 'Array destruct by ternary.' => [ 'fixer->configure($config); $this->doTest($expected); } public static function provideWithConfigCases(): iterable { yield [ [ 'identical' => false, ], ' false, 'identical' => false, ], 'event == \'created\') { foreach ($revision->getModified() as $col => $data) { $model->$col = $data[\'new\']; } } else { foreach ($revision->getModified() as $col => $data) { $model->$col = $data[\'old\']; } }', ]; } /** * @dataProvider provideFixPrePHP80Cases * * @requires PHP <8.0 */ public function testFixPrePHP80(string $expected, ?string $input = null): void { $this->doTest($expected, $input); } /** * @return iterable */ public static function provideFixPrePHP80Cases(): iterable { yield [ '$a[1] === $bar[$baz]{1}->$a[1][2][3]->$d[$z]{1};']; yield ['a{2}+1 == 2;']; yield ['fixer->configure($config); $this->doTest($expected, $input); } public static function provideFix80Cases(): iterable { yield [ 'getStuff() === $myVariable;', 'getStuff();', ['equal' => true, 'identical' => true, 'always_move_variable' => true], ]; yield [ 'b[5]?->c;', 'b[5]?->c === 42;', ]; yield [ 'myObject1?->{$index}+$b === "";', null, ['equal' => true, 'identical' => true], ]; yield [ 'doTest($expected, $input); } /** * @return iterable */ public static function provideFix81Cases(): iterable { yield 'does not make a lot of sense but is valid syntax, do not break 1' => [ ' [ '