update patch 1
This commit is contained in:
+82
-34
@@ -22,6 +22,7 @@ use Symfony\Component\Console\CommandLoader\CommandLoaderInterface;
|
||||
use Symfony\Component\Console\Completion\CompletionInput;
|
||||
use Symfony\Component\Console\Completion\CompletionSuggestions;
|
||||
use Symfony\Component\Console\Completion\Suggestion;
|
||||
use Symfony\Component\Console\Event\ConsoleAlarmEvent;
|
||||
use Symfony\Component\Console\Event\ConsoleCommandEvent;
|
||||
use Symfony\Component\Console\Event\ConsoleErrorEvent;
|
||||
use Symfony\Component\Console\Event\ConsoleSignalEvent;
|
||||
@@ -88,6 +89,7 @@ class Application implements ResetInterface
|
||||
private bool $initialized = false;
|
||||
private ?SignalRegistry $signalRegistry = null;
|
||||
private array $signalsToDispatchEvent = [];
|
||||
private ?int $alarmInterval = null;
|
||||
|
||||
public function __construct(
|
||||
private string $name = 'UNKNOWN',
|
||||
@@ -97,7 +99,7 @@ class Application implements ResetInterface
|
||||
$this->defaultCommand = 'list';
|
||||
if (\defined('SIGINT') && SignalRegistry::isSupported()) {
|
||||
$this->signalRegistry = new SignalRegistry();
|
||||
$this->signalsToDispatchEvent = [\SIGINT, \SIGQUIT, \SIGTERM, \SIGUSR1, \SIGUSR2];
|
||||
$this->signalsToDispatchEvent = [\SIGINT, \SIGQUIT, \SIGTERM, \SIGUSR1, \SIGUSR2, \SIGALRM];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +130,30 @@ class Application implements ResetInterface
|
||||
$this->signalsToDispatchEvent = $signalsToDispatchEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the interval to schedule a SIGALRM signal in seconds.
|
||||
*/
|
||||
public function setAlarmInterval(?int $seconds): void
|
||||
{
|
||||
$this->alarmInterval = $seconds;
|
||||
$this->scheduleAlarm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the interval in seconds on which a SIGALRM signal is dispatched.
|
||||
*/
|
||||
public function getAlarmInterval(): ?int
|
||||
{
|
||||
return $this->alarmInterval;
|
||||
}
|
||||
|
||||
private function scheduleAlarm(): void
|
||||
{
|
||||
if (null !== $this->alarmInterval) {
|
||||
$this->getSignalRegistry()->scheduleAlarm($this->alarmInterval);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the current application.
|
||||
*
|
||||
@@ -262,9 +288,9 @@ class Application implements ResetInterface
|
||||
|
||||
$style = new SymfonyStyle($input, $output);
|
||||
$output->writeln('');
|
||||
$formattedBlock = (new FormatterHelper())->formatBlock(sprintf('Command "%s" is not defined.', $name), 'error', true);
|
||||
$formattedBlock = (new FormatterHelper())->formatBlock(\sprintf('Command "%s" is not defined.', $name), 'error', true);
|
||||
$output->writeln($formattedBlock);
|
||||
if (!$style->confirm(sprintf('Do you want to run "%s" instead? ', $alternative), false)) {
|
||||
if (!$style->confirm(\sprintf('Do you want to run "%s" instead? ', $alternative), false)) {
|
||||
if (null !== $this->dispatcher) {
|
||||
$event = new ConsoleErrorEvent($input, $output, $e);
|
||||
$this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
|
||||
@@ -383,8 +409,6 @@ class Application implements ResetInterface
|
||||
|
||||
if (CompletionInput::TYPE_OPTION_NAME === $input->getCompletionType()) {
|
||||
$suggestions->suggestOptions($this->getDefinition()->getOptions());
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,7 +499,7 @@ class Application implements ResetInterface
|
||||
{
|
||||
if ('UNKNOWN' !== $this->getName()) {
|
||||
if ('UNKNOWN' !== $this->getVersion()) {
|
||||
return sprintf('%s <info>%s</info>', $this->getName(), $this->getVersion());
|
||||
return \sprintf('%s <info>%s</info>', $this->getName(), $this->getVersion());
|
||||
}
|
||||
|
||||
return $this->getName();
|
||||
@@ -530,7 +554,7 @@ class Application implements ResetInterface
|
||||
}
|
||||
|
||||
if (!$command->getName()) {
|
||||
throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_debug_type($command)));
|
||||
throw new LogicException(\sprintf('The command defined in "%s" cannot have an empty name.', get_debug_type($command)));
|
||||
}
|
||||
|
||||
$this->commands[$command->getName()] = $command;
|
||||
@@ -552,12 +576,12 @@ class Application implements ResetInterface
|
||||
$this->init();
|
||||
|
||||
if (!$this->has($name)) {
|
||||
throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name));
|
||||
throw new CommandNotFoundException(\sprintf('The command "%s" does not exist.', $name));
|
||||
}
|
||||
|
||||
// When the command has a different name than the one used at the command loader level
|
||||
if (!isset($this->commands[$name])) {
|
||||
throw new CommandNotFoundException(sprintf('The "%s" command cannot be found because it is registered under multiple names. Make sure you don\'t set a different name via constructor or "setName()".', $name));
|
||||
throw new CommandNotFoundException(\sprintf('The "%s" command cannot be found because it is registered under multiple names. Make sure you don\'t set a different name via constructor or "setName()".', $name));
|
||||
}
|
||||
|
||||
$command = $this->commands[$name];
|
||||
@@ -621,7 +645,7 @@ class Application implements ResetInterface
|
||||
$namespaces = preg_grep('{^'.$expr.'}', $allNamespaces);
|
||||
|
||||
if (!$namespaces) {
|
||||
$message = sprintf('There are no commands defined in the "%s" namespace.', $namespace);
|
||||
$message = \sprintf('There are no commands defined in the "%s" namespace.', $namespace);
|
||||
|
||||
if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) {
|
||||
if (1 == \count($alternatives)) {
|
||||
@@ -638,7 +662,7 @@ class Application implements ResetInterface
|
||||
|
||||
$exact = \in_array($namespace, $namespaces, true);
|
||||
if (\count($namespaces) > 1 && !$exact) {
|
||||
throw new NamespaceNotFoundException(sprintf("The namespace \"%s\" is ambiguous.\nDid you mean one of these?\n%s.", $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces));
|
||||
throw new NamespaceNotFoundException(\sprintf("The namespace \"%s\" is ambiguous.\nDid you mean one of these?\n%s.", $namespace, $this->getAbbreviationSuggestions(array_values($namespaces))), array_values($namespaces));
|
||||
}
|
||||
|
||||
return $exact ? $namespace : reset($namespaces);
|
||||
@@ -685,7 +709,7 @@ class Application implements ResetInterface
|
||||
$this->findNamespace(substr($name, 0, $pos));
|
||||
}
|
||||
|
||||
$message = sprintf('Command "%s" is not defined.', $name);
|
||||
$message = \sprintf('Command "%s" is not defined.', $name);
|
||||
|
||||
if ($alternatives = $this->findAlternatives($name, $allCommands)) {
|
||||
// remove hidden commands
|
||||
@@ -740,14 +764,14 @@ class Application implements ResetInterface
|
||||
if (\count($commands) > 1) {
|
||||
$suggestions = $this->getAbbreviationSuggestions(array_filter($abbrevs));
|
||||
|
||||
throw new CommandNotFoundException(sprintf("Command \"%s\" is ambiguous.\nDid you mean one of these?\n%s.", $name, $suggestions), array_values($commands));
|
||||
throw new CommandNotFoundException(\sprintf("Command \"%s\" is ambiguous.\nDid you mean one of these?\n%s.", $name, $suggestions), array_values($commands));
|
||||
}
|
||||
}
|
||||
|
||||
$command = $this->get(reset($commands));
|
||||
|
||||
if ($command->isHidden()) {
|
||||
throw new CommandNotFoundException(sprintf('The command "%s" does not exist.', $name));
|
||||
throw new CommandNotFoundException(\sprintf('The command "%s" does not exist.', $name));
|
||||
}
|
||||
|
||||
return $command;
|
||||
@@ -822,7 +846,7 @@ class Application implements ResetInterface
|
||||
$this->doRenderThrowable($e, $output);
|
||||
|
||||
if (null !== $this->runningCommand) {
|
||||
$output->writeln(sprintf('<info>%s</info>', OutputFormatter::escape(sprintf($this->runningCommand->getSynopsis(), $this->getName()))), OutputInterface::VERBOSITY_QUIET);
|
||||
$output->writeln(\sprintf('<info>%s</info>', OutputFormatter::escape(\sprintf($this->runningCommand->getSynopsis(), $this->getName()))), OutputInterface::VERBOSITY_QUIET);
|
||||
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
|
||||
}
|
||||
}
|
||||
@@ -833,7 +857,7 @@ class Application implements ResetInterface
|
||||
$message = trim($e->getMessage());
|
||||
if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
|
||||
$class = get_debug_type($e);
|
||||
$title = sprintf(' [%s%s] ', $class, 0 !== ($code = $e->getCode()) ? ' ('.$code.')' : '');
|
||||
$title = \sprintf(' [%s%s] ', $class, 0 !== ($code = $e->getCode()) ? ' ('.$code.')' : '');
|
||||
$len = Helper::width($title);
|
||||
} else {
|
||||
$len = 0;
|
||||
@@ -857,14 +881,14 @@ class Application implements ResetInterface
|
||||
|
||||
$messages = [];
|
||||
if (!$e instanceof ExceptionInterface || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
|
||||
$messages[] = sprintf('<comment>%s</comment>', OutputFormatter::escape(sprintf('In %s line %s:', basename($e->getFile()) ?: 'n/a', $e->getLine() ?: 'n/a')));
|
||||
$messages[] = \sprintf('<comment>%s</comment>', OutputFormatter::escape(\sprintf('In %s line %s:', basename($e->getFile()) ?: 'n/a', $e->getLine() ?: 'n/a')));
|
||||
}
|
||||
$messages[] = $emptyLine = sprintf('<error>%s</error>', str_repeat(' ', $len));
|
||||
$messages[] = $emptyLine = \sprintf('<error>%s</error>', str_repeat(' ', $len));
|
||||
if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
|
||||
$messages[] = sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - Helper::width($title))));
|
||||
$messages[] = \sprintf('<error>%s%s</error>', $title, str_repeat(' ', max(0, $len - Helper::width($title))));
|
||||
}
|
||||
foreach ($lines as $line) {
|
||||
$messages[] = sprintf('<error> %s %s</error>', OutputFormatter::escape($line[0]), str_repeat(' ', $len - $line[1]));
|
||||
$messages[] = \sprintf('<error> %s %s</error>', OutputFormatter::escape($line[0]), str_repeat(' ', $len - $line[1]));
|
||||
}
|
||||
$messages[] = $emptyLine;
|
||||
$messages[] = '';
|
||||
@@ -891,7 +915,7 @@ class Application implements ResetInterface
|
||||
$file = $trace[$i]['file'] ?? 'n/a';
|
||||
$line = $trace[$i]['line'] ?? 'n/a';
|
||||
|
||||
$output->writeln(sprintf(' %s%s at <info>%s:%s</info>', $class, $function ? $type.$function.'()' : '', $file, $line), OutputInterface::VERBOSITY_QUIET);
|
||||
$output->writeln(\sprintf(' %s%s at <info>%s:%s</info>', $class, $function ? $type.$function.'()' : '', $file, $line), OutputInterface::VERBOSITY_QUIET);
|
||||
}
|
||||
|
||||
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
|
||||
@@ -915,6 +939,9 @@ class Application implements ResetInterface
|
||||
}
|
||||
|
||||
switch ($shellVerbosity = (int) getenv('SHELL_VERBOSITY')) {
|
||||
case -2:
|
||||
$output->setVerbosity(OutputInterface::VERBOSITY_SILENT);
|
||||
break;
|
||||
case -1:
|
||||
$output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
|
||||
break;
|
||||
@@ -932,7 +959,10 @@ class Application implements ResetInterface
|
||||
break;
|
||||
}
|
||||
|
||||
if (true === $input->hasParameterOption(['--quiet', '-q'], true)) {
|
||||
if (true === $input->hasParameterOption(['--silent'], true)) {
|
||||
$output->setVerbosity(OutputInterface::VERBOSITY_SILENT);
|
||||
$shellVerbosity = -2;
|
||||
} elseif (true === $input->hasParameterOption(['--quiet', '-q'], true)) {
|
||||
$output->setVerbosity(OutputInterface::VERBOSITY_QUIET);
|
||||
$shellVerbosity = -1;
|
||||
} else {
|
||||
@@ -948,7 +978,7 @@ class Application implements ResetInterface
|
||||
}
|
||||
}
|
||||
|
||||
if (-1 === $shellVerbosity) {
|
||||
if (0 > $shellVerbosity) {
|
||||
$input->setInteractive(false);
|
||||
}
|
||||
|
||||
@@ -977,34 +1007,47 @@ class Application implements ResetInterface
|
||||
|
||||
$commandSignals = $command instanceof SignalableCommandInterface ? $command->getSubscribedSignals() : [];
|
||||
if ($commandSignals || $this->dispatcher && $this->signalsToDispatchEvent) {
|
||||
if (!$this->signalRegistry) {
|
||||
throw new RuntimeException('Unable to subscribe to signal events. Make sure that the "pcntl" extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.');
|
||||
}
|
||||
$signalRegistry = $this->getSignalRegistry();
|
||||
|
||||
if (Terminal::hasSttyAvailable()) {
|
||||
$sttyMode = shell_exec('stty -g');
|
||||
|
||||
foreach ([\SIGINT, \SIGQUIT, \SIGTERM] as $signal) {
|
||||
$this->signalRegistry->register($signal, static fn () => shell_exec('stty '.$sttyMode));
|
||||
$signalRegistry->register($signal, static fn () => shell_exec('stty '.$sttyMode));
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->dispatcher) {
|
||||
// We register application signals, so that we can dispatch the event
|
||||
foreach ($this->signalsToDispatchEvent as $signal) {
|
||||
$event = new ConsoleSignalEvent($command, $input, $output, $signal);
|
||||
$signalEvent = new ConsoleSignalEvent($command, $input, $output, $signal);
|
||||
$alarmEvent = \SIGALRM === $signal ? new ConsoleAlarmEvent($command, $input, $output) : null;
|
||||
|
||||
$this->signalRegistry->register($signal, function ($signal) use ($event, $command, $commandSignals) {
|
||||
$this->dispatcher->dispatch($event, ConsoleEvents::SIGNAL);
|
||||
$exitCode = $event->getExitCode();
|
||||
$signalRegistry->register($signal, function ($signal) use ($signalEvent, $alarmEvent, $command, $commandSignals, $input, $output) {
|
||||
$this->dispatcher->dispatch($signalEvent, ConsoleEvents::SIGNAL);
|
||||
$exitCode = $signalEvent->getExitCode();
|
||||
|
||||
if (null !== $alarmEvent) {
|
||||
if (false !== $exitCode) {
|
||||
$alarmEvent->setExitCode($exitCode);
|
||||
} else {
|
||||
$alarmEvent->abortExit();
|
||||
}
|
||||
$this->dispatcher->dispatch($alarmEvent);
|
||||
$exitCode = $alarmEvent->getExitCode();
|
||||
}
|
||||
|
||||
// If the command is signalable, we call the handleSignal() method
|
||||
if (\in_array($signal, $commandSignals, true)) {
|
||||
$exitCode = $command->handleSignal($signal, $exitCode);
|
||||
}
|
||||
|
||||
if (\SIGALRM === $signal) {
|
||||
$this->scheduleAlarm();
|
||||
}
|
||||
|
||||
if (false !== $exitCode) {
|
||||
$event = new ConsoleTerminateEvent($command, $event->getInput(), $event->getOutput(), $exitCode, $signal);
|
||||
$event = new ConsoleTerminateEvent($command, $input, $output, $exitCode, $signal);
|
||||
$this->dispatcher->dispatch($event, ConsoleEvents::TERMINATE);
|
||||
|
||||
exit($event->getExitCode());
|
||||
@@ -1017,7 +1060,11 @@ class Application implements ResetInterface
|
||||
}
|
||||
|
||||
foreach ($commandSignals as $signal) {
|
||||
$this->signalRegistry->register($signal, function (int $signal) use ($command): void {
|
||||
$signalRegistry->register($signal, function (int $signal) use ($command): void {
|
||||
if (\SIGALRM === $signal) {
|
||||
$this->scheduleAlarm();
|
||||
}
|
||||
|
||||
if (false !== $exitCode = $command->handleSignal($signal)) {
|
||||
exit($exitCode);
|
||||
}
|
||||
@@ -1084,7 +1131,8 @@ class Application implements ResetInterface
|
||||
return new InputDefinition([
|
||||
new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
|
||||
new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display help for the given command. When no command is given display help for the <info>'.$this->defaultCommand.'</info> command'),
|
||||
new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message'),
|
||||
new InputOption('--silent', null, InputOption::VALUE_NONE, 'Do not output any message'),
|
||||
new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Only errors are displayed. All other output is suppressed'),
|
||||
new InputOption('--verbose', '-v|vv|vvv', InputOption::VALUE_NONE, 'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'),
|
||||
new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version'),
|
||||
new InputOption('--ansi', '', InputOption::VALUE_NEGATABLE, 'Force (or disable --no-ansi) ANSI output', null),
|
||||
|
||||
Reference in New Issue
Block a user