CommandDataCollector.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  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' => $command->invokableCommandInfo ?? $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_SILENT => 'silent',
  41. OutputInterface::VERBOSITY_QUIET => 'quiet',
  42. OutputInterface::VERBOSITY_NORMAL => 'normal',
  43. OutputInterface::VERBOSITY_VERBOSE => 'verbose',
  44. OutputInterface::VERBOSITY_VERY_VERBOSE => 'very verbose',
  45. OutputInterface::VERBOSITY_DEBUG => 'debug',
  46. },
  47. 'interactive' => $command->isInteractive,
  48. 'validate_input' => !$command->ignoreValidation,
  49. 'enabled' => $command->isEnabled(),
  50. 'visible' => !$command->isHidden(),
  51. 'input' => $this->cloneVar($command->input),
  52. 'output' => $this->cloneVar($command->output),
  53. 'interactive_inputs' => array_map($this->cloneVar(...), $command->interactiveInputs),
  54. 'signalable' => $command->getSubscribedSignals(),
  55. 'handled_signals' => $command->handledSignals,
  56. 'helper_set' => array_map($this->cloneVar(...), iterator_to_array($command->getHelperSet())),
  57. ];
  58. $baseDefinition = $application->getDefinition();
  59. foreach ($command->arguments as $argName => $argValue) {
  60. if ($baseDefinition->hasArgument($argName)) {
  61. $this->data['application_inputs'][$argName] = $this->cloneVar($argValue);
  62. } else {
  63. $this->data['arguments'][$argName] = $this->cloneVar($argValue);
  64. }
  65. }
  66. foreach ($command->options as $optName => $optValue) {
  67. if ($baseDefinition->hasOption($optName)) {
  68. $this->data['application_inputs']['--'.$optName] = $this->cloneVar($optValue);
  69. } else {
  70. $this->data['options'][$optName] = $this->cloneVar($optValue);
  71. }
  72. }
  73. }
  74. public function getName(): string
  75. {
  76. return 'command';
  77. }
  78. /**
  79. * @return array{
  80. * class?: class-string,
  81. * executor?: string,
  82. * file: string,
  83. * line: int,
  84. * }
  85. */
  86. public function getCommand(): array
  87. {
  88. if (\is_array($this->data['command'])) {
  89. return $this->data['command'];
  90. }
  91. $class = $this->data['command']->getType();
  92. $r = new \ReflectionMethod($class, 'execute');
  93. if (Command::class !== $r->getDeclaringClass()) {
  94. return [
  95. 'executor' => $class.'::'.$r->name,
  96. 'file' => $r->getFileName(),
  97. 'line' => $r->getStartLine(),
  98. ];
  99. }
  100. $r = new \ReflectionClass($class);
  101. return [
  102. 'class' => $class,
  103. 'file' => $r->getFileName(),
  104. 'line' => $r->getStartLine(),
  105. ];
  106. }
  107. public function getInterruptedBySignal(): ?string
  108. {
  109. if (isset($this->data['interrupted_by_signal'])) {
  110. return \sprintf('%s (%d)', SignalMap::getSignalName($this->data['interrupted_by_signal']), $this->data['interrupted_by_signal']);
  111. }
  112. return null;
  113. }
  114. public function getDuration(): string
  115. {
  116. return $this->data['duration'];
  117. }
  118. public function getMaxMemoryUsage(): string
  119. {
  120. return $this->data['max_memory_usage'];
  121. }
  122. public function getVerbosityLevel(): string
  123. {
  124. return $this->data['verbosity_level'];
  125. }
  126. public function getInteractive(): bool
  127. {
  128. return $this->data['interactive'];
  129. }
  130. public function getValidateInput(): bool
  131. {
  132. return $this->data['validate_input'];
  133. }
  134. public function getEnabled(): bool
  135. {
  136. return $this->data['enabled'];
  137. }
  138. public function getVisible(): bool
  139. {
  140. return $this->data['visible'];
  141. }
  142. public function getInput(): Data
  143. {
  144. return $this->data['input'];
  145. }
  146. public function getOutput(): Data
  147. {
  148. return $this->data['output'];
  149. }
  150. /**
  151. * @return Data[]
  152. */
  153. public function getArguments(): array
  154. {
  155. return $this->data['arguments'] ?? [];
  156. }
  157. /**
  158. * @return Data[]
  159. */
  160. public function getOptions(): array
  161. {
  162. return $this->data['options'] ?? [];
  163. }
  164. /**
  165. * @return Data[]
  166. */
  167. public function getApplicationInputs(): array
  168. {
  169. return $this->data['application_inputs'] ?? [];
  170. }
  171. /**
  172. * @return Data[]
  173. */
  174. public function getInteractiveInputs(): array
  175. {
  176. return $this->data['interactive_inputs'] ?? [];
  177. }
  178. public function getSignalable(): array
  179. {
  180. return array_map(
  181. static fn (int $signal): string => \sprintf('%s (%d)', SignalMap::getSignalName($signal), $signal),
  182. $this->data['signalable']
  183. );
  184. }
  185. public function getHandledSignals(): array
  186. {
  187. $keys = array_map(
  188. static fn (int $signal): string => \sprintf('%s (%d)', SignalMap::getSignalName($signal), $signal),
  189. array_keys($this->data['handled_signals'])
  190. );
  191. return array_combine($keys, array_values($this->data['handled_signals']));
  192. }
  193. /**
  194. * @return Data[]
  195. */
  196. public function getHelperSet(): array
  197. {
  198. return $this->data['helper_set'] ?? [];
  199. }
  200. public function reset(): void
  201. {
  202. $this->data = [];
  203. }
  204. }