CommandDataCollector.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Console\DataCollector;
  11. use Symfony\Component\Console\Command\Command;
  12. use Symfony\Component\Console\Debug\CliRequest;
  13. use Symfony\Component\Console\Output\OutputInterface;
  14. use Symfony\Component\Console\SignalRegistry\SignalMap;
  15. use Symfony\Component\HttpFoundation\Request;
  16. use Symfony\Component\HttpFoundation\Response;
  17. use Symfony\Component\HttpKernel\DataCollector\DataCollector;
  18. use Symfony\Component\VarDumper\Cloner\Data;
  19. /**
  20. * @internal
  21. *
  22. * @author Jules Pietri <jules@heahprod.com>
  23. */
  24. final class CommandDataCollector extends DataCollector
  25. {
  26. public function collect(Request $request, Response $response, ?\Throwable $exception = null): void
  27. {
  28. if (!$request instanceof CliRequest) {
  29. return;
  30. }
  31. $command = $request->command;
  32. $application = $command->getApplication();
  33. $this->data = [
  34. 'command' => $this->cloneVar($command->command),
  35. 'exit_code' => $command->exitCode,
  36. 'interrupted_by_signal' => $command->interruptedBySignal,
  37. 'duration' => $command->duration,
  38. 'max_memory_usage' => $command->maxMemoryUsage,
  39. 'verbosity_level' => match ($command->output->getVerbosity()) {
  40. OutputInterface::VERBOSITY_QUIET => 'quiet',
  41. OutputInterface::VERBOSITY_NORMAL => 'normal',
  42. OutputInterface::VERBOSITY_VERBOSE => 'verbose',
  43. OutputInterface::VERBOSITY_VERY_VERBOSE => 'very verbose',
  44. OutputInterface::VERBOSITY_DEBUG => 'debug',
  45. },
  46. 'interactive' => $command->isInteractive,
  47. 'validate_input' => !$command->ignoreValidation,
  48. 'enabled' => $command->isEnabled(),
  49. 'visible' => !$command->isHidden(),
  50. 'input' => $this->cloneVar($command->input),
  51. 'output' => $this->cloneVar($command->output),
  52. 'interactive_inputs' => array_map($this->cloneVar(...), $command->interactiveInputs),
  53. 'signalable' => $command->getSubscribedSignals(),
  54. 'handled_signals' => $command->handledSignals,
  55. 'helper_set' => array_map($this->cloneVar(...), iterator_to_array($command->getHelperSet())),
  56. ];
  57. $baseDefinition = $application->getDefinition();
  58. foreach ($command->arguments as $argName => $argValue) {
  59. if ($baseDefinition->hasArgument($argName)) {
  60. $this->data['application_inputs'][$argName] = $this->cloneVar($argValue);
  61. } else {
  62. $this->data['arguments'][$argName] = $this->cloneVar($argValue);
  63. }
  64. }
  65. foreach ($command->options as $optName => $optValue) {
  66. if ($baseDefinition->hasOption($optName)) {
  67. $this->data['application_inputs']['--'.$optName] = $this->cloneVar($optValue);
  68. } else {
  69. $this->data['options'][$optName] = $this->cloneVar($optValue);
  70. }
  71. }
  72. }
  73. public function getName(): string
  74. {
  75. return 'command';
  76. }
  77. /**
  78. * @return array{
  79. * class?: class-string,
  80. * executor?: string,
  81. * file: string,
  82. * line: int,
  83. * }
  84. */
  85. public function getCommand(): array
  86. {
  87. $class = $this->data['command']->getType();
  88. $r = new \ReflectionMethod($class, 'execute');
  89. if (Command::class !== $r->getDeclaringClass()) {
  90. return [
  91. 'executor' => $class.'::'.$r->name,
  92. 'file' => $r->getFileName(),
  93. 'line' => $r->getStartLine(),
  94. ];
  95. }
  96. $r = new \ReflectionClass($class);
  97. return [
  98. 'class' => $class,
  99. 'file' => $r->getFileName(),
  100. 'line' => $r->getStartLine(),
  101. ];
  102. }
  103. public function getInterruptedBySignal(): ?string
  104. {
  105. if (isset($this->data['interrupted_by_signal'])) {
  106. return sprintf('%s (%d)', SignalMap::getSignalName($this->data['interrupted_by_signal']), $this->data['interrupted_by_signal']);
  107. }
  108. return null;
  109. }
  110. public function getDuration(): string
  111. {
  112. return $this->data['duration'];
  113. }
  114. public function getMaxMemoryUsage(): string
  115. {
  116. return $this->data['max_memory_usage'];
  117. }
  118. public function getVerbosityLevel(): string
  119. {
  120. return $this->data['verbosity_level'];
  121. }
  122. public function getInteractive(): bool
  123. {
  124. return $this->data['interactive'];
  125. }
  126. public function getValidateInput(): bool
  127. {
  128. return $this->data['validate_input'];
  129. }
  130. public function getEnabled(): bool
  131. {
  132. return $this->data['enabled'];
  133. }
  134. public function getVisible(): bool
  135. {
  136. return $this->data['visible'];
  137. }
  138. public function getInput(): Data
  139. {
  140. return $this->data['input'];
  141. }
  142. public function getOutput(): Data
  143. {
  144. return $this->data['output'];
  145. }
  146. /**
  147. * @return Data[]
  148. */
  149. public function getArguments(): array
  150. {
  151. return $this->data['arguments'] ?? [];
  152. }
  153. /**
  154. * @return Data[]
  155. */
  156. public function getOptions(): array
  157. {
  158. return $this->data['options'] ?? [];
  159. }
  160. /**
  161. * @return Data[]
  162. */
  163. public function getApplicationInputs(): array
  164. {
  165. return $this->data['application_inputs'] ?? [];
  166. }
  167. /**
  168. * @return Data[]
  169. */
  170. public function getInteractiveInputs(): array
  171. {
  172. return $this->data['interactive_inputs'] ?? [];
  173. }
  174. public function getSignalable(): array
  175. {
  176. return array_map(
  177. static fn (int $signal): string => sprintf('%s (%d)', SignalMap::getSignalName($signal), $signal),
  178. $this->data['signalable']
  179. );
  180. }
  181. public function getHandledSignals(): array
  182. {
  183. $keys = array_map(
  184. static fn (int $signal): string => sprintf('%s (%d)', SignalMap::getSignalName($signal), $signal),
  185. array_keys($this->data['handled_signals'])
  186. );
  187. return array_combine($keys, array_values($this->data['handled_signals']));
  188. }
  189. /**
  190. * @return Data[]
  191. */
  192. public function getHelperSet(): array
  193. {
  194. return $this->data['helper_set'] ?? [];
  195. }
  196. public function reset(): void
  197. {
  198. $this->data = [];
  199. }
  200. }