* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Console\DataCollector; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Debug\CliRequest; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\SignalRegistry\SignalMap; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\DataCollector\DataCollector; use Symfony\Component\VarDumper\Cloner\Data; /** * @internal * * @author Jules Pietri */ final class CommandDataCollector extends DataCollector { public function collect(Request $request, Response $response, \Throwable $exception = null): void { if (!$request instanceof CliRequest) { return; } $command = $request->command; $application = $command->getApplication(); $this->data = [ 'command' => $this->cloneVar($command->command), 'exit_code' => $command->exitCode, 'interrupted_by_signal' => $command->interruptedBySignal, 'duration' => $command->duration, 'max_memory_usage' => $command->maxMemoryUsage, 'verbosity_level' => match ($command->output->getVerbosity()) { OutputInterface::VERBOSITY_QUIET => 'quiet', OutputInterface::VERBOSITY_NORMAL => 'normal', OutputInterface::VERBOSITY_VERBOSE => 'verbose', OutputInterface::VERBOSITY_VERY_VERBOSE => 'very verbose', OutputInterface::VERBOSITY_DEBUG => 'debug', }, 'interactive' => $command->isInteractive, 'validate_input' => !$command->ignoreValidation, 'enabled' => $command->isEnabled(), 'visible' => !$command->isHidden(), 'input' => $this->cloneVar($command->input), 'output' => $this->cloneVar($command->output), 'interactive_inputs' => array_map($this->cloneVar(...), $command->interactiveInputs), 'signalable' => $command->getSubscribedSignals(), 'handled_signals' => $command->handledSignals, 'helper_set' => array_map($this->cloneVar(...), iterator_to_array($command->getHelperSet())), ]; $baseDefinition = $application->getDefinition(); foreach ($command->arguments as $argName => $argValue) { if ($baseDefinition->hasArgument($argName)) { $this->data['application_inputs'][$argName] = $this->cloneVar($argValue); } else { $this->data['arguments'][$argName] = $this->cloneVar($argValue); } } foreach ($command->options as $optName => $optValue) { if ($baseDefinition->hasOption($optName)) { $this->data['application_inputs']['--'.$optName] = $this->cloneVar($optValue); } else { $this->data['options'][$optName] = $this->cloneVar($optValue); } } } public function getName(): string { return 'command'; } /** * @return array{ * class?: class-string, * executor?: string, * file: string, * line: int, * } */ public function getCommand(): array { $class = $this->data['command']->getType(); $r = new \ReflectionMethod($class, 'execute'); if (Command::class !== $r->getDeclaringClass()) { return [ 'executor' => $class.'::'.$r->name, 'file' => $r->getFileName(), 'line' => $r->getStartLine(), ]; } $r = new \ReflectionClass($class); return [ 'class' => $class, 'file' => $r->getFileName(), 'line' => $r->getStartLine(), ]; } public function getInterruptedBySignal(): ?string { if (isset($this->data['interrupted_by_signal'])) { return sprintf('%s (%d)', SignalMap::getSignalName($this->data['interrupted_by_signal']), $this->data['interrupted_by_signal']); } return null; } public function getDuration(): string { return $this->data['duration']; } public function getMaxMemoryUsage(): string { return $this->data['max_memory_usage']; } public function getVerbosityLevel(): string { return $this->data['verbosity_level']; } public function getInteractive(): bool { return $this->data['interactive']; } public function getValidateInput(): bool { return $this->data['validate_input']; } public function getEnabled(): bool { return $this->data['enabled']; } public function getVisible(): bool { return $this->data['visible']; } public function getInput(): Data { return $this->data['input']; } public function getOutput(): Data { return $this->data['output']; } /** * @return Data[] */ public function getArguments(): array { return $this->data['arguments'] ?? []; } /** * @return Data[] */ public function getOptions(): array { return $this->data['options'] ?? []; } /** * @return Data[] */ public function getApplicationInputs(): array { return $this->data['application_inputs'] ?? []; } /** * @return Data[] */ public function getInteractiveInputs(): array { return $this->data['interactive_inputs'] ?? []; } public function getSignalable(): array { return array_map( static fn (int $signal): string => sprintf('%s (%d)', SignalMap::getSignalName($signal), $signal), $this->data['signalable'] ); } public function getHandledSignals(): array { $keys = array_map( static fn (int $signal): string => sprintf('%s (%d)', SignalMap::getSignalName($signal), $signal), array_keys($this->data['handled_signals']) ); return array_combine($keys, array_values($this->data['handled_signals'])); } /** * @return Data[] */ public function getHelperSet(): array { return $this->data['helper_set'] ?? []; } public function reset(): void { $this->data = []; } }