YodaStyleFixerTest.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4. * This file is part of PHP CS Fixer.
  5. *
  6. * (c) Fabien Potencier <fabien@symfony.com>
  7. * Dariusz Rumiński <dariusz.ruminski@gmail.com>
  8. *
  9. * This source file is subject to the MIT license that is bundled
  10. * with this source code in the file LICENSE.
  11. */
  12. namespace PhpCsFixer\Tests\Fixer\ControlStructure;
  13. use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException;
  14. use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
  15. use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
  16. /**
  17. * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
  18. *
  19. * @internal
  20. *
  21. * @covers \PhpCsFixer\Fixer\ControlStructure\YodaStyleFixer
  22. *
  23. * @extends AbstractFixerTestCase<\PhpCsFixer\Fixer\ControlStructure\YodaStyleFixer>
  24. *
  25. * @phpstan-import-type _AutogeneratedInputConfiguration from \PhpCsFixer\Fixer\ControlStructure\YodaStyleFixer
  26. */
  27. final class YodaStyleFixerTest extends AbstractFixerTestCase
  28. {
  29. /**
  30. * @param array<string, mixed> $extraConfig
  31. *
  32. * @dataProvider provideFixCases
  33. */
  34. public function testFix(string $expected, ?string $input = null, array $extraConfig = []): void
  35. {
  36. $this->fixer->configure(['equal' => true, 'identical' => true] + $extraConfig);
  37. $this->doTest($expected, $input);
  38. }
  39. /**
  40. * Test with the inverse config.
  41. *
  42. * @param array<string, mixed> $extraConfig
  43. *
  44. * @dataProvider provideFixCases
  45. */
  46. public function testFixInverse(string $expected, ?string $input = null, array $extraConfig = []): void
  47. {
  48. $this->fixer->configure(['equal' => false, 'identical' => false] + $extraConfig);
  49. if (null === $input) {
  50. $this->doTest($expected);
  51. } else {
  52. $this->doTest($input, $expected);
  53. }
  54. }
  55. public static function provideFixCases(): iterable
  56. {
  57. yield [
  58. '<?php $a = 1 + ($b + $c) === true ? 1 : 2;',
  59. null,
  60. ['always_move_variable' => true],
  61. ];
  62. yield [
  63. '<?php $a = true === ($b + $c) ? 1 : 2;',
  64. '<?php $a = ($b + $c) === true ? 1 : 2;',
  65. ['always_move_variable' => true],
  66. ];
  67. yield [
  68. '<?php
  69. if ((1 === $a) === 1) {
  70. return;
  71. }',
  72. '<?php
  73. if (($a === 1) === 1) {
  74. return;
  75. }',
  76. ['always_move_variable' => false],
  77. ];
  78. yield [
  79. '<?php
  80. if (true === (1 !== $foo[0])) {
  81. return;
  82. }',
  83. '<?php
  84. if (($foo[0] !== 1) === true) {
  85. return;
  86. }',
  87. ['always_move_variable' => true],
  88. ];
  89. yield [
  90. '<?php return 1 !== $a [$b];',
  91. '<?php return $a [$b] !== 1;',
  92. ];
  93. yield [
  94. '<?= 1 === $a ? 5 : 7;',
  95. '<?= $a === 1 ? 5 : 7;',
  96. ];
  97. yield [
  98. '<?php print 1 === 1343;',
  99. ];
  100. yield [
  101. '<?php
  102. echo 3 === $a ? 2 : 4;
  103. ',
  104. '<?php
  105. echo $a === 3 ? 2 : 4;
  106. ',
  107. ];
  108. yield [
  109. '<?php 1 === foo($a) ? 1 : 2;',
  110. '<?php foo($a) === 1 ? 1 : 2;',
  111. ];
  112. yield [
  113. '<?php 1 === $a::$a ? 1 : 2;',
  114. '<?php $a::$a === 1 ? 1 : 2;',
  115. ];
  116. yield [
  117. '<?php 1 === (bool) $a ? 8 : 7;',
  118. '<?php (bool) $a === 1 ? 8 : 7;',
  119. ];
  120. yield [
  121. '<?php 1 === new $a ? 1 : 2;',
  122. '<?php new $a === 1 ? 1 : 2;',
  123. ];
  124. yield [
  125. '<?php 1 === "a".$a ? 5 : 6;',
  126. '<?php "a".$a === 1 ? 5 : 6;',
  127. ];
  128. yield [
  129. '<?php 1 === __DIR__.$a ? 5 : 6;',
  130. '<?php __DIR__.$a === 1 ? 5 : 6;',
  131. ];
  132. yield [
  133. '<?php 1 === $a.$b ? 5 : 6;',
  134. '<?php $a.$b === 1 ? 5 : 6;',
  135. ];
  136. yield [
  137. '<?php echo 1 === (object) $a ? 8 : 7;',
  138. '<?php echo (object) $a === 1 ? 8 : 7;',
  139. ];
  140. yield [
  141. '<?php echo 1 === (int) $a ? 8 : 7;',
  142. '<?php echo (int) $a === 1 ? 8 : 7;',
  143. ];
  144. yield [
  145. '<?php echo 1 === (float) $a ? 8 : 7;',
  146. '<?php echo (float) $a === 1 ? 8 : 7;',
  147. ];
  148. yield [
  149. '<?php echo 1 === (string) $a ? 8 : 7;',
  150. '<?php echo (string) $a === 1 ? 8 : 7;',
  151. ];
  152. yield [
  153. '<?php echo 1 === (array) $a ? 8 : 7;',
  154. '<?php echo (array) $a === 1 ? 8 : 7;',
  155. ];
  156. yield [
  157. '<?php echo 1 === (bool) $a ? 8 : 7;',
  158. '<?php echo (bool) $a === 1 ? 8 : 7;',
  159. ];
  160. yield [
  161. '<?php
  162. if ($a = true === $obj instanceof A) {
  163. echo \'A\';
  164. }',
  165. '<?php
  166. if ($a = $obj instanceof A === true) {
  167. echo \'A\';
  168. }',
  169. ];
  170. yield [
  171. '<?php echo 1 === !!$a ? 8 : 7;',
  172. '<?php echo !!$a === 1 ? 8 : 7;',
  173. ];
  174. yield [
  175. '<?php $a = 1 === new b ? 1 : 2;',
  176. '<?php $a = new b === 1 ? 1 : 2;',
  177. ];
  178. yield [
  179. '<?php $a = 1 === empty($a) ? 1 : 2;',
  180. '<?php $a = empty($a) === 1 ? 1 : 2;',
  181. ];
  182. yield [
  183. '<?php $b = 1 === clone $a ? 5 : 9;',
  184. '<?php $b = clone $a === 1 ? 5 : 9;',
  185. ];
  186. yield [
  187. '<?php while(1 === $a ? 1 : 2){};',
  188. '<?php while($a === 1 ? 1 : 2){};',
  189. ];
  190. yield [
  191. '<?php switch(1 === $a){
  192. case true: echo 1;
  193. };',
  194. '<?php switch($a === 1){
  195. case true: echo 1;
  196. };',
  197. ];
  198. yield [
  199. '<?php echo 1 === $a ? 1 : 2;',
  200. '<?php echo $a === 1 ? 1 : 2;',
  201. ];
  202. // Don't fix cases.
  203. yield ['<?php $a = 1 === 1;'];
  204. yield ['<?php $b = $b === $c;'];
  205. yield ['<?php $c = $$b === $$c;'];
  206. yield ['<?php $d = count($this->array[$var]) === $a;'];
  207. yield ['<?php $e = $a === count($this->array[$var]);'];
  208. yield ['<?php $f = ($a123 & self::MY_BITMASK) === $a;'];
  209. yield ['<?php $g = $a === ($a456 & self::MY_BITMASK);'];
  210. yield ['<?php $h = $this->getStuff() === $myVariable;'];
  211. yield ['<?php $i = $myVariable === $this->getStuff();'];
  212. yield ['<?php $j = 2 * $myVar % 3 === $a;'];
  213. yield ['<?php return $k === 2 * $myVar % 3;'];
  214. yield ['<?php $l = $c > 2;'];
  215. yield ['<?php return $this->myObject1->{$index}+$b === "";'];
  216. yield ['<?php return $m[2]+1 == 2;'];
  217. yield ['<?php return $foo === $bar[$baz][1];'];
  218. yield ['<?php $a = $b[$key]["1"] === $c["2"];'];
  219. yield ['<?php return $foo->$a === $foo->$b->$c;'];
  220. yield ['<?php return $x === 2 - 1;'];
  221. yield ['<?php return $x === 2-1;'];
  222. // https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/pull/693
  223. yield ['<?php return array(2) == $o;'];
  224. yield ['<?php return $p == array(2);'];
  225. yield ['<?php return $p == array("2");'];
  226. yield ['<?php return $p == array(TWO);'];
  227. yield ['<?php return $p == array(array());'];
  228. yield ['<?php return $p == [[]];'];
  229. yield ['<?php return array($q) == $a;'];
  230. yield ['<?php return $r == array($a);'];
  231. yield ['<?php $s = ((array(2))) == $a;'];
  232. yield ['<?php $t = $a == ((array(2)));'];
  233. yield ['<?php list($a) = $c === array(1) ? $b : $d;'];
  234. yield ['<?php $b = 7 === list($a) = [7];'];
  235. yield ['<?php $a = function(){} === array(0);'];
  236. yield ['<?php $z = $n == list($a) = $b;'];
  237. yield ['<?php return $n == list($a) = $b;'];
  238. // Fix cases.
  239. yield 'Array destruct by ternary.' => [
  240. '<?php list($a) = 11 === $c ? $b : $d;',
  241. '<?php list($a) = $c === 11 ? $b : $d;',
  242. ];
  243. yield 'Less spacing.' => [
  244. '<?php $z=2==$a;$b=$c>1&&$c<=10;',
  245. '<?php $z=$a==2;$b=$c>1&&$c<=10;',
  246. ];
  247. yield 'Comments.' => [
  248. '<?php $z = /**/ /**/2/**/ /**/
  249. # aa
  250. /**/==/**/$a/***/;',
  251. '<?php $z = /**/ /**/$a/**/ /**/
  252. # aa
  253. /**/==/**/2/***/;',
  254. ];
  255. yield [
  256. '<?php return 2 == ($a)?>',
  257. ];
  258. yield [
  259. '<?php return ($a) == 2?>',
  260. ];
  261. yield [
  262. '<?php return 2 == ($a)?>',
  263. '<?php return ($a) == 2?>',
  264. ['always_move_variable' => true],
  265. ];
  266. yield [
  267. '<?php $a = ($c === ((null === $b)));',
  268. '<?php $a = ($c === (($b === null)));',
  269. ];
  270. yield [
  271. '<?php return null == $a[2];',
  272. '<?php return $a[2] == null;',
  273. ];
  274. yield [
  275. '<?php return "" === $this->myArray[$index];',
  276. '<?php return $this->myArray[$index] === "";',
  277. ];
  278. yield [
  279. '<?php return "" === $this->myArray[$index]->/*1*//*2*//*3*/a;',
  280. '<?php return $this->myArray[$index]->/*1*//*2*//*3*/a === "";',
  281. ];
  282. yield [
  283. '<?php return "" === $this->myArray[$index]->a;',
  284. '<?php return $this->myArray[$index]->a === "";',
  285. ];
  286. yield [
  287. '<?php return "" === $this->myObject2-> {$index};',
  288. '<?php return $this->myObject2-> {$index} === "";',
  289. ];
  290. yield [
  291. '<?php return "" === $this->myObject3->{$index}->a;',
  292. '<?php return $this->myObject3->{$index}->a === "";',
  293. ];
  294. yield [
  295. '<?php return "" === $this->myObject4->{$index}->{$index}->a;',
  296. '<?php return $this->myObject4->{$index}->{$index}->a === "";',
  297. ];
  298. yield [
  299. '<?php return "" === $this->myObject4->$index->a;',
  300. '<?php return $this->myObject4->$index->a === "";',
  301. ];
  302. yield [
  303. '<?php return self::MY_CONST === self::$myVariable;',
  304. '<?php return self::$myVariable === self::MY_CONST;',
  305. ];
  306. yield [
  307. '<?php return \A\B\C::MY_CONST === \A\B\C::$myVariable;',
  308. '<?php return \A\B\C::$myVariable === \A\B\C::MY_CONST;',
  309. ];
  310. yield [
  311. '<?php $a = 1 == $$a?>',
  312. '<?php $a = $$a == 1?>',
  313. ];
  314. yield 'Nested case' => [
  315. '<?php return null === $a[0 === $b ? $c : $d];',
  316. '<?php return $a[$b === 0 ? $c : $d] === null;',
  317. ];
  318. yield [
  319. '<?php return null === $this->{null === $a ? "a" : "b"};',
  320. '<?php return $this->{$a === null ? "a" : "b"} === null;',
  321. ];
  322. yield 'Complex code sample.' => [
  323. '<?php
  324. if ($a == $b) {
  325. return null === $b ? (null === $a ? 0 : 0 === $a->b) : 0 === $b->a;
  326. } else {
  327. if ($c === (null === $b)) {
  328. return false === $d;
  329. }
  330. }',
  331. '<?php
  332. if ($a == $b) {
  333. return $b === null ? ($a === null ? 0 : $a->b === 0) : $b->a === 0;
  334. } else {
  335. if ($c === ($b === null)) {
  336. return $d === false;
  337. }
  338. }',
  339. ];
  340. yield [
  341. '<?php $b = list($a) = 7 === [7];', // makes no sense, but valid PHP syntax
  342. '<?php $b = list($a) = [7] === 7;',
  343. ];
  344. yield [
  345. '<?php $a = 1 === function(){};',
  346. '<?php $a = function(){} === 1;',
  347. ];
  348. yield [
  349. '<?php
  350. $z#1
  351. #2
  352. =
  353. #3
  354. 1#4
  355. #5
  356. ===#6
  357. #7
  358. $a#8
  359. #9
  360. ;#10',
  361. '<?php
  362. $z#1
  363. #2
  364. =
  365. #3
  366. $a#4
  367. #5
  368. ===#6
  369. #7
  370. 1#8
  371. #9
  372. ;#10',
  373. ];
  374. yield [
  375. '<?php $i = 2 === $this/*a*//*b*//*c*//*d*//*e*//*f*/->getStuff();',
  376. '<?php $i = $this/*a*//*b*//*c*//*d*//*e*//*f*/->getStuff() === 2;',
  377. ];
  378. yield [
  379. '<?php return "" === $this->myObject5->{$index}->/*1*//*2*/b;',
  380. '<?php return $this->myObject5->{$index}->/*1*//*2*/b === "";',
  381. ];
  382. yield [
  383. '<?php
  384. function hello() {}
  385. 1 === $a ? b() : c();
  386. ',
  387. '<?php
  388. function hello() {}
  389. $a === 1 ? b() : c();
  390. ',
  391. ];
  392. yield [
  393. '<?php
  394. class A{}
  395. 1 === $a ? b() : c();
  396. ',
  397. '<?php
  398. class A{}
  399. $a === 1 ? b() : c();
  400. ',
  401. ];
  402. yield [
  403. '<?php
  404. function foo() {
  405. foreach ($arr as $key => $value) {
  406. false !== uniqid() ? 1 : 2;
  407. }
  408. false !== uniqid() ? 1 : 2;
  409. }',
  410. '<?php
  411. function foo() {
  412. foreach ($arr as $key => $value) {
  413. uniqid() !== false ? 1 : 2;
  414. }
  415. uniqid() !== false ? 1 : 2;
  416. }',
  417. ];
  418. yield [
  419. '<?php false === $a = array();',
  420. ];
  421. yield [
  422. '<?php $e = count($this->array[$var]) === $a;',
  423. '<?php $e = $a === count($this->array[$var]);',
  424. ['always_move_variable' => true],
  425. ];
  426. yield [
  427. '<?php $i = $this->getStuff() === $myVariable;',
  428. '<?php $i = $myVariable === $this->getStuff();',
  429. ['always_move_variable' => true],
  430. ];
  431. yield [
  432. '<?php $g = ($a789 & self::MY_BITMASK) === $a;',
  433. null,
  434. ['always_move_variable' => true],
  435. ];
  436. yield [
  437. '<?php return $myVar + 2 === $k;',
  438. '<?php return $k === $myVar + 2;',
  439. ['always_move_variable' => true],
  440. ];
  441. yield [
  442. '<?php return $myVar . $b === $k;',
  443. '<?php return $k === $myVar . $b;',
  444. ['always_move_variable' => true],
  445. ];
  446. yield [
  447. '<?php return $myVar - 2 === $k;',
  448. '<?php return $k === $myVar - 2;',
  449. ['always_move_variable' => true],
  450. ];
  451. yield [
  452. '<?php return $myVar * 2 === $k;',
  453. '<?php return $k === $myVar * 2;',
  454. ['always_move_variable' => true],
  455. ];
  456. yield [
  457. '<?php return $myVar / 2 === $k;',
  458. '<?php return $k === $myVar / 2;',
  459. ['always_move_variable' => true],
  460. ];
  461. yield [
  462. '<?php return $myVar % 2 === $k;',
  463. '<?php return $k === $myVar % 2;',
  464. ['always_move_variable' => true],
  465. ];
  466. yield [
  467. '<?php return $myVar ** 2 === $k;',
  468. '<?php return $k === $myVar ** 2;',
  469. ['always_move_variable' => true],
  470. ];
  471. yield [
  472. '<?php return $myVar < 2 === $k;',
  473. '<?php return $k === $myVar < 2;',
  474. ['always_move_variable' => true],
  475. ];
  476. yield [
  477. '<?php return $myVar > 2 === $k;',
  478. '<?php return $k === $myVar > 2;',
  479. ['always_move_variable' => true],
  480. ];
  481. yield [
  482. '<?php return $myVar <= 2 === $k;',
  483. '<?php return $k === $myVar <= 2;',
  484. ['always_move_variable' => true],
  485. ];
  486. yield [
  487. '<?php return $myVar >= 2 === $k;',
  488. '<?php return $k === $myVar >= 2;',
  489. ['always_move_variable' => true],
  490. ];
  491. yield [
  492. '<?php return $myVar . 2 === $k;',
  493. '<?php return $k === $myVar . 2;',
  494. ['always_move_variable' => true],
  495. ];
  496. yield [
  497. '<?php return $myVar << 2 === $k;',
  498. '<?php return $k === $myVar << 2;',
  499. ['always_move_variable' => true],
  500. ];
  501. yield [
  502. '<?php return $myVar >> 2 === $k;',
  503. '<?php return $k === $myVar >> 2;',
  504. ['always_move_variable' => true],
  505. ];
  506. yield [
  507. '<?php return !$myVar === $k;',
  508. '<?php return $k === !$myVar;',
  509. ['always_move_variable' => true],
  510. ];
  511. yield [
  512. '<?php return $myVar instanceof Foo === $k;',
  513. '<?php return $k === $myVar instanceof Foo;',
  514. ['always_move_variable' => true],
  515. ];
  516. yield [
  517. '<?php return (bool) $myVar === $k;',
  518. '<?php return $k === (bool) $myVar;',
  519. ['always_move_variable' => true],
  520. ];
  521. yield [
  522. '<?php return (int) $myVar === $k;',
  523. '<?php return $k === (int) $myVar;',
  524. ['always_move_variable' => true],
  525. ];
  526. yield [
  527. '<?php return (float) $myVar === $k;',
  528. '<?php return $k === (float) $myVar;',
  529. ['always_move_variable' => true],
  530. ];
  531. yield [
  532. '<?php return (string) $myVar === $k;',
  533. '<?php return $k === (string) $myVar;',
  534. ['always_move_variable' => true],
  535. ];
  536. yield [
  537. '<?php return (array) $myVar === $k;',
  538. '<?php return $k === (array) $myVar;',
  539. ['always_move_variable' => true],
  540. ];
  541. yield [
  542. '<?php return (object) $myVar === $k;',
  543. '<?php return $k === (object) $myVar;',
  544. ['always_move_variable' => true],
  545. ];
  546. yield [
  547. '<?php $a = null === foo();',
  548. '<?php $a = foo() === null;',
  549. ];
  550. yield [
  551. '<?php $a = \'foo\' === foo();',
  552. '<?php $a = foo() === \'foo\';',
  553. ];
  554. yield [
  555. '<?php $a = "foo" === foo();',
  556. '<?php $a = foo() === "foo";',
  557. ];
  558. yield [
  559. '<?php $a = 1 === foo();',
  560. '<?php $a = foo() === 1;',
  561. ];
  562. yield [
  563. '<?php $a = 1.2 === foo();',
  564. '<?php $a = foo() === 1.2;',
  565. ];
  566. yield [
  567. '<?php $a = true === foo();',
  568. '<?php $a = foo() === true;',
  569. ];
  570. yield [
  571. '<?php $a = false === foo();',
  572. '<?php $a = foo() === false;',
  573. ];
  574. yield [
  575. '<?php $a = -1 === reset($foo);',
  576. '<?php $a = reset($foo) === -1;',
  577. ];
  578. yield [
  579. '<?php $a = - 1 === reset($foo);',
  580. '<?php $a = reset($foo) === - 1;',
  581. ];
  582. yield [
  583. '<?php $a = -/* bar */1 === reset($foo);',
  584. '<?php $a = reset($foo) === -/* bar */1;',
  585. ];
  586. yield [
  587. '<?php return array() === $array;',
  588. '<?php return $array === array();',
  589. ];
  590. yield [
  591. '<?php return [] === $array;',
  592. '<?php return $array === [];',
  593. ];
  594. yield [
  595. '<?php return array(/* foo */) === $array;',
  596. '<?php return $array === array(/* foo */);',
  597. ];
  598. yield [
  599. '<?php return [
  600. // 1
  601. ] === $array;',
  602. '<?php return $array === [
  603. // 1
  604. ];',
  605. ];
  606. yield [
  607. '<?php $a = $b = null === $c;',
  608. '<?php $a = $b = $c === null;',
  609. ];
  610. $template = '<?php $a = ($b + $c) %s 1 === true ? 1 : 2;';
  611. $operators = ['||', '&&'];
  612. foreach ($operators as $operator) {
  613. yield [
  614. \sprintf($template, $operator),
  615. null,
  616. ['always_move_variable' => true],
  617. ];
  618. }
  619. $assignmentOperators = ['=', '**=', '*=', '|=', '+=', '-=', '^=', '<<=', '>>=', '&=', '.=', '/=', '%=', '??='];
  620. $logicalOperators = ['xor', 'or', 'and', '||', '&&', '??'];
  621. foreach ([...$assignmentOperators, ...$logicalOperators] as $operator) {
  622. yield [
  623. \sprintf('<?php $a %s 4 === $b ? 2 : 3;', $operator),
  624. \sprintf('<?php $a %s $b === 4 ? 2 : 3;', $operator),
  625. ];
  626. }
  627. foreach ($assignmentOperators as $operator) {
  628. yield [
  629. \sprintf('<?php 1 === $x %s 2;', $operator),
  630. ];
  631. }
  632. yield ['<?php $a = $b + 1 <=> $d;'];
  633. yield [
  634. '<?php $a = new class(10) extends SomeClass implements SomeInterface {} === $a;/**/',
  635. ];
  636. yield [
  637. '<?php $a = $b ?? 1 ?? 2 == $d;',
  638. '<?php $a = $b ?? 1 ?? $d == 2;',
  639. ];
  640. yield [
  641. '<?php $a = 1 === new class(10) extends SomeClass implements SomeInterface {};/**/',
  642. '<?php $a = new class(10) extends SomeClass implements SomeInterface {} === 1;/**/',
  643. ];
  644. yield [
  645. '<?php
  646. function a() {
  647. for ($i = 1; $i <= 3; $i++) {
  648. echo yield 1 === $i ? 1 : 2;
  649. }
  650. }
  651. ',
  652. '<?php
  653. function a() {
  654. for ($i = 1; $i <= 3; $i++) {
  655. echo yield $i === 1 ? 1 : 2;
  656. }
  657. }
  658. ',
  659. ];
  660. yield [
  661. '<?php function test() {return yield 1 !== $a [$b];};',
  662. '<?php function test() {return yield $a [$b] !== 1;};',
  663. ];
  664. yield [
  665. '<?php function test() {return yield 1 === $a;};',
  666. '<?php function test() {return yield $a === 1;};',
  667. ];
  668. yield [
  669. '<?php
  670. $a = 1;
  671. switch ($a) {
  672. case 1 === $a:
  673. echo 123;
  674. break;
  675. }
  676. ',
  677. '<?php
  678. $a = 1;
  679. switch ($a) {
  680. case $a === 1:
  681. echo 123;
  682. break;
  683. }
  684. ',
  685. ];
  686. yield 'require' => [
  687. '<?php require 1 === $var ? "A.php" : "B.php";',
  688. '<?php require $var === 1 ? "A.php" : "B.php";',
  689. ];
  690. yield 'require_once' => [
  691. '<?php require_once 1 === $var ? "A.php" : "B.php";',
  692. '<?php require_once $var === 1 ? "A.php" : "B.php";',
  693. ];
  694. yield 'include' => [
  695. '<?php include 1 === $var ? "A.php" : "B.php";',
  696. '<?php include $var === 1 ? "A.php" : "B.php";',
  697. ];
  698. yield 'include_once' => [
  699. '<?php include_once 1 === $var ? "A.php" : "B.php";',
  700. '<?php include_once $var === 1 ? "A.php" : "B.php";',
  701. ];
  702. yield 'yield from' => [
  703. '<?php function test() {return yield from 1 === $a ? $c : $d;};',
  704. '<?php function test() {return yield from $a === 1 ? $c : $d;};',
  705. ];
  706. yield [
  707. '<?php if (1_000 === $b);',
  708. '<?php if ($b === 1_000);',
  709. ];
  710. yield [
  711. '<?php fn() => $c === array(1) ? $b : $d;',
  712. null,
  713. [
  714. 'less_and_greater' => false,
  715. ],
  716. ];
  717. }
  718. /**
  719. * @dataProvider provideLessGreaterCases
  720. */
  721. public function testFixLessGreater(string $expected, string $input): void
  722. {
  723. $this->fixer->configure(['less_and_greater' => true]);
  724. $this->doTest($expected, $input);
  725. }
  726. /**
  727. * Test with the inverse config.
  728. *
  729. * @dataProvider provideLessGreaterCases
  730. */
  731. public function testFixLessGreaterInverse(string $expected, string $input): void
  732. {
  733. $this->fixer->configure(['less_and_greater' => false]);
  734. $this->doTest($input, $expected);
  735. }
  736. /**
  737. * @return iterable<array{string, string}>
  738. */
  739. public static function provideLessGreaterCases(): iterable
  740. {
  741. yield [
  742. '<?php $a = 3 <= $b;',
  743. '<?php $a = $b >= 3;',
  744. ];
  745. yield [
  746. '<?php $a = 3 > $b;',
  747. '<?php $a = $b < 3;',
  748. ];
  749. yield [
  750. '<?php $a = (3 > $b) || $d;',
  751. '<?php $a = ($b < 3) || $d;',
  752. ];
  753. }
  754. public function testComplexConfiguration(): void
  755. {
  756. $this->fixer->configure([
  757. 'equal' => null,
  758. 'identical' => true,
  759. 'less_and_greater' => false,
  760. ]);
  761. $this->doTest(
  762. '<?php
  763. $a = 1 === $b;
  764. $b = $c != 1;
  765. $c = $c > 3;
  766. ',
  767. '<?php
  768. $a = $b === 1;
  769. $b = $c != 1;
  770. $c = $c > 3;
  771. '
  772. );
  773. }
  774. /**
  775. * @param _AutogeneratedInputConfiguration $config
  776. *
  777. * @dataProvider provideInvalidConfigCases
  778. */
  779. public function testInvalidConfig(array $config, string $expectedMessage): void
  780. {
  781. $this->expectException(InvalidFixerConfigurationException::class);
  782. $this->expectExceptionMessageMatches("#^\\[{$this->fixer->getName()}\\] {$expectedMessage}$#");
  783. $this->fixer->configure($config);
  784. }
  785. public static function provideInvalidConfigCases(): iterable
  786. {
  787. 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)"\.'];
  788. yield [['_invalid_' => true], 'Invalid configuration: The option "_invalid_" does not exist\. Defined options are: "always_move_variable", "equal", "identical", "less_and_greater"\.'];
  789. }
  790. public function testDefinition(): void
  791. {
  792. self::assertInstanceOf(FixerDefinitionInterface::class, $this->fixer->getDefinition());
  793. }
  794. /**
  795. * @dataProvider providePHP71Cases
  796. */
  797. public function testPHP71(string $expected, ?string $input = null): void
  798. {
  799. $this->fixer->configure(['equal' => true, 'identical' => true]);
  800. $this->doTest($expected, $input);
  801. }
  802. /**
  803. * Test with the inverse config.
  804. *
  805. * @dataProvider providePHP71Cases
  806. */
  807. public function testPHP71Inverse(string $expected, ?string $input = null): void
  808. {
  809. $this->fixer->configure(['equal' => false, 'identical' => false]);
  810. if (null === $input) {
  811. $this->doTest($expected);
  812. } else {
  813. $this->doTest($input, $expected);
  814. }
  815. }
  816. /**
  817. * @return iterable<int|string, array{0: string, 1?: string}>
  818. */
  819. public static function providePHP71Cases(): iterable
  820. {
  821. // no fix cases
  822. yield ['<?php list("a" => $a, "b" => $b, "c" => $c) = $c === array(1) ? $b : $d;'];
  823. yield ['<?php list(list("x" => $x1, "y" => $y1), list("x" => $x2, "y" => $y2)) = $points;'];
  824. yield ['<?php list("first" => list($x1, $y1), "second" => list($x2, $y2)) = $points;'];
  825. yield ['<?php [$a, $b, $c] = [1, 2, 3];'];
  826. yield ['<?php ["a" => $a, "b" => $b, "c" => $c] = $a[0];'];
  827. yield ['<?php $b = 7 === [$a] = [7];']; // makes no sense, but valid PHP syntax
  828. yield ['<?php [$a] = $c === array(1) ? $b : $d;'];
  829. yield ['<?php $z = $n == [$a] = $b;'];
  830. yield ['<?php return $n == [$a] = $b;'];
  831. // fix cases
  832. yield [
  833. '<?php list("a" => $a, "b" => $b, "c" => $c) = 1 === $c ? $b : $d;',
  834. '<?php list("a" => $a, "b" => $b, "c" => $c) = $c === 1 ? $b : $d;',
  835. ];
  836. yield [
  837. '<?php list("a" => $a, "b" => $b, "c" => $c) = A::B === $c ? $b : $d;',
  838. '<?php list("a" => $a, "b" => $b, "c" => $c) = $c === A::B ? $b : $d;',
  839. ];
  840. yield [
  841. '<?php list( (2 === $c ? "a" : "b") => $b) = ["a" => 7 === $c ? 5 : 1, "b" => 7];',
  842. '<?php list( ($c === 2 ? "a" : "b") => $b) = ["a" => $c === 7 ? 5 : 1, "b" => 7];',
  843. ];
  844. yield [
  845. '<?php [ (ABC::A === $c ? "a" : "b") => $b] = ["a" => 7 === $c ? 5 : 1, "b" => 7];',
  846. '<?php [ ($c === ABC::A ? "a" : "b") => $b] = ["a" => $c === 7 ? 5 : 1, "b" => 7];',
  847. ];
  848. yield 'Array destruct by ternary.' => [
  849. '<?php [$a] = 11 === $c ? $b : $d;',
  850. '<?php [$a] = $c === 11 ? $b : $d;',
  851. ];
  852. yield [
  853. '<?php $b = [$a] = 7 === [7];', // makes no sense, but valid PHP syntax
  854. '<?php $b = [$a] = [7] === 7;',
  855. ];
  856. }
  857. /**
  858. * @param _AutogeneratedInputConfiguration $config
  859. *
  860. * @dataProvider provideWithConfigCases
  861. */
  862. public function testWithConfig(array $config, string $expected): void
  863. {
  864. $this->fixer->configure($config);
  865. $this->doTest($expected);
  866. }
  867. public static function provideWithConfigCases(): iterable
  868. {
  869. yield [
  870. [
  871. 'identical' => false,
  872. ],
  873. '<?php
  874. $a = [1, 2, 3];
  875. while (2 !== $b = array_pop($c));
  876. ',
  877. ];
  878. yield [
  879. [
  880. 'equal' => false,
  881. 'identical' => false,
  882. ],
  883. '<?php
  884. if ($revision->event == \'created\') {
  885. foreach ($revision->getModified() as $col => $data) {
  886. $model->$col = $data[\'new\'];
  887. }
  888. } else {
  889. foreach ($revision->getModified() as $col => $data) {
  890. $model->$col = $data[\'old\'];
  891. }
  892. }',
  893. ];
  894. }
  895. /**
  896. * @dataProvider provideFixPrePHP80Cases
  897. *
  898. * @requires PHP <8.0
  899. */
  900. public function testFixPrePHP80(string $expected, ?string $input = null): void
  901. {
  902. $this->doTest($expected, $input);
  903. }
  904. /**
  905. * @return iterable<array{0: string, 1?: string}>
  906. */
  907. public static function provideFixPrePHP80Cases(): iterable
  908. {
  909. yield [
  910. '<?php return \A/*5*/\/*6*/B\/*7*/C::MY_CONST === \A/*1*//*1*//*1*//*1*//*1*/\/*2*/B/*3*/\C/*4*/::$myVariable;',
  911. '<?php return \A/*1*//*1*//*1*//*1*//*1*/\/*2*/B/*3*/\C/*4*/::$myVariable === \A/*5*/\/*6*/B\/*7*/C::MY_CONST;',
  912. ];
  913. yield [
  914. '<?php return A\/**//**//**/B/*a*//*a*//*a*//*a*/::MY_CONST === B\C::$myVariable;',
  915. '<?php return B\C::$myVariable === A\/**//**//**/B/*a*//*a*//*a*//*a*/::MY_CONST;',
  916. ];
  917. yield ['<?php return $foo === $bar[$baz]{1};'];
  918. yield ['<?php return $foo->$a[1] === $bar[$baz]{1}->$a[1][2][3]->$d[$z]{1};'];
  919. yield ['<?php return $m->a{2}+1 == 2;'];
  920. yield ['<?php return $m{2}+1 == 2;'];
  921. yield [
  922. '<?php echo 1 === (unset) $a ? 8 : 7;',
  923. '<?php echo (unset) $a === 1 ? 8 : 7;',
  924. ];
  925. }
  926. /**
  927. * @param _AutogeneratedInputConfiguration $config
  928. *
  929. * @dataProvider provideFix80Cases
  930. *
  931. * @requires PHP 8.0
  932. */
  933. public function testFix80(string $expected, ?string $input = null, array $config = []): void
  934. {
  935. $this->fixer->configure($config);
  936. $this->doTest($expected, $input);
  937. }
  938. public static function provideFix80Cases(): iterable
  939. {
  940. yield [
  941. '<?php
  942. if ($a = true === $obj instanceof (foo())) {
  943. echo 1;
  944. }',
  945. '<?php
  946. if ($a = $obj instanceof (foo()) === true) {
  947. echo 1;
  948. }',
  949. ];
  950. yield [
  951. '<?php $i = $this?->getStuff() === $myVariable;',
  952. '<?php $i = $myVariable === $this?->getStuff();',
  953. ['equal' => true, 'identical' => true, 'always_move_variable' => true],
  954. ];
  955. yield [
  956. '<?php 42 === $a->b[5]?->c;',
  957. '<?php $a->b[5]?->c === 42;',
  958. ];
  959. yield [
  960. '<?php return $this->myObject1?->{$index}+$b === "";',
  961. null,
  962. ['equal' => true, 'identical' => true],
  963. ];
  964. yield [
  965. '<?php new Foo(bar: 1 === $var);',
  966. '<?php new Foo(bar: $var === 1);',
  967. ];
  968. }
  969. /**
  970. * @dataProvider provideFix81Cases
  971. *
  972. * @requires PHP 8.1
  973. */
  974. public function testFix81(string $expected, ?string $input = null): void
  975. {
  976. $this->doTest($expected, $input);
  977. }
  978. /**
  979. * @return iterable<string, array{string}>
  980. */
  981. public static function provideFix81Cases(): iterable
  982. {
  983. yield 'does not make a lot of sense but is valid syntax, do not break 1' => [
  984. '<?php $b = strlen( ... ) === $a;',
  985. ];
  986. yield 'does not make a lot of sense but is valid syntax, do not break 2' => [
  987. '<?php $b = $a === strlen( ... );',
  988. ];
  989. }
  990. }