VisibilityRequiredFixerTest.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  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\ClassNotation;
  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\ClassNotation\VisibilityRequiredFixer
  21. *
  22. * @extends AbstractFixerTestCase<\PhpCsFixer\Fixer\ClassNotation\VisibilityRequiredFixer>
  23. *
  24. * @phpstan-import-type _AutogeneratedInputConfiguration from \PhpCsFixer\Fixer\ClassNotation\VisibilityRequiredFixer
  25. */
  26. final class VisibilityRequiredFixerTest extends AbstractFixerTestCase
  27. {
  28. /**
  29. * @param _AutogeneratedInputConfiguration $configuration
  30. *
  31. * @dataProvider provideFixCases
  32. */
  33. public function testFix(string $expected, ?string $input = null, array $configuration = []): void
  34. {
  35. $this->fixer->configure($configuration);
  36. $this->doTest($expected, $input);
  37. }
  38. public static function provideFixCases(): iterable
  39. {
  40. yield 'properties' => [
  41. <<<'EOF'
  42. <?php
  43. class Foo {
  44. public $var;
  45. protected $var_foo;
  46. private $FooBar;
  47. public static $var1;
  48. protected static $var_foo2;
  49. private static $FooBar1;
  50. public static $var2;
  51. protected static $var_foo3;
  52. private static $FooBar2;
  53. private static $FooBar3;
  54. public $old = 'foo';
  55. }
  56. EOF,
  57. <<<'EOF'
  58. <?php
  59. class Foo {
  60. public $var;
  61. protected $var_foo;
  62. private $FooBar;
  63. static public $var1;
  64. static protected $var_foo2;
  65. static private $FooBar1;
  66. public static $var2;
  67. protected static $var_foo3;
  68. private static $FooBar2;
  69. private static
  70. $FooBar3;
  71. var $old = 'foo';
  72. }
  73. EOF,
  74. ];
  75. yield 'properties after method' => [
  76. <<<'EOF'
  77. <?php
  78. class Foo {
  79. public function aaa() {}
  80. public $bbb;
  81. }
  82. EOF,
  83. <<<'EOF'
  84. <?php
  85. class Foo {
  86. public function aaa() {}
  87. var $bbb;
  88. }
  89. EOF,
  90. ];
  91. yield 'methods' => [
  92. <<<'EOF'
  93. <?php
  94. class MyTestWithAnonymousClass extends TestCase
  95. {
  96. public function setUp()
  97. {
  98. $provider = new class(function () {}) {};
  99. }
  100. public function testSomethingWithMoney(
  101. Money $amount
  102. ) {
  103. }
  104. }
  105. EOF,
  106. <<<'EOF'
  107. <?php
  108. class MyTestWithAnonymousClass extends TestCase
  109. {
  110. function setUp()
  111. {
  112. $provider = new class(function () {}) {};
  113. }
  114. public function testSomethingWithMoney(
  115. Money $amount
  116. ) {
  117. }
  118. }
  119. EOF,
  120. ];
  121. yield [
  122. <<<'EOF'
  123. <?php
  124. abstract class Foo {
  125. public function& foo0() {}
  126. public function & foo1() {}
  127. public function &foo2() {}
  128. protected function foo3($b) {}
  129. abstract protected function foo4();
  130. private function foo5() {}
  131. final public function foo6() {}
  132. abstract public function foo7();
  133. final public function foo8() {}
  134. abstract public function foo9();
  135. public static function fooA() {}
  136. public static function fooD() {}
  137. final public static function fooE() {}
  138. abstract public function fooF();
  139. public function fooG ($foo) {}
  140. public function fooH() {
  141. static $foo;
  142. $bar = function($baz) {};
  143. }
  144. }
  145. EOF,
  146. <<<'EOF'
  147. <?php
  148. abstract class Foo {
  149. public function& foo0() {}
  150. public function & foo1() {}
  151. function &foo2() {}
  152. protected function foo3($b) {}
  153. protected
  154. abstract function foo4();
  155. private function foo5() {}
  156. final public function foo6() {}
  157. abstract public function foo7();
  158. public final function foo8() {}
  159. public abstract function foo9();
  160. public static function fooA() {}
  161. public static
  162. function fooD() {}
  163. final static function fooE() {}
  164. abstract function fooF();
  165. function fooG ($foo) {}
  166. function fooH() {
  167. static $foo;
  168. $bar = function($baz) {};
  169. }
  170. }
  171. EOF,
  172. ];
  173. yield [
  174. <<<'EOF'
  175. <?php
  176. abstract class Foo1 {
  177. public function& foo0($a) {}
  178. }
  179. EOF,
  180. <<<'EOF'
  181. <?php
  182. abstract class Foo1 {
  183. function& foo0($a) {}
  184. }
  185. EOF,
  186. ];
  187. yield 'leave functions alone' => [<<<'EOF'
  188. <?php
  189. function foo() {
  190. static $foo;
  191. }
  192. EOF];
  193. yield 'leave functions alone with variables matching OOP words' => [<<<'EOF'
  194. <?php
  195. function foo() {
  196. static $class;
  197. $interface = 'foo';
  198. $trait = 'bar';
  199. }
  200. EOF];
  201. yield 'leave functions alone inside conditionals' => [<<<'EOF'
  202. <?php
  203. if (!function_exists('foo')) {
  204. function foo($arg)
  205. {
  206. return $arg;
  207. }
  208. }
  209. EOF];
  210. yield 'leave functions alone inside conditionals with OOP word in comment' => [<<<'EOF'
  211. <?php
  212. /* class <= this is just a stop-word */
  213. if (!function_exists('foo')) {
  214. function foo($arg)
  215. {
  216. return $arg;
  217. }
  218. }
  219. EOF];
  220. yield 'leave functions alone with OOP word in comment' => [<<<'EOF'
  221. <?php
  222. /* class */
  223. function foo($arg)
  224. {
  225. return $arg;
  226. }
  227. EOF];
  228. yield 'leave functions alone outside classes with OOP word in inline HTML' => [<<<'EOF'
  229. <?php
  230. if (!function_exists('foo')) {
  231. function foo($arg)
  232. {
  233. ?>
  234. <div class="test"></div>
  235. <?php
  236. return $arg;
  237. }
  238. }
  239. EOF];
  240. yield 'leave functions alone outside classes with OOP word in string value' => [<<<'EOF'
  241. <?php
  242. if (!function_exists('foo')) {
  243. function foo($arg)
  244. {
  245. return 'she has class right?';
  246. }
  247. }
  248. EOF];
  249. yield 'leave functions alone outside classes with OOP word in function name' => [<<<'EOF'
  250. <?php
  251. comment_class();
  252. if (!function_exists('foo')) {
  253. function foo($arg)
  254. {
  255. return $arg;
  256. }
  257. }
  258. EOF];
  259. yield 'leave functions alone after class' => [<<<'EOF'
  260. <?php
  261. class Foo
  262. {
  263. public $foo;
  264. }
  265. if (!function_exists('bar')) {
  266. function bar()
  267. {
  268. return 'bar';
  269. }
  270. }
  271. EOF];
  272. yield 'curly open syntax' => [<<<'EOF'
  273. <?php
  274. class Foo
  275. {
  276. private $bar;
  277. public function foo()
  278. {
  279. $foo = "foo";
  280. $fooA = "ab{$foo}cd";
  281. $bar = "bar"; // test if variable after T_CURLY_OPEN is intact
  282. }
  283. }
  284. EOF];
  285. yield 'dollar open curly braces syntax' => [<<<'EOF'
  286. <?php
  287. class Foo {
  288. public function bar()
  289. {
  290. $foo = "foo${width}foo";
  291. $bar = "bar"; // test if variable after T_DOLLAR_OPEN_CURLY_BRACES is intact
  292. }
  293. }
  294. EOF];
  295. yield 'leave JavaScript outside PHP alone' => [<<<'EOF'
  296. <?php
  297. function foo()
  298. {
  299. return "foo";
  300. }
  301. ?>
  302. <script type="text/javascript">
  303. function foo(bar) {
  304. alert(bar);
  305. }
  306. </script>
  307. EOF];
  308. yield 'leave JavaScript in string alone' => [<<<'EOF'
  309. <?php
  310. function registerJS()
  311. {
  312. echo '<script type="text/javascript">
  313. function foo(bar) {
  314. alert(bar);
  315. }
  316. </script>';
  317. }
  318. EOF];
  319. yield 'leave JavaScript in variable alone' => [<<<'EOF'
  320. <?php
  321. class Foo
  322. {
  323. public function bar()
  324. {
  325. $script = <<<JAVASCRIPT
  326. <script type="text/javascript">
  327. function foo(bar) {
  328. alert(bar);
  329. }
  330. </script>
  331. JAVASCRIPT;
  332. return $script;
  333. }
  334. }
  335. EOF];
  336. yield 'comma separated properties' => [<<<'EOF'
  337. <?php
  338. class Foo
  339. {
  340. public $foo1;
  341. private $foo2;
  342. protected $bar1, $bar2;
  343. public $baz1 = null, $baz2, $baz3 = false;
  344. public $foo, $bar;
  345. }
  346. EOF,
  347. <<<'EOF'
  348. <?php
  349. class Foo
  350. {
  351. var $foo1;
  352. private $foo2;
  353. protected $bar1, $bar2;
  354. public $baz1 = null, $baz2, $baz3 = false;
  355. var $foo, $bar;
  356. }
  357. EOF];
  358. yield 'var declarations with array value' => [<<<'EOF'
  359. <?php
  360. class Foo
  361. {
  362. public $foo1 = 1;
  363. public $foo2a = array('foo');
  364. public $foo2b = ['foo'];
  365. public $foo3a = array('foo', 'bar');
  366. public $foo3b = ['foo', 'bar'];
  367. public $foo4a = '1a', $foo5a = array(1, 2, 3), $foo6a = 10;
  368. public $foo4b = '1b', $foo5b = array(1, 2, 3), $foo6b = 10;
  369. }
  370. EOF,
  371. <<<'EOF'
  372. <?php
  373. class Foo
  374. {
  375. var $foo1 = 1;
  376. var $foo2a = array('foo');
  377. var $foo2b = ['foo'];
  378. var $foo3a = array('foo', 'bar');
  379. var $foo3b = ['foo', 'bar'];
  380. public $foo4a = '1a', $foo5a = array(1, 2, 3), $foo6a = 10;
  381. public $foo4b = '1b', $foo5b = array(1, 2, 3), $foo6b = 10;
  382. }
  383. EOF];
  384. yield [
  385. '<?php class A { public const B=1; }',
  386. '<?php class A { const B=1; }',
  387. ['elements' => ['const']],
  388. ];
  389. yield [
  390. '<?php class A { public const B=1;public const C=1;/**/public const#a
  391. D=1;public const E=1;//
  392. public const F=1; }',
  393. '<?php class A { const B=1;const C=1;/**/const#a
  394. D=1;const E=1;//
  395. const F=1; }',
  396. ['elements' => ['const']],
  397. ];
  398. yield [
  399. '<?php class A { private const B=1; protected const C=2; public const D=4; public $a; function A(){} }',
  400. '<?php class A { private const B=1; protected const C=2; const D=4; public $a; function A(){} }',
  401. ['elements' => ['const']],
  402. ];
  403. yield [
  404. '<?php
  405. class foo
  406. {
  407. public const A = 1, B =2, C =3;
  408. public const TWO = ONE * 2;
  409. public const THREE = ONE + self::TWO;
  410. public const SENTENCE = "The value of THREE is ".self::THREE;
  411. }
  412. ',
  413. '<?php
  414. class foo
  415. {
  416. const A = 1, B =2, C =3;
  417. const TWO = ONE * 2;
  418. const THREE = ONE + self::TWO;
  419. const SENTENCE = "The value of THREE is ".self::THREE;
  420. }
  421. ',
  422. ['elements' => ['const']],
  423. ];
  424. yield 'comment' => [
  425. '<?php
  426. class A
  427. {# We will have a function below
  428. # It will be static
  429. # and awesome
  430. public static function# <- this is the function
  431. AB# <- this is the name
  432. (#
  433. )#
  434. {#
  435. }#
  436. }
  437. ',
  438. '<?php
  439. class A
  440. {# We will have a function below
  441. static# It will be static
  442. # and awesome
  443. function# <- this is the function
  444. AB# <- this is the name
  445. (#
  446. )#
  447. {#
  448. }#
  449. }
  450. ',
  451. ];
  452. yield 'anonymous class' => [
  453. '<?php
  454. $a = new class() {
  455. public function a() {
  456. }
  457. };
  458. class C
  459. {
  460. public function A()
  461. {
  462. $a = new class() {public function a() {}};
  463. }
  464. }
  465. ',
  466. '<?php
  467. $a = new class() {
  468. function a() {
  469. }
  470. };
  471. class C
  472. {
  473. function A()
  474. {
  475. $a = new class() {function a() {}};
  476. }
  477. }
  478. ',
  479. ];
  480. yield 'removing newlines between keywords' => [
  481. '<?php
  482. class Foo
  483. {
  484. public $bar;
  485. final public static function bar() {}
  486. final public static function baz() {}
  487. }',
  488. '<?php
  489. class Foo
  490. {
  491. var
  492. $bar;
  493. final
  494. public
  495. static
  496. function bar() {}
  497. static
  498. final
  499. function baz() {}
  500. }',
  501. ];
  502. yield 'keeping comment' => [
  503. '<?php
  504. class Foo
  505. {
  506. /* constant */ private const BAR = 3;
  507. /* variable */ private $bar;
  508. /* function */ private function bar() {}
  509. }',
  510. '<?php
  511. class Foo
  512. {
  513. private /* constant */ const BAR = 3;
  514. private /* variable */ $bar;
  515. private /* function */ function bar() {}
  516. }',
  517. ['elements' => ['property', 'method', 'const']],
  518. ];
  519. yield 'fixing with all keywords' => [
  520. '<?php
  521. abstract class Foo
  522. {
  523. abstract protected static function fooA();
  524. abstract protected static function fooB();
  525. abstract protected static function fooC();
  526. abstract protected static function fooD();
  527. abstract protected static function fooE();
  528. abstract protected static function fooF();
  529. abstract public static function fooG();
  530. abstract public static function fooH();
  531. }
  532. ',
  533. '<?php
  534. abstract class Foo
  535. {
  536. abstract protected static function fooA();
  537. abstract static protected function fooB();
  538. protected abstract static function fooC();
  539. protected static abstract function fooD();
  540. static abstract protected function fooE();
  541. static protected abstract function fooF();
  542. abstract static function fooG();
  543. static abstract function fooH();
  544. }
  545. ',
  546. ];
  547. yield [
  548. '<?php class Foo { private int $foo; }',
  549. ];
  550. yield [
  551. '<?php class Foo { protected ?string $foo; }',
  552. ];
  553. yield [
  554. '<?php class Foo { public ? string $foo; }',
  555. ];
  556. yield [
  557. '<?php class Foo { public ? string $foo; }',
  558. '<?php class Foo { var ? string $foo; }',
  559. ];
  560. yield [
  561. '<?php class Foo { public static Foo\Bar $foo; }',
  562. '<?php class Foo { static public Foo\Bar $foo; }',
  563. ];
  564. yield [
  565. '<?php class Foo { public array $foo; }',
  566. ];
  567. yield [
  568. '<?php class Foo { public ?array $foo; }',
  569. ];
  570. yield [
  571. '<?php class Foo { public static ?array $foo; }',
  572. '<?php class Foo { static public ?array $foo; }',
  573. ];
  574. }
  575. /**
  576. * @requires PHP 8.0
  577. *
  578. * @dataProvider provideFix80Cases
  579. */
  580. public function testFix80(string $expected, ?string $input = null): void
  581. {
  582. $this->doTest($expected, $input);
  583. }
  584. /**
  585. * @return iterable<array{0: string, 1?: string}>
  586. */
  587. public static function provideFix80Cases(): iterable
  588. {
  589. yield [
  590. '<?php class Foo { private int|float|null $foo; }',
  591. ];
  592. yield [
  593. '<?php class Foo { private int | /* or empty */ null $foo; }',
  594. ];
  595. yield [
  596. '<?php class Foo { private array|null $foo; }',
  597. ];
  598. yield [
  599. '<?php class Foo { private null|array $foo; }',
  600. ];
  601. yield [
  602. '<?php class Foo { public static null|array $foo; }',
  603. '<?php class Foo { static null|array $foo; }',
  604. ];
  605. }
  606. /**
  607. * @dataProvider provideFix81Cases
  608. *
  609. * @requires PHP 8.1
  610. */
  611. public function testFix81(string $expected, ?string $input = null): void
  612. {
  613. $this->doTest($expected, $input);
  614. }
  615. /**
  616. * @return iterable<array{0: string, 1?: string}>
  617. */
  618. public static function provideFix81Cases(): iterable
  619. {
  620. yield [
  621. '<?php class Foo { public Foo1&Bar $foo; }',
  622. ];
  623. yield [
  624. '<?php
  625. class Foo
  626. {
  627. public readonly string $prop2a;
  628. }
  629. ',
  630. '<?php
  631. class Foo
  632. {
  633. readonly public string $prop2a;
  634. }
  635. ',
  636. ];
  637. yield [
  638. '<?php
  639. class Foo
  640. {
  641. public readonly string $prop1;
  642. public readonly string $prop2;
  643. }
  644. ',
  645. '<?php
  646. class Foo
  647. {
  648. readonly string $prop1;
  649. public readonly string $prop2;
  650. }
  651. ',
  652. ];
  653. yield [
  654. '<?php
  655. class Foo
  656. {
  657. final public const B = "2";
  658. }
  659. ',
  660. '<?php
  661. class Foo
  662. {
  663. public final const B = "2";
  664. }
  665. ',
  666. ];
  667. yield [
  668. '<?php
  669. class Foo
  670. {
  671. final public const B = "2";
  672. }
  673. ',
  674. '<?php
  675. class Foo
  676. {
  677. final const B = "2";
  678. }
  679. ',
  680. ];
  681. yield [
  682. '<?php
  683. enum Foo {
  684. case CAT;
  685. public function test(): self { return $this; }
  686. }
  687. var_dump(Foo::CAT->test());',
  688. '<?php
  689. enum Foo {
  690. case CAT;
  691. function test(): self { return $this; }
  692. }
  693. var_dump(Foo::CAT->test());',
  694. ];
  695. }
  696. /**
  697. * @requires PHP 8.2
  698. *
  699. * @dataProvider provideFix82Cases
  700. */
  701. public function testFix82(string $expected, ?string $input = null): void
  702. {
  703. $this->doTest($expected, $input);
  704. }
  705. /**
  706. * @return iterable<array{0: string, 1?: string}>
  707. */
  708. public static function provideFix82Cases(): iterable
  709. {
  710. yield [
  711. '<?php trait Foo { public const Bar = 1; }',
  712. '<?php trait Foo { const Bar = 1; }',
  713. ];
  714. yield [
  715. '<?php class Foo {
  716. public (A&B)|C|D $x;
  717. protected A|(B&C)|D $y;
  718. private A|B|(C&D) $z;
  719. }',
  720. ];
  721. }
  722. /**
  723. * @param _AutogeneratedInputConfiguration $config
  724. *
  725. * @dataProvider provideInvalidConfigurationCases
  726. */
  727. public function testInvalidConfiguration(array $config, string $expectedMessage): void
  728. {
  729. $this->expectException(InvalidFixerConfigurationException::class);
  730. $this->expectExceptionMessageMatches($expectedMessage);
  731. $this->fixer->configure($config);
  732. }
  733. /**
  734. * @return iterable<string, array{array<string, mixed>, string}>
  735. */
  736. public static function provideInvalidConfigurationCases(): iterable
  737. {
  738. yield 'invalid type' => [
  739. ['elements' => [null]],
  740. '/^\[visibility_required\] Invalid configuration: The option "elements" .*\.$/',
  741. ];
  742. yield 'invalid value' => [
  743. ['elements' => ['_unknown_']],
  744. '/^\[visibility_required\] Invalid configuration: The option "elements" .*\.$/',
  745. ];
  746. }
  747. }