Parser.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * This file is part of Hyperf.
  5. *
  6. * @link https://www.hyperf.io
  7. * @document https://hyperf.wiki
  8. * @contact group@hyperf.io
  9. * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  10. */
  11. namespace Hyperf\Command;
  12. use Hyperf\Stringable\Str;
  13. use InvalidArgumentException;
  14. use Symfony\Component\Console\Input\InputArgument;
  15. use Symfony\Component\Console\Input\InputOption;
  16. class Parser
  17. {
  18. /**
  19. * Parse the given console command definition into an array.
  20. *
  21. * @throws InvalidArgumentException
  22. */
  23. public static function parse(string $expression): array
  24. {
  25. $name = static::name($expression);
  26. if (preg_match_all('/\{\s*(.*?)\s*\}/', $expression, $matches)) {
  27. if (count($matches[1])) {
  28. return array_merge([$name], static::parameters($matches[1]));
  29. }
  30. }
  31. return [$name, [], []];
  32. }
  33. /**
  34. * Extract the name of the command from the expression.
  35. *
  36. * @throws InvalidArgumentException
  37. */
  38. protected static function name(string $expression): string
  39. {
  40. if (! preg_match('/[^\s]+/', $expression, $matches)) {
  41. throw new InvalidArgumentException('Unable to determine command name from signature.');
  42. }
  43. return $matches[0];
  44. }
  45. /**
  46. * Extract all the parameters from the tokens.
  47. */
  48. protected static function parameters(array $tokens): array
  49. {
  50. $arguments = [];
  51. $options = [];
  52. foreach ($tokens as $token) {
  53. if (preg_match('/^-{2,}(.*)/', $token, $matches)) {
  54. $options[] = static::parseOption($matches[1]);
  55. } else {
  56. $arguments[] = static::parseArgument($token);
  57. }
  58. }
  59. return [$arguments, $options];
  60. }
  61. /**
  62. * Parse an argument expression.
  63. */
  64. protected static function parseArgument(string $token): InputArgument
  65. {
  66. [$token, $description] = static::extractDescription($token);
  67. switch (true) {
  68. case Str::endsWith($token, '?*'):
  69. return new InputArgument(trim($token, '?*'), InputArgument::IS_ARRAY, $description);
  70. case Str::endsWith($token, '*'):
  71. return new InputArgument(trim($token, '*'), InputArgument::IS_ARRAY | InputArgument::REQUIRED, $description);
  72. case Str::endsWith($token, '?'):
  73. return new InputArgument(trim($token, '?'), InputArgument::OPTIONAL, $description);
  74. case preg_match('/(.+)\=\*(.+)/', $token, $matches):
  75. return new InputArgument($matches[1], InputArgument::IS_ARRAY, $description, preg_split('/,\s?/', $matches[2]));
  76. case preg_match('/(.+)\=(.+)/', $token, $matches):
  77. return new InputArgument($matches[1], InputArgument::OPTIONAL, $description, $matches[2]);
  78. default:
  79. return new InputArgument($token, InputArgument::REQUIRED, $description);
  80. }
  81. }
  82. /**
  83. * Parse an option expression.
  84. */
  85. protected static function parseOption(string $token): InputOption
  86. {
  87. [$token, $description] = static::extractDescription($token);
  88. $matches = preg_split('/\s*\|\s*/', $token, 2);
  89. if (isset($matches[1])) {
  90. $shortcut = $matches[0];
  91. $token = $matches[1];
  92. } else {
  93. $shortcut = null;
  94. }
  95. switch (true) {
  96. case Str::endsWith($token, '='):
  97. return new InputOption(trim($token, '='), $shortcut, InputOption::VALUE_OPTIONAL, $description);
  98. case Str::endsWith($token, '=*'):
  99. return new InputOption(trim($token, '=*'), $shortcut, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, $description);
  100. case preg_match('/(.+)\=\*(.+)/', $token, $matches):
  101. return new InputOption($matches[1], $shortcut, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, $description, preg_split('/,\s?/', $matches[2]));
  102. case preg_match('/(.+)\=(.+)/', $token, $matches):
  103. return new InputOption($matches[1], $shortcut, InputOption::VALUE_OPTIONAL, $description, $matches[2]);
  104. default:
  105. return new InputOption($token, $shortcut, InputOption::VALUE_NONE, $description);
  106. }
  107. }
  108. /**
  109. * Parse the token into its token and description segments.
  110. */
  111. protected static function extractDescription(string $token): array
  112. {
  113. $parts = preg_split('/\s+:\s+/', trim($token), 2);
  114. return count($parts) === 2 ? $parts : [$token, ''];
  115. }
  116. }