Parser.php 4.8KB

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