PhpdocOrderFixer.php 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. <?php
  2. /*
  3. * This file is part of the PHP CS utility.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * This source file is subject to the MIT license that is bundled
  8. * with this source code in the file LICENSE.
  9. */
  10. namespace Symfony\CS\Fixer\Contrib;
  11. use Symfony\CS\AbstractFixer;
  12. use Symfony\CS\DocBlock\DocBlock;
  13. use Symfony\CS\Tokenizer\Tokens;
  14. /**
  15. * @author Graham Campbell <graham@mineuk.com>
  16. */
  17. class PhpdocOrderFixer extends AbstractFixer
  18. {
  19. /**
  20. * {@inheritdoc}
  21. */
  22. public function fix(\SplFileInfo $file, Tokens $tokens)
  23. {
  24. foreach ($tokens->findGivenKind(T_DOC_COMMENT) as $token) {
  25. $content = $token->getContent();
  26. // move param to start, return to end, leave throws in the middle
  27. $content = $this->moveParamAnnotations($content);
  28. // we're parsing the content again to make sure the internal
  29. // state of the dockblock is correct after the modifications
  30. $content = $this->moveReturnAnnotations($content);
  31. // persist the content at the end
  32. $token->setContent($content);
  33. }
  34. }
  35. /**
  36. * {@inheritdoc}
  37. */
  38. public function getDescription()
  39. {
  40. return 'Annotations in phpdocs should be ordered so that param annotations come first, then throws annotations, then return annotations.';
  41. }
  42. /**
  43. * {@inheritdoc}
  44. */
  45. public function getPriority()
  46. {
  47. // must be run before the PhpdocSeperationFixer
  48. /*
  49. * Should be run before the php_doc_seperation fixer so that if we
  50. * create incorrect annotation grouping while moving the annotations
  51. * about, we're still ok.
  52. */
  53. return 5;
  54. }
  55. /**
  56. * Move all param annotations in before throws and return annotations.
  57. *
  58. * @param string $content
  59. *
  60. * @return string
  61. */
  62. private function moveParamAnnotations($content)
  63. {
  64. $doc = new DocBlock($content);
  65. $params = $doc->getAnnotationsOfType('param');
  66. // nothing to do if there are no param annotations
  67. if (empty($params)) {
  68. return $content;
  69. }
  70. $others = $doc->getAnnotationsOfType(array('throws', 'return'));
  71. if (empty($others)) {
  72. return $content;
  73. }
  74. // get the index of the final line of the final param annotation
  75. $end = end($params)->getEnd();
  76. $line = $doc->getLine($end);
  77. // move stuff about if required
  78. foreach ($others as $other) {
  79. if ($other->getStart() < $end) {
  80. // we're doing this to maintain the original line indexes
  81. $line->setContent($line->getContent().$other->getContent());
  82. $other->remove();
  83. }
  84. }
  85. return $doc->getContent();
  86. }
  87. /**
  88. * Move all return annotations after param and throws annotations.
  89. *
  90. * @param string $content
  91. *
  92. * @return string
  93. */
  94. private function moveReturnAnnotations($content)
  95. {
  96. $doc = new DocBlock($content);
  97. $returns = $doc->getAnnotationsOfType('return');
  98. // nothing to do if there are no return annotations
  99. if (empty($returns)) {
  100. return $content;
  101. }
  102. $others = $doc->getAnnotationsOfType(array('param', 'throws'));
  103. // nothing to do if there are no other annotations
  104. if (empty($others)) {
  105. return $content;
  106. }
  107. // get the index of the first line of the first return annotation
  108. $start = $returns[0]->getStart();
  109. $line = $doc->getLine($start);
  110. // move stuff about if required
  111. foreach (array_reverse($others) as $other) {
  112. if ($other->getEnd() > $start) {
  113. // we're doing this to maintain the original line indexes
  114. $line->setContent($other->getContent().$line->getContent());
  115. $other->remove();
  116. }
  117. }
  118. return $doc->getContent();
  119. }
  120. }