HeaderCommentFixerTest.php 15 KB

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