HeaderCommentFixerTest.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848
  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\Comment;
  13. use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException;
  14. use PhpCsFixer\ConfigurationException\RequiredFixerConfigurationException;
  15. use PhpCsFixer\Fixer\Comment\HeaderCommentFixer;
  16. use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
  17. use PhpCsFixer\WhitespacesFixerConfig;
  18. /**
  19. * @internal
  20. *
  21. * @covers \PhpCsFixer\Fixer\Comment\HeaderCommentFixer
  22. */
  23. final class HeaderCommentFixerTest extends AbstractFixerTestCase
  24. {
  25. /**
  26. * @param array<string, mixed> $configuration
  27. *
  28. * @dataProvider provideFixCases
  29. */
  30. public function testFix(array $configuration, string $expected, ?string $input = null): void
  31. {
  32. $this->fixer->configure($configuration);
  33. $this->doTest($expected, $input);
  34. }
  35. public static function provideFixCases(): iterable
  36. {
  37. return [
  38. [
  39. ['header' => ''],
  40. '<?php
  41. $a;',
  42. '<?php
  43. /**
  44. * new
  45. */
  46. $a;',
  47. ],
  48. [
  49. [
  50. 'header' => 'tmp',
  51. 'location' => 'after_declare_strict',
  52. ],
  53. '<?php
  54. declare(strict_types=1);
  55. /*
  56. * tmp
  57. */
  58. namespace A\B;
  59. echo 1;',
  60. '<?php
  61. declare(strict_types=1);namespace A\B;
  62. echo 1;',
  63. ],
  64. [
  65. [
  66. 'header' => 'tmp',
  67. 'location' => 'after_declare_strict',
  68. 'separate' => 'bottom',
  69. 'comment_type' => HeaderCommentFixer::HEADER_PHPDOC,
  70. ],
  71. '<?php
  72. declare(strict_types=1);
  73. /**
  74. * tmp
  75. */
  76. namespace A\B;
  77. echo 1;',
  78. '<?php
  79. declare(strict_types=1);
  80. namespace A\B;
  81. echo 1;',
  82. ],
  83. [
  84. [
  85. 'header' => 'tmp',
  86. 'location' => 'after_open',
  87. ],
  88. '<?php
  89. /*
  90. * tmp
  91. */
  92. declare(strict_types=1);
  93. namespace A\B;
  94. echo 1;',
  95. '<?php
  96. declare(strict_types=1);
  97. namespace A\B;
  98. echo 1;',
  99. ],
  100. [
  101. [
  102. 'header' => 'new',
  103. 'comment_type' => HeaderCommentFixer::HEADER_COMMENT,
  104. ],
  105. '<?php
  106. /*
  107. * new
  108. */
  109. ',
  110. '<?php
  111. /** test */
  112. ',
  113. ],
  114. [
  115. [
  116. 'header' => 'new',
  117. 'comment_type' => HeaderCommentFixer::HEADER_PHPDOC,
  118. ],
  119. '<?php
  120. /**
  121. * new
  122. */
  123. ',
  124. '<?php
  125. /* test */
  126. ',
  127. ],
  128. [
  129. [
  130. 'header' => 'def',
  131. 'comment_type' => HeaderCommentFixer::HEADER_PHPDOC,
  132. ],
  133. '<?php
  134. /**
  135. * def
  136. */
  137. ',
  138. '<?php
  139. ',
  140. ],
  141. [
  142. ['header' => 'xyz'],
  143. '<?php
  144. /*
  145. * xyz
  146. */
  147. $b;',
  148. '<?php
  149. $b;',
  150. ],
  151. [
  152. [
  153. 'header' => 'xyz123',
  154. 'separate' => 'none',
  155. ],
  156. '<?php
  157. /*
  158. * xyz123
  159. */
  160. $a;',
  161. '<?php
  162. $a;',
  163. ],
  164. [
  165. [
  166. 'header' => 'abc',
  167. 'comment_type' => HeaderCommentFixer::HEADER_PHPDOC,
  168. ],
  169. '<?php
  170. /**
  171. * abc
  172. */
  173. $c;',
  174. '<?php
  175. $c;',
  176. ],
  177. [
  178. [
  179. 'header' => 'ghi',
  180. 'separate' => 'both',
  181. ],
  182. '<?php
  183. /*
  184. * ghi
  185. */
  186. $d;',
  187. '<?php
  188. $d;',
  189. ],
  190. [
  191. [
  192. 'header' => 'ghi',
  193. 'separate' => 'top',
  194. ],
  195. '<?php
  196. /*
  197. * ghi
  198. */
  199. $d;',
  200. '<?php
  201. $d;',
  202. ],
  203. [
  204. [
  205. 'header' => 'tmp',
  206. 'location' => 'after_declare_strict',
  207. ],
  208. '<?php
  209. /*
  210. * tmp
  211. */
  212. declare(ticks=1);
  213. echo 1;',
  214. '<?php
  215. declare(ticks=1);
  216. echo 1;',
  217. ],
  218. [
  219. ['header' => 'Foo'],
  220. '<?php
  221. /*
  222. * Foo
  223. */
  224. echo \'bar\';',
  225. '<?php echo \'bar\';',
  226. ],
  227. [
  228. ['header' => 'x'],
  229. '<?php
  230. /*
  231. * x
  232. */
  233. echo \'a\';',
  234. '<?php
  235. /*
  236. * y
  237. * z
  238. */
  239. echo \'a\';',
  240. ],
  241. [
  242. ['header' => "a\na"],
  243. '<?php
  244. /*
  245. * a
  246. * a
  247. */
  248. echo \'x\';',
  249. '<?php
  250. /*
  251. * b
  252. * c
  253. */
  254. echo \'x\';',
  255. ],
  256. [
  257. [
  258. 'header' => 'foo',
  259. 'location' => 'after_open',
  260. 'separate' => 'bottom',
  261. 'comment_type' => HeaderCommentFixer::HEADER_PHPDOC,
  262. ],
  263. '<?php
  264. /**
  265. * foo
  266. */
  267. declare(strict_types=1);
  268. namespace A;
  269. echo 1;',
  270. '<?php
  271. declare(strict_types=1);
  272. /**
  273. * foo
  274. */
  275. namespace A;
  276. echo 1;',
  277. ],
  278. [
  279. [
  280. 'header' => 'foo',
  281. 'location' => 'after_open',
  282. 'separate' => 'bottom',
  283. 'comment_type' => HeaderCommentFixer::HEADER_PHPDOC,
  284. ],
  285. '<?php
  286. /**
  287. * foo
  288. */
  289. declare(strict_types=1);
  290. /**
  291. * bar
  292. */
  293. namespace A;
  294. echo 1;',
  295. '<?php
  296. declare(strict_types=1);
  297. /**
  298. * bar
  299. */
  300. namespace A;
  301. echo 1;',
  302. ],
  303. [
  304. [
  305. 'header' => 'Foo',
  306. 'separate' => 'none',
  307. ],
  308. '<?php
  309. declare(strict_types=1);
  310. /*
  311. * Foo
  312. */
  313. namespace SebastianBergmann\Foo;
  314. class Bar
  315. {
  316. }',
  317. '<?php
  318. /*
  319. * Foo
  320. */
  321. declare(strict_types=1);
  322. namespace SebastianBergmann\Foo;
  323. class Bar
  324. {
  325. }',
  326. ],
  327. [
  328. ['header' => 'tmp'],
  329. '<?php
  330. /*
  331. * tmp
  332. */
  333. /**
  334. * Foo class doc.
  335. */
  336. class Foo {}',
  337. '<?php
  338. /**
  339. * Foo class doc.
  340. */
  341. class Foo {}',
  342. ],
  343. [
  344. ['header' => 'tmp'],
  345. '<?php
  346. /*
  347. * tmp
  348. */
  349. class Foo {}',
  350. '<?php
  351. /*
  352. * Foo class doc.
  353. */
  354. class Foo {}',
  355. ],
  356. [
  357. [
  358. 'header' => 'tmp',
  359. 'comment_type' => HeaderCommentFixer::HEADER_PHPDOC,
  360. ],
  361. '<?php
  362. /**
  363. * tmp
  364. */
  365. /**
  366. * Foo class doc.
  367. */
  368. class Foo {}',
  369. '<?php
  370. /**
  371. * Foo class doc.
  372. */
  373. class Foo {}',
  374. ],
  375. [
  376. [
  377. 'header' => 'tmp',
  378. 'comment_type' => HeaderCommentFixer::HEADER_PHPDOC,
  379. ],
  380. '<?php
  381. /**
  382. * tmp
  383. */
  384. class Foo {}',
  385. '<?php
  386. /**
  387. * tmp
  388. */
  389. class Foo {}',
  390. ],
  391. [
  392. [
  393. 'header' => 'tmp',
  394. 'separate' => 'top',
  395. ],
  396. '<?php
  397. /*
  398. * tmp
  399. */
  400. class Foo {}',
  401. '<?php
  402. /**
  403. * Foo class doc.
  404. */
  405. class Foo {}',
  406. ],
  407. [
  408. [
  409. 'header' => 'bar',
  410. 'location' => 'after_open',
  411. ],
  412. '<?php
  413. /*
  414. * bar
  415. */
  416. declare(strict_types=1);
  417. // foo
  418. foo();',
  419. '<?php
  420. /*
  421. * foo
  422. */
  423. declare(strict_types=1);
  424. // foo
  425. foo();',
  426. ],
  427. [
  428. [
  429. 'header' => 'bar',
  430. 'location' => 'after_open',
  431. ],
  432. '<?php
  433. /*
  434. * bar
  435. */
  436. declare(strict_types=1);
  437. /* foo */
  438. foo();',
  439. '<?php
  440. /*
  441. * foo
  442. */
  443. declare(strict_types=1);
  444. /* foo */
  445. foo();',
  446. ],
  447. [
  448. [
  449. 'header' => 'tmp',
  450. 'location' => 'after_declare_strict',
  451. ],
  452. '<?php
  453. /*
  454. * tmp
  455. */
  456. declare(strict_types=1) ?>',
  457. '<?php
  458. declare(strict_types=1) ?>',
  459. ],
  460. [
  461. [
  462. 'header' => 'tmp',
  463. 'location' => 'after_declare_strict',
  464. ],
  465. '#!/usr/bin/env php
  466. <?php
  467. declare(strict_types=1);
  468. /*
  469. * tmp
  470. */
  471. namespace A\B;
  472. echo 1;',
  473. '#!/usr/bin/env php
  474. <?php
  475. declare(strict_types=1);namespace A\B;
  476. echo 1;',
  477. ],
  478. [
  479. [
  480. 'header' => 'tmp',
  481. 'location' => 'after_open',
  482. ],
  483. 'Short mixed file A
  484. Hello<?php echo "World!"; ?>',
  485. ],
  486. [
  487. [
  488. 'header' => 'tmp',
  489. 'location' => 'after_open',
  490. ],
  491. 'Short mixed file B
  492. <?php echo "Hello"; ?>World!',
  493. ],
  494. [
  495. [
  496. 'header' => 'tmp',
  497. 'location' => 'after_open',
  498. ],
  499. 'File with anything at the beginning and with multiple opening tags are not supported
  500. <?php
  501. echo 1;
  502. ?>Hello World!<?php
  503. script_continues_here();',
  504. ],
  505. ];
  506. }
  507. public function testDefaultConfiguration(): void
  508. {
  509. $this->fixer->configure(['header' => 'a']);
  510. $this->doTest(
  511. '<?php
  512. /*
  513. * a
  514. */
  515. echo 1;',
  516. '<?php
  517. echo 1;'
  518. );
  519. }
  520. /**
  521. * @param null|array<string, mixed> $configuration
  522. *
  523. * @dataProvider provideMisconfigurationCases
  524. */
  525. public function testMisconfiguration(?array $configuration, string $exceptionMessage): void
  526. {
  527. $this->expectException(InvalidFixerConfigurationException::class);
  528. $this->expectExceptionMessageMatches("#^\\[header_comment\\] {$exceptionMessage}$#");
  529. $this->fixer->configure($configuration);
  530. }
  531. public static function provideMisconfigurationCases(): iterable
  532. {
  533. return [
  534. [[], 'Missing required configuration: The required option "header" is missing.'],
  535. [
  536. ['header' => 1],
  537. 'Invalid configuration: The option "header" with value 1 is expected to be of type "string", but is of type "(int|integer)"\.',
  538. ],
  539. [
  540. [
  541. 'header' => '',
  542. 'comment_type' => 'foo',
  543. ],
  544. 'Invalid configuration: The option "comment_type" with value "foo" is invalid\. Accepted values are: "PHPDoc", "comment"\.',
  545. ],
  546. [
  547. [
  548. 'header' => '',
  549. 'comment_type' => new \stdClass(),
  550. ],
  551. 'Invalid configuration: The option "comment_type" with value stdClass is invalid\. Accepted values are: "PHPDoc", "comment"\.',
  552. ],
  553. [
  554. [
  555. 'header' => '',
  556. 'location' => new \stdClass(),
  557. ],
  558. 'Invalid configuration: The option "location" with value stdClass is invalid\. Accepted values are: "after_open", "after_declare_strict"\.',
  559. ],
  560. [
  561. [
  562. 'header' => '',
  563. 'separate' => new \stdClass(),
  564. ],
  565. 'Invalid configuration: The option "separate" with value stdClass is invalid\. Accepted values are: "both", "top", "bottom", "none"\.',
  566. ],
  567. ];
  568. }
  569. /**
  570. * @dataProvider provideHeaderGenerationCases
  571. */
  572. public function testHeaderGeneration(string $expected, string $header, string $type): void
  573. {
  574. $this->fixer->configure([
  575. 'header' => $header,
  576. 'comment_type' => $type,
  577. ]);
  578. $this->doTest(
  579. '<?php
  580. '.$expected.'
  581. echo 1;',
  582. '<?php
  583. echo 1;'
  584. );
  585. }
  586. public static function provideHeaderGenerationCases(): iterable
  587. {
  588. return [
  589. [
  590. '/*
  591. * a
  592. */',
  593. 'a',
  594. HeaderCommentFixer::HEADER_COMMENT,
  595. ],
  596. [
  597. '/**
  598. * a
  599. */',
  600. 'a',
  601. HeaderCommentFixer::HEADER_PHPDOC,
  602. ],
  603. ];
  604. }
  605. /**
  606. * @dataProvider provideDoNotTouchCases
  607. */
  608. public function testDoNotTouch(string $expected): void
  609. {
  610. $this->fixer->configure([
  611. 'header' => '',
  612. ]);
  613. $this->doTest($expected);
  614. }
  615. public static function provideDoNotTouchCases(): iterable
  616. {
  617. return [
  618. ["<?php\nphpinfo();\n?>\n<?"],
  619. [" <?php\nphpinfo();\n"],
  620. ["<?php\nphpinfo();\n?><hr/>"],
  621. [" <?php\n"],
  622. ['<?= 1?>'],
  623. ["<?= 1?><?php\n"],
  624. ["<?= 1?>\n<?php\n"],
  625. ["<?php\n// comment 1\n?><?php\n// comment 2\n"],
  626. ];
  627. }
  628. public function testWithoutConfiguration(): void
  629. {
  630. $this->expectException(RequiredFixerConfigurationException::class);
  631. $this->doTest('<?php echo 1;');
  632. }
  633. /**
  634. * @param array<string, mixed> $configuration
  635. *
  636. * @dataProvider provideMessyWhitespacesCases
  637. */
  638. public function testMessyWhitespaces(array $configuration, string $expected, ?string $input = null): void
  639. {
  640. $this->fixer->setWhitespacesConfig(new WhitespacesFixerConfig("\t", "\r\n"));
  641. $this->fixer->configure($configuration);
  642. $this->doTest($expected, $input);
  643. }
  644. public static function provideMessyWhitespacesCases(): iterable
  645. {
  646. return [
  647. [
  648. [
  649. 'header' => 'whitemess',
  650. 'location' => 'after_declare_strict',
  651. 'separate' => 'bottom',
  652. 'comment_type' => HeaderCommentFixer::HEADER_PHPDOC,
  653. ],
  654. "<?php\r\ndeclare(strict_types=1);\r\n/**\r\n * whitemess\r\n */\r\n\r\nnamespace A\\B;\r\n\r\necho 1;",
  655. "<?php\r\ndeclare(strict_types=1);\r\n\r\nnamespace A\\B;\r\n\r\necho 1;",
  656. ],
  657. ];
  658. }
  659. public function testConfigurationUpdatedWithWhitespsacesConfig(): void
  660. {
  661. $this->fixer->configure(['header' => 'Foo']);
  662. $this->doTest(
  663. "<?php\n\n/*\n * Foo\n */\n\necho 1;",
  664. "<?php\necho 1;"
  665. );
  666. $this->fixer->setWhitespacesConfig(new WhitespacesFixerConfig(' ', "\r\n"));
  667. $this->doTest(
  668. "<?php\r\n\r\n/*\r\n * Foo\r\n */\r\n\r\necho 1;",
  669. "<?php\r\necho 1;"
  670. );
  671. $this->fixer->configure(['header' => 'Bar']);
  672. $this->doTest(
  673. "<?php\r\n\r\n/*\r\n * Bar\r\n */\r\n\r\necho 1;",
  674. "<?php\r\necho 1;"
  675. );
  676. $this->fixer->setWhitespacesConfig(new WhitespacesFixerConfig(' ', "\n"));
  677. $this->doTest(
  678. "<?php\n\n/*\n * Bar\n */\n\necho 1;",
  679. "<?php\necho 1;"
  680. );
  681. }
  682. public function testInvalidHeaderConfiguration(): void
  683. {
  684. $this->expectException(InvalidFixerConfigurationException::class);
  685. $this->expectExceptionMessageMatches('#^\[header_comment\] Cannot use \'\*/\' in header\.$#');
  686. $this->fixer->configure([
  687. 'header' => '/** test */',
  688. 'comment_type' => HeaderCommentFixer::HEADER_PHPDOC,
  689. ]);
  690. }
  691. /**
  692. * @param array<string, mixed> $configuration
  693. *
  694. * @dataProvider provideFix81Cases
  695. *
  696. * @requires PHP 8.1
  697. */
  698. public function testFix81(array $configuration, string $expected, ?string $input = null): void
  699. {
  700. $this->fixer->configure($configuration);
  701. $this->doTest($expected, $input);
  702. }
  703. public static function provideFix81Cases(): iterable
  704. {
  705. yield [
  706. ['header' => 'tmp'],
  707. '<?php
  708. /*
  709. * tmp
  710. */
  711. /**
  712. * Foo class doc.
  713. */
  714. enum Foo {}',
  715. '<?php
  716. /**
  717. * Foo class doc.
  718. */
  719. enum Foo {}',
  720. ];
  721. }
  722. }