ArgumentsAnalyzer.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  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\Tokenizer\Analyzer;
  13. use PhpCsFixer\Tokenizer\Analyzer\Analysis\ArgumentAnalysis;
  14. use PhpCsFixer\Tokenizer\Analyzer\Analysis\TypeAnalysis;
  15. use PhpCsFixer\Tokenizer\CT;
  16. use PhpCsFixer\Tokenizer\Tokens;
  17. /**
  18. * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
  19. * @author Vladimir Reznichenko <kalessil@gmail.com>
  20. *
  21. * @internal
  22. */
  23. final class ArgumentsAnalyzer
  24. {
  25. /**
  26. * Count amount of parameters in a function/method reference.
  27. */
  28. public function countArguments(Tokens $tokens, int $openParenthesis, int $closeParenthesis): int
  29. {
  30. return \count($this->getArguments($tokens, $openParenthesis, $closeParenthesis));
  31. }
  32. /**
  33. * Returns start and end token indices of arguments.
  34. *
  35. * Returns an array with each key being the first token of an
  36. * argument and the value the last. Including non-function tokens
  37. * such as comments and white space tokens, but without the separation
  38. * tokens like '(', ',' and ')'.
  39. *
  40. * @return array<int, int>
  41. */
  42. public function getArguments(Tokens $tokens, int $openParenthesis, int $closeParenthesis): array
  43. {
  44. $arguments = [];
  45. $firstSensibleToken = $tokens->getNextMeaningfulToken($openParenthesis);
  46. if ($tokens[$firstSensibleToken]->equals(')')) {
  47. return $arguments;
  48. }
  49. $paramContentIndex = $openParenthesis + 1;
  50. $argumentsStart = $paramContentIndex;
  51. for (; $paramContentIndex < $closeParenthesis; ++$paramContentIndex) {
  52. $token = $tokens[$paramContentIndex];
  53. // skip nested (), [], {} constructs
  54. $blockDefinitionProbe = Tokens::detectBlockType($token);
  55. if (null !== $blockDefinitionProbe && true === $blockDefinitionProbe['isStart']) {
  56. $paramContentIndex = $tokens->findBlockEnd($blockDefinitionProbe['type'], $paramContentIndex);
  57. continue;
  58. }
  59. // if comma matched, increase arguments counter
  60. if ($token->equals(',')) {
  61. if ($tokens->getNextMeaningfulToken($paramContentIndex) === $closeParenthesis) {
  62. break; // trailing ',' in function call (PHP 7.3)
  63. }
  64. $arguments[$argumentsStart] = $paramContentIndex - 1;
  65. $argumentsStart = $paramContentIndex + 1;
  66. }
  67. }
  68. $arguments[$argumentsStart] = $paramContentIndex - 1;
  69. return $arguments;
  70. }
  71. public function getArgumentInfo(Tokens $tokens, int $argumentStart, int $argumentEnd): ArgumentAnalysis
  72. {
  73. static $skipTypes = null;
  74. if (null === $skipTypes) {
  75. $skipTypes = [T_ELLIPSIS, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE];
  76. if (\defined('T_READONLY')) { // @TODO: drop condition when PHP 8.1+ is required
  77. $skipTypes[] = T_READONLY;
  78. }
  79. }
  80. $info = [
  81. 'default' => null,
  82. 'name' => null,
  83. 'name_index' => null,
  84. 'type' => null,
  85. 'type_index_start' => null,
  86. 'type_index_end' => null,
  87. ];
  88. $sawName = false;
  89. for ($index = $argumentStart; $index <= $argumentEnd; ++$index) {
  90. $token = $tokens[$index];
  91. if (\defined('T_ATTRIBUTE') && $token->isGivenKind(T_ATTRIBUTE)) {
  92. $index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ATTRIBUTE, $index);
  93. continue;
  94. }
  95. if (
  96. $token->isComment()
  97. || $token->isWhitespace()
  98. || $token->isGivenKind($skipTypes)
  99. || $token->equals('&')
  100. ) {
  101. continue;
  102. }
  103. if ($token->isGivenKind(T_VARIABLE)) {
  104. $sawName = true;
  105. $info['name_index'] = $index;
  106. $info['name'] = $token->getContent();
  107. continue;
  108. }
  109. if ($token->equals('=')) {
  110. continue;
  111. }
  112. if ($sawName) {
  113. $info['default'] .= $token->getContent();
  114. } else {
  115. $info['type_index_start'] = ($info['type_index_start'] > 0) ? $info['type_index_start'] : $index;
  116. $info['type_index_end'] = $index;
  117. $info['type'] .= $token->getContent();
  118. }
  119. }
  120. if (null === $info['name']) {
  121. $info['type'] = null;
  122. }
  123. return new ArgumentAnalysis(
  124. $info['name'],
  125. $info['name_index'],
  126. $info['default'],
  127. null !== $info['type'] ? new TypeAnalysis($info['type'], $info['type_index_start'], $info['type_index_end']) : null
  128. );
  129. }
  130. }