SingleLineCommentStyleFixer.php 4.6 KB

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