SingleLineCommentStyleFixer.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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\Fixer\Comment;
  13. use PhpCsFixer\AbstractFixer;
  14. use PhpCsFixer\Fixer\ConfigurableFixerInterface;
  15. use PhpCsFixer\FixerConfiguration\AllowedValueSubset;
  16. use PhpCsFixer\FixerConfiguration\FixerConfigurationResolver;
  17. use PhpCsFixer\FixerConfiguration\FixerConfigurationResolverInterface;
  18. use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
  19. use PhpCsFixer\FixerDefinition\CodeSample;
  20. use PhpCsFixer\FixerDefinition\FixerDefinition;
  21. use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
  22. use PhpCsFixer\Preg;
  23. use PhpCsFixer\Tokenizer\Token;
  24. use PhpCsFixer\Tokenizer\Tokens;
  25. /**
  26. * @author Filippo Tessarotto <zoeslam@gmail.com>
  27. */
  28. final class SingleLineCommentStyleFixer extends AbstractFixer implements ConfigurableFixerInterface
  29. {
  30. /**
  31. * @var bool
  32. */
  33. private $asteriskEnabled;
  34. /**
  35. * @var bool
  36. */
  37. private $hashEnabled;
  38. /**
  39. * {@inheritdoc}
  40. */
  41. public function configure(array $configuration): void
  42. {
  43. parent::configure($configuration);
  44. $this->asteriskEnabled = \in_array('asterisk', $this->configuration['comment_types'], true);
  45. $this->hashEnabled = \in_array('hash', $this->configuration['comment_types'], true);
  46. }
  47. /**
  48. * {@inheritdoc}
  49. */
  50. public function getDefinition(): FixerDefinitionInterface
  51. {
  52. return new FixerDefinition(
  53. 'Single-line comments and multi-line comments with only one line of actual content should use the `//` syntax.',
  54. [
  55. new CodeSample(
  56. '<?php
  57. /* asterisk comment */
  58. $a = 1;
  59. # hash comment
  60. $b = 2;
  61. /*
  62. * multi-line
  63. * comment
  64. */
  65. $c = 3;
  66. '
  67. ),
  68. new CodeSample(
  69. '<?php
  70. /* first comment */
  71. $a = 1;
  72. /*
  73. * second comment
  74. */
  75. $b = 2;
  76. /*
  77. * third
  78. * comment
  79. */
  80. $c = 3;
  81. ',
  82. ['comment_types' => ['asterisk']]
  83. ),
  84. new CodeSample(
  85. "<?php # comment\n",
  86. ['comment_types' => ['hash']]
  87. ),
  88. ]
  89. );
  90. }
  91. /**
  92. * {@inheritdoc}
  93. *
  94. * Must run after HeaderCommentFixer, NoUselessReturnFixer, PhpdocToCommentFixer.
  95. */
  96. public function getPriority(): int
  97. {
  98. return -31;
  99. }
  100. /**
  101. * {@inheritdoc}
  102. */
  103. public function isCandidate(Tokens $tokens): bool
  104. {
  105. return $tokens->isTokenKindFound(T_COMMENT);
  106. }
  107. /**
  108. * {@inheritdoc}
  109. */
  110. protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
  111. {
  112. foreach ($tokens as $index => $token) {
  113. if (!$token->isGivenKind(T_COMMENT)) {
  114. continue;
  115. }
  116. $content = $token->getContent();
  117. $commentContent = substr($content, 2, -2) ?: '';
  118. if ($this->hashEnabled && str_starts_with($content, '#')) {
  119. if (isset($content[1]) && '[' === $content[1]) {
  120. continue; // This might be an attribute on PHP8, do not change
  121. }
  122. $tokens[$index] = new Token([$token->getId(), '//'.substr($content, 1)]);
  123. continue;
  124. }
  125. if (
  126. !$this->asteriskEnabled
  127. || str_contains($commentContent, '?>')
  128. || !str_starts_with($content, '/*')
  129. || 1 === Preg::match('/[^\s\*].*\R.*[^\s\*]/s', $commentContent)
  130. ) {
  131. continue;
  132. }
  133. $nextTokenIndex = $index + 1;
  134. if (isset($tokens[$nextTokenIndex])) {
  135. $nextToken = $tokens[$nextTokenIndex];
  136. if (!$nextToken->isWhitespace() || 1 !== Preg::match('/\R/', $nextToken->getContent())) {
  137. continue;
  138. }
  139. $tokens[$nextTokenIndex] = new Token([$nextToken->getId(), ltrim($nextToken->getContent(), " \t")]);
  140. }
  141. $content = '//';
  142. if (1 === Preg::match('/[^\s\*]/', $commentContent)) {
  143. $content = '// '.Preg::replace('/[\s\*]*([^\s\*](?:.+[^\s\*])?)[\s\*]*/', '\1', $commentContent);
  144. }
  145. $tokens[$index] = new Token([$token->getId(), $content]);
  146. }
  147. }
  148. /**
  149. * {@inheritdoc}
  150. */
  151. protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
  152. {
  153. return new FixerConfigurationResolver([
  154. (new FixerOptionBuilder('comment_types', 'List of comment types to fix'))
  155. ->setAllowedTypes(['array'])
  156. ->setAllowedValues([new AllowedValueSubset(['asterisk', 'hash'])])
  157. ->setDefault(['asterisk', 'hash'])
  158. ->getOption(),
  159. ]);
  160. }
  161. }