YodaStyleFixerTest.php 31 KB

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