diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..ad93c14a0 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,5 @@ +{ + "image": "mcr.microsoft.com/devcontainers/universal:2", + "features": { + } +} diff --git a/Application.php b/Application.php index 113778b43..78d885d25 100644 --- a/Application.php +++ b/Application.php @@ -22,6 +22,7 @@ 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 @@ public function __construct( $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 @@ public function setSignalsToDispatchEvent(int ...$signalsToDispatchEvent): void $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 @@ public function doRun(InputInterface $input, OutputInterface $output): int $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 @@ public function complete(CompletionInput $input, CompletionSuggestions $suggesti if (CompletionInput::TYPE_OPTION_NAME === $input->getCompletionType()) { $suggestions->suggestOptions($this->getDefinition()->getOptions()); - - return; } } @@ -475,7 +499,7 @@ public function getLongVersion(): string { if ('UNKNOWN' !== $this->getName()) { if ('UNKNOWN' !== $this->getVersion()) { - return sprintf('%s %s', $this->getName(), $this->getVersion()); + return \sprintf('%s %s', $this->getName(), $this->getVersion()); } return $this->getName(); @@ -530,7 +554,7 @@ public function add(Command $command): ?Command } 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 @@ public function get(string $name): Command $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 @@ public function findNamespace(string $namespace): string $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 @@ public function findNamespace(string $namespace): string $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 @@ public function find(string $name): Command $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 @@ public function find(string $name): Command 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 @@ public function renderThrowable(\Throwable $e, OutputInterface $output): void $this->doRenderThrowable($e, $output); if (null !== $this->runningCommand) { - $output->writeln(sprintf('%s', OutputFormatter::escape(sprintf($this->runningCommand->getSynopsis(), $this->getName()))), OutputInterface::VERBOSITY_QUIET); + $output->writeln(\sprintf('%s', OutputFormatter::escape(\sprintf($this->runningCommand->getSynopsis(), $this->getName()))), OutputInterface::VERBOSITY_QUIET); $output->writeln('', OutputInterface::VERBOSITY_QUIET); } } @@ -833,7 +857,7 @@ protected function doRenderThrowable(\Throwable $e, OutputInterface $output): vo $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 @@ protected function doRenderThrowable(\Throwable $e, OutputInterface $output): vo $messages = []; if (!$e instanceof ExceptionInterface || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { - $messages[] = sprintf('%s', OutputFormatter::escape(sprintf('In %s line %s:', basename($e->getFile()) ?: 'n/a', $e->getLine() ?: 'n/a'))); + $messages[] = \sprintf('%s', OutputFormatter::escape(\sprintf('In %s line %s:', basename($e->getFile()) ?: 'n/a', $e->getLine() ?: 'n/a'))); } - $messages[] = $emptyLine = sprintf('%s', str_repeat(' ', $len)); + $messages[] = $emptyLine = \sprintf('%s', str_repeat(' ', $len)); if ('' === $message || OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { - $messages[] = sprintf('%s%s', $title, str_repeat(' ', max(0, $len - Helper::width($title)))); + $messages[] = \sprintf('%s%s', $title, str_repeat(' ', max(0, $len - Helper::width($title)))); } foreach ($lines as $line) { - $messages[] = sprintf(' %s %s', OutputFormatter::escape($line[0]), str_repeat(' ', $len - $line[1])); + $messages[] = \sprintf(' %s %s', OutputFormatter::escape($line[0]), str_repeat(' ', $len - $line[1])); } $messages[] = $emptyLine; $messages[] = ''; @@ -891,7 +915,7 @@ protected function doRenderThrowable(\Throwable $e, OutputInterface $output): vo $file = $trace[$i]['file'] ?? 'n/a'; $line = $trace[$i]['line'] ?? 'n/a'; - $output->writeln(sprintf(' %s%s at %s:%s', $class, $function ? $type.$function.'()' : '', $file, $line), OutputInterface::VERBOSITY_QUIET); + $output->writeln(\sprintf(' %s%s at %s:%s', $class, $function ? $type.$function.'()' : '', $file, $line), OutputInterface::VERBOSITY_QUIET); } $output->writeln('', OutputInterface::VERBOSITY_QUIET); @@ -915,6 +939,9 @@ protected function configureIO(InputInterface $input, OutputInterface $output): } 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 @@ protected function configureIO(InputInterface $input, OutputInterface $output): 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 @@ protected function configureIO(InputInterface $input, OutputInterface $output): } } - if (-1 === $shellVerbosity) { + if (0 > $shellVerbosity) { $input->setInteractive(false); } @@ -977,34 +1007,47 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI $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); - - $this->signalRegistry->register($signal, function ($signal) use ($event, $command, $commandSignals) { - $this->dispatcher->dispatch($event, ConsoleEvents::SIGNAL); - $exitCode = $event->getExitCode(); + $signalEvent = new ConsoleSignalEvent($command, $input, $output, $signal); + $alarmEvent = \SIGALRM === $signal ? new ConsoleAlarmEvent($command, $input, $output) : null; + + $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 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI } 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 @@ protected function getDefaultInputDefinition(): InputDefinition 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 '.$this->defaultCommand.' 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), diff --git a/CHANGELOG.md b/CHANGELOG.md index 25d7f7179..2c963568c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,16 @@ CHANGELOG ========= +7.2 +--- + + * Add support for `FORCE_COLOR` environment variable + * Add `verbosity` argument to `mustRun` process helper method + * [BC BREAK] Add silent verbosity (`--silent`/`SHELL_VERBOSITY=-2`) to suppress all output, including errors + * Add `OutputInterface::isSilent()`, `Output::isSilent()`, `OutputStyle::isSilent()` methods + * Add a configurable finished indicator to the progress indicator to show that the progress is finished + * Add ability to schedule alarm signals and a `ConsoleAlarmEvent` + 7.1 --- diff --git a/CI/GithubActionReporter.php b/CI/GithubActionReporter.php index 2cae6fd8b..952d380d5 100644 --- a/CI/GithubActionReporter.php +++ b/CI/GithubActionReporter.php @@ -20,8 +20,6 @@ */ class GithubActionReporter { - private OutputInterface $output; - /** * @see https://github.com/actions/toolkit/blob/5e5e1b7aacba68a53836a34db4a288c3c1c1585b/packages/core/src/command.ts#L80-L85 */ @@ -42,9 +40,9 @@ class GithubActionReporter ',' => '%2C', ]; - public function __construct(OutputInterface $output) - { - $this->output = $output; + public function __construct( + private OutputInterface $output, + ) { } public static function isGithubActionEnvironment(): bool @@ -89,11 +87,11 @@ private function log(string $type, string $message, ?string $file = null, ?int $ if (!$file) { // No file provided, output the message solely: - $this->output->writeln(sprintf('::%s::%s', $type, $message)); + $this->output->writeln(\sprintf('::%s::%s', $type, $message)); return; } - $this->output->writeln(sprintf('::%s file=%s,line=%s,col=%s::%s', $type, strtr($file, self::ESCAPED_PROPERTIES), strtr($line ?? 1, self::ESCAPED_PROPERTIES), strtr($col ?? 0, self::ESCAPED_PROPERTIES), $message)); + $this->output->writeln(\sprintf('::%s file=%s,line=%s,col=%s::%s', $type, strtr($file, self::ESCAPED_PROPERTIES), strtr($line ?? 1, self::ESCAPED_PROPERTIES), strtr($col ?? 0, self::ESCAPED_PROPERTIES), $message)); } } diff --git a/Color.php b/Color.php index 60ed046a6..b1914c19a 100644 --- a/Color.php +++ b/Color.php @@ -60,7 +60,7 @@ public function __construct(string $foreground = '', string $background = '', ar foreach ($options as $option) { if (!isset(self::AVAILABLE_OPTIONS[$option])) { - throw new InvalidArgumentException(sprintf('Invalid option specified: "%s". Expected one of (%s).', $option, implode(', ', array_keys(self::AVAILABLE_OPTIONS)))); + throw new InvalidArgumentException(\sprintf('Invalid option specified: "%s". Expected one of (%s).', $option, implode(', ', array_keys(self::AVAILABLE_OPTIONS)))); } $this->options[$option] = self::AVAILABLE_OPTIONS[$option]; @@ -88,7 +88,7 @@ public function set(): string return ''; } - return sprintf("\033[%sm", implode(';', $setCodes)); + return \sprintf("\033[%sm", implode(';', $setCodes)); } public function unset(): string @@ -107,7 +107,7 @@ public function unset(): string return ''; } - return sprintf("\033[%sm", implode(';', $unsetCodes)); + return \sprintf("\033[%sm", implode(';', $unsetCodes)); } private function parseColor(string $color, bool $background = false): string @@ -128,6 +128,6 @@ private function parseColor(string $color, bool $background = false): string return ($background ? '10' : '9').self::BRIGHT_COLORS[$color]; } - throw new InvalidArgumentException(sprintf('Invalid "%s" color; expected one of (%s).', $color, implode(', ', array_merge(array_keys(self::COLORS), array_keys(self::BRIGHT_COLORS))))); + throw new InvalidArgumentException(\sprintf('Invalid "%s" color; expected one of (%s).', $color, implode(', ', array_merge(array_keys(self::COLORS), array_keys(self::BRIGHT_COLORS))))); } } diff --git a/Command/Command.php b/Command/Command.php index 03da6db43..244a419f2 100644 --- a/Command/Command.php +++ b/Command/Command.php @@ -395,7 +395,7 @@ public function getDefinition(): InputDefinition */ public function getNativeDefinition(): InputDefinition { - return $this->definition ?? throw new LogicException(sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', static::class)); + return $this->definition ?? throw new LogicException(\sprintf('Command class "%s" is not correctly initialized. You probably forgot to call the parent constructor.', static::class)); } /** @@ -603,7 +603,7 @@ public function getSynopsis(bool $short = false): string $key = $short ? 'short' : 'long'; if (!isset($this->synopsis[$key])) { - $this->synopsis[$key] = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis($short))); + $this->synopsis[$key] = trim(\sprintf('%s %s', $this->name, $this->definition->getSynopsis($short))); } return $this->synopsis[$key]; @@ -617,7 +617,7 @@ public function getSynopsis(bool $short = false): string public function addUsage(string $usage): static { if (!str_starts_with($usage, $this->name)) { - $usage = sprintf('%s %s', $this->name, $usage); + $usage = \sprintf('%s %s', $this->name, $usage); } $this->usages[] = $usage; @@ -642,7 +642,7 @@ public function getUsages(): array public function getHelper(string $name): HelperInterface { if (null === $this->helperSet) { - throw new LogicException(sprintf('Cannot retrieve helper "%s" because there is no HelperSet defined. Did you forget to add your command to the application or to set the application on the command using the setApplication() method? You can also set the HelperSet directly using the setHelperSet() method.', $name)); + throw new LogicException(\sprintf('Cannot retrieve helper "%s" because there is no HelperSet defined. Did you forget to add your command to the application or to set the application on the command using the setApplication() method? You can also set the HelperSet directly using the setHelperSet() method.', $name)); } return $this->helperSet->get($name); @@ -658,7 +658,7 @@ public function getHelper(string $name): HelperInterface private function validateName(string $name): void { if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) { - throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name)); + throw new InvalidArgumentException(\sprintf('Command name "%s" is invalid.', $name)); } } } diff --git a/Command/CompleteCommand.php b/Command/CompleteCommand.php index 38aa737f0..15eeea16a 100644 --- a/Command/CompleteCommand.php +++ b/Command/CompleteCommand.php @@ -74,7 +74,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int // "symfony" must be kept for compat with the shell scripts generated by Symfony Console 5.4 - 6.1 $version = $input->getOption('symfony') ? '1' : $input->getOption('api-version'); if ($version && version_compare($version, self::COMPLETION_API_VERSION, '<')) { - $message = sprintf('Completion script version is not supported ("%s" given, ">=%s" required).', $version, self::COMPLETION_API_VERSION); + $message = \sprintf('Completion script version is not supported ("%s" given, ">=%s" required).', $version, self::COMPLETION_API_VERSION); $this->log($message); $output->writeln($message.' Install the Symfony completion script again by using the "completion" command.'); @@ -88,7 +88,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } if (!$completionOutput = $this->completionOutputs[$shell] ?? false) { - throw new \RuntimeException(sprintf('Shell completion is not supported for your shell: "%s" (supported: "%s").', $shell, implode('", "', array_keys($this->completionOutputs)))); + throw new \RuntimeException(\sprintf('Shell completion is not supported for your shell: "%s" (supported: "%s").', $shell, implode('", "', array_keys($this->completionOutputs)))); } $completionInput = $this->createCompletionInput($input); @@ -98,13 +98,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int '', ''.date('Y-m-d H:i:s').'', 'Input: ("|" indicates the cursor position)', - ' '.(string) $completionInput, + ' '.$completionInput, 'Command:', - ' '.(string) implode(' ', $_SERVER['argv']), + ' '.implode(' ', $_SERVER['argv']), 'Messages:', ]); - $command = $this->findCommand($completionInput, $output); + $command = $this->findCommand($completionInput); if (null === $command) { $this->log(' No command found, completing using the Application class.'); @@ -185,7 +185,7 @@ private function createCompletionInput(InputInterface $input): CompletionInput return $completionInput; } - private function findCommand(CompletionInput $completionInput, OutputInterface $output): ?Command + private function findCommand(CompletionInput $completionInput): ?Command { try { $inputName = $completionInput->getFirstArgument(); diff --git a/Command/DumpCompletionCommand.php b/Command/DumpCompletionCommand.php index be6f54592..2853fc5f4 100644 --- a/Command/DumpCompletionCommand.php +++ b/Command/DumpCompletionCommand.php @@ -35,7 +35,7 @@ protected function configure(): void $commandName = basename($fullCommand); $fullCommand = @realpath($fullCommand) ?: $fullCommand; - $shell = $this->guessShell(); + $shell = self::guessShell(); [$rcFile, $completionFile] = match ($shell) { 'fish' => ['~/.config/fish/config.fish', "/etc/fish/completions/$commandName.fish"], 'zsh' => ['~/.zshrc', '$fpath[1]/_'.$commandName], @@ -98,9 +98,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int $output = $output->getErrorOutput(); } if ($shell) { - $output->writeln(sprintf('Detected shell "%s", which is not supported by Symfony shell completion (supported shells: "%s").', $shell, implode('", "', $supportedShells))); + $output->writeln(\sprintf('Detected shell "%s", which is not supported by Symfony shell completion (supported shells: "%s").', $shell, implode('", "', $supportedShells))); } else { - $output->writeln(sprintf('Shell not detected, Symfony shell completion only supports "%s").', implode('", "', $supportedShells))); + $output->writeln(\sprintf('Shell not detected, Symfony shell completion only supports "%s").', implode('", "', $supportedShells))); } return 2; diff --git a/CommandLoader/ContainerCommandLoader.php b/CommandLoader/ContainerCommandLoader.php index 84e25be76..eb4945135 100644 --- a/CommandLoader/ContainerCommandLoader.php +++ b/CommandLoader/ContainerCommandLoader.php @@ -34,7 +34,7 @@ public function __construct( public function get(string $name): Command { if (!$this->has($name)) { - throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name)); + throw new CommandNotFoundException(\sprintf('Command "%s" does not exist.', $name)); } return $this->container->get($this->commandMap[$name]); diff --git a/CommandLoader/FactoryCommandLoader.php b/CommandLoader/FactoryCommandLoader.php index ae16bf6f1..2d13139c2 100644 --- a/CommandLoader/FactoryCommandLoader.php +++ b/CommandLoader/FactoryCommandLoader.php @@ -37,7 +37,7 @@ public function has(string $name): bool public function get(string $name): Command { if (!isset($this->factories[$name])) { - throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name)); + throw new CommandNotFoundException(\sprintf('Command "%s" does not exist.', $name)); } $factory = $this->factories[$name]; diff --git a/Completion/CompletionInput.php b/Completion/CompletionInput.php index 79c2f659a..9f9619e18 100644 --- a/Completion/CompletionInput.php +++ b/Completion/CompletionInput.php @@ -123,13 +123,13 @@ public function bind(InputDefinition $definition): void if ($this->currentIndex >= \count($this->tokens)) { if (!isset($this->arguments[$argumentName]) || $this->definition->getArgument($argumentName)->isArray()) { $this->completionName = $argumentName; - $this->completionValue = ''; } else { // we've reached the end $this->completionType = self::TYPE_NONE; $this->completionName = null; - $this->completionValue = ''; } + + $this->completionValue = ''; } } @@ -226,7 +226,7 @@ private function isCursorFree(): bool return $this->currentIndex >= $nrOfTokens; } - public function __toString() + public function __toString(): string { $str = ''; foreach ($this->tokens as $i => $token) { diff --git a/Cursor.php b/Cursor.php index 965f996e0..e2618cf1d 100644 --- a/Cursor.php +++ b/Cursor.php @@ -36,7 +36,7 @@ public function __construct( */ public function moveUp(int $lines = 1): static { - $this->output->write(sprintf("\x1b[%dA", $lines)); + $this->output->write(\sprintf("\x1b[%dA", $lines)); return $this; } @@ -46,7 +46,7 @@ public function moveUp(int $lines = 1): static */ public function moveDown(int $lines = 1): static { - $this->output->write(sprintf("\x1b[%dB", $lines)); + $this->output->write(\sprintf("\x1b[%dB", $lines)); return $this; } @@ -56,7 +56,7 @@ public function moveDown(int $lines = 1): static */ public function moveRight(int $columns = 1): static { - $this->output->write(sprintf("\x1b[%dC", $columns)); + $this->output->write(\sprintf("\x1b[%dC", $columns)); return $this; } @@ -66,7 +66,7 @@ public function moveRight(int $columns = 1): static */ public function moveLeft(int $columns = 1): static { - $this->output->write(sprintf("\x1b[%dD", $columns)); + $this->output->write(\sprintf("\x1b[%dD", $columns)); return $this; } @@ -76,7 +76,7 @@ public function moveLeft(int $columns = 1): static */ public function moveToColumn(int $column): static { - $this->output->write(sprintf("\x1b[%dG", $column)); + $this->output->write(\sprintf("\x1b[%dG", $column)); return $this; } @@ -86,7 +86,7 @@ public function moveToColumn(int $column): static */ public function moveToPosition(int $column, int $row): static { - $this->output->write(sprintf("\x1b[%d;%dH", $row + 1, $column)); + $this->output->write(\sprintf("\x1b[%d;%dH", $row + 1, $column)); return $this; } @@ -195,7 +195,7 @@ public function getCurrentPosition(): array $code = trim(fread($this->input, 1024)); - shell_exec(sprintf('stty %s', $sttyMode)); + shell_exec(\sprintf('stty %s', $sttyMode)); sscanf($code, "\033[%d;%dR", $row, $col); diff --git a/DataCollector/CommandDataCollector.php b/DataCollector/CommandDataCollector.php index 45138c7dc..3cbe72b59 100644 --- a/DataCollector/CommandDataCollector.php +++ b/DataCollector/CommandDataCollector.php @@ -118,7 +118,7 @@ public function getCommand(): array 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 \sprintf('%s (%d)', SignalMap::getSignalName($this->data['interrupted_by_signal']), $this->data['interrupted_by_signal']); } return null; @@ -204,7 +204,7 @@ public function getInteractiveInputs(): array public function getSignalable(): array { return array_map( - static fn (int $signal): string => sprintf('%s (%d)', SignalMap::getSignalName($signal), $signal), + static fn (int $signal): string => \sprintf('%s (%d)', SignalMap::getSignalName($signal), $signal), $this->data['signalable'] ); } @@ -212,7 +212,7 @@ public function getSignalable(): array public function getHandledSignals(): array { $keys = array_map( - static fn (int $signal): string => sprintf('%s (%d)', SignalMap::getSignalName($signal), $signal), + static fn (int $signal): string => \sprintf('%s (%d)', SignalMap::getSignalName($signal), $signal), array_keys($this->data['handled_signals']) ); diff --git a/DependencyInjection/AddConsoleCommandPass.php b/DependencyInjection/AddConsoleCommandPass.php index f712c614a..f1521602a 100644 --- a/DependencyInjection/AddConsoleCommandPass.php +++ b/DependencyInjection/AddConsoleCommandPass.php @@ -45,15 +45,15 @@ public function process(ContainerBuilder $container): void $aliases = $tags[0]['command']; } else { if (!$r = $container->getReflectionClass($class)) { - throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); + throw new InvalidArgumentException(\sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); } if (!$r->isSubclassOf(Command::class)) { - throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, 'console.command', Command::class)); + throw new InvalidArgumentException(\sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, 'console.command', Command::class)); } $aliases = str_replace('%', '%%', $class::getDefaultName() ?? ''); } - $aliases = explode('|', $aliases ?? ''); + $aliases = explode('|', $aliases); $commandName = array_shift($aliases); if ($isHidden = '' === $commandName) { @@ -102,10 +102,10 @@ public function process(ContainerBuilder $container): void if (!$description) { if (!$r = $container->getReflectionClass($class)) { - throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); + throw new InvalidArgumentException(\sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); } if (!$r->isSubclassOf(Command::class)) { - throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, 'console.command', Command::class)); + throw new InvalidArgumentException(\sprintf('The service "%s" tagged "%s" must be a subclass of "%s".', $id, 'console.command', Command::class)); } $description = str_replace('%', '%%', $class::getDefaultDescription() ?? ''); } diff --git a/Descriptor/ApplicationDescription.php b/Descriptor/ApplicationDescription.php index 5149fde4c..802d68560 100644 --- a/Descriptor/ApplicationDescription.php +++ b/Descriptor/ApplicationDescription.php @@ -70,7 +70,7 @@ public function getCommands(): array public function getCommand(string $name): Command { if (!isset($this->commands[$name]) && !isset($this->aliases[$name])) { - throw new CommandNotFoundException(sprintf('Command "%s" does not exist.', $name)); + throw new CommandNotFoundException(\sprintf('Command "%s" does not exist.', $name)); } return $this->commands[$name] ?? $this->aliases[$name]; diff --git a/Descriptor/Descriptor.php b/Descriptor/Descriptor.php index 7b2509c60..2143a17c3 100644 --- a/Descriptor/Descriptor.php +++ b/Descriptor/Descriptor.php @@ -38,7 +38,7 @@ public function describe(OutputInterface $output, object $object, array $options $object instanceof InputDefinition => $this->describeInputDefinition($object, $options), $object instanceof Command => $this->describeCommand($object, $options), $object instanceof Application => $this->describeApplication($object, $options), - default => throw new InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_debug_type($object))), + default => throw new InvalidArgumentException(\sprintf('Object of type "%s" is not describable.', get_debug_type($object))), }; } diff --git a/Descriptor/MarkdownDescriptor.php b/Descriptor/MarkdownDescriptor.php index b3f16ee90..8b7075943 100644 --- a/Descriptor/MarkdownDescriptor.php +++ b/Descriptor/MarkdownDescriptor.php @@ -149,7 +149,7 @@ protected function describeApplication(Application $application, array $options } $this->write("\n\n"); - $this->write(implode("\n", array_map(fn ($commandName) => sprintf('* [`%s`](#%s)', $commandName, str_replace(':', '', $description->getCommand($commandName)->getName())), $namespace['commands']))); + $this->write(implode("\n", array_map(fn ($commandName) => \sprintf('* [`%s`](#%s)', $commandName, str_replace(':', '', $description->getCommand($commandName)->getName())), $namespace['commands']))); } foreach ($description->getCommands() as $command) { @@ -162,7 +162,7 @@ private function getApplicationTitle(Application $application): string { if ('UNKNOWN' !== $application->getName()) { if ('UNKNOWN' !== $application->getVersion()) { - return sprintf('%s %s', $application->getName(), $application->getVersion()); + return \sprintf('%s %s', $application->getName(), $application->getVersion()); } return $application->getName(); diff --git a/Descriptor/ReStructuredTextDescriptor.php b/Descriptor/ReStructuredTextDescriptor.php index f12fecb67..d2dde6fba 100644 --- a/Descriptor/ReStructuredTextDescriptor.php +++ b/Descriptor/ReStructuredTextDescriptor.php @@ -92,7 +92,7 @@ protected function describeInputOption(InputOption $option, array $options = []) protected function describeInputDefinition(InputDefinition $definition, array $options = []): void { if ($showArguments = ((bool) $definition->getArguments())) { - $this->write("Arguments\n".str_repeat($this->subsubsectionChar, 9))."\n\n"; + $this->write("Arguments\n".str_repeat($this->subsubsectionChar, 9)); foreach ($definition->getArguments() as $argument) { $this->write("\n\n"); $this->describeInputArgument($argument); @@ -167,7 +167,7 @@ private function getApplicationTitle(Application $application): string return 'Console Tool'; } if ('UNKNOWN' !== $application->getVersion()) { - return sprintf('%s %s', $application->getName(), $application->getVersion()); + return \sprintf('%s %s', $application->getName(), $application->getVersion()); } return $application->getName(); @@ -209,7 +209,7 @@ private function createTableOfContents(ApplicationDescription $description, Appl $commands = $this->removeAliasesAndHiddenCommands($commands); $this->write("\n\n"); - $this->write(implode("\n", array_map(static fn ($commandName) => sprintf('- `%s`_', $commandName), array_keys($commands)))); + $this->write(implode("\n", array_map(static fn ($commandName) => \sprintf('- `%s`_', $commandName), array_keys($commands)))); } } @@ -217,6 +217,7 @@ private function getNonDefaultOptions(InputDefinition $definition): array { $globalOptions = [ 'help', + 'silent', 'quiet', 'verbose', 'version', diff --git a/Descriptor/TextDescriptor.php b/Descriptor/TextDescriptor.php index d04d10238..51c411f46 100644 --- a/Descriptor/TextDescriptor.php +++ b/Descriptor/TextDescriptor.php @@ -31,7 +31,7 @@ class TextDescriptor extends Descriptor protected function describeInputArgument(InputArgument $argument, array $options = []): void { if (null !== $argument->getDefault() && (!\is_array($argument->getDefault()) || \count($argument->getDefault()))) { - $default = sprintf(' [default: %s]', $this->formatDefaultValue($argument->getDefault())); + $default = \sprintf(' [default: %s]', $this->formatDefaultValue($argument->getDefault())); } else { $default = ''; } @@ -39,7 +39,7 @@ protected function describeInputArgument(InputArgument $argument, array $options $totalWidth = $options['total_width'] ?? Helper::width($argument->getName()); $spacingWidth = $totalWidth - \strlen($argument->getName()); - $this->writeText(sprintf(' %s %s%s%s', + $this->writeText(\sprintf(' %s %s%s%s', $argument->getName(), str_repeat(' ', $spacingWidth), // + 4 = 2 spaces before , 2 spaces after @@ -51,7 +51,7 @@ protected function describeInputArgument(InputArgument $argument, array $options protected function describeInputOption(InputOption $option, array $options = []): void { if ($option->acceptValue() && null !== $option->getDefault() && (!\is_array($option->getDefault()) || \count($option->getDefault()))) { - $default = sprintf(' [default: %s]', $this->formatDefaultValue($option->getDefault())); + $default = \sprintf(' [default: %s]', $this->formatDefaultValue($option->getDefault())); } else { $default = ''; } @@ -66,14 +66,14 @@ protected function describeInputOption(InputOption $option, array $options = []) } $totalWidth = $options['total_width'] ?? $this->calculateTotalWidthForOptions([$option]); - $synopsis = sprintf('%s%s', - $option->getShortcut() ? sprintf('-%s, ', $option->getShortcut()) : ' ', - sprintf($option->isNegatable() ? '--%1$s|--no-%1$s' : '--%1$s%2$s', $option->getName(), $value) + $synopsis = \sprintf('%s%s', + $option->getShortcut() ? \sprintf('-%s, ', $option->getShortcut()) : ' ', + \sprintf($option->isNegatable() ? '--%1$s|--no-%1$s' : '--%1$s%2$s', $option->getName(), $value) ); $spacingWidth = $totalWidth - Helper::width($synopsis); - $this->writeText(sprintf(' %s %s%s%s%s', + $this->writeText(\sprintf(' %s %s%s%s%s', $synopsis, str_repeat(' ', $spacingWidth), // + 4 = 2 spaces before , 2 spaces after @@ -166,7 +166,7 @@ protected function describeApplication(Application $application, array $options $width = $this->getColumnWidth($description->getCommands()); foreach ($description->getCommands() as $command) { - $this->writeText(sprintf("%-{$width}s %s", $command->getName(), $command->getDescription()), $options); + $this->writeText(\sprintf("%-{$width}s %s", $command->getName(), $command->getDescription()), $options); $this->writeText("\n"); } } else { @@ -196,7 +196,7 @@ protected function describeApplication(Application $application, array $options $width = $this->getColumnWidth(array_merge(...array_values(array_map(fn ($namespace) => array_intersect($namespace['commands'], array_keys($commands)), array_values($namespaces))))); if ($describedNamespace) { - $this->writeText(sprintf('Available commands for the "%s" namespace:', $describedNamespace), $options); + $this->writeText(\sprintf('Available commands for the "%s" namespace:', $describedNamespace), $options); } else { $this->writeText('Available commands:', $options); } @@ -218,7 +218,7 @@ protected function describeApplication(Application $application, array $options $spacingWidth = $width - Helper::width($name); $command = $commands[$name]; $commandAliases = $name === $command->getName() ? $this->getCommandAliasesText($command) : ''; - $this->writeText(sprintf(' %s%s%s', $name, str_repeat(' ', $spacingWidth), $commandAliases.$command->getDescription()), $options); + $this->writeText(\sprintf(' %s%s%s', $name, str_repeat(' ', $spacingWidth), $commandAliases.$command->getDescription()), $options); } } diff --git a/Descriptor/XmlDescriptor.php b/Descriptor/XmlDescriptor.php index 8e44c88c4..00055557c 100644 --- a/Descriptor/XmlDescriptor.php +++ b/Descriptor/XmlDescriptor.php @@ -208,11 +208,9 @@ private function getInputOptionDocument(InputOption $option): \DOMDocument $defaults = \is_array($option->getDefault()) ? $option->getDefault() : (\is_bool($option->getDefault()) ? [var_export($option->getDefault(), true)] : ($option->getDefault() ? [$option->getDefault()] : [])); $objectXML->appendChild($defaultsXML = $dom->createElement('defaults')); - if ($defaults) { - foreach ($defaults as $default) { - $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); - $defaultXML->appendChild($dom->createTextNode($default)); - } + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); } } diff --git a/Event/ConsoleAlarmEvent.php b/Event/ConsoleAlarmEvent.php new file mode 100644 index 000000000..876ab59b9 --- /dev/null +++ b/Event/ConsoleAlarmEvent.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Event; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +final class ConsoleAlarmEvent extends ConsoleEvent +{ + public function __construct( + Command $command, + InputInterface $input, + OutputInterface $output, + private int|false $exitCode = 0, + ) { + parent::__construct($command, $input, $output); + } + + public function setExitCode(int $exitCode): void + { + if ($exitCode < 0 || $exitCode > 255) { + throw new \InvalidArgumentException('Exit code must be between 0 and 255.'); + } + + $this->exitCode = $exitCode; + } + + public function abortExit(): void + { + $this->exitCode = false; + } + + public function getExitCode(): int|false + { + return $this->exitCode; + } +} diff --git a/EventListener/ErrorListener.php b/EventListener/ErrorListener.php index 49915a498..9acb0e41d 100644 --- a/EventListener/ErrorListener.php +++ b/EventListener/ErrorListener.php @@ -37,7 +37,7 @@ public function onConsoleError(ConsoleErrorEvent $event): void $error = $event->getError(); - if (!$inputString = $this->getInputString($event)) { + if (!$inputString = self::getInputString($event)) { $this->logger->critical('An error occurred while using the console. Message: "{message}"', ['exception' => $error, 'message' => $error->getMessage()]); return; @@ -58,7 +58,7 @@ public function onConsoleTerminate(ConsoleTerminateEvent $event): void return; } - if (!$inputString = $this->getInputString($event)) { + if (!$inputString = self::getInputString($event)) { $this->logger->debug('The console exited with code "{code}"', ['code' => $exitCode]); return; @@ -75,19 +75,15 @@ public static function getSubscribedEvents(): array ]; } - private static function getInputString(ConsoleEvent $event): ?string + private static function getInputString(ConsoleEvent $event): string { $commandName = $event->getCommand()?->getName(); - $input = $event->getInput(); + $inputString = (string) $event->getInput(); - if ($input instanceof \Stringable) { - if ($commandName) { - return str_replace(["'$commandName'", "\"$commandName\""], $commandName, (string) $input); - } - - return (string) $input; + if ($commandName) { + return str_replace(["'$commandName'", "\"$commandName\""], $commandName, $inputString); } - return $commandName; + return $inputString; } } diff --git a/Formatter/OutputFormatter.php b/Formatter/OutputFormatter.php index 8e81e5900..3c8c287e8 100644 --- a/Formatter/OutputFormatter.php +++ b/Formatter/OutputFormatter.php @@ -23,7 +23,6 @@ */ class OutputFormatter implements WrappableOutputFormatterInterface { - private bool $decorated; private array $styles = []; private OutputFormatterStyleStack $styleStack; @@ -67,10 +66,10 @@ public static function escapeTrailingBackslash(string $text): string * * @param OutputFormatterStyleInterface[] $styles Array of "name => FormatterStyle" instances */ - public function __construct(bool $decorated = false, array $styles = []) - { - $this->decorated = $decorated; - + public function __construct( + private bool $decorated = false, + array $styles = [], + ) { $this->setStyle('error', new OutputFormatterStyle('white', 'red')); $this->setStyle('info', new OutputFormatterStyle('green')); $this->setStyle('comment', new OutputFormatterStyle('yellow')); @@ -106,7 +105,7 @@ public function hasStyle(string $name): bool public function getStyle(string $name): OutputFormatterStyleInterface { if (!$this->hasStyle($name)) { - throw new InvalidArgumentException(sprintf('Undefined style: "%s".', $name)); + throw new InvalidArgumentException(\sprintf('Undefined style: "%s".', $name)); } return $this->styles[strtolower($name)]; diff --git a/Helper/DebugFormatterHelper.php b/Helper/DebugFormatterHelper.php index 9ea7fb914..dfdb8a82c 100644 --- a/Helper/DebugFormatterHelper.php +++ b/Helper/DebugFormatterHelper.php @@ -31,7 +31,7 @@ public function start(string $id, string $message, string $prefix = 'RUN'): stri { $this->started[$id] = ['border' => ++$this->count % \count(self::COLORS)]; - return sprintf("%s %s %s\n", $this->getBorder($id), $prefix, $message); + return \sprintf("%s %s %s\n", $this->getBorder($id), $prefix, $message); } /** @@ -47,22 +47,22 @@ public function progress(string $id, string $buffer, bool $error = false, string unset($this->started[$id]['out']); } if (!isset($this->started[$id]['err'])) { - $message .= sprintf('%s %s ', $this->getBorder($id), $errorPrefix); + $message .= \sprintf('%s %s ', $this->getBorder($id), $errorPrefix); $this->started[$id]['err'] = true; } - $message .= str_replace("\n", sprintf("\n%s %s ", $this->getBorder($id), $errorPrefix), $buffer); + $message .= str_replace("\n", \sprintf("\n%s %s ", $this->getBorder($id), $errorPrefix), $buffer); } else { if (isset($this->started[$id]['err'])) { $message .= "\n"; unset($this->started[$id]['err']); } if (!isset($this->started[$id]['out'])) { - $message .= sprintf('%s %s ', $this->getBorder($id), $prefix); + $message .= \sprintf('%s %s ', $this->getBorder($id), $prefix); $this->started[$id]['out'] = true; } - $message .= str_replace("\n", sprintf("\n%s %s ", $this->getBorder($id), $prefix), $buffer); + $message .= str_replace("\n", \sprintf("\n%s %s ", $this->getBorder($id), $prefix), $buffer); } return $message; @@ -76,10 +76,10 @@ public function stop(string $id, string $message, bool $successful, string $pref $trailingEOL = isset($this->started[$id]['out']) || isset($this->started[$id]['err']) ? "\n" : ''; if ($successful) { - return sprintf("%s%s %s %s\n", $trailingEOL, $this->getBorder($id), $prefix, $message); + return \sprintf("%s%s %s %s\n", $trailingEOL, $this->getBorder($id), $prefix, $message); } - $message = sprintf("%s%s %s %s\n", $trailingEOL, $this->getBorder($id), $prefix, $message); + $message = \sprintf("%s%s %s %s\n", $trailingEOL, $this->getBorder($id), $prefix, $message); unset($this->started[$id]['out'], $this->started[$id]['err']); @@ -88,7 +88,7 @@ public function stop(string $id, string $message, bool $successful, string $pref private function getBorder(string $id): string { - return sprintf(' ', self::COLORS[$this->started[$id]['border']]); + return \sprintf(' ', self::COLORS[$this->started[$id]['border']]); } public function getName(): string diff --git a/Helper/DescriptorHelper.php b/Helper/DescriptorHelper.php index 300c7b102..9422271fb 100644 --- a/Helper/DescriptorHelper.php +++ b/Helper/DescriptorHelper.php @@ -60,7 +60,7 @@ public function describe(OutputInterface $output, ?object $object, array $option ], $options); if (!isset($this->descriptors[$options['format']])) { - throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $options['format'])); + throw new InvalidArgumentException(\sprintf('Unsupported format "%s".', $options['format'])); } $descriptor = $this->descriptors[$options['format']]; diff --git a/Helper/FormatterHelper.php b/Helper/FormatterHelper.php index 279e4c799..3646b3d6f 100644 --- a/Helper/FormatterHelper.php +++ b/Helper/FormatterHelper.php @@ -25,7 +25,7 @@ class FormatterHelper extends Helper */ public function formatSection(string $section, string $message, string $style = 'info'): string { - return sprintf('<%s>[%s] %s', $style, $section, $style, $message); + return \sprintf('<%s>[%s] %s', $style, $section, $style, $message); } /** @@ -41,7 +41,7 @@ public function formatBlock(string|array $messages, string $style, bool $large = $lines = []; foreach ($messages as $message) { $message = OutputFormatter::escape($message); - $lines[] = sprintf($large ? ' %s ' : ' %s ', $message); + $lines[] = \sprintf($large ? ' %s ' : ' %s ', $message); $len = max(self::width($message) + ($large ? 4 : 2), $len); } @@ -54,7 +54,7 @@ public function formatBlock(string|array $messages, string $style, bool $large = } for ($i = 0; isset($messages[$i]); ++$i) { - $messages[$i] = sprintf('<%s>%s', $style, $messages[$i], $style); + $messages[$i] = \sprintf('<%s>%s', $style, $messages[$i], $style); } return implode("\n", $messages); diff --git a/Helper/Helper.php b/Helper/Helper.php index de090063a..3981bbf3a 100644 --- a/Helper/Helper.php +++ b/Helper/Helper.php @@ -128,18 +128,18 @@ public static function formatTime(int|float $secs, int $precision = 1): string public static function formatMemory(int $memory): string { if ($memory >= 1024 * 1024 * 1024) { - return sprintf('%.1f GiB', $memory / 1024 / 1024 / 1024); + return \sprintf('%.1f GiB', $memory / 1024 / 1024 / 1024); } if ($memory >= 1024 * 1024) { - return sprintf('%.1f MiB', $memory / 1024 / 1024); + return \sprintf('%.1f MiB', $memory / 1024 / 1024); } if ($memory >= 1024) { - return sprintf('%d KiB', $memory / 1024); + return \sprintf('%d KiB', $memory / 1024); } - return sprintf('%d B', $memory); + return \sprintf('%d B', $memory); } public static function removeDecoration(OutputFormatterInterface $formatter, ?string $string): string diff --git a/Helper/HelperSet.php b/Helper/HelperSet.php index 30df9f958..ffe756c9d 100644 --- a/Helper/HelperSet.php +++ b/Helper/HelperSet.php @@ -61,7 +61,7 @@ public function has(string $name): bool public function get(string $name): HelperInterface { if (!$this->has($name)) { - throw new InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name)); + throw new InvalidArgumentException(\sprintf('The helper "%s" is not defined.', $name)); } return $this->helpers[$name]; diff --git a/Helper/OutputWrapper.php b/Helper/OutputWrapper.php index 0ea2b7056..a615ed2f9 100644 --- a/Helper/OutputWrapper.php +++ b/Helper/OutputWrapper.php @@ -59,7 +59,7 @@ public function wrap(string $text, int $width, string $break = "\n"): string return $text; } - $tagPattern = sprintf('<(?:(?:%s)|/(?:%s)?)>', self::TAG_OPEN_REGEX_SEGMENT, self::TAG_CLOSE_REGEX_SEGMENT); + $tagPattern = \sprintf('<(?:(?:%s)|/(?:%s)?)>', self::TAG_OPEN_REGEX_SEGMENT, self::TAG_CLOSE_REGEX_SEGMENT); $limitPattern = "{1,$width}"; $patternBlocks = [$tagPattern]; if (!$this->allowCutUrls) { @@ -68,7 +68,7 @@ public function wrap(string $text, int $width, string $break = "\n"): string $patternBlocks[] = '.'; $blocks = implode('|', $patternBlocks); $rowPattern = "(?:$blocks)$limitPattern"; - $pattern = sprintf('#(?:((?>(%1$s)((?<=[^\S\r\n])[^\S\r\n]?|(?=\r?\n)|$|[^\S\r\n]))|(%1$s))(?:\r?\n)?|(?:\r?\n|$))#imux', $rowPattern); + $pattern = \sprintf('#(?:((?>(%1$s)((?<=[^\S\r\n])[^\S\r\n]?|(?=\r?\n)|$|[^\S\r\n]))|(%1$s))(?:\r?\n)?|(?:\r?\n|$))#imux', $rowPattern); $output = rtrim(preg_replace($pattern, '\\1'.$break, $text), $break); return str_replace(' '.$break, $break, $output); diff --git a/Helper/ProcessHelper.php b/Helper/ProcessHelper.php index 3ef6f71f7..4a8cfc9d9 100644 --- a/Helper/ProcessHelper.php +++ b/Helper/ProcessHelper.php @@ -55,7 +55,7 @@ public function run(OutputInterface $output, array|Process $cmd, ?string $error $process = $cmd[0]; unset($cmd[0]); } else { - throw new \InvalidArgumentException(sprintf('Invalid command provided to "%s()": the command should be an array whose first element is either the path to the binary to run or a "Process" object.', __METHOD__)); + throw new \InvalidArgumentException(\sprintf('Invalid command provided to "%s()": the command should be an array whose first element is either the path to the binary to run or a "Process" object.', __METHOD__)); } if ($verbosity <= $output->getVerbosity()) { @@ -69,12 +69,12 @@ public function run(OutputInterface $output, array|Process $cmd, ?string $error $process->run($callback, $cmd); if ($verbosity <= $output->getVerbosity()) { - $message = $process->isSuccessful() ? 'Command ran successfully' : sprintf('%s Command did not run successfully', $process->getExitCode()); + $message = $process->isSuccessful() ? 'Command ran successfully' : \sprintf('%s Command did not run successfully', $process->getExitCode()); $output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful())); } if (!$process->isSuccessful() && null !== $error) { - $output->writeln(sprintf('%s', $this->escapeString($error))); + $output->writeln(\sprintf('%s', $this->escapeString($error))); } return $process; @@ -94,9 +94,9 @@ public function run(OutputInterface $output, array|Process $cmd, ?string $error * * @see run() */ - public function mustRun(OutputInterface $output, array|Process $cmd, ?string $error = null, ?callable $callback = null): Process + public function mustRun(OutputInterface $output, array|Process $cmd, ?string $error = null, ?callable $callback = null, int $verbosity = OutputInterface::VERBOSITY_VERY_VERBOSE): Process { - $process = $this->run($output, $cmd, $error, $callback); + $process = $this->run($output, $cmd, $error, $callback, $verbosity); if (!$process->isSuccessful()) { throw new ProcessFailedException($process); diff --git a/Helper/ProgressBar.php b/Helper/ProgressBar.php index 619eafea2..49e34d8d8 100644 --- a/Helper/ProgressBar.php +++ b/Helper/ProgressBar.php @@ -610,7 +610,7 @@ private function buildLine(): string { \assert(null !== $this->format); - $regex = "{%([a-z\-_]+)(?:\:([^%]+))?%}i"; + $regex = '{%([a-z\-_]+)(?:\:([^%]+))?%}i'; $callback = function ($matches) { if ($formatter = $this->getPlaceholderFormatter($matches[1])) { $text = $formatter($this, $this->output); @@ -621,7 +621,7 @@ private function buildLine(): string } if (isset($matches[2])) { - $text = sprintf('%'.$matches[2], $text); + $text = \sprintf('%'.$matches[2], $text); } return $text; diff --git a/Helper/ProgressIndicator.php b/Helper/ProgressIndicator.php index 969d83539..b6bbd0cfa 100644 --- a/Helper/ProgressIndicator.php +++ b/Helper/ProgressIndicator.php @@ -36,8 +36,10 @@ class ProgressIndicator private ?string $message = null; private array $indicatorValues; private int $indicatorCurrent; + private string $finishedIndicatorValue; private float $indicatorUpdateTime; private bool $started = false; + private bool $finished = false; /** * @var array @@ -53,11 +55,12 @@ public function __construct( ?string $format = null, private int $indicatorChangeInterval = 100, ?array $indicatorValues = null, + ?string $finishedIndicatorValue = null, ) { - $format ??= $this->determineBestFormat(); $indicatorValues ??= ['-', '\\', '|', '/']; $indicatorValues = array_values($indicatorValues); + $finishedIndicatorValue ??= '✔'; if (2 > \count($indicatorValues)) { throw new InvalidArgumentException('Must have at least 2 indicator value characters.'); @@ -65,6 +68,7 @@ public function __construct( $this->format = self::getFormatDefinition($format); $this->indicatorValues = $indicatorValues; + $this->finishedIndicatorValue = $finishedIndicatorValue; $this->startTime = time(); } @@ -89,6 +93,7 @@ public function start(string $message): void $this->message = $message; $this->started = true; + $this->finished = false; $this->startTime = time(); $this->indicatorUpdateTime = $this->getCurrentTimeInMilliseconds() + $this->indicatorChangeInterval; $this->indicatorCurrent = 0; @@ -123,13 +128,25 @@ public function advance(): void /** * Finish the indicator with message. + * + * @param ?string $finishedIndicator */ - public function finish(string $message): void + public function finish(string $message/* , ?string $finishedIndicator = null */): void { + $finishedIndicator = 1 < \func_num_args() ? func_get_arg(1) : null; + if (null !== $finishedIndicator && !\is_string($finishedIndicator)) { + throw new \TypeError(\sprintf('Argument 2 passed to "%s()" must be of the type string or null, "%s" given.', __METHOD__, get_debug_type($finishedIndicator))); + } + if (!$this->started) { throw new LogicException('Progress indicator has not yet been started.'); } + if (null !== $finishedIndicator) { + $this->finishedIndicatorValue = $finishedIndicator; + } + + $this->finished = true; $this->message = $message; $this->display(); $this->output->writeln(''); @@ -172,7 +189,7 @@ private function display(): void return; } - $this->overwrite(preg_replace_callback("{%([a-z\-_]+)(?:\:([^%]+))?%}i", function ($matches) { + $this->overwrite(preg_replace_callback('{%([a-z\-_]+)(?:\:([^%]+))?%}i', function ($matches) { if ($formatter = self::getPlaceholderFormatterDefinition($matches[1])) { return $formatter($this); } @@ -216,7 +233,7 @@ private function getCurrentTimeInMilliseconds(): float private static function initPlaceholderFormatters(): array { return [ - 'indicator' => fn (self $indicator) => $indicator->indicatorValues[$indicator->indicatorCurrent % \count($indicator->indicatorValues)], + 'indicator' => fn (self $indicator) => $indicator->finished ? $indicator->finishedIndicatorValue : $indicator->indicatorValues[$indicator->indicatorCurrent % \count($indicator->indicatorValues)], 'message' => fn (self $indicator) => $indicator->message, 'elapsed' => fn (self $indicator) => Helper::formatTime(time() - $indicator->startTime, 2), 'memory' => fn () => Helper::formatMemory(memory_get_usage(true)), diff --git a/Helper/QuestionHelper.php b/Helper/QuestionHelper.php index 54825c6cd..69afc2a67 100644 --- a/Helper/QuestionHelper.php +++ b/Helper/QuestionHelper.php @@ -211,7 +211,7 @@ protected function formatChoiceQuestionChoices(ChoiceQuestion $question, string foreach ($choices as $key => $value) { $padding = str_repeat(' ', $maxWidth - self::width($key)); - $messages[] = sprintf(" [<$tag>%s$padding] %s", $key, $value); + $messages[] = \sprintf(" [<$tag>%s$padding] %s", $key, $value); } return $messages; diff --git a/Helper/SymfonyQuestionHelper.php b/Helper/SymfonyQuestionHelper.php index 48d947b75..b452bf047 100644 --- a/Helper/SymfonyQuestionHelper.php +++ b/Helper/SymfonyQuestionHelper.php @@ -31,17 +31,17 @@ protected function writePrompt(OutputInterface $output, Question $question): voi $default = $question->getDefault(); if ($question->isMultiline()) { - $text .= sprintf(' (press %s to continue)', $this->getEofShortcut()); + $text .= \sprintf(' (press %s to continue)', $this->getEofShortcut()); } switch (true) { case null === $default: - $text = sprintf(' %s:', $text); + $text = \sprintf(' %s:', $text); break; case $question instanceof ConfirmationQuestion: - $text = sprintf(' %s (yes/no) [%s]:', $text, $default ? 'yes' : 'no'); + $text = \sprintf(' %s (yes/no) [%s]:', $text, $default ? 'yes' : 'no'); break; @@ -53,18 +53,18 @@ protected function writePrompt(OutputInterface $output, Question $question): voi $default[$key] = $choices[trim($value)]; } - $text = sprintf(' %s [%s]:', $text, OutputFormatter::escape(implode(', ', $default))); + $text = \sprintf(' %s [%s]:', $text, OutputFormatter::escape(implode(', ', $default))); break; case $question instanceof ChoiceQuestion: $choices = $question->getChoices(); - $text = sprintf(' %s [%s]:', $text, OutputFormatter::escape($choices[$default] ?? $default)); + $text = \sprintf(' %s [%s]:', $text, OutputFormatter::escape($choices[$default] ?? $default)); break; default: - $text = sprintf(' %s [%s]:', $text, OutputFormatter::escape($default)); + $text = \sprintf(' %s [%s]:', $text, OutputFormatter::escape($default)); } $output->writeln($text); diff --git a/Helper/Table.php b/Helper/Table.php index 09709a28e..9ff73d2cc 100644 --- a/Helper/Table.php +++ b/Helper/Table.php @@ -79,7 +79,7 @@ public static function getStyleDefinition(string $name): TableStyle { self::$styles ??= self::initStyles(); - return self::$styles[$name] ?? throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); + return self::$styles[$name] ?? throw new InvalidArgumentException(\sprintf('Style "%s" is not defined.', $name)); } /** @@ -164,7 +164,7 @@ public function setColumnWidths(array $widths): static public function setColumnMaxWidth(int $columnIndex, int $width): static { if (!$this->output->getFormatter() instanceof WrappableOutputFormatterInterface) { - throw new \LogicException(sprintf('Setting a maximum column width is only supported when using a "%s" formatter, got "%s".', WrappableOutputFormatterInterface::class, get_debug_type($this->output->getFormatter()))); + throw new \LogicException(\sprintf('Setting a maximum column width is only supported when using a "%s" formatter, got "%s".', WrappableOutputFormatterInterface::class, get_debug_type($this->output->getFormatter()))); } $this->columnMaxWidths[$columnIndex] = $width; @@ -233,7 +233,7 @@ public function addRow(TableSeparator|array $row): static public function appendRow(TableSeparator|array $row): static { if (!$this->output instanceof ConsoleSectionOutput) { - throw new RuntimeException(sprintf('Output should be an instance of "%s" when calling "%s".', ConsoleSectionOutput::class, __METHOD__)); + throw new RuntimeException(\sprintf('Output should be an instance of "%s" when calling "%s".', ConsoleSectionOutput::class, __METHOD__)); } if ($this->rendered) { @@ -364,14 +364,14 @@ public function render(): void foreach ($parts as $idx => $part) { if ($headers && !$containsColspan) { if (0 === $idx) { - $rows[] = [sprintf( + $rows[] = [\sprintf( '%s%s: %s', str_repeat(' ', $maxHeaderLength - Helper::width(Helper::removeDecoration($formatter, $headers[$i] ?? ''))), $headers[$i] ?? '', $part )]; } else { - $rows[] = [sprintf( + $rows[] = [\sprintf( '%s %s', str_pad('', $maxHeaderLength, ' ', \STR_PAD_LEFT), $part @@ -491,12 +491,12 @@ private function renderRowSeparator(int $type = self::SEPARATOR_MID, ?string $ti } if (null !== $title) { - $titleLength = Helper::width(Helper::removeDecoration($formatter = $this->output->getFormatter(), $formattedTitle = sprintf($titleFormat, $title))); + $titleLength = Helper::width(Helper::removeDecoration($formatter = $this->output->getFormatter(), $formattedTitle = \sprintf($titleFormat, $title))); $markupLength = Helper::width($markup); if ($titleLength > $limit = $markupLength - 4) { $titleLength = $limit; - $formatLength = Helper::width(Helper::removeDecoration($formatter, sprintf($titleFormat, ''))); - $formattedTitle = sprintf($titleFormat, Helper::substr($title, 0, $limit - $formatLength - 3).'...'); + $formatLength = Helper::width(Helper::removeDecoration($formatter, \sprintf($titleFormat, ''))); + $formattedTitle = \sprintf($titleFormat, Helper::substr($title, 0, $limit - $formatLength - 3).'...'); } $titleStart = intdiv($markupLength - $titleLength, 2); @@ -507,7 +507,7 @@ private function renderRowSeparator(int $type = self::SEPARATOR_MID, ?string $ti } } - $this->output->writeln(sprintf($this->style->getBorderFormat(), $markup)); + $this->output->writeln(\sprintf($this->style->getBorderFormat(), $markup)); } /** @@ -517,7 +517,7 @@ private function renderColumnSeparator(int $type = self::BORDER_OUTSIDE): string { $borders = $this->style->getBorderChars(); - return sprintf($this->style->getBorderFormat(), self::BORDER_OUTSIDE === $type ? $borders[1] : $borders[3]); + return \sprintf($this->style->getBorderFormat(), self::BORDER_OUTSIDE === $type ? $borders[1] : $borders[3]); } /** @@ -565,11 +565,11 @@ private function renderCell(array $row, int $column, string $cellFormat): string $style = $this->getColumnStyle($column); if ($cell instanceof TableSeparator) { - return sprintf($style->getBorderFormat(), str_repeat($style->getBorderChars()[2], $width)); + return \sprintf($style->getBorderFormat(), str_repeat($style->getBorderChars()[2], $width)); } $width += Helper::length($cell) - Helper::length(Helper::removeDecoration($this->output->getFormatter(), $cell)); - $content = sprintf($style->getCellRowContentFormat(), $cell); + $content = \sprintf($style->getCellRowContentFormat(), $cell); $padType = $style->getPadType(); if ($cell instanceof TableCell && $cell->getStyle() instanceof TableCellStyle) { @@ -594,7 +594,7 @@ private function renderCell(array $row, int $column, string $cellFormat): string $padType = $cell->getStyle()->getPadByAlign(); } - return sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $padType)); + return \sprintf($cellFormat, str_pad($content, $width, $style->getPaddingChar(), $padType)); } /** @@ -691,7 +691,7 @@ private function fillNextRows(array $rows, int $line): array $unmergedRows = []; foreach ($rows[$line] as $column => $cell) { if (null !== $cell && !$cell instanceof TableCell && !\is_scalar($cell) && !$cell instanceof \Stringable) { - throw new InvalidArgumentException(sprintf('A cell must be a TableCell, a scalar or an object implementing "__toString()", "%s" given.', get_debug_type($cell))); + throw new InvalidArgumentException(\sprintf('A cell must be a TableCell, a scalar or an object implementing "__toString()", "%s" given.', get_debug_type($cell))); } if ($cell instanceof TableCell && $cell->getRowspan() > 1) { $nbLines = $cell->getRowspan() - 1; @@ -836,7 +836,7 @@ private function calculateColumnsWidth(iterable $groups): void private function getColumnSeparatorWidth(): int { - return Helper::width(sprintf($this->style->getBorderFormat(), $this->style->getBorderChars()[3])); + return Helper::width(\sprintf($this->style->getBorderFormat(), $this->style->getBorderChars()[3])); } private function getCellWidth(array $row, int $column): int @@ -919,6 +919,6 @@ private function resolveStyle(TableStyle|string $name): TableStyle return $name; } - return self::$styles[$name] ?? throw new InvalidArgumentException(sprintf('Style "%s" is not defined.', $name)); + return self::$styles[$name] ?? throw new InvalidArgumentException(\sprintf('Style "%s" is not defined.', $name)); } } diff --git a/Helper/TableCell.php b/Helper/TableCell.php index 1c4eeea20..ab8339204 100644 --- a/Helper/TableCell.php +++ b/Helper/TableCell.php @@ -30,7 +30,7 @@ public function __construct( ) { // check option names if ($diff = array_diff(array_keys($options), array_keys($this->options))) { - throw new InvalidArgumentException(sprintf('The TableCell does not support the following options: \'%s\'.', implode('\', \'', $diff))); + throw new InvalidArgumentException(\sprintf('The TableCell does not support the following options: \'%s\'.', implode('\', \'', $diff))); } if (isset($options['style']) && !$options['style'] instanceof TableCellStyle) { diff --git a/Helper/TableCellStyle.php b/Helper/TableCellStyle.php index 49b97f853..af1a17e96 100644 --- a/Helper/TableCellStyle.php +++ b/Helper/TableCellStyle.php @@ -43,11 +43,11 @@ class TableCellStyle public function __construct(array $options = []) { if ($diff = array_diff(array_keys($options), array_keys($this->options))) { - throw new InvalidArgumentException(sprintf('The TableCellStyle does not support the following options: \'%s\'.', implode('\', \'', $diff))); + throw new InvalidArgumentException(\sprintf('The TableCellStyle does not support the following options: \'%s\'.', implode('\', \'', $diff))); } if (isset($options['align']) && !\array_key_exists($options['align'], self::ALIGN_MAP)) { - throw new InvalidArgumentException(sprintf('Wrong align value. Value must be following: \'%s\'.', implode('\', \'', array_keys(self::ALIGN_MAP)))); + throw new InvalidArgumentException(\sprintf('Wrong align value. Value must be following: \'%s\'.', implode('\', \'', array_keys(self::ALIGN_MAP)))); } $this->options = array_merge($this->options, $options); diff --git a/Input/ArgvInput.php b/Input/ArgvInput.php index 95703ba5f..fe25b861a 100644 --- a/Input/ArgvInput.php +++ b/Input/ArgvInput.php @@ -49,6 +49,12 @@ public function __construct(?array $argv = null, ?InputDefinition $definition = { $argv ??= $_SERVER['argv'] ?? []; + foreach ($argv as $arg) { + if (!\is_scalar($arg) && !$arg instanceof \Stringable) { + throw new RuntimeException(\sprintf('Argument values expected to be all scalars, got "%s".', get_debug_type($arg))); + } + } + // strip the application name array_shift($argv); @@ -119,7 +125,7 @@ private function parseShortOptionSet(string $name): void for ($i = 0; $i < $len; ++$i) { if (!$this->definition->hasShortcut($name[$i])) { $encoding = mb_detect_encoding($name, null, true); - throw new RuntimeException(sprintf('The "-%s" option does not exist.', false === $encoding ? $name[$i] : mb_substr($name, $i, 1, $encoding))); + throw new RuntimeException(\sprintf('The "-%s" option does not exist.', false === $encoding ? $name[$i] : mb_substr($name, $i, 1, $encoding))); } $option = $this->definition->getOptionForShortcut($name[$i]); @@ -127,9 +133,9 @@ private function parseShortOptionSet(string $name): void $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1)); break; - } else { - $this->addLongOption($option->getName(), null); } + + $this->addLongOption($option->getName(), null); } } @@ -180,14 +186,14 @@ private function parseArgument(string $token): void if (\count($all)) { if ($symfonyCommandName) { - $message = sprintf('Too many arguments to "%s" command, expected arguments "%s".', $symfonyCommandName, implode('" "', array_keys($all))); + $message = \sprintf('Too many arguments to "%s" command, expected arguments "%s".', $symfonyCommandName, implode('" "', array_keys($all))); } else { - $message = sprintf('Too many arguments, expected arguments "%s".', implode('" "', array_keys($all))); + $message = \sprintf('Too many arguments, expected arguments "%s".', implode('" "', array_keys($all))); } } elseif ($symfonyCommandName) { - $message = sprintf('No arguments expected for "%s" command, got "%s".', $symfonyCommandName, $token); + $message = \sprintf('No arguments expected for "%s" command, got "%s".', $symfonyCommandName, $token); } else { - $message = sprintf('No arguments expected, got "%s".', $token); + $message = \sprintf('No arguments expected, got "%s".', $token); } throw new RuntimeException($message); @@ -202,7 +208,7 @@ private function parseArgument(string $token): void private function addShortOption(string $shortcut, mixed $value): void { if (!$this->definition->hasShortcut($shortcut)) { - throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut)); + throw new RuntimeException(\sprintf('The "-%s" option does not exist.', $shortcut)); } $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); @@ -217,12 +223,12 @@ private function addLongOption(string $name, mixed $value): void { if (!$this->definition->hasOption($name)) { if (!$this->definition->hasNegation($name)) { - throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name)); + throw new RuntimeException(\sprintf('The "--%s" option does not exist.', $name)); } $optionName = $this->definition->negationToName($name); if (null !== $value) { - throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name)); + throw new RuntimeException(\sprintf('The "--%s" option does not accept a value.', $name)); } $this->options[$optionName] = false; @@ -232,7 +238,7 @@ private function addLongOption(string $name, mixed $value): void $option = $this->definition->getOption($name); if (null !== $value && !$option->acceptValue()) { - throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name)); + throw new RuntimeException(\sprintf('The "--%s" option does not accept a value.', $name)); } if (\in_array($value, ['', null], true) && $option->acceptValue() && \count($this->parsed)) { @@ -248,7 +254,7 @@ private function addLongOption(string $name, mixed $value): void if (null === $value) { if ($option->isValueRequired()) { - throw new RuntimeException(sprintf('The "--%s" option requires a value.', $name)); + throw new RuntimeException(\sprintf('The "--%s" option requires a value.', $name)); } if (!$option->isArray() && !$option->isValueOptional()) { diff --git a/Input/ArrayInput.php b/Input/ArrayInput.php index d27ff411e..7335632bf 100644 --- a/Input/ArrayInput.php +++ b/Input/ArrayInput.php @@ -135,7 +135,7 @@ protected function parse(): void private function addShortOption(string $shortcut, mixed $value): void { if (!$this->definition->hasShortcut($shortcut)) { - throw new InvalidOptionException(sprintf('The "-%s" option does not exist.', $shortcut)); + throw new InvalidOptionException(\sprintf('The "-%s" option does not exist.', $shortcut)); } $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); @@ -151,7 +151,7 @@ private function addLongOption(string $name, mixed $value): void { if (!$this->definition->hasOption($name)) { if (!$this->definition->hasNegation($name)) { - throw new InvalidOptionException(sprintf('The "--%s" option does not exist.', $name)); + throw new InvalidOptionException(\sprintf('The "--%s" option does not exist.', $name)); } $optionName = $this->definition->negationToName($name); @@ -164,7 +164,7 @@ private function addLongOption(string $name, mixed $value): void if (null === $value) { if ($option->isValueRequired()) { - throw new InvalidOptionException(sprintf('The "--%s" option requires a value.', $name)); + throw new InvalidOptionException(\sprintf('The "--%s" option requires a value.', $name)); } if (!$option->isValueOptional()) { @@ -183,7 +183,7 @@ private function addLongOption(string $name, mixed $value): void private function addArgument(string|int $name, mixed $value): void { if (!$this->definition->hasArgument($name)) { - throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + throw new InvalidArgumentException(\sprintf('The "%s" argument does not exist.', $name)); } $this->arguments[$name] = $value; diff --git a/Input/Input.php b/Input/Input.php index 5a8b9a27a..d2881c60f 100644 --- a/Input/Input.php +++ b/Input/Input.php @@ -66,7 +66,7 @@ public function validate(): void $missingArguments = array_filter(array_keys($definition->getArguments()), fn ($argument) => !\array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired()); if (\count($missingArguments) > 0) { - throw new RuntimeException(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments))); + throw new RuntimeException(\sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments))); } } @@ -88,7 +88,7 @@ public function getArguments(): array public function getArgument(string $name): mixed { if (!$this->definition->hasArgument($name)) { - throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + throw new InvalidArgumentException(\sprintf('The "%s" argument does not exist.', $name)); } return $this->arguments[$name] ?? $this->definition->getArgument($name)->getDefault(); @@ -97,7 +97,7 @@ public function getArgument(string $name): mixed public function setArgument(string $name, mixed $value): void { if (!$this->definition->hasArgument($name)) { - throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + throw new InvalidArgumentException(\sprintf('The "%s" argument does not exist.', $name)); } $this->arguments[$name] = $value; @@ -124,7 +124,7 @@ public function getOption(string $name): mixed } if (!$this->definition->hasOption($name)) { - throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + throw new InvalidArgumentException(\sprintf('The "%s" option does not exist.', $name)); } return \array_key_exists($name, $this->options) ? $this->options[$name] : $this->definition->getOption($name)->getDefault(); @@ -137,7 +137,7 @@ public function setOption(string $name, mixed $value): void return; } elseif (!$this->definition->hasOption($name)) { - throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + throw new InvalidArgumentException(\sprintf('The "%s" option does not exist.', $name)); } $this->options[$name] = $value; diff --git a/Input/InputArgument.php b/Input/InputArgument.php index a5d949277..6fbb64ed0 100644 --- a/Input/InputArgument.php +++ b/Input/InputArgument.php @@ -62,7 +62,7 @@ public function __construct( if (null === $mode) { $mode = self::OPTIONAL; } elseif ($mode >= (self::IS_ARRAY << 1) || $mode < 1) { - throw new InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode)); + throw new InvalidArgumentException(\sprintf('Argument mode "%s" is not valid.', $mode)); } $this->mode = $mode; @@ -143,7 +143,7 @@ public function complete(CompletionInput $input, CompletionSuggestions $suggesti { $values = $this->suggestedValues; if ($values instanceof \Closure && !\is_array($values = $values($input))) { - throw new LogicException(sprintf('Closure for argument "%s" must return an array. Got "%s".', $this->name, get_debug_type($values))); + throw new LogicException(\sprintf('Closure for argument "%s" must return an array. Got "%s".', $this->name, get_debug_type($values))); } if ($values) { $suggestions->suggestValues($values); diff --git a/Input/InputDefinition.php b/Input/InputDefinition.php index f27e29748..a8b006d48 100644 --- a/Input/InputDefinition.php +++ b/Input/InputDefinition.php @@ -97,15 +97,15 @@ public function addArguments(?array $arguments = []): void public function addArgument(InputArgument $argument): void { if (isset($this->arguments[$argument->getName()])) { - throw new LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName())); + throw new LogicException(\sprintf('An argument with name "%s" already exists.', $argument->getName())); } if (null !== $this->lastArrayArgument) { - throw new LogicException(sprintf('Cannot add a required argument "%s" after an array argument "%s".', $argument->getName(), $this->lastArrayArgument->getName())); + throw new LogicException(\sprintf('Cannot add a required argument "%s" after an array argument "%s".', $argument->getName(), $this->lastArrayArgument->getName())); } if ($argument->isRequired() && null !== $this->lastOptionalArgument) { - throw new LogicException(sprintf('Cannot add a required argument "%s" after an optional one "%s".', $argument->getName(), $this->lastOptionalArgument->getName())); + throw new LogicException(\sprintf('Cannot add a required argument "%s" after an optional one "%s".', $argument->getName(), $this->lastOptionalArgument->getName())); } if ($argument->isArray()) { @@ -129,7 +129,7 @@ public function addArgument(InputArgument $argument): void public function getArgument(string|int $name): InputArgument { if (!$this->hasArgument($name)) { - throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + throw new InvalidArgumentException(\sprintf('The "%s" argument does not exist.', $name)); } $arguments = \is_int($name) ? array_values($this->arguments) : $this->arguments; @@ -217,16 +217,16 @@ public function addOptions(array $options = []): void public function addOption(InputOption $option): void { if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) { - throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName())); + throw new LogicException(\sprintf('An option named "%s" already exists.', $option->getName())); } if (isset($this->negations[$option->getName()])) { - throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName())); + throw new LogicException(\sprintf('An option named "%s" already exists.', $option->getName())); } if ($option->getShortcut()) { foreach (explode('|', $option->getShortcut()) as $shortcut) { if (isset($this->shortcuts[$shortcut]) && !$option->equals($this->options[$this->shortcuts[$shortcut]])) { - throw new LogicException(sprintf('An option with shortcut "%s" already exists.', $shortcut)); + throw new LogicException(\sprintf('An option with shortcut "%s" already exists.', $shortcut)); } } } @@ -241,7 +241,7 @@ public function addOption(InputOption $option): void if ($option->isNegatable()) { $negatedName = 'no-'.$option->getName(); if (isset($this->options[$negatedName])) { - throw new LogicException(sprintf('An option named "%s" already exists.', $negatedName)); + throw new LogicException(\sprintf('An option named "%s" already exists.', $negatedName)); } $this->negations[$negatedName] = $option->getName(); } @@ -255,7 +255,7 @@ public function addOption(InputOption $option): void public function getOption(string $name): InputOption { if (!$this->hasOption($name)) { - throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name)); + throw new InvalidArgumentException(\sprintf('The "--%s" option does not exist.', $name)); } return $this->options[$name]; @@ -329,7 +329,7 @@ public function getOptionDefaults(): array public function shortcutToName(string $shortcut): string { if (!isset($this->shortcuts[$shortcut])) { - throw new InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut)); + throw new InvalidArgumentException(\sprintf('The "-%s" option does not exist.', $shortcut)); } return $this->shortcuts[$shortcut]; @@ -345,7 +345,7 @@ public function shortcutToName(string $shortcut): string public function negationToName(string $negation): string { if (!isset($this->negations[$negation])) { - throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $negation)); + throw new InvalidArgumentException(\sprintf('The "--%s" option does not exist.', $negation)); } return $this->negations[$negation]; @@ -364,7 +364,7 @@ public function getSynopsis(bool $short = false): string foreach ($this->getOptions() as $option) { $value = ''; if ($option->acceptValue()) { - $value = sprintf( + $value = \sprintf( ' %s%s%s', $option->isValueOptional() ? '[' : '', strtoupper($option->getName()), @@ -372,9 +372,9 @@ public function getSynopsis(bool $short = false): string ); } - $shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : ''; - $negation = $option->isNegatable() ? sprintf('|--no-%s', $option->getName()) : ''; - $elements[] = sprintf('[%s--%s%s%s]', $shortcut, $option->getName(), $value, $negation); + $shortcut = $option->getShortcut() ? \sprintf('-%s|', $option->getShortcut()) : ''; + $negation = $option->isNegatable() ? \sprintf('|--no-%s', $option->getName()) : ''; + $elements[] = \sprintf('[%s--%s%s%s]', $shortcut, $option->getName(), $value, $negation); } } diff --git a/Input/InputOption.php b/Input/InputOption.php index 617c348d5..25fb91782 100644 --- a/Input/InputOption.php +++ b/Input/InputOption.php @@ -99,7 +99,7 @@ public function __construct( if (null === $mode) { $mode = self::VALUE_NONE; } elseif ($mode >= (self::VALUE_NEGATABLE << 1) || $mode < 1) { - throw new InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode)); + throw new InvalidArgumentException(\sprintf('Option mode "%s" is not valid.', $mode)); } $this->name = $name; @@ -238,7 +238,7 @@ public function complete(CompletionInput $input, CompletionSuggestions $suggesti { $values = $this->suggestedValues; if ($values instanceof \Closure && !\is_array($values = $values($input))) { - throw new LogicException(sprintf('Closure for option "%s" must return an array. Got "%s".', $this->name, get_debug_type($values))); + throw new LogicException(\sprintf('Closure for option "%s" must return an array. Got "%s".', $this->name, get_debug_type($values))); } if ($values) { $suggestions->suggestValues($values); diff --git a/Input/StringInput.php b/Input/StringInput.php index 835700143..a70f048f9 100644 --- a/Input/StringInput.php +++ b/Input/StringInput.php @@ -70,7 +70,7 @@ private function tokenize(string $input): array $token .= $match[1]; } else { // should never happen - throw new InvalidArgumentException(sprintf('Unable to parse input near "... %s ...".', substr($input, $cursor, 10))); + throw new InvalidArgumentException(\sprintf('Unable to parse input near "... %s ...".', substr($input, $cursor, 10))); } $cursor += \strlen($match[0]); diff --git a/Logger/ConsoleLogger.php b/Logger/ConsoleLogger.php index ad6e49ce1..a6ef49ea9 100644 --- a/Logger/ConsoleLogger.php +++ b/Logger/ConsoleLogger.php @@ -63,7 +63,7 @@ public function __construct( public function log($level, $message, array $context = []): void { if (!isset($this->verbosityLevelMap[$level])) { - throw new InvalidArgumentException(sprintf('The log level "%s" does not exist.', $level)); + throw new InvalidArgumentException(\sprintf('The log level "%s" does not exist.', $level)); } $output = $this->output; @@ -79,7 +79,7 @@ public function log($level, $message, array $context = []): void // the if condition check isn't necessary -- it's the same one that $output will do internally anyway. // We only do it for efficiency here as the message formatting is relatively expensive. if ($output->getVerbosity() >= $this->verbosityLevelMap[$level]) { - $output->writeln(sprintf('<%1$s>[%2$s] %3$s', $this->formatLevelMap[$level], $level, $this->interpolate($message, $context)), $this->verbosityLevelMap[$level]); + $output->writeln(\sprintf('<%1$s>[%2$s] %3$s', $this->formatLevelMap[$level], $level, $this->interpolate($message, $context)), $this->verbosityLevelMap[$level]); } } diff --git a/Messenger/RunCommandMessageHandler.php b/Messenger/RunCommandMessageHandler.php index 1bc499494..0fdf7d017 100644 --- a/Messenger/RunCommandMessageHandler.php +++ b/Messenger/RunCommandMessageHandler.php @@ -41,7 +41,7 @@ public function __invoke(RunCommandMessage $message): RunCommandContext } if ($message->throwOnFailure && Command::SUCCESS !== $exitCode) { - throw new RunCommandFailedException(sprintf('Command "%s" exited with code "%s".', $message->input, $exitCode), new RunCommandContext($message, $exitCode, $output->fetch())); + throw new RunCommandFailedException(\sprintf('Command "%s" exited with code "%s".', $message->input, $exitCode), new RunCommandContext($message, $exitCode, $output->fetch())); } return new RunCommandContext($message, $exitCode, $output->fetch()); diff --git a/Output/AnsiColorMode.php b/Output/AnsiColorMode.php index ca40ffb79..0e1422a27 100644 --- a/Output/AnsiColorMode.php +++ b/Output/AnsiColorMode.php @@ -51,7 +51,7 @@ public function convertFromHexToAnsiColorCode(string $hexColor): string } if (6 !== \strlen($hexColor)) { - throw new InvalidArgumentException(sprintf('Invalid "#%s" color.', $hexColor)); + throw new InvalidArgumentException(\sprintf('Invalid "#%s" color.', $hexColor)); } $color = hexdec($hexColor); @@ -62,8 +62,8 @@ public function convertFromHexToAnsiColorCode(string $hexColor): string return match ($this) { self::Ansi4 => (string) $this->convertFromRGB($r, $g, $b), - self::Ansi8 => '8;5;'.((string) $this->convertFromRGB($r, $g, $b)), - self::Ansi24 => sprintf('8;2;%d;%d;%d', $r, $g, $b), + self::Ansi8 => '8;5;'.$this->convertFromRGB($r, $g, $b), + self::Ansi24 => \sprintf('8;2;%d;%d;%d', $r, $g, $b), }; } @@ -96,11 +96,11 @@ private function degradeHexColorToAnsi8(int $r, int $g, int $b): int } return (int) round(($r - 8) / 247 * 24) + 232; - } else { - return 16 + - (36 * (int) round($r / 255 * 5)) + - (6 * (int) round($g / 255 * 5)) + - (int) round($b / 255 * 5); } + + return 16 + + (36 * (int) round($r / 255 * 5)) + + (6 * (int) round($g / 255 * 5)) + + (int) round($b / 255 * 5); } } diff --git a/Output/ConsoleSectionOutput.php b/Output/ConsoleSectionOutput.php index 09aa7fe99..44728dfd4 100644 --- a/Output/ConsoleSectionOutput.php +++ b/Output/ConsoleSectionOutput.php @@ -222,7 +222,7 @@ private function popStreamContentUntilCurrentSection(int $numberOfLinesToClearFr if ($numberOfLinesToClear > 0) { // move cursor up n lines - parent::doWrite(sprintf("\x1b[%dA", $numberOfLinesToClear), false); + parent::doWrite(\sprintf("\x1b[%dA", $numberOfLinesToClear), false); // erase to end of screen parent::doWrite("\x1b[0J", false); } diff --git a/Output/NullOutput.php b/Output/NullOutput.php index 40ae33282..8bec706d4 100644 --- a/Output/NullOutput.php +++ b/Output/NullOutput.php @@ -54,14 +54,19 @@ public function setVerbosity(int $level): void public function getVerbosity(): int { - return self::VERBOSITY_QUIET; + return self::VERBOSITY_SILENT; } - public function isQuiet(): bool + public function isSilent(): bool { return true; } + public function isQuiet(): bool + { + return false; + } + public function isVerbose(): bool { return false; diff --git a/Output/Output.php b/Output/Output.php index 2bb105748..32e6cb241 100644 --- a/Output/Output.php +++ b/Output/Output.php @@ -17,13 +17,14 @@ /** * Base class for output classes. * - * There are five levels of verbosity: + * There are six levels of verbosity: * * * normal: no option passed (normal output) * * verbose: -v (more output) * * very verbose: -vv (highly extended output) * * debug: -vvv (all debug output) - * * quiet: -q (no output) + * * quiet: -q (only output errors) + * * silent: --silent (no output) * * @author Fabien Potencier */ @@ -74,6 +75,11 @@ public function getVerbosity(): int return $this->verbosity; } + public function isSilent(): bool + { + return self::VERBOSITY_SILENT === $this->verbosity; + } + public function isQuiet(): bool { return self::VERBOSITY_QUIET === $this->verbosity; diff --git a/Output/OutputInterface.php b/Output/OutputInterface.php index 41315fbf2..969a3b022 100644 --- a/Output/OutputInterface.php +++ b/Output/OutputInterface.php @@ -17,9 +17,12 @@ * OutputInterface is the interface implemented by all Output classes. * * @author Fabien Potencier + * + * @method bool isSilent() */ interface OutputInterface { + public const VERBOSITY_SILENT = 8; public const VERBOSITY_QUIET = 16; public const VERBOSITY_NORMAL = 32; public const VERBOSITY_VERBOSE = 64; diff --git a/Output/StreamOutput.php b/Output/StreamOutput.php index e94a5eb8b..ce5a825e8 100644 --- a/Output/StreamOutput.php +++ b/Output/StreamOutput.php @@ -94,6 +94,11 @@ protected function hasColorSupport(): bool return false; } + // Follow https://force-color.org/ + if ('' !== (($_SERVER['FORCE_COLOR'] ?? getenv('FORCE_COLOR'))[0] ?? '')) { + return true; + } + // Detect msysgit/mingw and assume this is a tty because detection // does not work correctly, see https://github.com/composer/composer/issues/9690 if (!@stream_isatty($this->stream) && !\in_array(strtoupper((string) getenv('MSYSTEM')), ['MINGW32', 'MINGW64'], true)) { diff --git a/Output/TrimmedBufferOutput.php b/Output/TrimmedBufferOutput.php index c1862a2bd..33db072c5 100644 --- a/Output/TrimmedBufferOutput.php +++ b/Output/TrimmedBufferOutput.php @@ -27,7 +27,7 @@ class TrimmedBufferOutput extends Output public function __construct(int $maxLength, ?int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = false, ?OutputFormatterInterface $formatter = null) { if ($maxLength <= 0) { - throw new InvalidArgumentException(sprintf('"%s()" expects a strictly positive maxLength. Got %d.', __METHOD__, $maxLength)); + throw new InvalidArgumentException(\sprintf('"%s()" expects a strictly positive maxLength. Got %d.', __METHOD__, $maxLength)); } parent::__construct($verbosity, $decorated, $formatter); @@ -53,6 +53,6 @@ protected function doWrite(string $message, bool $newline): void $this->buffer .= \PHP_EOL; } - $this->buffer = substr($this->buffer, 0 - $this->maxLength); + $this->buffer = substr($this->buffer, -$this->maxLength); } } diff --git a/Question/ChoiceQuestion.php b/Question/ChoiceQuestion.php index d062ce78d..36c240d37 100644 --- a/Question/ChoiceQuestion.php +++ b/Question/ChoiceQuestion.php @@ -121,7 +121,7 @@ private function getDefaultValidator(): callable if ($multiselect) { // Check for a separated comma values if (!preg_match('/^[^,]+(?:,[^,]+)*$/', (string) $selected, $matches)) { - throw new InvalidArgumentException(sprintf($errorMessage, $selected)); + throw new InvalidArgumentException(\sprintf($errorMessage, $selected)); } $selectedChoices = explode(',', (string) $selected); @@ -145,7 +145,7 @@ private function getDefaultValidator(): callable } if (\count($results) > 1) { - throw new InvalidArgumentException(sprintf('The provided answer is ambiguous. Value should be one of "%s".', implode('" or "', $results))); + throw new InvalidArgumentException(\sprintf('The provided answer is ambiguous. Value should be one of "%s".', implode('" or "', $results))); } $result = array_search($value, $choices); @@ -161,7 +161,7 @@ private function getDefaultValidator(): callable } if (false === $result) { - throw new InvalidArgumentException(sprintf($errorMessage, $value)); + throw new InvalidArgumentException(\sprintf($errorMessage, $value)); } // For associative choices, consistently return the key as string: diff --git a/SignalRegistry/SignalRegistry.php b/SignalRegistry/SignalRegistry.php index ef2e5f04e..8c2939eec 100644 --- a/SignalRegistry/SignalRegistry.php +++ b/SignalRegistry/SignalRegistry.php @@ -54,4 +54,12 @@ public function handle(int $signal): void $signalHandler($signal, $hasNext); } } + + /** + * @internal + */ + public function scheduleAlarm(int $seconds): void + { + pcntl_alarm($seconds); + } } diff --git a/SingleCommandApplication.php b/SingleCommandApplication.php index ff1c17247..2b54fb870 100644 --- a/SingleCommandApplication.php +++ b/SingleCommandApplication.php @@ -67,6 +67,6 @@ public function run(?InputInterface $input = null, ?OutputInterface $output = nu $this->running = false; } - return $ret ?? 1; + return $ret; } } diff --git a/Style/OutputStyle.php b/Style/OutputStyle.php index 9f62ea312..89a3a4177 100644 --- a/Style/OutputStyle.php +++ b/Style/OutputStyle.php @@ -78,6 +78,12 @@ public function getFormatter(): OutputFormatterInterface return $this->output->getFormatter(); } + public function isSilent(): bool + { + // @deprecated since Symfony 7.2, change to $this->output->isSilent() in 8.0 + return method_exists($this->output, 'isSilent') ? $this->output->isSilent() : self::VERBOSITY_SILENT === $this->output->getVerbosity(); + } + public function isQuiet(): bool { return $this->output->isQuiet(); diff --git a/Style/SymfonyStyle.php b/Style/SymfonyStyle.php index 19ad892ef..4cf62cdba 100644 --- a/Style/SymfonyStyle.php +++ b/Style/SymfonyStyle.php @@ -73,8 +73,8 @@ public function title(string $message): void { $this->autoPrependBlock(); $this->writeln([ - sprintf('%s', OutputFormatter::escapeTrailingBackslash($message)), - sprintf('%s', str_repeat('=', Helper::width(Helper::removeDecoration($this->getFormatter(), $message)))), + \sprintf('%s', OutputFormatter::escapeTrailingBackslash($message)), + \sprintf('%s', str_repeat('=', Helper::width(Helper::removeDecoration($this->getFormatter(), $message)))), ]); $this->newLine(); } @@ -83,8 +83,8 @@ public function section(string $message): void { $this->autoPrependBlock(); $this->writeln([ - sprintf('%s', OutputFormatter::escapeTrailingBackslash($message)), - sprintf('%s', str_repeat('-', Helper::width(Helper::removeDecoration($this->getFormatter(), $message)))), + \sprintf('%s', OutputFormatter::escapeTrailingBackslash($message)), + \sprintf('%s', str_repeat('-', Helper::width(Helper::removeDecoration($this->getFormatter(), $message)))), ]); $this->newLine(); } @@ -92,7 +92,7 @@ public function section(string $message): void public function listing(array $elements): void { $this->autoPrependText(); - $elements = array_map(fn ($element) => sprintf(' * %s', $element), $elements); + $elements = array_map(fn ($element) => \sprintf(' * %s', $element), $elements); $this->writeln($elements); $this->newLine(); @@ -104,7 +104,7 @@ public function text(string|array $message): void $messages = \is_array($message) ? array_values($message) : [$message]; foreach ($messages as $message) { - $this->writeln(sprintf(' %s', $message)); + $this->writeln(\sprintf(' %s', $message)); } } @@ -404,7 +404,7 @@ private function createBlock(iterable $messages, ?string $type = null, ?string $ $lines = []; if (null !== $type) { - $type = sprintf('[%s] ', $type); + $type = \sprintf('[%s] ', $type); $indentLength = Helper::width($type); $lineIndentation = str_repeat(' ', $indentLength); } @@ -446,7 +446,7 @@ private function createBlock(iterable $messages, ?string $type = null, ?string $ $line .= str_repeat(' ', max($this->lineLength - Helper::width(Helper::removeDecoration($this->getFormatter(), $line)), 0)); if ($style) { - $line = sprintf('<%s>%s', $style, $line); + $line = \sprintf('<%s>%s', $style, $line); } } diff --git a/Tester/Constraint/CommandIsSuccessful.php b/Tester/Constraint/CommandIsSuccessful.php index 09c6194b9..d677c27aa 100644 --- a/Tester/Constraint/CommandIsSuccessful.php +++ b/Tester/Constraint/CommandIsSuccessful.php @@ -38,6 +38,6 @@ protected function additionalFailureDescription($other): string Command::INVALID => 'Command was invalid.', ]; - return $mapping[$other] ?? sprintf('Command returned exit status %d.', $other); + return $mapping[$other] ?? \sprintf('Command returned exit status %d.', $other); } } diff --git a/Tests/ApplicationTest.php b/Tests/ApplicationTest.php index ac1d47245..4f6e6cb96 100644 --- a/Tests/ApplicationTest.php +++ b/Tests/ApplicationTest.php @@ -22,6 +22,7 @@ use Symfony\Component\Console\CommandLoader\FactoryCommandLoader; use Symfony\Component\Console\ConsoleEvents; use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass; +use Symfony\Component\Console\Event\ConsoleAlarmEvent; use Symfony\Component\Console\Event\ConsoleCommandEvent; use Symfony\Component\Console\Event\ConsoleErrorEvent; use Symfony\Component\Console\Event\ConsoleSignalEvent; @@ -70,11 +71,15 @@ protected function tearDown(): void unset($_SERVER['SHELL_VERBOSITY']); if (\function_exists('pcntl_signal')) { + // We cancel any pending alarms + pcntl_alarm(0); + // We reset all signals to their default value to avoid side effects pcntl_signal(\SIGINT, \SIG_DFL); pcntl_signal(\SIGTERM, \SIG_DFL); pcntl_signal(\SIGUSR1, \SIG_DFL); pcntl_signal(\SIGUSR2, \SIG_DFL); + pcntl_signal(\SIGALRM, \SIG_DFL); } } @@ -635,7 +640,7 @@ public function testFindAlternativeCommands() } catch (\Exception $e) { $this->assertInstanceOf(CommandNotFoundException::class, $e, '->find() throws a CommandNotFoundException if command does not exist'); $this->assertSame([], $e->getAlternatives()); - $this->assertEquals(sprintf('Command "%s" is not defined.', $commandName), $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, without alternatives'); + $this->assertEquals(\sprintf('Command "%s" is not defined.', $commandName), $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, without alternatives'); } // Test if "bar1" command throw a "CommandNotFoundException" and does not contain @@ -646,7 +651,7 @@ public function testFindAlternativeCommands() } catch (\Exception $e) { $this->assertInstanceOf(CommandNotFoundException::class, $e, '->find() throws a CommandNotFoundException if command does not exist'); $this->assertSame(['afoobar1', 'foo:bar1'], $e->getAlternatives()); - $this->assertMatchesRegularExpression(sprintf('/Command "%s" is not defined./', $commandName), $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); + $this->assertMatchesRegularExpression(\sprintf('/Command "%s" is not defined./', $commandName), $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternatives'); $this->assertMatchesRegularExpression('/afoobar1/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternative : "afoobar1"'); $this->assertMatchesRegularExpression('/foo:bar1/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, with alternative : "foo:bar1"'); $this->assertDoesNotMatchRegularExpression('/foo:bar(?!1)/', $e->getMessage(), '->find() throws a CommandNotFoundException if command does not exist, without "foo:bar" alternative'); @@ -851,12 +856,15 @@ public function testRenderException() putenv('COLUMNS=120'); $tester = new ApplicationTester($application); - $tester->run(['command' => 'foo'], ['decorated' => false, 'capture_stderr_separately' => true]); + $tester->run(['command' => 'foo'], ['decorated' => false, 'verbosity' => Output::VERBOSITY_QUIET, 'capture_stderr_separately' => true]); $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception1.txt', $tester->getErrorOutput(true), '->renderException() renders a pretty exception'); $tester->run(['command' => 'foo'], ['decorated' => false, 'verbosity' => Output::VERBOSITY_VERBOSE, 'capture_stderr_separately' => true]); $this->assertStringContainsString('Exception trace', $tester->getErrorOutput(), '->renderException() renders a pretty exception with a stack trace when verbosity is verbose'); + $tester->run(['command' => 'foo'], ['decorated' => false, 'verbosity' => Output::VERBOSITY_SILENT, 'capture_stderr_separately' => true]); + $this->assertSame('', $tester->getErrorOutput(true), '->renderException() renders nothing in SILENT verbosity'); + $tester->run(['command' => 'list', '--foo' => true], ['decorated' => false, 'capture_stderr_separately' => true]); $this->assertStringEqualsFile(self::$fixturesPath.'/application_renderexception2.txt', $tester->getErrorOutput(true), '->renderException() renders the command synopsis when an exception occurs in the context of a command'); @@ -965,7 +973,7 @@ public function testRenderAnonymousException() $application = new Application(); $application->setAutoExit(false); $application->register('foo')->setCode(function () { - throw new \InvalidArgumentException(sprintf('Dummy type "%s" is invalid.', (new class() {})::class)); + throw new \InvalidArgumentException(\sprintf('Dummy type "%s" is invalid.', (new class {})::class)); }); $tester = new ApplicationTester($application); @@ -991,7 +999,7 @@ public function testRenderExceptionStackTraceContainsRootException() $application = new Application(); $application->setAutoExit(false); $application->register('foo')->setCode(function () { - throw new \InvalidArgumentException(sprintf('Dummy type "%s" is invalid.', (new class() {})::class)); + throw new \InvalidArgumentException(\sprintf('Dummy type "%s" is invalid.', (new class {})::class)); }); $tester = new ApplicationTester($application); @@ -2227,6 +2235,168 @@ public function testSignalableRestoresStty() $this->assertSame($previousSttyMode, $sttyMode); } + /** + * @requires extension pcntl + */ + public function testAlarmSubscriberNotCalledByDefault() + { + $command = new BaseSignableCommand(false); + + $subscriber = new AlarmEventSubscriber(); + + $dispatcher = new EventDispatcher(); + $dispatcher->addSubscriber($subscriber); + + $application = $this->createSignalableApplication($command, $dispatcher); + + $this->assertSame(0, $application->run(new ArrayInput(['signal']))); + $this->assertFalse($subscriber->signaled); + } + + /** + * @requires extension pcntl + */ + public function testAlarmSubscriberNotCalledForOtherSignals() + { + $command = new SignableCommand(); + + $subscriber1 = new SignalEventSubscriber(); + $subscriber2 = new AlarmEventSubscriber(); + + $dispatcher = new EventDispatcher(); + $dispatcher->addSubscriber($subscriber1); + $dispatcher->addSubscriber($subscriber2); + + $application = $this->createSignalableApplication($command, $dispatcher); + + $this->assertSame(1, $application->run(new ArrayInput(['signal']))); + $this->assertTrue($subscriber1->signaled); + $this->assertFalse($subscriber2->signaled); + } + + /** + * @requires extension pcntl + */ + public function testAlarmSubscriber() + { + $command = new BaseSignableCommand(signal: \SIGALRM); + + $subscriber1 = new AlarmEventSubscriber(); + $subscriber2 = new AlarmEventSubscriber(); + + $dispatcher = new EventDispatcher(); + $dispatcher->addSubscriber($subscriber1); + $dispatcher->addSubscriber($subscriber2); + + $application = $this->createSignalableApplication($command, $dispatcher); + + $this->assertSame(1, $application->run(new ArrayInput(['signal']))); + $this->assertTrue($subscriber1->signaled); + $this->assertTrue($subscriber2->signaled); + } + + /** + * @requires extension pcntl + */ + public function testAlarmDispatchWithoutEventDispatcher() + { + $command = new AlarmableCommand(1); + $command->loop = 11000; + + $application = $this->createSignalableApplication($command, null); + + $this->assertSame(1, $application->run(new ArrayInput(['alarm']))); + $this->assertSame(1, $application->getAlarmInterval()); + $this->assertTrue($command->signaled); + } + + /** + * @requires extension pcntl + */ + public function testAlarmableCommandWithoutInterval() + { + $command = new AlarmableCommand(0); + $command->loop = 11000; + + $dispatcher = new EventDispatcher(); + + $application = new Application(); + $application->setAutoExit(false); + $application->setDispatcher($dispatcher); + $application->add($command); + + $this->assertSame(0, $application->run(new ArrayInput(['alarm']))); + $this->assertFalse($command->signaled); + } + + /** + * @requires extension pcntl + */ + public function testAlarmableCommandHandlerCalledAfterEventListener() + { + $command = new AlarmableCommand(1); + $command->loop = 11000; + + $subscriber = new AlarmEventSubscriber(); + + $dispatcher = new EventDispatcher(); + $dispatcher->addSubscriber($subscriber); + + $application = $this->createSignalableApplication($command, $dispatcher); + + $this->assertSame(1, $application->run(new ArrayInput(['alarm']))); + $this->assertSame([AlarmEventSubscriber::class, AlarmableCommand::class], $command->signalHandlers); + } + + /** + * @requires extension pcntl + * + * @testWith [false] + * [4] + */ + public function testAlarmSubscriberCalledAfterSignalSubscriberAndInheritsExitCode(int|false $exitCode) + { + $command = new BaseSignableCommand(signal: \SIGALRM); + + $subscriber1 = new class($exitCode) extends SignalEventSubscriber { + public function __construct(private int|false $exitCode) + { + } + + public function onSignal(ConsoleSignalEvent $event): void + { + parent::onSignal($event); + + if (false === $this->exitCode) { + $event->abortExit(); + } else { + $event->setExitCode($this->exitCode); + } + } + }; + $subscriber2 = new class($exitCode) extends AlarmEventSubscriber { + public function __construct(private int|false $exitCode) + { + } + + public function onAlarm(ConsoleAlarmEvent $event): void + { + TestCase::assertSame($this->exitCode, $event->getExitCode()); + + parent::onAlarm($event); + } + }; + + $dispatcher = new EventDispatcher(); + $dispatcher->addSubscriber($subscriber1); + $dispatcher->addSubscriber($subscriber2); + + $application = $this->createSignalableApplication($command, $dispatcher); + + $this->assertSame(1, $application->run(new ArrayInput(['signal']))); + $this->assertSame([SignalEventSubscriber::class, AlarmEventSubscriber::class], $command->signalHandlers); + } + private function createSignalableApplication(Command $command, ?EventDispatcherInterface $dispatcher): Application { $application = new Application(); @@ -2234,7 +2404,7 @@ private function createSignalableApplication(Command $command, ?EventDispatcherI if ($dispatcher) { $application->setDispatcher($dispatcher); } - $application->add(new LazyCommand('signal', [], '', false, fn () => $command, true)); + $application->add(new LazyCommand($command::getDefaultName(), [], '', false, fn () => $command, true)); return $application; } @@ -2424,3 +2594,51 @@ public static function getSubscribedEvents(): array return ['console.signal' => 'onSignal']; } } + +#[AsCommand(name: 'alarm')] +class AlarmableCommand extends BaseSignableCommand implements SignalableCommandInterface +{ + public function __construct(private int $alarmInterval) + { + parent::__construct(false); + } + + protected function initialize(InputInterface $input, OutputInterface $output): void + { + $this->getApplication()->setAlarmInterval($this->alarmInterval); + } + + public function getSubscribedSignals(): array + { + return [\SIGALRM]; + } + + public function handleSignal(int $signal, false|int $previousExitCode = 0): int|false + { + if (\SIGALRM === $signal) { + $this->signaled = true; + $this->signalHandlers[] = __CLASS__; + } + + return false; + } +} + +class AlarmEventSubscriber implements EventSubscriberInterface +{ + public bool $signaled = false; + + public function onAlarm(ConsoleAlarmEvent $event): void + { + $this->signaled = true; + $event->getCommand()->signaled = true; + $event->getCommand()->signalHandlers[] = __CLASS__; + + $event->abortExit(); + } + + public static function getSubscribedEvents(): array + { + return [ConsoleAlarmEvent::class => 'onAlarm']; + } +} diff --git a/Tests/Command/CommandTest.php b/Tests/Command/CommandTest.php index f3e4b51d1..199c0c309 100644 --- a/Tests/Command/CommandTest.php +++ b/Tests/Command/CommandTest.php @@ -139,7 +139,7 @@ public function testGetNamespaceGetNameSetName() public function testInvalidCommandNames($name) { $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage(sprintf('Command name "%s" is invalid.', $name)); + $this->expectExceptionMessage(\sprintf('Command name "%s" is invalid.', $name)); (new \TestCommand())->setName($name); } diff --git a/Tests/Command/CompleteCommandTest.php b/Tests/Command/CompleteCommandTest.php index 0f64fbc8e..75519eb49 100644 --- a/Tests/Command/CompleteCommandTest.php +++ b/Tests/Command/CompleteCommandTest.php @@ -53,6 +53,8 @@ public function testUnsupportedShellOption() public function testAdditionalShellSupport() { + $this->expectNotToPerformAssertions(); + $this->command = new CompleteCommand(['supported' => BashCompletionOutput::class]); $this->command->setApplication($this->application); $this->tester = new CommandTester($this->command); @@ -61,8 +63,6 @@ public function testAdditionalShellSupport() // verify that the default set of shells is still supported $this->execute(['--shell' => 'bash', '--current' => '1', '--input' => ['bin/console']]); - - $this->assertTrue(true); } /** @@ -119,9 +119,9 @@ public function testCompleteCommandInputDefinition(array $input, array $suggesti public static function provideCompleteCommandInputDefinitionInputs() { - yield 'definition' => [['bin/console', 'hello', '-'], ['--help', '--quiet', '--verbose', '--version', '--ansi', '--no-ansi', '--no-interaction']]; + yield 'definition' => [['bin/console', 'hello', '-'], ['--help', '--silent', '--quiet', '--verbose', '--version', '--ansi', '--no-ansi', '--no-interaction']]; yield 'custom' => [['bin/console', 'hello'], ['Fabien', 'Robin', 'Wouter']]; - yield 'definition-aliased' => [['bin/console', 'ahoy', '-'], ['--help', '--quiet', '--verbose', '--version', '--ansi', '--no-ansi', '--no-interaction']]; + yield 'definition-aliased' => [['bin/console', 'ahoy', '-'], ['--help', '--silent', '--quiet', '--verbose', '--version', '--ansi', '--no-ansi', '--no-interaction']]; yield 'custom-aliased' => [['bin/console', 'ahoy'], ['Fabien', 'Robin', 'Wouter']]; } diff --git a/Tests/Command/ListCommandTest.php b/Tests/Command/ListCommandTest.php index 20dfa8d30..a6ffc8ab5 100644 --- a/Tests/Command/ListCommandTest.php +++ b/Tests/Command/ListCommandTest.php @@ -80,7 +80,8 @@ public function testExecuteListsCommandsOrder() Options: -h, --help Display help for the given command. When no command is given display help for the list command - -q, --quiet Do not output any message + --silent Do not output any message + -q, --quiet Only errors are displayed. All other output is suppressed -V, --version Display this application version --ansi|--no-ansi Force (or disable --no-ansi) ANSI output -n, --no-interaction Do not ask any interactive question diff --git a/Tests/Command/SingleCommandApplicationTest.php b/Tests/Command/SingleCommandApplicationTest.php index 8fae4876b..98000c0a7 100644 --- a/Tests/Command/SingleCommandApplicationTest.php +++ b/Tests/Command/SingleCommandApplicationTest.php @@ -21,7 +21,7 @@ class SingleCommandApplicationTest extends TestCase { public function testRun() { - $command = new class() extends SingleCommandApplication { + $command = new class extends SingleCommandApplication { protected function execute(InputInterface $input, OutputInterface $output): int { return 0; diff --git a/Tests/ConsoleEventsTest.php b/Tests/ConsoleEventsTest.php index 9c04d2706..408f8c0d3 100644 --- a/Tests/ConsoleEventsTest.php +++ b/Tests/ConsoleEventsTest.php @@ -39,6 +39,7 @@ protected function tearDown(): void pcntl_signal(\SIGTERM, \SIG_DFL); pcntl_signal(\SIGUSR1, \SIG_DFL); pcntl_signal(\SIGUSR2, \SIG_DFL); + pcntl_signal(\SIGALRM, \SIG_DFL); } } diff --git a/Tests/Descriptor/AbstractDescriptorTestCase.php b/Tests/Descriptor/AbstractDescriptorTestCase.php index 1f813048d..93658f4be 100644 --- a/Tests/Descriptor/AbstractDescriptorTestCase.php +++ b/Tests/Descriptor/AbstractDescriptorTestCase.php @@ -87,7 +87,7 @@ protected static function getDescriptionTestData(array $objects) { $data = []; foreach ($objects as $name => $object) { - $description = file_get_contents(sprintf('%s/../Fixtures/%s.%s', __DIR__, $name, static::getFormat())); + $description = file_get_contents(\sprintf('%s/../Fixtures/%s.%s', __DIR__, $name, static::getFormat())); $data[] = [$object, $description]; } diff --git a/Tests/Fixtures/Style/SymfonyStyle/command/command_19.php b/Tests/Fixtures/Style/SymfonyStyle/command/command_19.php index e44b18b76..e25a7ef29 100644 --- a/Tests/Fixtures/Style/SymfonyStyle/command/command_19.php +++ b/Tests/Fixtures/Style/SymfonyStyle/command/command_19.php @@ -1,6 +1,5 @@ list command", "default": false }, + "silent": { + "name": "--silent", + "shortcut": "", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Do not output any message", + "default": false + }, "quiet": { "name": "--quiet", "shortcut": "-q", "accept_value": false, "is_value_required": false, "is_multiple": false, - "description": "Do not output any message", + "description": "Only errors are displayed. All other output is suppressed", "default": false }, "verbose": { @@ -150,13 +159,22 @@ "description": "Display help for the given command. When no command is given display help for the list command", "default": false }, + "silent": { + "name": "--silent", + "shortcut": "", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Do not output any message", + "default": false + }, "quiet": { "name": "--quiet", "shortcut": "-q", "accept_value": false, "is_value_required": false, "is_multiple": false, - "description": "Do not output any message", + "description": "Only errors are displayed. All other output is suppressed", "default": false }, "verbose": { @@ -262,13 +280,22 @@ "description": "Display help for the given command. When no command is given display help for the list command", "default": false }, + "silent": { + "name": "--silent", + "shortcut": "", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Do not output any message", + "default": false + }, "quiet": { "name": "--quiet", "shortcut": "-q", "accept_value": false, "is_value_required": false, "is_multiple": false, - "description": "Do not output any message", + "description": "Only errors are displayed. All other output is suppressed", "default": false }, "verbose": { @@ -365,13 +392,22 @@ "description": "Display help for the given command. When no command is given display help for the list command", "default": false }, + "silent": { + "name": "--silent", + "shortcut": "", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Do not output any message", + "default": false + }, "quiet": { "name": "--quiet", "shortcut": "-q", "accept_value": false, "is_value_required": false, "is_multiple": false, - "description": "Do not output any message", + "description": "Only errors are displayed. All other output is suppressed", "default": false }, "verbose": { diff --git a/Tests/Fixtures/application_1.md b/Tests/Fixtures/application_1.md index bb722c077..79d9b27aa 100644 --- a/Tests/Fixtures/application_1.md +++ b/Tests/Fixtures/application_1.md @@ -48,7 +48,7 @@ Display help for the given command. When no command is given display help for th * Is negatable: no * Default: `false` -#### `--quiet|-q` +#### `--silent` Do not output any message @@ -58,6 +58,16 @@ Do not output any message * Is negatable: no * Default: `false` +#### `--quiet|-q` + +Only errors are displayed. All other output is suppressed + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + #### `--verbose|-v|-vv|-vvv` Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug @@ -159,7 +169,7 @@ Display help for the given command. When no command is given display help for th * Is negatable: no * Default: `false` -#### `--quiet|-q` +#### `--silent` Do not output any message @@ -169,6 +179,16 @@ Do not output any message * Is negatable: no * Default: `false` +#### `--quiet|-q` + +Only errors are displayed. All other output is suppressed + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + #### `--verbose|-v|-vv|-vvv` Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug @@ -286,7 +306,7 @@ Display help for the given command. When no command is given display help for th * Is negatable: no * Default: `false` -#### `--quiet|-q` +#### `--silent` Do not output any message @@ -296,6 +316,16 @@ Do not output any message * Is negatable: no * Default: `false` +#### `--quiet|-q` + +Only errors are displayed. All other output is suppressed + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + #### `--verbose|-v|-vv|-vvv` Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug diff --git a/Tests/Fixtures/application_1.txt b/Tests/Fixtures/application_1.txt index f72f43a07..7fce7ce52 100644 --- a/Tests/Fixtures/application_1.txt +++ b/Tests/Fixtures/application_1.txt @@ -5,7 +5,8 @@ Console Tool Options: -h, --help Display help for the given command. When no command is given display help for the list command - -q, --quiet Do not output any message + --silent Do not output any message + -q, --quiet Only errors are displayed. All other output is suppressed -V, --version Display this application version --ansi|--no-ansi Force (or disable --no-ansi) ANSI output -n, --no-interaction Do not ask any interactive question diff --git a/Tests/Fixtures/application_1.xml b/Tests/Fixtures/application_1.xml index d109e055f..d726cee35 100644 --- a/Tests/Fixtures/application_1.xml +++ b/Tests/Fixtures/application_1.xml @@ -32,9 +32,12 @@ - + @@ -71,9 +74,12 @@ - + @@ -126,9 +132,12 @@ - + @@ -188,9 +197,12 @@ - + diff --git a/Tests/Fixtures/application_2.json b/Tests/Fixtures/application_2.json index b3eb10bda..4a6f411f5 100644 --- a/Tests/Fixtures/application_2.json +++ b/Tests/Fixtures/application_2.json @@ -33,13 +33,22 @@ "description": "Display help for the given command. When no command is given display help for the list command", "default": false }, + "silent": { + "name": "--silent", + "shortcut": "", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Do not output any message", + "default": false + }, "quiet": { "name": "--quiet", "shortcut": "-q", "accept_value": false, "is_value_required": false, "is_multiple": false, - "description": "Do not output any message", + "description": "Only errors are displayed. All other output is suppressed", "default": false }, "verbose": { @@ -154,13 +163,22 @@ "description": "Display help for the given command. When no command is given display help for the list command", "default": false }, + "silent": { + "name": "--silent", + "shortcut": "", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Do not output any message", + "default": false + }, "quiet": { "name": "--quiet", "shortcut": "-q", "accept_value": false, "is_value_required": false, "is_multiple": false, - "description": "Do not output any message", + "description": "Only errors are displayed. All other output is suppressed", "default": false }, "verbose": { @@ -266,13 +284,22 @@ "description": "Display help for the given command. When no command is given display help for the list command", "default": false }, + "silent": { + "name": "--silent", + "shortcut": "", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Do not output any message", + "default": false + }, "quiet": { "name": "--quiet", "shortcut": "-q", "accept_value": false, "is_value_required": false, "is_multiple": false, - "description": "Do not output any message", + "description": "Only errors are displayed. All other output is suppressed", "default": false }, "verbose": { @@ -369,13 +396,22 @@ "description": "Display help for the given command. When no command is given display help for the list command", "default": false }, + "silent": { + "name": "--silent", + "shortcut": "", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Do not output any message", + "default": false + }, "quiet": { "name": "--quiet", "shortcut": "-q", "accept_value": false, "is_value_required": false, "is_multiple": false, - "description": "Do not output any message", + "description": "Only errors are displayed. All other output is suppressed", "default": false }, "verbose": { @@ -457,13 +493,22 @@ "description": "Display help for the given command. When no command is given display help for the list command", "default": false }, + "silent": { + "name": "--silent", + "shortcut": "", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Do not output any message", + "default": false + }, "quiet": { "name": "--quiet", "shortcut": "-q", "accept_value": false, "is_value_required": false, "is_multiple": false, - "description": "Do not output any message", + "description": "Only errors are displayed. All other output is suppressed", "default": false }, "verbose": { @@ -553,13 +598,22 @@ "description": "Display help for the given command. When no command is given display help for the list command", "default": false }, + "silent": { + "name": "--silent", + "shortcut": "", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Do not output any message", + "default": false + }, "quiet": { "name": "--quiet", "shortcut": "-q", "accept_value": false, "is_value_required": false, "is_multiple": false, - "description": "Do not output any message", + "description": "Only errors are displayed. All other output is suppressed", "default": false }, "verbose": { @@ -630,13 +684,22 @@ "description": "Display help for the given command. When no command is given display help for the list command", "default": false }, + "silent": { + "name": "--silent", + "shortcut": "", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Do not output any message", + "default": false + }, "quiet": { "name": "--quiet", "shortcut": "-q", "accept_value": false, "is_value_required": false, "is_multiple": false, - "description": "Do not output any message", + "description": "Only errors are displayed. All other output is suppressed", "default": false }, "verbose": { @@ -709,13 +772,22 @@ "description": "Display help for the given command. When no command is given display help for the list command", "default": false }, + "silent": { + "name": "--silent", + "shortcut": "", + "accept_value": false, + "is_value_required": false, + "is_multiple": false, + "description": "Do not output any message", + "default": false + }, "quiet": { "name": "--quiet", "shortcut": "-q", "accept_value": false, "is_value_required": false, "is_multiple": false, - "description": "Do not output any message", + "description": "Only errors are displayed. All other output is suppressed", "default": false }, "verbose": { diff --git a/Tests/Fixtures/application_2.md b/Tests/Fixtures/application_2.md index d4802c747..37e6c28fc 100644 --- a/Tests/Fixtures/application_2.md +++ b/Tests/Fixtures/application_2.md @@ -61,7 +61,7 @@ Display help for the given command. When no command is given display help for th * Is negatable: no * Default: `false` -#### `--quiet|-q` +#### `--silent` Do not output any message @@ -71,6 +71,16 @@ Do not output any message * Is negatable: no * Default: `false` +#### `--quiet|-q` + +Only errors are displayed. All other output is suppressed + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + #### `--verbose|-v|-vv|-vvv` Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug @@ -172,7 +182,7 @@ Display help for the given command. When no command is given display help for th * Is negatable: no * Default: `false` -#### `--quiet|-q` +#### `--silent` Do not output any message @@ -182,6 +192,16 @@ Do not output any message * Is negatable: no * Default: `false` +#### `--quiet|-q` + +Only errors are displayed. All other output is suppressed + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + #### `--verbose|-v|-vv|-vvv` Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug @@ -299,7 +319,7 @@ Display help for the given command. When no command is given display help for th * Is negatable: no * Default: `false` -#### `--quiet|-q` +#### `--silent` Do not output any message @@ -309,6 +329,16 @@ Do not output any message * Is negatable: no * Default: `false` +#### `--quiet|-q` + +Only errors are displayed. All other output is suppressed + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + #### `--verbose|-v|-vv|-vvv` Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug @@ -374,7 +404,7 @@ Display help for the given command. When no command is given display help for th * Is negatable: no * Default: `false` -#### `--quiet|-q` +#### `--silent` Do not output any message @@ -384,6 +414,16 @@ Do not output any message * Is negatable: no * Default: `false` +#### `--quiet|-q` + +Only errors are displayed. All other output is suppressed + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + #### `--verbose|-v|-vv|-vvv` Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug @@ -465,7 +505,7 @@ Display help for the given command. When no command is given display help for th * Is negatable: no * Default: `false` -#### `--quiet|-q` +#### `--silent` Do not output any message @@ -475,6 +515,16 @@ Do not output any message * Is negatable: no * Default: `false` +#### `--quiet|-q` + +Only errors are displayed. All other output is suppressed + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + #### `--verbose|-v|-vv|-vvv` Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug @@ -537,7 +587,7 @@ Display help for the given command. When no command is given display help for th * Is negatable: no * Default: `false` -#### `--quiet|-q` +#### `--silent` Do not output any message @@ -547,6 +597,16 @@ Do not output any message * Is negatable: no * Default: `false` +#### `--quiet|-q` + +Only errors are displayed. All other output is suppressed + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + #### `--verbose|-v|-vv|-vvv` Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug diff --git a/Tests/Fixtures/application_2.txt b/Tests/Fixtures/application_2.txt index aed535fa4..1725b5fa6 100644 --- a/Tests/Fixtures/application_2.txt +++ b/Tests/Fixtures/application_2.txt @@ -5,7 +5,8 @@ My Symfony application v1.0 Options: -h, --help Display help for the given command. When no command is given display help for the list command - -q, --quiet Do not output any message + --silent Do not output any message + -q, --quiet Only errors are displayed. All other output is suppressed -V, --version Display this application version --ansi|--no-ansi Force (or disable --no-ansi) ANSI output -n, --no-interaction Do not ask any interactive question diff --git a/Tests/Fixtures/application_2.xml b/Tests/Fixtures/application_2.xml index 6ee45c1fa..dd4b1800a 100644 --- a/Tests/Fixtures/application_2.xml +++ b/Tests/Fixtures/application_2.xml @@ -32,9 +32,12 @@ - + @@ -71,9 +74,12 @@ - + @@ -126,9 +132,12 @@ - + @@ -188,9 +197,12 @@ - + @@ -221,9 +233,12 @@ - + @@ -262,9 +277,12 @@ - + @@ -293,9 +311,12 @@ - + @@ -326,9 +347,12 @@ - + diff --git a/Tests/Fixtures/application_filtered_namespace.txt b/Tests/Fixtures/application_filtered_namespace.txt index c24da0bbc..762a7f68d 100644 --- a/Tests/Fixtures/application_filtered_namespace.txt +++ b/Tests/Fixtures/application_filtered_namespace.txt @@ -5,7 +5,8 @@ My Symfony application v1.0 Options: -h, --help Display help for the given command. When no command is given display help for the list command - -q, --quiet Do not output any message + --silent Do not output any message + -q, --quiet Only errors are displayed. All other output is suppressed -V, --version Display this application version --ansi|--no-ansi Force (or disable --no-ansi) ANSI output -n, --no-interaction Do not ask any interactive question diff --git a/Tests/Fixtures/application_mbstring.md b/Tests/Fixtures/application_mbstring.md index e7bc69c71..5e31b7ef4 100644 --- a/Tests/Fixtures/application_mbstring.md +++ b/Tests/Fixtures/application_mbstring.md @@ -52,7 +52,7 @@ Display help for the given command. When no command is given display help for th * Is negatable: no * Default: `false` -#### `--quiet|-q` +#### `--silent` Do not output any message @@ -62,6 +62,16 @@ Do not output any message * Is negatable: no * Default: `false` +#### `--quiet|-q` + +Only errors are displayed. All other output is suppressed + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + #### `--verbose|-v|-vv|-vvv` Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug @@ -163,7 +173,7 @@ Display help for the given command. When no command is given display help for th * Is negatable: no * Default: `false` -#### `--quiet|-q` +#### `--silent` Do not output any message @@ -173,6 +183,16 @@ Do not output any message * Is negatable: no * Default: `false` +#### `--quiet|-q` + +Only errors are displayed. All other output is suppressed + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + #### `--verbose|-v|-vv|-vvv` Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug @@ -290,7 +310,7 @@ Display help for the given command. When no command is given display help for th * Is negatable: no * Default: `false` -#### `--quiet|-q` +#### `--silent` Do not output any message @@ -300,6 +320,16 @@ Do not output any message * Is negatable: no * Default: `false` +#### `--quiet|-q` + +Only errors are displayed. All other output is suppressed + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + #### `--verbose|-v|-vv|-vvv` Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug @@ -381,7 +411,7 @@ Display help for the given command. When no command is given display help for th * Is negatable: no * Default: `false` -#### `--quiet|-q` +#### `--silent` Do not output any message @@ -391,6 +421,16 @@ Do not output any message * Is negatable: no * Default: `false` +#### `--quiet|-q` + +Only errors are displayed. All other output is suppressed + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + #### `--verbose|-v|-vv|-vvv` Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug diff --git a/Tests/Fixtures/application_mbstring.txt b/Tests/Fixtures/application_mbstring.txt index 73a47fff4..e904ddf05 100644 --- a/Tests/Fixtures/application_mbstring.txt +++ b/Tests/Fixtures/application_mbstring.txt @@ -5,7 +5,8 @@ MbString åpplicätion Options: -h, --help Display help for the given command. When no command is given display help for the list command - -q, --quiet Do not output any message + --silent Do not output any message + -q, --quiet Only errors are displayed. All other output is suppressed -V, --version Display this application version --ansi|--no-ansi Force (or disable --no-ansi) ANSI output -n, --no-interaction Do not ask any interactive question diff --git a/Tests/Fixtures/application_run1.txt b/Tests/Fixtures/application_run1.txt index 0b24a777c..2d6f6c666 100644 --- a/Tests/Fixtures/application_run1.txt +++ b/Tests/Fixtures/application_run1.txt @@ -5,7 +5,8 @@ Usage: Options: -h, --help Display help for the given command. When no command is given display help for the list command - -q, --quiet Do not output any message + --silent Do not output any message + -q, --quiet Only errors are displayed. All other output is suppressed -V, --version Display this application version --ansi|--no-ansi Force (or disable --no-ansi) ANSI output -n, --no-interaction Do not ask any interactive question diff --git a/Tests/Fixtures/application_run2.txt b/Tests/Fixtures/application_run2.txt index ccd73d14c..8523e16a6 100644 --- a/Tests/Fixtures/application_run2.txt +++ b/Tests/Fixtures/application_run2.txt @@ -12,7 +12,8 @@ Options: --format=FORMAT The output format (txt, xml, json, or md) [default: "txt"] --short To skip describing commands' arguments -h, --help Display help for the given command. When no command is given display help for the list command - -q, --quiet Do not output any message + --silent Do not output any message + -q, --quiet Only errors are displayed. All other output is suppressed -V, --version Display this application version --ansi|--no-ansi Force (or disable --no-ansi) ANSI output -n, --no-interaction Do not ask any interactive question diff --git a/Tests/Fixtures/application_run3.txt b/Tests/Fixtures/application_run3.txt index ccd73d14c..8523e16a6 100644 --- a/Tests/Fixtures/application_run3.txt +++ b/Tests/Fixtures/application_run3.txt @@ -12,7 +12,8 @@ Options: --format=FORMAT The output format (txt, xml, json, or md) [default: "txt"] --short To skip describing commands' arguments -h, --help Display help for the given command. When no command is given display help for the list command - -q, --quiet Do not output any message + --silent Do not output any message + -q, --quiet Only errors are displayed. All other output is suppressed -V, --version Display this application version --ansi|--no-ansi Force (or disable --no-ansi) ANSI output -n, --no-interaction Do not ask any interactive question diff --git a/Tests/Fixtures/application_run5.txt b/Tests/Fixtures/application_run5.txt index de3fdd346..c5696492d 100644 --- a/Tests/Fixtures/application_run5.txt +++ b/Tests/Fixtures/application_run5.txt @@ -11,7 +11,8 @@ Options: --format=FORMAT The output format (txt, xml, json, or md) [default: "txt"] --raw To output raw command help -h, --help Display help for the given command. When no command is given display help for the list command - -q, --quiet Do not output any message + --silent Do not output any message + -q, --quiet Only errors are displayed. All other output is suppressed -V, --version Display this application version --ansi|--no-ansi Force (or disable --no-ansi) ANSI output -n, --no-interaction Do not ask any interactive question diff --git a/Tests/Fixtures/application_signalable.php b/Tests/Fixtures/application_signalable.php index 12cf744ea..978406637 100644 --- a/Tests/Fixtures/application_signalable.php +++ b/Tests/Fixtures/application_signalable.php @@ -12,7 +12,7 @@ } require $vendor.'/vendor/autoload.php'; -(new class() extends SingleCommandApplication implements SignalableCommandInterface { +(new class extends SingleCommandApplication implements SignalableCommandInterface { public function getSubscribedSignals(): array { return [SIGINT]; diff --git a/Tests/Helper/ProgressBarTest.php b/Tests/Helper/ProgressBarTest.php index fd4aaef55..3d1bfa48f 100644 --- a/Tests/Helper/ProgressBarTest.php +++ b/Tests/Helper/ProgressBarTest.php @@ -112,8 +112,10 @@ public function testRegularTimeEstimation() public function testRegularTimeRemainingWithDifferentStartAtAndCustomDisplay() { + $this->expectNotToPerformAssertions(); + ProgressBar::setFormatDefinition('custom', ' %current%/%max% [%bar%] %percent:3s%% %remaining% %estimated%'); - $bar = new ProgressBar($output = $this->getOutputStream(), 1_200, 0); + $bar = new ProgressBar($this->getOutputStream(), 1_200, 0); $bar->setFormat('custom'); $bar->start(1_200, 600); } diff --git a/Tests/Helper/ProgressIndicatorTest.php b/Tests/Helper/ProgressIndicatorTest.php index 7f7dbc0a0..2a4441d57 100644 --- a/Tests/Helper/ProgressIndicatorTest.php +++ b/Tests/Helper/ProgressIndicatorTest.php @@ -54,11 +54,11 @@ public function testDefaultIndicator() $this->generateOutput(' \\ Starting...'). $this->generateOutput(' \\ Advancing...'). $this->generateOutput(' | Advancing...'). - $this->generateOutput(' | Done...'). + $this->generateOutput(' ✔ Done...'). \PHP_EOL. $this->generateOutput(' - Starting Again...'). $this->generateOutput(' \\ Starting Again...'). - $this->generateOutput(' \\ Done Again...'). + $this->generateOutput(' ✔ Done Again...'). \PHP_EOL, stream_get_contents($output->getStream()) ); @@ -109,6 +109,39 @@ public function testCustomIndicatorValues() ); } + public function testCustomFinishedIndicatorValue() + { + $bar = new ProgressIndicator($output = $this->getOutputStream(), null, 100, ['a', 'b'], '✅'); + + $bar->start('Starting...'); + usleep(101000); + $bar->finish('Done'); + + rewind($output->getStream()); + + $this->assertSame( + $this->generateOutput(' a Starting...'). + $this->generateOutput(' ✅ Done').\PHP_EOL, + stream_get_contents($output->getStream()) + ); + } + + public function testCustomFinishedIndicatorWhenFinishingProcess() + { + $bar = new ProgressIndicator($output = $this->getOutputStream(), null, 100, ['a', 'b']); + + $bar->start('Starting...'); + $bar->finish('Process failed', '❌'); + + rewind($output->getStream()); + + $this->assertEquals( + $this->generateOutput(' a Starting...'). + $this->generateOutput(' ❌ Process failed').\PHP_EOL, + stream_get_contents($output->getStream()) + ); + } + public function testCannotSetInvalidIndicatorCharacters() { $this->expectException(\InvalidArgumentException::class); @@ -179,6 +212,6 @@ protected function generateOutput($expected) { $count = substr_count($expected, "\n"); - return "\x0D\x1B[2K".($count ? sprintf("\033[%dA", $count) : '').$expected; + return "\x0D\x1B[2K".($count ? \sprintf("\033[%dA", $count) : '').$expected; } } diff --git a/Tests/Helper/TableCellStyleTest.php b/Tests/Helper/TableCellStyleTest.php index ac80750eb..d934cf801 100644 --- a/Tests/Helper/TableCellStyleTest.php +++ b/Tests/Helper/TableCellStyleTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Console\Tests\Helper; use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Helper\TableCellStyle; class TableCellStyleTest extends TestCase @@ -21,7 +22,8 @@ public function testCreateTableCellStyle() $tableCellStyle = new TableCellStyle(['fg' => 'red']); $this->assertEquals('red', $tableCellStyle->getOptions()['fg']); - $this->expectException(\Symfony\Component\Console\Exception\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); + new TableCellStyle(['wrong_key' => null]); } } diff --git a/Tests/Input/ArgvInputTest.php b/Tests/Input/ArgvInputTest.php index 80ea06d67..0e76f9ee6 100644 --- a/Tests/Input/ArgvInputTest.php +++ b/Tests/Input/ArgvInputTest.php @@ -26,17 +26,17 @@ public function testConstructor() $r = new \ReflectionObject($input); $p = $r->getProperty('tokens'); - $this->assertEquals(['foo'], $p->getValue($input), '__construct() automatically get its input from the argv server variable'); + $this->assertSame(['foo'], $p->getValue($input), '__construct() automatically get its input from the argv server variable'); } public function testParseArguments() { $input = new ArgvInput(['cli.php', 'foo']); $input->bind(new InputDefinition([new InputArgument('name')])); - $this->assertEquals(['name' => 'foo'], $input->getArguments(), '->parse() parses required arguments'); + $this->assertSame(['name' => 'foo'], $input->getArguments(), '->parse() parses required arguments'); $input->bind(new InputDefinition([new InputArgument('name')])); - $this->assertEquals(['name' => 'foo'], $input->getArguments(), '->parse() is stateless'); + $this->assertSame(['name' => 'foo'], $input->getArguments(), '->parse() is stateless'); } /** @@ -57,7 +57,7 @@ public function testParseOptionsNegatable($input, $options, $expectedOptions, $m { $input = new ArgvInput($input); $input->bind(new InputDefinition($options)); - $this->assertEquals($expectedOptions, $input->getOptions(), $message); + $this->assertSame($expectedOptions, $input->getOptions(), $message); } public static function provideOptions() @@ -324,6 +324,11 @@ public static function provideInvalidInput(): array new InputDefinition([new InputArgument('name', InputArgument::REQUIRED)]), 'Too many arguments, expected arguments "name".', ], + [ + ['cli.php', ['array']], + new InputDefinition(), + 'Argument values expected to be all scalars, got "array".', + ], ]; } @@ -358,7 +363,7 @@ public function testParseArrayArgument() $input = new ArgvInput(['cli.php', 'foo', 'bar', 'baz', 'bat']); $input->bind(new InputDefinition([new InputArgument('name', InputArgument::IS_ARRAY)])); - $this->assertEquals(['name' => ['foo', 'bar', 'baz', 'bat']], $input->getArguments(), '->parse() parses array arguments'); + $this->assertSame(['name' => ['foo', 'bar', 'baz', 'bat']], $input->getArguments(), '->parse() parses array arguments'); } public function testParseArrayOption() @@ -366,11 +371,11 @@ public function testParseArrayOption() $input = new ArgvInput(['cli.php', '--name=foo', '--name=bar', '--name=baz']); $input->bind(new InputDefinition([new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY)])); - $this->assertEquals(['name' => ['foo', 'bar', 'baz']], $input->getOptions(), '->parse() parses array options ("--option=value" syntax)'); + $this->assertSame(['name' => ['foo', 'bar', 'baz']], $input->getOptions(), '->parse() parses array options ("--option=value" syntax)'); $input = new ArgvInput(['cli.php', '--name', 'foo', '--name', 'bar', '--name', 'baz']); $input->bind(new InputDefinition([new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY)])); - $this->assertEquals(['name' => ['foo', 'bar', 'baz']], $input->getOptions(), '->parse() parses array options ("--option value" syntax)'); + $this->assertSame(['name' => ['foo', 'bar', 'baz']], $input->getOptions(), '->parse() parses array options ("--option value" syntax)'); $input = new ArgvInput(['cli.php', '--name=foo', '--name=bar', '--name=']); $input->bind(new InputDefinition([new InputOption('name', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY)])); @@ -388,12 +393,12 @@ public function testParseNegativeNumberAfterDoubleDash() { $input = new ArgvInput(['cli.php', '--', '-1']); $input->bind(new InputDefinition([new InputArgument('number')])); - $this->assertEquals(['number' => '-1'], $input->getArguments(), '->parse() parses arguments with leading dashes as arguments after having encountered a double-dash sequence'); + $this->assertSame(['number' => '-1'], $input->getArguments(), '->parse() parses arguments with leading dashes as arguments after having encountered a double-dash sequence'); $input = new ArgvInput(['cli.php', '-f', 'bar', '--', '-1']); $input->bind(new InputDefinition([new InputArgument('number'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)])); - $this->assertEquals(['foo' => 'bar'], $input->getOptions(), '->parse() parses arguments with leading dashes as options before having encountered a double-dash sequence'); - $this->assertEquals(['number' => '-1'], $input->getArguments(), '->parse() parses arguments with leading dashes as arguments after having encountered a double-dash sequence'); + $this->assertSame(['foo' => 'bar'], $input->getOptions(), '->parse() parses arguments with leading dashes as options before having encountered a double-dash sequence'); + $this->assertSame(['number' => '-1'], $input->getArguments(), '->parse() parses arguments with leading dashes as arguments after having encountered a double-dash sequence'); } public function testParseEmptyStringArgument() @@ -401,7 +406,7 @@ public function testParseEmptyStringArgument() $input = new ArgvInput(['cli.php', '-f', 'bar', '']); $input->bind(new InputDefinition([new InputArgument('empty'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)])); - $this->assertEquals(['empty' => ''], $input->getArguments(), '->parse() parses empty string arguments'); + $this->assertSame(['empty' => ''], $input->getArguments(), '->parse() parses empty string arguments'); } public function testGetFirstArgument() @@ -410,7 +415,7 @@ public function testGetFirstArgument() $this->assertNull($input->getFirstArgument(), '->getFirstArgument() returns null when there is no arguments'); $input = new ArgvInput(['cli.php', '-fbbar', 'foo']); - $this->assertEquals('foo', $input->getFirstArgument(), '->getFirstArgument() returns the first argument from the raw input'); + $this->assertSame('foo', $input->getFirstArgument(), '->getFirstArgument() returns the first argument from the raw input'); $input = new ArgvInput(['cli.php', '--foo', 'fooval', 'bar']); $input->bind(new InputDefinition([new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputArgument('arg')])); @@ -490,7 +495,7 @@ public function testNoWarningOnInvalidParameterOption() // No warning thrown $this->assertFalse($input->hasParameterOption(['-m', ''])); - $this->assertEquals('dev', $input->getParameterOption(['-e', ''])); + $this->assertSame('dev', $input->getParameterOption(['-e', ''])); // No warning thrown $this->assertFalse($input->getParameterOption(['-m', ''])); } @@ -498,10 +503,10 @@ public function testNoWarningOnInvalidParameterOption() public function testToString() { $input = new ArgvInput(['cli.php', '-f', 'foo']); - $this->assertEquals('-f foo', (string) $input); + $this->assertSame('-f foo', (string) $input); $input = new ArgvInput(['cli.php', '-f', '--bar=foo', 'a b c d', "A\nB'C"]); - $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d').' '.escapeshellarg("A\nB'C"), (string) $input); + $this->assertSame('-f --bar=foo '.escapeshellarg('a b c d').' '.escapeshellarg("A\nB'C"), (string) $input); } /** @@ -510,7 +515,7 @@ public function testToString() public function testGetParameterOptionEqualSign($argv, $key, $default, $onlyParams, $expected) { $input = new ArgvInput($argv); - $this->assertEquals($expected, $input->getParameterOption($key, $default, $onlyParams), '->getParameterOption() returns the expected value'); + $this->assertSame($expected, $input->getParameterOption($key, $default, $onlyParams), '->getParameterOption() returns the expected value'); } public static function provideGetParameterOptionValues() @@ -534,33 +539,33 @@ public function testParseSingleDashAsArgument() { $input = new ArgvInput(['cli.php', '-']); $input->bind(new InputDefinition([new InputArgument('file')])); - $this->assertEquals(['file' => '-'], $input->getArguments(), '->parse() parses single dash as an argument'); + $this->assertSame(['file' => '-'], $input->getArguments(), '->parse() parses single dash as an argument'); } public function testParseOptionWithValueOptionalGivenEmptyAndRequiredArgument() { $input = new ArgvInput(['cli.php', '--foo=', 'bar']); $input->bind(new InputDefinition([new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputArgument('name', InputArgument::REQUIRED)])); - $this->assertEquals(['foo' => null], $input->getOptions(), '->parse() parses optional options with empty value as null'); - $this->assertEquals(['name' => 'bar'], $input->getArguments(), '->parse() parses required arguments'); + $this->assertSame(['foo' => ''], $input->getOptions(), '->parse() parses optional options with empty value as null'); + $this->assertSame(['name' => 'bar'], $input->getArguments(), '->parse() parses required arguments'); $input = new ArgvInput(['cli.php', '--foo=0', 'bar']); $input->bind(new InputDefinition([new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputArgument('name', InputArgument::REQUIRED)])); - $this->assertEquals(['foo' => '0'], $input->getOptions(), '->parse() parses optional options with empty value as null'); - $this->assertEquals(['name' => 'bar'], $input->getArguments(), '->parse() parses required arguments'); + $this->assertSame(['foo' => '0'], $input->getOptions(), '->parse() parses optional options with empty value as null'); + $this->assertSame(['name' => 'bar'], $input->getArguments(), '->parse() parses required arguments'); } public function testParseOptionWithValueOptionalGivenEmptyAndOptionalArgument() { $input = new ArgvInput(['cli.php', '--foo=', 'bar']); $input->bind(new InputDefinition([new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputArgument('name', InputArgument::OPTIONAL)])); - $this->assertEquals(['foo' => null], $input->getOptions(), '->parse() parses optional options with empty value as null'); - $this->assertEquals(['name' => 'bar'], $input->getArguments(), '->parse() parses optional arguments'); + $this->assertSame(['foo' => ''], $input->getOptions(), '->parse() parses optional options with empty value as null'); + $this->assertSame(['name' => 'bar'], $input->getArguments(), '->parse() parses optional arguments'); $input = new ArgvInput(['cli.php', '--foo=0', 'bar']); $input->bind(new InputDefinition([new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputArgument('name', InputArgument::OPTIONAL)])); - $this->assertEquals(['foo' => '0'], $input->getOptions(), '->parse() parses optional options with empty value as null'); - $this->assertEquals(['name' => 'bar'], $input->getArguments(), '->parse() parses optional arguments'); + $this->assertSame(['foo' => '0'], $input->getOptions(), '->parse() parses optional options with empty value as null'); + $this->assertSame(['name' => 'bar'], $input->getArguments(), '->parse() parses optional arguments'); } public function testGetRawTokensFalse() diff --git a/Tests/Input/ArrayInputTest.php b/Tests/Input/ArrayInputTest.php index d6fe32bb3..74d2c089f 100644 --- a/Tests/Input/ArrayInputTest.php +++ b/Tests/Input/ArrayInputTest.php @@ -24,9 +24,9 @@ public function testGetFirstArgument() $input = new ArrayInput([]); $this->assertNull($input->getFirstArgument(), '->getFirstArgument() returns null if no argument were passed'); $input = new ArrayInput(['name' => 'Fabien']); - $this->assertEquals('Fabien', $input->getFirstArgument(), '->getFirstArgument() returns the first passed argument'); + $this->assertSame('Fabien', $input->getFirstArgument(), '->getFirstArgument() returns the first passed argument'); $input = new ArrayInput(['--foo' => 'bar', 'name' => 'Fabien']); - $this->assertEquals('Fabien', $input->getFirstArgument(), '->getFirstArgument() returns the first passed argument'); + $this->assertSame('Fabien', $input->getFirstArgument(), '->getFirstArgument() returns the first passed argument'); } public function testHasParameterOption() @@ -46,22 +46,22 @@ public function testHasParameterOption() public function testGetParameterOption() { $input = new ArrayInput(['name' => 'Fabien', '--foo' => 'bar']); - $this->assertEquals('bar', $input->getParameterOption('--foo'), '->getParameterOption() returns the option of specified name'); - $this->assertEquals('default', $input->getParameterOption('--bar', 'default'), '->getParameterOption() returns the default value if an option is not present in the passed parameters'); + $this->assertSame('bar', $input->getParameterOption('--foo'), '->getParameterOption() returns the option of specified name'); + $this->assertSame('default', $input->getParameterOption('--bar', 'default'), '->getParameterOption() returns the default value if an option is not present in the passed parameters'); $input = new ArrayInput(['Fabien', '--foo' => 'bar']); - $this->assertEquals('bar', $input->getParameterOption('--foo'), '->getParameterOption() returns the option of specified name'); + $this->assertSame('bar', $input->getParameterOption('--foo'), '->getParameterOption() returns the option of specified name'); $input = new ArrayInput(['--foo', '--', '--bar' => 'woop']); - $this->assertEquals('woop', $input->getParameterOption('--bar'), '->getParameterOption() returns the correct value if an option is present in the passed parameters'); - $this->assertEquals('default', $input->getParameterOption('--bar', 'default', true), '->getParameterOption() returns the default value if an option is present in the passed parameters after an end of options signal'); + $this->assertSame('woop', $input->getParameterOption('--bar'), '->getParameterOption() returns the correct value if an option is present in the passed parameters'); + $this->assertSame('default', $input->getParameterOption('--bar', 'default', true), '->getParameterOption() returns the default value if an option is present in the passed parameters after an end of options signal'); } public function testParseArguments() { $input = new ArrayInput(['name' => 'foo'], new InputDefinition([new InputArgument('name')])); - $this->assertEquals(['name' => 'foo'], $input->getArguments(), '->parse() parses required arguments'); + $this->assertSame(['name' => 'foo'], $input->getArguments(), '->parse() parses required arguments'); } /** @@ -71,7 +71,7 @@ public function testParseOptions($input, $options, $expectedOptions, $message) { $input = new ArrayInput($input, new InputDefinition($options)); - $this->assertEquals($expectedOptions, $input->getOptions(), $message); + $this->assertSame($expectedOptions, $input->getOptions(), $message); } public static function provideOptions(): array @@ -162,7 +162,7 @@ public static function provideInvalidInput(): array public function testToString() { $input = new ArrayInput(['-f' => null, '-b' => 'bar', '--foo' => 'b a z', '--lala' => null, 'test' => 'Foo', 'test2' => "A\nB'C"]); - $this->assertEquals('-f -b bar --foo='.escapeshellarg('b a z').' --lala Foo '.escapeshellarg("A\nB'C"), (string) $input); + $this->assertSame('-f -b bar --foo='.escapeshellarg('b a z').' --lala Foo '.escapeshellarg("A\nB'C"), (string) $input); $input = new ArrayInput(['-b' => ['bval_1', 'bval_2'], '--f' => ['fval_1', 'fval_2']]); $this->assertSame('-b bval_1 -b bval_2 --f=fval_1 --f=fval_2', (string) $input); diff --git a/Tests/Input/InputArgumentTest.php b/Tests/Input/InputArgumentTest.php index 05447426c..a9d612f97 100644 --- a/Tests/Input/InputArgumentTest.php +++ b/Tests/Input/InputArgumentTest.php @@ -23,7 +23,7 @@ class InputArgumentTest extends TestCase public function testConstructor() { $argument = new InputArgument('foo'); - $this->assertEquals('foo', $argument->getName(), '__construct() takes a name as its first argument'); + $this->assertSame('foo', $argument->getName(), '__construct() takes a name as its first argument'); } public function testModes() @@ -62,13 +62,13 @@ public function testIsArray() public function testGetDescription() { $argument = new InputArgument('foo', null, 'Some description'); - $this->assertEquals('Some description', $argument->getDescription(), '->getDescription() return the message description'); + $this->assertSame('Some description', $argument->getDescription(), '->getDescription() return the message description'); } public function testGetDefault() { $argument = new InputArgument('foo', InputArgument::OPTIONAL, '', 'default'); - $this->assertEquals('default', $argument->getDefault(), '->getDefault() return the default value'); + $this->assertSame('default', $argument->getDefault(), '->getDefault() return the default value'); } public function testSetDefault() @@ -77,11 +77,11 @@ public function testSetDefault() $argument->setDefault(null); $this->assertNull($argument->getDefault(), '->setDefault() can reset the default value by passing null'); $argument->setDefault('another'); - $this->assertEquals('another', $argument->getDefault(), '->setDefault() changes the default value'); + $this->assertSame('another', $argument->getDefault(), '->setDefault() changes the default value'); $argument = new InputArgument('foo', InputArgument::OPTIONAL | InputArgument::IS_ARRAY); $argument->setDefault([1, 2]); - $this->assertEquals([1, 2], $argument->getDefault(), '->setDefault() changes the default value'); + $this->assertSame([1, 2], $argument->getDefault(), '->setDefault() changes the default value'); } public function testSetDefaultWithRequiredArgument() diff --git a/Tests/Input/InputDefinitionTest.php b/Tests/Input/InputDefinitionTest.php index 470f3ca74..ab203e6e5 100644 --- a/Tests/Input/InputDefinitionTest.php +++ b/Tests/Input/InputDefinitionTest.php @@ -36,10 +36,10 @@ public function testConstructorArguments() $this->initializeArguments(); $definition = new InputDefinition(); - $this->assertEquals([], $definition->getArguments(), '__construct() creates a new InputDefinition object'); + $this->assertSame([], $definition->getArguments(), '__construct() creates a new InputDefinition object'); $definition = new InputDefinition([$this->foo, $this->bar]); - $this->assertEquals(['foo' => $this->foo, 'bar' => $this->bar], $definition->getArguments(), '__construct() takes an array of InputArgument objects as its first argument'); + $this->assertSame(['foo' => $this->foo, 'bar' => $this->bar], $definition->getArguments(), '__construct() takes an array of InputArgument objects as its first argument'); } public function testConstructorOptions() @@ -47,10 +47,10 @@ public function testConstructorOptions() $this->initializeOptions(); $definition = new InputDefinition(); - $this->assertEquals([], $definition->getOptions(), '__construct() creates a new InputDefinition object'); + $this->assertSame([], $definition->getOptions(), '__construct() creates a new InputDefinition object'); $definition = new InputDefinition([$this->foo, $this->bar]); - $this->assertEquals(['foo' => $this->foo, 'bar' => $this->bar], $definition->getOptions(), '__construct() takes an array of InputOption objects as its first argument'); + $this->assertSame(['foo' => $this->foo, 'bar' => $this->bar], $definition->getOptions(), '__construct() takes an array of InputOption objects as its first argument'); } public function testSetArguments() @@ -59,10 +59,10 @@ public function testSetArguments() $definition = new InputDefinition(); $definition->setArguments([$this->foo]); - $this->assertEquals(['foo' => $this->foo], $definition->getArguments(), '->setArguments() sets the array of InputArgument objects'); + $this->assertSame(['foo' => $this->foo], $definition->getArguments(), '->setArguments() sets the array of InputArgument objects'); $definition->setArguments([$this->bar]); - $this->assertEquals(['bar' => $this->bar], $definition->getArguments(), '->setArguments() clears all InputArgument objects'); + $this->assertSame(['bar' => $this->bar], $definition->getArguments(), '->setArguments() clears all InputArgument objects'); } public function testAddArguments() @@ -71,9 +71,9 @@ public function testAddArguments() $definition = new InputDefinition(); $definition->addArguments([$this->foo]); - $this->assertEquals(['foo' => $this->foo], $definition->getArguments(), '->addArguments() adds an array of InputArgument objects'); + $this->assertSame(['foo' => $this->foo], $definition->getArguments(), '->addArguments() adds an array of InputArgument objects'); $definition->addArguments([$this->bar]); - $this->assertEquals(['foo' => $this->foo, 'bar' => $this->bar], $definition->getArguments(), '->addArguments() does not clear existing InputArgument objects'); + $this->assertSame(['foo' => $this->foo, 'bar' => $this->bar], $definition->getArguments(), '->addArguments() does not clear existing InputArgument objects'); } public function testAddArgument() @@ -82,9 +82,9 @@ public function testAddArgument() $definition = new InputDefinition(); $definition->addArgument($this->foo); - $this->assertEquals(['foo' => $this->foo], $definition->getArguments(), '->addArgument() adds a InputArgument object'); + $this->assertSame(['foo' => $this->foo], $definition->getArguments(), '->addArgument() adds a InputArgument object'); $definition->addArgument($this->bar); - $this->assertEquals(['foo' => $this->foo, 'bar' => $this->bar], $definition->getArguments(), '->addArgument() adds a InputArgument object'); + $this->assertSame(['foo' => $this->foo, 'bar' => $this->bar], $definition->getArguments(), '->addArgument() adds a InputArgument object'); } public function testArgumentsMustHaveDifferentNames() @@ -113,7 +113,7 @@ public function testRequiredArgumentCannotFollowAnOptionalOne() { $this->initializeArguments(); $this->expectException(\LogicException::class); - $this->expectExceptionMessage(sprintf('Cannot add a required argument "%s" after an optional one "%s".', $this->foo2->getName(), $this->foo->getName())); + $this->expectExceptionMessage(\sprintf('Cannot add a required argument "%s" after an optional one "%s".', $this->foo2->getName(), $this->foo->getName())); $definition = new InputDefinition(); $definition->addArgument($this->foo); @@ -126,7 +126,7 @@ public function testGetArgument() $definition = new InputDefinition(); $definition->addArguments([$this->foo]); - $this->assertEquals($this->foo, $definition->getArgument('foo'), '->getArgument() returns a InputArgument by its name'); + $this->assertSame($this->foo, $definition->getArgument('foo'), '->getArgument() returns a InputArgument by its name'); } public function testGetInvalidArgument() @@ -157,9 +157,9 @@ public function testGetArgumentRequiredCount() $definition = new InputDefinition(); $definition->addArgument($this->foo2); - $this->assertEquals(1, $definition->getArgumentRequiredCount(), '->getArgumentRequiredCount() returns the number of required arguments'); + $this->assertSame(1, $definition->getArgumentRequiredCount(), '->getArgumentRequiredCount() returns the number of required arguments'); $definition->addArgument($this->foo); - $this->assertEquals(1, $definition->getArgumentRequiredCount(), '->getArgumentRequiredCount() returns the number of required arguments'); + $this->assertSame(1, $definition->getArgumentRequiredCount(), '->getArgumentRequiredCount() returns the number of required arguments'); } public function testGetArgumentCount() @@ -168,9 +168,9 @@ public function testGetArgumentCount() $definition = new InputDefinition(); $definition->addArgument($this->foo2); - $this->assertEquals(1, $definition->getArgumentCount(), '->getArgumentCount() returns the number of arguments'); + $this->assertSame(1, $definition->getArgumentCount(), '->getArgumentCount() returns the number of arguments'); $definition->addArgument($this->foo); - $this->assertEquals(2, $definition->getArgumentCount(), '->getArgumentCount() returns the number of arguments'); + $this->assertSame(2, $definition->getArgumentCount(), '->getArgumentCount() returns the number of arguments'); } public function testGetArgumentDefaults() @@ -179,14 +179,14 @@ public function testGetArgumentDefaults() new InputArgument('foo1', InputArgument::OPTIONAL), new InputArgument('foo2', InputArgument::OPTIONAL, '', 'default'), new InputArgument('foo3', InputArgument::OPTIONAL | InputArgument::IS_ARRAY), - // new InputArgument('foo4', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, '', [1, 2]), + // new InputArgument('foo4', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, '', [1, 2]), ]); - $this->assertEquals(['foo1' => null, 'foo2' => 'default', 'foo3' => []], $definition->getArgumentDefaults(), '->getArgumentDefaults() return the default values for each argument'); + $this->assertSame(['foo1' => null, 'foo2' => 'default', 'foo3' => []], $definition->getArgumentDefaults(), '->getArgumentDefaults() return the default values for each argument'); $definition = new InputDefinition([ new InputArgument('foo4', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, '', [1, 2]), ]); - $this->assertEquals(['foo4' => [1, 2]], $definition->getArgumentDefaults(), '->getArgumentDefaults() return the default values for each argument'); + $this->assertSame(['foo4' => [1, 2]], $definition->getArgumentDefaults(), '->getArgumentDefaults() return the default values for each argument'); } public function testSetOptions() @@ -194,9 +194,9 @@ public function testSetOptions() $this->initializeOptions(); $definition = new InputDefinition([$this->foo]); - $this->assertEquals(['foo' => $this->foo], $definition->getOptions(), '->setOptions() sets the array of InputOption objects'); + $this->assertSame(['foo' => $this->foo], $definition->getOptions(), '->setOptions() sets the array of InputOption objects'); $definition->setOptions([$this->bar]); - $this->assertEquals(['bar' => $this->bar], $definition->getOptions(), '->setOptions() clears all InputOption objects'); + $this->assertSame(['bar' => $this->bar], $definition->getOptions(), '->setOptions() clears all InputOption objects'); } public function testSetOptionsClearsOptions() @@ -215,9 +215,9 @@ public function testAddOptions() $this->initializeOptions(); $definition = new InputDefinition([$this->foo]); - $this->assertEquals(['foo' => $this->foo], $definition->getOptions(), '->addOptions() adds an array of InputOption objects'); + $this->assertSame(['foo' => $this->foo], $definition->getOptions(), '->addOptions() adds an array of InputOption objects'); $definition->addOptions([$this->bar]); - $this->assertEquals(['foo' => $this->foo, 'bar' => $this->bar], $definition->getOptions(), '->addOptions() does not clear existing InputOption objects'); + $this->assertSame(['foo' => $this->foo, 'bar' => $this->bar], $definition->getOptions(), '->addOptions() does not clear existing InputOption objects'); } public function testAddOption() @@ -226,9 +226,9 @@ public function testAddOption() $definition = new InputDefinition(); $definition->addOption($this->foo); - $this->assertEquals(['foo' => $this->foo], $definition->getOptions(), '->addOption() adds a InputOption object'); + $this->assertSame(['foo' => $this->foo], $definition->getOptions(), '->addOption() adds a InputOption object'); $definition->addOption($this->bar); - $this->assertEquals(['foo' => $this->foo, 'bar' => $this->bar], $definition->getOptions(), '->addOption() adds a InputOption object'); + $this->assertSame(['foo' => $this->foo, 'bar' => $this->bar], $definition->getOptions(), '->addOption() adds a InputOption object'); } public function testAddDuplicateOption() @@ -278,7 +278,7 @@ public function testGetOption() $this->initializeOptions(); $definition = new InputDefinition([$this->foo]); - $this->assertEquals($this->foo, $definition->getOption('foo'), '->getOption() returns a InputOption by its name'); + $this->assertSame($this->foo, $definition->getOption('foo'), '->getOption() returns a InputOption by its name'); } public function testGetInvalidOption() @@ -314,7 +314,7 @@ public function testGetOptionForShortcut() $this->initializeOptions(); $definition = new InputDefinition([$this->foo]); - $this->assertEquals($this->foo, $definition->getOptionForShortcut('f'), '->getOptionForShortcut() returns a InputOption by its shortcut'); + $this->assertSame($this->foo, $definition->getOptionForShortcut('f'), '->getOptionForShortcut() returns a InputOption by its shortcut'); } public function testGetOptionForMultiShortcut() @@ -322,8 +322,8 @@ public function testGetOptionForMultiShortcut() $this->initializeOptions(); $definition = new InputDefinition([$this->multi]); - $this->assertEquals($this->multi, $definition->getOptionForShortcut('m'), '->getOptionForShortcut() returns a InputOption by its shortcut'); - $this->assertEquals($this->multi, $definition->getOptionForShortcut('mmm'), '->getOptionForShortcut() returns a InputOption by its shortcut'); + $this->assertSame($this->multi, $definition->getOptionForShortcut('m'), '->getOptionForShortcut() returns a InputOption by its shortcut'); + $this->assertSame($this->multi, $definition->getOptionForShortcut('mmm'), '->getOptionForShortcut() returns a InputOption by its shortcut'); } public function testGetOptionForInvalidShortcut() @@ -364,7 +364,7 @@ public function testGetOptionDefaults() */ public function testGetSynopsis(InputDefinition $definition, $expectedSynopsis, $message = null) { - $this->assertEquals($expectedSynopsis, $definition->getSynopsis(), $message ? '->getSynopsis() '.$message : ''); + $this->assertSame($expectedSynopsis, $definition->getSynopsis(), $message ? '->getSynopsis() '.$message : ''); } public static function getGetSynopsisData() @@ -388,7 +388,7 @@ public static function getGetSynopsisData() public function testGetShortSynopsis() { $definition = new InputDefinition([new InputOption('foo'), new InputOption('bar'), new InputArgument('cat')]); - $this->assertEquals('[options] [--] []', $definition->getSynopsis(true), '->getSynopsis(true) groups options in [options]'); + $this->assertSame('[options] [--] []', $definition->getSynopsis(true), '->getSynopsis(true) groups options in [options]'); } protected function initializeArguments() diff --git a/Tests/Input/InputOptionTest.php b/Tests/Input/InputOptionTest.php index 7e3fb16da..47ab503f7 100644 --- a/Tests/Input/InputOptionTest.php +++ b/Tests/Input/InputOptionTest.php @@ -23,9 +23,9 @@ class InputOptionTest extends TestCase public function testConstructor() { $option = new InputOption('foo'); - $this->assertEquals('foo', $option->getName(), '__construct() takes a name as its first argument'); + $this->assertSame('foo', $option->getName(), '__construct() takes a name as its first argument'); $option = new InputOption('--foo'); - $this->assertEquals('foo', $option->getName(), '__construct() removes the leading -- of the option name'); + $this->assertSame('foo', $option->getName(), '__construct() removes the leading -- of the option name'); } public function testArrayModeWithoutValue() @@ -52,11 +52,11 @@ public function testBooleanWithOptional() public function testShortcut() { $option = new InputOption('foo', 'f'); - $this->assertEquals('f', $option->getShortcut(), '__construct() can take a shortcut as its second argument'); + $this->assertSame('f', $option->getShortcut(), '__construct() can take a shortcut as its second argument'); $option = new InputOption('foo', '-f|-ff|fff'); - $this->assertEquals('f|ff|fff', $option->getShortcut(), '__construct() removes the leading - of the shortcuts'); + $this->assertSame('f|ff|fff', $option->getShortcut(), '__construct() removes the leading - of the shortcuts'); $option = new InputOption('foo', ['f', 'ff', '-fff']); - $this->assertEquals('f|ff|fff', $option->getShortcut(), '__construct() removes the leading - of the shortcuts'); + $this->assertSame('f|ff|fff', $option->getShortcut(), '__construct() removes the leading - of the shortcuts'); $option = new InputOption('foo'); $this->assertNull($option->getShortcut(), '__construct() makes the shortcut null by default'); $option = new InputOption('foo', ''); @@ -64,15 +64,15 @@ public function testShortcut() $option = new InputOption('foo', []); $this->assertNull($option->getShortcut(), '__construct() makes the shortcut null when given an empty array'); $option = new InputOption('foo', ['f', '', 'fff']); - $this->assertEquals('f|fff', $option->getShortcut(), '__construct() removes empty shortcuts'); + $this->assertSame('f|fff', $option->getShortcut(), '__construct() removes empty shortcuts'); $option = new InputOption('foo', 'f||fff'); - $this->assertEquals('f|fff', $option->getShortcut(), '__construct() removes empty shortcuts'); + $this->assertSame('f|fff', $option->getShortcut(), '__construct() removes empty shortcuts'); $option = new InputOption('foo', '0'); - $this->assertEquals('0', $option->getShortcut(), '-0 is an acceptable shortcut value'); + $this->assertSame('0', $option->getShortcut(), '-0 is an acceptable shortcut value'); $option = new InputOption('foo', ['0', 'z']); - $this->assertEquals('0|z', $option->getShortcut(), '-0 is an acceptable shortcut value when embedded in an array'); + $this->assertSame('0|z', $option->getShortcut(), '-0 is an acceptable shortcut value when embedded in an array'); $option = new InputOption('foo', '0|z'); - $this->assertEquals('0|z', $option->getShortcut(), '-0 is an acceptable shortcut value when embedded in a string-list'); + $this->assertSame('0|z', $option->getShortcut(), '-0 is an acceptable shortcut value when embedded in a string-list'); $option = new InputOption('foo', false); $this->assertNull($option->getShortcut(), '__construct() makes the shortcut null when given a false as value'); } @@ -142,22 +142,22 @@ public function testIsArray() public function testGetDescription() { $option = new InputOption('foo', 'f', null, 'Some description'); - $this->assertEquals('Some description', $option->getDescription(), '->getDescription() returns the description message'); + $this->assertSame('Some description', $option->getDescription(), '->getDescription() returns the description message'); } public function testGetDefault() { $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL, '', 'default'); - $this->assertEquals('default', $option->getDefault(), '->getDefault() returns the default value'); + $this->assertSame('default', $option->getDefault(), '->getDefault() returns the default value'); $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED, '', 'default'); - $this->assertEquals('default', $option->getDefault(), '->getDefault() returns the default value'); + $this->assertSame('default', $option->getDefault(), '->getDefault() returns the default value'); $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED); $this->assertNull($option->getDefault(), '->getDefault() returns null if no default value is configured'); $option = new InputOption('foo', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY); - $this->assertEquals([], $option->getDefault(), '->getDefault() returns an empty array if option is an array'); + $this->assertSame([], $option->getDefault(), '->getDefault() returns an empty array if option is an array'); $option = new InputOption('foo', null, InputOption::VALUE_NONE); $this->assertFalse($option->getDefault(), '->getDefault() returns false if the option does not take a value'); @@ -169,11 +169,11 @@ public function testSetDefault() $option->setDefault(null); $this->assertNull($option->getDefault(), '->setDefault() can reset the default value by passing null'); $option->setDefault('another'); - $this->assertEquals('another', $option->getDefault(), '->setDefault() changes the default value'); + $this->assertSame('another', $option->getDefault(), '->setDefault() changes the default value'); $option = new InputOption('foo', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY); $option->setDefault([1, 2]); - $this->assertEquals([1, 2], $option->getDefault(), '->setDefault() changes the default value'); + $this->assertSame([1, 2], $option->getDefault(), '->setDefault() changes the default value'); } public function testDefaultValueWithValueNoneMode() diff --git a/Tests/Input/InputTest.php b/Tests/Input/InputTest.php index 34fb4833b..19a840da6 100644 --- a/Tests/Input/InputTest.php +++ b/Tests/Input/InputTest.php @@ -22,29 +22,29 @@ class InputTest extends TestCase public function testConstructor() { $input = new ArrayInput(['name' => 'foo'], new InputDefinition([new InputArgument('name')])); - $this->assertEquals('foo', $input->getArgument('name'), '->__construct() takes a InputDefinition as an argument'); + $this->assertSame('foo', $input->getArgument('name'), '->__construct() takes a InputDefinition as an argument'); } public function testOptions() { $input = new ArrayInput(['--name' => 'foo'], new InputDefinition([new InputOption('name')])); - $this->assertEquals('foo', $input->getOption('name'), '->getOption() returns the value for the given option'); + $this->assertSame('foo', $input->getOption('name'), '->getOption() returns the value for the given option'); $input->setOption('name', 'bar'); - $this->assertEquals('bar', $input->getOption('name'), '->setOption() sets the value for a given option'); - $this->assertEquals(['name' => 'bar'], $input->getOptions(), '->getOptions() returns all option values'); + $this->assertSame('bar', $input->getOption('name'), '->setOption() sets the value for a given option'); + $this->assertSame(['name' => 'bar'], $input->getOptions(), '->getOptions() returns all option values'); $input = new ArrayInput(['--name' => 'foo'], new InputDefinition([new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')])); - $this->assertEquals('default', $input->getOption('bar'), '->getOption() returns the default value for optional options'); - $this->assertEquals(['name' => 'foo', 'bar' => 'default'], $input->getOptions(), '->getOptions() returns all option values, even optional ones'); + $this->assertSame('default', $input->getOption('bar'), '->getOption() returns the default value for optional options'); + $this->assertSame(['name' => 'foo', 'bar' => 'default'], $input->getOptions(), '->getOptions() returns all option values, even optional ones'); $input = new ArrayInput(['--name' => 'foo', '--bar' => ''], new InputDefinition([new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')])); - $this->assertEquals('', $input->getOption('bar'), '->getOption() returns null for options explicitly passed without value (or an empty value)'); - $this->assertEquals(['name' => 'foo', 'bar' => ''], $input->getOptions(), '->getOptions() returns all option values.'); + $this->assertSame('', $input->getOption('bar'), '->getOption() returns null for options explicitly passed without value (or an empty value)'); + $this->assertSame(['name' => 'foo', 'bar' => ''], $input->getOptions(), '->getOptions() returns all option values.'); $input = new ArrayInput(['--name' => 'foo', '--bar' => null], new InputDefinition([new InputOption('name'), new InputOption('bar', '', InputOption::VALUE_OPTIONAL, '', 'default')])); $this->assertNull($input->getOption('bar'), '->getOption() returns null for options explicitly passed without value (or an empty value)'); - $this->assertEquals(['name' => 'foo', 'bar' => null], $input->getOptions(), '->getOptions() returns all option values'); + $this->assertSame(['name' => 'foo', 'bar' => null], $input->getOptions(), '->getOptions() returns all option values'); $input = new ArrayInput(['--name' => null], new InputDefinition([new InputOption('name', null, InputOption::VALUE_NEGATABLE)])); $this->assertTrue($input->hasOption('name')); @@ -84,15 +84,15 @@ public function testGetInvalidOption() public function testArguments() { $input = new ArrayInput(['name' => 'foo'], new InputDefinition([new InputArgument('name')])); - $this->assertEquals('foo', $input->getArgument('name'), '->getArgument() returns the value for the given argument'); + $this->assertSame('foo', $input->getArgument('name'), '->getArgument() returns the value for the given argument'); $input->setArgument('name', 'bar'); - $this->assertEquals('bar', $input->getArgument('name'), '->setArgument() sets the value for a given argument'); - $this->assertEquals(['name' => 'bar'], $input->getArguments(), '->getArguments() returns all argument values'); + $this->assertSame('bar', $input->getArgument('name'), '->setArgument() sets the value for a given argument'); + $this->assertSame(['name' => 'bar'], $input->getArguments(), '->getArguments() returns all argument values'); $input = new ArrayInput(['name' => 'foo'], new InputDefinition([new InputArgument('name'), new InputArgument('bar', InputArgument::OPTIONAL, '', 'default')])); - $this->assertEquals('default', $input->getArgument('bar'), '->getArgument() returns the default value for optional arguments'); - $this->assertEquals(['name' => 'foo', 'bar' => 'default'], $input->getArguments(), '->getArguments() returns all argument values, even optional ones'); + $this->assertSame('default', $input->getArgument('bar'), '->getArgument() returns the default value for optional arguments'); + $this->assertSame(['name' => 'foo', 'bar' => 'default'], $input->getArguments(), '->getArguments() returns all argument values, even optional ones'); } public function testSetInvalidArgument() diff --git a/Tests/Input/StringInputTest.php b/Tests/Input/StringInputTest.php index 338c1428a..92425daab 100644 --- a/Tests/Input/StringInputTest.php +++ b/Tests/Input/StringInputTest.php @@ -27,7 +27,7 @@ public function testTokenize($input, $tokens, $message) $input = new StringInput($input); $r = new \ReflectionClass(ArgvInput::class); $p = $r->getProperty('tokens'); - $this->assertEquals($tokens, $p->getValue($input), $message); + $this->assertSame($tokens, $p->getValue($input), $message); } public function testInputOptionWithGivenString() @@ -39,7 +39,7 @@ public function testInputOptionWithGivenString() // call to bind $input = new StringInput('--foo=bar'); $input->bind($definition); - $this->assertEquals('bar', $input->getOption('foo')); + $this->assertSame('bar', $input->getOption('foo')); } public static function getTokenizeData() @@ -77,12 +77,12 @@ public static function getTokenizeData() public function testToString() { $input = new StringInput('-f foo'); - $this->assertEquals('-f foo', (string) $input); + $this->assertSame('-f foo', (string) $input); $input = new StringInput('-f --bar=foo "a b c d"'); - $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d'), (string) $input); + $this->assertSame('-f --bar=foo '.escapeshellarg('a b c d'), (string) $input); $input = new StringInput('-f --bar=foo \'a b c d\' '."'A\nB\\'C'"); - $this->assertEquals('-f --bar=foo '.escapeshellarg('a b c d').' '.escapeshellarg("A\nB'C"), (string) $input); + $this->assertSame('-f --bar=foo '.escapeshellarg('a b c d').' '.escapeshellarg("A\nB'C"), (string) $input); } } diff --git a/Tests/Logger/ConsoleLoggerTest.php b/Tests/Logger/ConsoleLoggerTest.php index 43d779631..0464c8c5f 100644 --- a/Tests/Logger/ConsoleLoggerTest.php +++ b/Tests/Logger/ConsoleLoggerTest.php @@ -151,11 +151,7 @@ public function testContextReplacement() public function testObjectCastToString() { - if (method_exists($this, 'createPartialMock')) { - $dummy = $this->createPartialMock(DummyTest::class, ['__toString']); - } else { - $dummy = $this->createPartialMock(DummyTest::class, ['__toString']); - } + $dummy = $this->createPartialMock(DummyTest::class, ['__toString']); $dummy->method('__toString')->willReturn('DUMMY'); $this->getLogger()->warning($dummy); diff --git a/Tests/Messenger/RunCommandMessageHandlerTest.php b/Tests/Messenger/RunCommandMessageHandlerTest.php index adc31e0ec..58b33d565 100644 --- a/Tests/Messenger/RunCommandMessageHandlerTest.php +++ b/Tests/Messenger/RunCommandMessageHandlerTest.php @@ -86,7 +86,7 @@ private function createApplicationWithCommand(): Application $application = new Application(); $application->setAutoExit(false); $application->addCommands([ - new class() extends Command { + new class extends Command { public function configure(): void { $this diff --git a/Tests/Output/ConsoleSectionOutputTest.php b/Tests/Output/ConsoleSectionOutputTest.php index e50f8d54a..6d2fa6c3c 100644 --- a/Tests/Output/ConsoleSectionOutputTest.php +++ b/Tests/Output/ConsoleSectionOutputTest.php @@ -44,7 +44,7 @@ public function testClearAll() $output->clear(); rewind($output->getStream()); - $this->assertEquals('Foo'.\PHP_EOL.'Bar'.\PHP_EOL.sprintf("\x1b[%dA", 2)."\x1b[0J", stream_get_contents($output->getStream())); + $this->assertEquals('Foo'.\PHP_EOL.'Bar'.\PHP_EOL.\sprintf("\x1b[%dA", 2)."\x1b[0J", stream_get_contents($output->getStream())); } public function testClearNumberOfLines() @@ -56,7 +56,7 @@ public function testClearNumberOfLines() $output->clear(2); rewind($output->getStream()); - $this->assertEquals("Foo\nBar\nBaz\nFooBar".\PHP_EOL.sprintf("\x1b[%dA", 2)."\x1b[0J", stream_get_contents($output->getStream())); + $this->assertEquals("Foo\nBar\nBaz\nFooBar".\PHP_EOL.\sprintf("\x1b[%dA", 2)."\x1b[0J", stream_get_contents($output->getStream())); } public function testClearNumberOfLinesWithMultipleSections() @@ -197,7 +197,7 @@ public function testOverwriteMultipleLines() $output->overwrite('Bar'); rewind($output->getStream()); - $this->assertEquals('Foo'.\PHP_EOL.'Bar'.\PHP_EOL.'Baz'.\PHP_EOL.sprintf("\x1b[%dA", 3)."\x1b[0J".'Bar'.\PHP_EOL, stream_get_contents($output->getStream())); + $this->assertEquals('Foo'.\PHP_EOL.'Bar'.\PHP_EOL.'Baz'.\PHP_EOL.\sprintf("\x1b[%dA", 3)."\x1b[0J".'Bar'.\PHP_EOL, stream_get_contents($output->getStream())); } public function testAddingMultipleSections() diff --git a/Tests/Output/NullOutputTest.php b/Tests/Output/NullOutputTest.php index 1e0967ea5..4da46cf8f 100644 --- a/Tests/Output/NullOutputTest.php +++ b/Tests/Output/NullOutputTest.php @@ -35,10 +35,10 @@ public function testConstructor() public function testVerbosity() { $output = new NullOutput(); - $this->assertSame(OutputInterface::VERBOSITY_QUIET, $output->getVerbosity(), '->getVerbosity() returns VERBOSITY_QUIET for NullOutput by default'); + $this->assertSame(OutputInterface::VERBOSITY_SILENT, $output->getVerbosity(), '->getVerbosity() returns VERBOSITY_SILENT for NullOutput by default'); $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); - $this->assertSame(OutputInterface::VERBOSITY_QUIET, $output->getVerbosity(), '->getVerbosity() always returns VERBOSITY_QUIET for NullOutput'); + $this->assertSame(OutputInterface::VERBOSITY_SILENT, $output->getVerbosity(), '->getVerbosity() always returns VERBOSITY_QUIET for NullOutput'); } public function testGetFormatter() @@ -60,7 +60,7 @@ public function testSetVerbosity() { $output = new NullOutput(); $output->setVerbosity(Output::VERBOSITY_NORMAL); - $this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity()); + $this->assertEquals(Output::VERBOSITY_SILENT, $output->getVerbosity()); } public function testSetDecorated() @@ -70,10 +70,16 @@ public function testSetDecorated() $this->assertFalse($output->isDecorated()); } + public function testIsSilent() + { + $output = new NullOutput(); + $this->assertTrue($output->isSilent()); + } + public function testIsQuiet() { $output = new NullOutput(); - $this->assertTrue($output->isQuiet()); + $this->assertFalse($output->isQuiet()); } public function testIsVerbose() diff --git a/Tests/Output/OutputTest.php b/Tests/Output/OutputTest.php index 8a1e2840e..64e491048 100644 --- a/Tests/Output/OutputTest.php +++ b/Tests/Output/OutputTest.php @@ -164,6 +164,7 @@ public function testWriteWithVerbosityOption($verbosity, $expected, $msg) public static function verbosityProvider() { return [ + [Output::VERBOSITY_SILENT, '', '->write() in SILENT mode never outputs'], [Output::VERBOSITY_QUIET, '2', '->write() in QUIET mode only outputs when an explicit QUIET verbosity is passed'], [Output::VERBOSITY_NORMAL, '123', '->write() in NORMAL mode outputs anything below an explicit VERBOSE verbosity'], [Output::VERBOSITY_VERBOSE, '1234', '->write() in VERBOSE mode outputs anything below an explicit VERY_VERBOSE verbosity'], diff --git a/Tests/Question/ConfirmationQuestionTest.php b/Tests/Question/ConfirmationQuestionTest.php index 44f4c870b..bd11047b3 100644 --- a/Tests/Question/ConfirmationQuestionTest.php +++ b/Tests/Question/ConfirmationQuestionTest.php @@ -26,7 +26,7 @@ public function testDefaultRegexUsecases($default, $answers, $expected, $message foreach ($answers as $answer) { $normalizer = $sut->getNormalizer(); $actual = $normalizer($answer); - $this->assertEquals($expected, $actual, sprintf($message, $answer)); + $this->assertEquals($expected, $actual, \sprintf($message, $answer)); } } diff --git a/Tests/Question/QuestionTest.php b/Tests/Question/QuestionTest.php index 0bc6f75db..15d8212b9 100644 --- a/Tests/Question/QuestionTest.php +++ b/Tests/Question/QuestionTest.php @@ -20,7 +20,6 @@ class QuestionTest extends TestCase protected function setUp(): void { - parent::setUp(); $this->question = new Question('Test question'); } diff --git a/Tests/SignalRegistry/SignalRegistryTest.php b/Tests/SignalRegistry/SignalRegistryTest.php index f997f7c1d..92d500f9e 100644 --- a/Tests/SignalRegistry/SignalRegistryTest.php +++ b/Tests/SignalRegistry/SignalRegistryTest.php @@ -27,6 +27,7 @@ protected function tearDown(): void pcntl_signal(\SIGTERM, \SIG_DFL); pcntl_signal(\SIGUSR1, \SIG_DFL); pcntl_signal(\SIGUSR2, \SIG_DFL); + pcntl_signal(\SIGALRM, \SIG_DFL); } public function testOneCallbackForASignalSignalIsHandled() diff --git a/Tests/Tester/Constraint/CommandIsSuccessfulTest.php b/Tests/Tester/Constraint/CommandIsSuccessfulTest.php index 7a2b4c719..61ab5d0f8 100644 --- a/Tests/Tester/Constraint/CommandIsSuccessfulTest.php +++ b/Tests/Tester/Constraint/CommandIsSuccessfulTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestFailure; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Tester\Constraint\CommandIsSuccessful; @@ -35,16 +34,9 @@ public function testUnsuccessfulCommand(string $expectedException, int $exitCode { $constraint = new CommandIsSuccessful(); - try { - $constraint->evaluate($exitCode); - } catch (ExpectationFailedException $e) { - $this->assertStringContainsString('Failed asserting that the command is successful.', TestFailure::exceptionToString($e)); - $this->assertStringContainsString($expectedException, TestFailure::exceptionToString($e)); - - return; - } - - $this->fail(); + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessageMatches('/Failed asserting that the command is successful\..*'.$expectedException.'/s'); + $constraint->evaluate($exitCode); } public static function providesUnsuccessful(): iterable diff --git a/Tests/phpt/alarm/command_exit.phpt b/Tests/phpt/alarm/command_exit.phpt new file mode 100644 index 000000000..d3015ad9d --- /dev/null +++ b/Tests/phpt/alarm/command_exit.phpt @@ -0,0 +1,64 @@ +--TEST-- +Test command that exits +--SKIPIF-- + +--FILE-- +getApplication()->setAlarmInterval(1); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + sleep(5); + + $output->writeln('should not be displayed'); + + return 0; + } + + public function getSubscribedSignals(): array + { + return [\SIGALRM]; + } + + public function handleSignal(int $signal, int|false $previousExitCode = 0): int|false + { + if (\SIGALRM === $signal) { + echo "Received alarm!"; + + return 0; + } + + return false; + } +} + +$app = new Application(); +$app->setDispatcher(new \Symfony\Component\EventDispatcher\EventDispatcher()); +$app->add(new MyCommand('foo')); + +$app + ->setDefaultCommand('foo', true) + ->run() +; +--EXPECT-- +Received alarm! diff --git a/Tests/phpt/signal/command_exit.phpt b/Tests/phpt/signal/command_exit.phpt index fde3793a8..379476189 100644 --- a/Tests/phpt/signal/command_exit.phpt +++ b/Tests/phpt/signal/command_exit.phpt @@ -1,7 +1,7 @@ --TEST-- -Test command that exist +Test command that exits --SKIPIF-- - + --FILE--