composer update, composer corelibs update, admin pages update

This commit is contained in:
Clemens Schwaighofer
2023-05-31 16:17:14 +09:00
parent 513b115d57
commit 3d6b461b20
211 changed files with 10013 additions and 1461 deletions

View File

@@ -105,11 +105,14 @@ class Application implements ResetInterface
/**
* @final
*/
public function setDispatcher(EventDispatcherInterface $dispatcher)
public function setDispatcher(EventDispatcherInterface $dispatcher): void
{
$this->dispatcher = $dispatcher;
}
/**
* @return void
*/
public function setCommandLoader(CommandLoaderInterface $commandLoader)
{
$this->commandLoader = $commandLoader;
@@ -118,12 +121,15 @@ class Application implements ResetInterface
public function getSignalRegistry(): SignalRegistry
{
if (!$this->signalRegistry) {
throw new RuntimeException('Signals are not supported. Make sure that the `pcntl` extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.');
throw new RuntimeException('Signals are not supported. Make sure that the "pcntl" extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.');
}
return $this->signalRegistry;
}
/**
* @return void
*/
public function setSignalsToDispatchEvent(int ...$signalsToDispatchEvent)
{
$this->signalsToDispatchEvent = $signalsToDispatchEvent;
@@ -317,10 +323,16 @@ class Application implements ResetInterface
return $exitCode;
}
/**
* @return void
*/
public function reset()
{
}
/**
* @return void
*/
public function setHelperSet(HelperSet $helperSet)
{
$this->helperSet = $helperSet;
@@ -334,6 +346,9 @@ class Application implements ResetInterface
return $this->helperSet ??= $this->getDefaultHelperSet();
}
/**
* @return void
*/
public function setDefinition(InputDefinition $definition)
{
$this->definition = $definition;
@@ -404,6 +419,8 @@ class Application implements ResetInterface
/**
* Sets whether to catch exceptions or not during commands execution.
*
* @return void
*/
public function setCatchExceptions(bool $boolean)
{
@@ -420,6 +437,8 @@ class Application implements ResetInterface
/**
* Sets whether to automatically exit after a command execution or not.
*
* @return void
*/
public function setAutoExit(bool $boolean)
{
@@ -436,7 +455,9 @@ class Application implements ResetInterface
/**
* Sets the application name.
**/
*
* @return void
*/
public function setName(string $name)
{
$this->name = $name;
@@ -452,6 +473,8 @@ class Application implements ResetInterface
/**
* Sets the application version.
*
* @return void
*/
public function setVersion(string $version)
{
@@ -490,6 +513,8 @@ class Application implements ResetInterface
* If a Command is not enabled it will not be added.
*
* @param Command[] $commands An array of commands
*
* @return void
*/
public function addCommands(array $commands)
{
@@ -687,9 +712,7 @@ class Application implements ResetInterface
if ($alternatives = $this->findAlternatives($name, $allCommands)) {
// remove hidden commands
$alternatives = array_filter($alternatives, function ($name) {
return !$this->get($name)->isHidden();
});
$alternatives = array_filter($alternatives, fn ($name) => !$this->get($name)->isHidden());
if (1 == \count($alternatives)) {
$message .= "\n\nDid you mean this?\n ";
@@ -840,9 +863,7 @@ class Application implements ResetInterface
}
if (str_contains($message, "@anonymous\0")) {
$message = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) {
return class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0];
}, $message);
$message = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', fn ($m) => class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0], $message);
}
$width = $this->terminal->getWidth() ? $this->terminal->getWidth() - 1 : \PHP_INT_MAX;
@@ -903,6 +924,8 @@ class Application implements ResetInterface
/**
* Configures the input and output instances based on the user arguments and options.
*
* @return void
*/
protected function configureIO(InputInterface $input, OutputInterface $output)
{
@@ -977,44 +1000,62 @@ class Application implements ResetInterface
}
}
if ($this->signalsToDispatchEvent) {
$commandSignals = $command instanceof SignalableCommandInterface ? $command->getSubscribedSignals() : [];
$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.');
}
if ($commandSignals || null !== $this->dispatcher) {
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.');
}
if (Terminal::hasSttyAvailable()) {
$sttyMode = shell_exec('stty -g');
if (Terminal::hasSttyAvailable()) {
$sttyMode = shell_exec('stty -g');
foreach ([\SIGINT, \SIGTERM] as $signal) {
$this->signalRegistry->register($signal, static function () use ($sttyMode) {
shell_exec('stty '.$sttyMode);
});
}
foreach ([\SIGINT, \SIGTERM] as $signal) {
$this->signalRegistry->register($signal, static fn () => shell_exec('stty '.$sttyMode));
}
}
if (null !== $this->dispatcher) {
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, $hasNext) use ($event) {
$this->signalRegistry->register($signal, function ($signal) use ($event, $command, $commandSignals) {
$this->dispatcher->dispatch($event, ConsoleEvents::SIGNAL);
$exitCode = $event->getExitCode();
// No more handlers, we try to simulate PHP default behavior
if (!$hasNext) {
if (!\in_array($signal, [\SIGUSR1, \SIGUSR2], true)) {
exit(0);
// If the command is signalable, we call the handleSignal() method
if (\in_array($signal, $commandSignals, true)) {
$exitCode = $command->handleSignal($signal, $exitCode);
// BC layer for Symfony <= 5
if (null === $exitCode) {
trigger_deprecation('symfony/console', '6.3', 'Not returning an exit code from "%s::handleSignal()" is deprecated, return "false" to keep the command running or "0" to exit successfully.', get_debug_type($command));
$exitCode = 0;
}
}
if (false !== $exitCode) {
exit($exitCode);
}
});
}
// then we register command signals, but not if already handled after the dispatcher
$commandSignals = array_diff($commandSignals, $this->signalsToDispatchEvent);
}
foreach ($commandSignals as $signal) {
$this->signalRegistry->register($signal, [$command, 'handleSignal']);
$this->signalRegistry->register($signal, function (int $signal) use ($command): void {
$exitCode = $command->handleSignal($signal);
// BC layer for Symfony <= 5
if (null === $exitCode) {
trigger_deprecation('symfony/console', '6.3', 'Not returning an exit code from "%s::handleSignal()" is deprecated, return "false" to keep the command running or "0" to exit successfully.', get_debug_type($command));
$exitCode = 0;
}
if (false !== $exitCode) {
exit($exitCode);
}
});
}
}
@@ -1170,7 +1211,7 @@ class Application implements ResetInterface
}
}
$alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; });
$alternatives = array_filter($alternatives, fn ($lev) => $lev < 2 * $threshold);
ksort($alternatives, \SORT_NATURAL | \SORT_FLAG_CASE);
return array_keys($alternatives);
@@ -1261,7 +1302,7 @@ class Application implements ResetInterface
return $namespaces;
}
private function init()
private function init(): void
{
if ($this->initialized) {
return;

View File

@@ -1,6 +1,13 @@
CHANGELOG
=========
6.3
---
* Add support for choosing exit code while handling signal, or to not exit at all
* Add `ProgressBar::setPlaceholderFormatter` to set a placeholder attached to a instance, instead of being global.
* Add `ReStructuredTextDescriptor`
6.2
---

View File

@@ -141,12 +141,17 @@ class Command
* Ignores validation errors.
*
* This is mainly useful for the help command.
*
* @return void
*/
public function ignoreValidationErrors()
{
$this->ignoreValidationErrors = true;
}
/**
* @return void
*/
public function setApplication(Application $application = null)
{
if (1 > \func_num_args()) {
@@ -162,6 +167,9 @@ class Command
$this->fullDefinition = null;
}
/**
* @return void
*/
public function setHelperSet(HelperSet $helperSet)
{
$this->helperSet = $helperSet;
@@ -198,6 +206,8 @@ class Command
/**
* Configures the current command.
*
* @return void
*/
protected function configure()
{
@@ -228,6 +238,8 @@ class Command
* This method is executed before the InputDefinition is validated.
* This means that this is the only place where the command can
* interactively ask for values of missing required arguments.
*
* @return void
*/
protected function interact(InputInterface $input, OutputInterface $output)
{
@@ -242,6 +254,8 @@ class Command
*
* @see InputInterface::bind()
* @see InputInterface::validate()
*
* @return void
*/
protected function initialize(InputInterface $input, OutputInterface $output)
{
@@ -378,7 +392,7 @@ class Command
*
* @internal
*/
public function mergeApplicationDefinition(bool $mergeArgs = true)
public function mergeApplicationDefinition(bool $mergeArgs = true): void
{
if (null === $this->application) {
return;
@@ -702,7 +716,7 @@ class Command
*
* @throws InvalidArgumentException When the name is invalid
*/
private function validateName(string $name)
private function validateName(string $name): void
{
if (!preg_match('/^[^\:]++(\:[^\:]++)*$/', $name)) {
throw new InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name));

View File

@@ -74,7 +74,7 @@ final class CompleteCommand extends Command
;
}
protected function initialize(InputInterface $input, OutputInterface $output)
protected function initialize(InputInterface $input, OutputInterface $output): void
{
$this->isDebug = filter_var(getenv('SYMFONY_COMPLETION_DEBUG'), \FILTER_VALIDATE_BOOL);
}
@@ -134,12 +134,12 @@ final class CompleteCommand extends Command
$completionInput->bind($command->getDefinition());
if (CompletionInput::TYPE_OPTION_NAME === $completionInput->getCompletionType()) {
$this->log(' Completing option names for the <comment>'.\get_class($command instanceof LazyCommand ? $command->getCommand() : $command).'</> command.');
$this->log(' Completing option names for the <comment>'.($command instanceof LazyCommand ? $command->getCommand() : $command)::class.'</> command.');
$suggestions->suggestOptions($command->getDefinition()->getOptions());
} else {
$this->log([
' Completing using the <comment>'.\get_class($command instanceof LazyCommand ? $command->getCommand() : $command).'</> class.',
' Completing using the <comment>'.($command instanceof LazyCommand ? $command->getCommand() : $command)::class.'</> class.',
' Completing <comment>'.$completionInput->getCompletionType().'</> for <comment>'.$completionInput->getCompletionName().'</>',
]);
if (null !== $compval = $completionInput->getCompletionValue()) {
@@ -155,7 +155,7 @@ final class CompleteCommand extends Command
$this->log('<info>Suggestions:</>');
if ($options = $suggestions->getOptionSuggestions()) {
$this->log(' --'.implode(' --', array_map(function ($o) { return $o->getName(); }, $options)));
$this->log(' --'.implode(' --', array_map(fn ($o) => $o->getName(), $options)));
} elseif ($values = $suggestions->getValueSuggestions()) {
$this->log(' '.implode(' ', $values));
} else {
@@ -173,10 +173,10 @@ final class CompleteCommand extends Command
throw $e;
}
return self::FAILURE;
return 2;
}
return self::SUCCESS;
return 0;
}
private function createCompletionInput(InputInterface $input): CompletionInput

View File

@@ -39,7 +39,7 @@ final class DumpCompletionCommand extends Command
private array $supportedShells;
protected function configure()
protected function configure(): void
{
$fullCommand = $_SERVER['PHP_SELF'];
$commandName = basename($fullCommand);
@@ -48,14 +48,16 @@ final class DumpCompletionCommand extends Command
$shell = $this->guessShell();
[$rcFile, $completionFile] = match ($shell) {
'fish' => ['~/.config/fish/config.fish', "/etc/fish/completions/$commandName.fish"],
'zsh' => ['~/.zshrc', '$fpath[1]/'.$commandName],
'zsh' => ['~/.zshrc', '$fpath[1]/_'.$commandName],
default => ['~/.bashrc', "/etc/bash_completion.d/$commandName"],
};
$supportedShells = implode(', ', $this->getSupportedShells());
$this
->setHelp(<<<EOH
The <info>%command.name%</> command dumps the shell completion script required
to use shell autocompletion (currently, bash and fish completion is supported).
to use shell autocompletion (currently, {$supportedShells} completion are supported).
<comment>Static installation
-------------------</>
@@ -94,7 +96,7 @@ EOH
if ($input->getOption('debug')) {
$this->tailDebugLog($commandName, $output);
return self::SUCCESS;
return 0;
}
$shell = $input->getArgument('shell') ?? self::guessShell();
@@ -111,12 +113,12 @@ EOH
$output->writeln(sprintf('<error>Shell not detected, Symfony shell completion only supports "%s").</>', implode('", "', $supportedShells)));
}
return self::INVALID;
return 2;
}
$output->write(str_replace(['{{ COMMAND_NAME }}', '{{ VERSION }}'], [$commandName, CompleteCommand::COMPLETION_API_VERSION], file_get_contents($completionFile)));
return self::SUCCESS;
return 0;
}
private static function guessShell(): string
@@ -141,8 +143,19 @@ EOH
*/
private function getSupportedShells(): array
{
return $this->supportedShells ??= array_map(function ($f) {
return pathinfo($f, \PATHINFO_EXTENSION);
}, glob(__DIR__.'/../Resources/completion.*'));
if (isset($this->supportedShells)) {
return $this->supportedShells;
}
$shells = [];
foreach (new \DirectoryIterator(__DIR__.'/../Resources/') as $file) {
if (str_starts_with($file->getBasename(), 'completion.') && $file->isFile()) {
$shells[] = $file->getExtension();
}
}
sort($shells);
return $this->supportedShells = $shells;
}
}

View File

@@ -27,6 +27,9 @@ class HelpCommand extends Command
{
private Command $command;
/**
* @return void
*/
protected function configure()
{
$this->ignoreValidationErrors();
@@ -34,12 +37,8 @@ class HelpCommand extends Command
$this
->setName('help')
->setDefinition([
new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help', function () {
return array_keys((new ApplicationDescription($this->getApplication()))->getCommands());
}),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt', function () {
return (new DescriptorHelper())->getFormats();
}),
new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help', fn () => array_keys((new ApplicationDescription($this->getApplication()))->getCommands())),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt', fn () => (new DescriptorHelper())->getFormats()),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command help'),
])
->setDescription('Display help for a command')
@@ -58,6 +57,9 @@ EOF
;
}
/**
* @return void
*/
public function setCommand(Command $command)
{
$this->command = $command;

View File

@@ -25,18 +25,17 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class ListCommand extends Command
{
/**
* @return void
*/
protected function configure()
{
$this
->setName('list')
->setDefinition([
new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name', null, function () {
return array_keys((new ApplicationDescription($this->getApplication()))->getNamespaces());
}),
new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name', null, fn () => array_keys((new ApplicationDescription($this->getApplication()))->getNamespaces())),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt', function () {
return (new DescriptorHelper())->getFormats();
}),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt', fn () => (new DescriptorHelper())->getFormats()),
new InputOption('short', null, InputOption::VALUE_NONE, 'To skip describing commands\' arguments'),
])
->setDescription('List commands')

View File

@@ -32,7 +32,7 @@ trait LockableTrait
private function lock(string $name = null, bool $blocking = false): bool
{
if (!class_exists(SemaphoreStore::class)) {
throw new LogicException('To enable the locking feature you must install the symfony/lock component.');
throw new LogicException('To enable the locking feature you must install the symfony/lock component. Try running "composer require symfony/lock".');
}
if (null !== $this->lock) {
@@ -58,7 +58,7 @@ trait LockableTrait
/**
* Releases the command lock if there is one.
*/
private function release()
private function release(): void
{
if ($this->lock) {
$this->lock->release();

View File

@@ -25,6 +25,10 @@ interface SignalableCommandInterface
/**
* The method will be called when the application is signaled.
*
* @param int|false $previousExitCode
* @return int|false The exit code to return or false to continue the normal execution
*/
public function handleSignal(int $signal): void;
public function handleSignal(int $signal, /* int|false $previousExitCode = 0 */);
}

View File

@@ -34,7 +34,7 @@ final class CompletionInput extends ArgvInput
private $tokens;
private $currentIndex;
private $completionType;
private $completionName = null;
private $completionName;
private $completionValue = '';
/**

View File

@@ -29,6 +29,9 @@ use Symfony\Component\DependencyInjection\TypedReference;
*/
class AddConsoleCommandPass implements CompilerPassInterface
{
/**
* @return void
*/
public function process(ContainerBuilder $container)
{
$commandServices = $container->findTaggedServiceIds('console.command', true);

View File

@@ -79,7 +79,7 @@ class ApplicationDescription
return $this->commands[$name] ?? $this->aliases[$name];
}
private function inspectApplication()
private function inspectApplication(): void
{
$this->commands = [];
$this->namespaces = [];

View File

@@ -26,12 +26,9 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
abstract class Descriptor implements DescriptorInterface
{
/**
* @var OutputInterface
*/
protected $output;
protected OutputInterface $output;
public function describe(OutputInterface $output, object $object, array $options = [])
public function describe(OutputInterface $output, object $object, array $options = []): void
{
$this->output = $output;
@@ -45,10 +42,7 @@ abstract class Descriptor implements DescriptorInterface
};
}
/**
* Writes content to output.
*/
protected function write(string $content, bool $decorated = false)
protected function write(string $content, bool $decorated = false): void
{
$this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW);
}
@@ -56,25 +50,25 @@ abstract class Descriptor implements DescriptorInterface
/**
* Describes an InputArgument instance.
*/
abstract protected function describeInputArgument(InputArgument $argument, array $options = []);
abstract protected function describeInputArgument(InputArgument $argument, array $options = []): void;
/**
* Describes an InputOption instance.
*/
abstract protected function describeInputOption(InputOption $option, array $options = []);
abstract protected function describeInputOption(InputOption $option, array $options = []): void;
/**
* Describes an InputDefinition instance.
*/
abstract protected function describeInputDefinition(InputDefinition $definition, array $options = []);
abstract protected function describeInputDefinition(InputDefinition $definition, array $options = []): void;
/**
* Describes a Command instance.
*/
abstract protected function describeCommand(Command $command, array $options = []);
abstract protected function describeCommand(Command $command, array $options = []): void;
/**
* Describes an Application instance.
*/
abstract protected function describeApplication(Application $application, array $options = []);
abstract protected function describeApplication(Application $application, array $options = []): void;
}

View File

@@ -20,5 +20,8 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
interface DescriptorInterface
{
/**
* @return void
*/
public function describe(OutputInterface $output, object $object, array $options = []);
}

View File

@@ -26,12 +26,12 @@ use Symfony\Component\Console\Input\InputOption;
*/
class JsonDescriptor extends Descriptor
{
protected function describeInputArgument(InputArgument $argument, array $options = [])
protected function describeInputArgument(InputArgument $argument, array $options = []): void
{
$this->writeData($this->getInputArgumentData($argument), $options);
}
protected function describeInputOption(InputOption $option, array $options = [])
protected function describeInputOption(InputOption $option, array $options = []): void
{
$this->writeData($this->getInputOptionData($option), $options);
if ($option->isNegatable()) {
@@ -39,17 +39,17 @@ class JsonDescriptor extends Descriptor
}
}
protected function describeInputDefinition(InputDefinition $definition, array $options = [])
protected function describeInputDefinition(InputDefinition $definition, array $options = []): void
{
$this->writeData($this->getInputDefinitionData($definition), $options);
}
protected function describeCommand(Command $command, array $options = [])
protected function describeCommand(Command $command, array $options = []): void
{
$this->writeData($this->getCommandData($command, $options['short'] ?? false), $options);
}
protected function describeApplication(Application $application, array $options = [])
protected function describeApplication(Application $application, array $options = []): void
{
$describedNamespace = $options['namespace'] ?? null;
$description = new ApplicationDescription($application, $describedNamespace, true);
@@ -81,7 +81,7 @@ class JsonDescriptor extends Descriptor
/**
* Writes data as json.
*/
private function writeData(array $data, array $options)
private function writeData(array $data, array $options): void
{
$flags = $options['json_encoding'] ?? 0;

View File

@@ -28,7 +28,7 @@ use Symfony\Component\Console\Output\OutputInterface;
*/
class MarkdownDescriptor extends Descriptor
{
public function describe(OutputInterface $output, object $object, array $options = [])
public function describe(OutputInterface $output, object $object, array $options = []): void
{
$decorated = $output->isDecorated();
$output->setDecorated(false);
@@ -38,12 +38,12 @@ class MarkdownDescriptor extends Descriptor
$output->setDecorated($decorated);
}
protected function write(string $content, bool $decorated = true)
protected function write(string $content, bool $decorated = true): void
{
parent::write($content, $decorated);
}
protected function describeInputArgument(InputArgument $argument, array $options = [])
protected function describeInputArgument(InputArgument $argument, array $options = []): void
{
$this->write(
'#### `'.($argument->getName() ?: '<none>')."`\n\n"
@@ -54,7 +54,7 @@ class MarkdownDescriptor extends Descriptor
);
}
protected function describeInputOption(InputOption $option, array $options = [])
protected function describeInputOption(InputOption $option, array $options = []): void
{
$name = '--'.$option->getName();
if ($option->isNegatable()) {
@@ -75,15 +75,13 @@ class MarkdownDescriptor extends Descriptor
);
}
protected function describeInputDefinition(InputDefinition $definition, array $options = [])
protected function describeInputDefinition(InputDefinition $definition, array $options = []): void
{
if ($showArguments = \count($definition->getArguments()) > 0) {
$this->write('### Arguments');
foreach ($definition->getArguments() as $argument) {
$this->write("\n\n");
if (null !== $describeInputArgument = $this->describeInputArgument($argument)) {
$this->write($describeInputArgument);
}
$this->describeInputArgument($argument);
}
}
@@ -95,14 +93,12 @@ class MarkdownDescriptor extends Descriptor
$this->write('### Options');
foreach ($definition->getOptions() as $option) {
$this->write("\n\n");
if (null !== $describeInputOption = $this->describeInputOption($option)) {
$this->write($describeInputOption);
}
$this->describeInputOption($option);
}
}
}
protected function describeCommand(Command $command, array $options = [])
protected function describeCommand(Command $command, array $options = []): void
{
if ($options['short'] ?? false) {
$this->write(
@@ -110,9 +106,7 @@ class MarkdownDescriptor extends Descriptor
.str_repeat('-', Helper::width($command->getName()) + 2)."\n\n"
.($command->getDescription() ? $command->getDescription()."\n\n" : '')
.'### Usage'."\n\n"
.array_reduce($command->getAliases(), function ($carry, $usage) {
return $carry.'* `'.$usage.'`'."\n";
})
.array_reduce($command->getAliases(), fn ($carry, $usage) => $carry.'* `'.$usage.'`'."\n")
);
return;
@@ -125,9 +119,7 @@ class MarkdownDescriptor extends Descriptor
.str_repeat('-', Helper::width($command->getName()) + 2)."\n\n"
.($command->getDescription() ? $command->getDescription()."\n\n" : '')
.'### Usage'."\n\n"
.array_reduce(array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()), function ($carry, $usage) {
return $carry.'* `'.$usage.'`'."\n";
})
.array_reduce(array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()), fn ($carry, $usage) => $carry.'* `'.$usage.'`'."\n")
);
if ($help = $command->getProcessedHelp()) {
@@ -142,7 +134,7 @@ class MarkdownDescriptor extends Descriptor
}
}
protected function describeApplication(Application $application, array $options = [])
protected function describeApplication(Application $application, array $options = []): void
{
$describedNamespace = $options['namespace'] ?? null;
$description = new ApplicationDescription($application, $describedNamespace);
@@ -157,16 +149,12 @@ class MarkdownDescriptor extends Descriptor
}
$this->write("\n\n");
$this->write(implode("\n", array_map(function ($commandName) use ($description) {
return 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) {
$this->write("\n\n");
if (null !== $describeCommand = $this->describeCommand($command, $options)) {
$this->write($describeCommand);
}
$this->describeCommand($command, $options);
}
}

View File

@@ -0,0 +1,272 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Descriptor;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Helper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\String\UnicodeString;
class ReStructuredTextDescriptor extends Descriptor
{
// <h1>
private string $partChar = '=';
// <h2>
private string $chapterChar = '-';
// <h3>
private string $sectionChar = '~';
// <h4>
private string $subsectionChar = '.';
// <h5>
private string $subsubsectionChar = '^';
// <h6>
private string $paragraphsChar = '"';
private array $visibleNamespaces = [];
public function describe(OutputInterface $output, object $object, array $options = []): void
{
$decorated = $output->isDecorated();
$output->setDecorated(false);
parent::describe($output, $object, $options);
$output->setDecorated($decorated);
}
/**
* Override parent method to set $decorated = true.
*/
protected function write(string $content, bool $decorated = true): void
{
parent::write($content, $decorated);
}
protected function describeInputArgument(InputArgument $argument, array $options = []): void
{
$this->write(
$argument->getName() ?: '<none>'."\n".str_repeat($this->paragraphsChar, Helper::width($argument->getName()))."\n\n"
.($argument->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n", $argument->getDescription())."\n\n" : '')
.'- **Is required**: '.($argument->isRequired() ? 'yes' : 'no')."\n"
.'- **Is array**: '.($argument->isArray() ? 'yes' : 'no')."\n"
.'- **Default**: ``'.str_replace("\n", '', var_export($argument->getDefault(), true)).'``'
);
}
protected function describeInputOption(InputOption $option, array $options = []): void
{
$name = '\-\-'.$option->getName();
if ($option->isNegatable()) {
$name .= '|\-\-no-'.$option->getName();
}
if ($option->getShortcut()) {
$name .= '|-'.str_replace('|', '|-', $option->getShortcut());
}
$optionDescription = $option->getDescription() ? preg_replace('/\s*[\r\n]\s*/', "\n\n", $option->getDescription())."\n\n" : '';
$optionDescription = (new UnicodeString($optionDescription))->ascii();
$this->write(
$name."\n".str_repeat($this->paragraphsChar, Helper::width($name))."\n\n"
.$optionDescription
.'- **Accept value**: '.($option->acceptValue() ? 'yes' : 'no')."\n"
.'- **Is value required**: '.($option->isValueRequired() ? 'yes' : 'no')."\n"
.'- **Is multiple**: '.($option->isArray() ? 'yes' : 'no')."\n"
.'- **Is negatable**: '.($option->isNegatable() ? 'yes' : 'no')."\n"
.'- **Default**: ``'.str_replace("\n", '', var_export($option->getDefault(), true)).'``'."\n"
);
}
protected function describeInputDefinition(InputDefinition $definition, array $options = []): void
{
if ($showArguments = ((bool) $definition->getArguments())) {
$this->write("Arguments\n".str_repeat($this->subsubsectionChar, 9))."\n\n";
foreach ($definition->getArguments() as $argument) {
$this->write("\n\n");
$this->describeInputArgument($argument);
}
}
if ($nonDefaultOptions = $this->getNonDefaultOptions($definition)) {
if ($showArguments) {
$this->write("\n\n");
}
$this->write("Options\n".str_repeat($this->subsubsectionChar, 7)."\n\n");
foreach ($nonDefaultOptions as $option) {
$this->describeInputOption($option);
$this->write("\n");
}
}
}
protected function describeCommand(Command $command, array $options = []): void
{
if ($options['short'] ?? false) {
$this->write(
'``'.$command->getName()."``\n"
.str_repeat($this->subsectionChar, Helper::width($command->getName()))."\n\n"
.($command->getDescription() ? $command->getDescription()."\n\n" : '')
."Usage\n".str_repeat($this->paragraphsChar, 5)."\n\n"
.array_reduce($command->getAliases(), static fn ($carry, $usage) => $carry.'- ``'.$usage.'``'."\n")
);
return;
}
$command->mergeApplicationDefinition(false);
foreach ($command->getAliases() as $alias) {
$this->write('.. _'.$alias.":\n\n");
}
$this->write(
$command->getName()."\n"
.str_repeat($this->subsectionChar, Helper::width($command->getName()))."\n\n"
.($command->getDescription() ? $command->getDescription()."\n\n" : '')
."Usage\n".str_repeat($this->subsubsectionChar, 5)."\n\n"
.array_reduce(array_merge([$command->getSynopsis()], $command->getAliases(), $command->getUsages()), static fn ($carry, $usage) => $carry.'- ``'.$usage.'``'."\n")
);
if ($help = $command->getProcessedHelp()) {
$this->write("\n");
$this->write($help);
}
$definition = $command->getDefinition();
if ($definition->getOptions() || $definition->getArguments()) {
$this->write("\n\n");
$this->describeInputDefinition($definition);
}
}
protected function describeApplication(Application $application, array $options = []): void
{
$description = new ApplicationDescription($application, $options['namespace'] ?? null);
$title = $this->getApplicationTitle($application);
$this->write($title."\n".str_repeat($this->partChar, Helper::width($title)));
$this->createTableOfContents($description, $application);
$this->describeCommands($application, $options);
}
private function getApplicationTitle(Application $application): string
{
if ('UNKNOWN' === $application->getName()) {
return 'Console Tool';
}
if ('UNKNOWN' !== $application->getVersion()) {
return sprintf('%s %s', $application->getName(), $application->getVersion());
}
return $application->getName();
}
private function describeCommands($application, array $options): void
{
$title = 'Commands';
$this->write("\n\n$title\n".str_repeat($this->chapterChar, Helper::width($title))."\n\n");
foreach ($this->visibleNamespaces as $namespace) {
if ('_global' === $namespace) {
$commands = $application->all('');
$this->write('Global'."\n".str_repeat($this->sectionChar, Helper::width('Global'))."\n\n");
} else {
$commands = $application->all($namespace);
$this->write($namespace."\n".str_repeat($this->sectionChar, Helper::width($namespace))."\n\n");
}
foreach ($this->removeAliasesAndHiddenCommands($commands) as $command) {
$this->describeCommand($command, $options);
$this->write("\n\n");
}
}
}
private function createTableOfContents(ApplicationDescription $description, Application $application): void
{
$this->setVisibleNamespaces($description);
$chapterTitle = 'Table of Contents';
$this->write("\n\n$chapterTitle\n".str_repeat($this->chapterChar, Helper::width($chapterTitle))."\n\n");
foreach ($this->visibleNamespaces as $namespace) {
if ('_global' === $namespace) {
$commands = $application->all('');
} else {
$commands = $application->all($namespace);
$this->write("\n\n");
$this->write($namespace."\n".str_repeat($this->sectionChar, Helper::width($namespace))."\n\n");
}
$commands = $this->removeAliasesAndHiddenCommands($commands);
$this->write("\n\n");
$this->write(implode("\n", array_map(static fn ($commandName) => sprintf('- `%s`_', $commandName), array_keys($commands))));
}
}
private function getNonDefaultOptions(InputDefinition $definition): array
{
$globalOptions = [
'help',
'quiet',
'verbose',
'version',
'ansi',
'no-interaction',
];
$nonDefaultOptions = [];
foreach ($definition->getOptions() as $option) {
// Skip global options.
if (!\in_array($option->getName(), $globalOptions)) {
$nonDefaultOptions[] = $option;
}
}
return $nonDefaultOptions;
}
private function setVisibleNamespaces(ApplicationDescription $description): void
{
$commands = $description->getCommands();
foreach ($description->getNamespaces() as $namespace) {
try {
$namespaceCommands = $namespace['commands'];
foreach ($namespaceCommands as $key => $commandName) {
if (!\array_key_exists($commandName, $commands)) {
// If the array key does not exist, then this is an alias.
unset($namespaceCommands[$key]);
} elseif ($commands[$commandName]->isHidden()) {
unset($namespaceCommands[$key]);
}
}
if (!$namespaceCommands) {
// If the namespace contained only aliases or hidden commands, skip the namespace.
continue;
}
} catch (\Exception) {
}
$this->visibleNamespaces[] = $namespace['id'];
}
}
private function removeAliasesAndHiddenCommands(array $commands): array
{
foreach ($commands as $key => $command) {
if ($command->isHidden() || \in_array($key, $command->getAliases(), true)) {
unset($commands[$key]);
}
}
unset($commands['completion']);
return $commands;
}
}

View File

@@ -28,7 +28,7 @@ use Symfony\Component\Console\Input\InputOption;
*/
class TextDescriptor extends Descriptor
{
protected function describeInputArgument(InputArgument $argument, array $options = [])
protected function describeInputArgument(InputArgument $argument, array $options = []): void
{
if (null !== $argument->getDefault() && (!\is_array($argument->getDefault()) || \count($argument->getDefault()))) {
$default = sprintf('<comment> [default: %s]</comment>', $this->formatDefaultValue($argument->getDefault()));
@@ -48,7 +48,7 @@ class TextDescriptor extends Descriptor
), $options);
}
protected function describeInputOption(InputOption $option, 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('<comment> [default: %s]</comment>', $this->formatDefaultValue($option->getDefault()));
@@ -83,7 +83,7 @@ class TextDescriptor extends Descriptor
), $options);
}
protected function describeInputDefinition(InputDefinition $definition, array $options = [])
protected function describeInputDefinition(InputDefinition $definition, array $options = []): void
{
$totalWidth = $this->calculateTotalWidthForOptions($definition->getOptions());
foreach ($definition->getArguments() as $argument) {
@@ -122,7 +122,7 @@ class TextDescriptor extends Descriptor
}
}
protected function describeCommand(Command $command, array $options = [])
protected function describeCommand(Command $command, array $options = []): void
{
$command->mergeApplicationDefinition(false);
@@ -157,7 +157,7 @@ class TextDescriptor extends Descriptor
}
}
protected function describeApplication(Application $application, array $options = [])
protected function describeApplication(Application $application, array $options = []): void
{
$describedNamespace = $options['namespace'] ?? null;
$description = new ApplicationDescription($application, $describedNamespace);
@@ -193,9 +193,7 @@ class TextDescriptor extends Descriptor
}
// calculate max. width based on available commands per namespace
$width = $this->getColumnWidth(array_merge(...array_values(array_map(function ($namespace) use ($commands) {
return array_intersect($namespace['commands'], array_keys($commands));
}, array_values($namespaces)))));
$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('<comment>Available commands for the "%s" namespace:</comment>', $describedNamespace), $options);
@@ -204,9 +202,7 @@ class TextDescriptor extends Descriptor
}
foreach ($namespaces as $namespace) {
$namespace['commands'] = array_filter($namespace['commands'], function ($name) use ($commands) {
return isset($commands[$name]);
});
$namespace['commands'] = array_filter($namespace['commands'], fn ($name) => isset($commands[$name]));
if (!$namespace['commands']) {
continue;
@@ -230,7 +226,7 @@ class TextDescriptor extends Descriptor
}
}
private function writeText(string $content, array $options = [])
private function writeText(string $content, array $options = []): void
{
$this->write(
isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content,

View File

@@ -120,27 +120,27 @@ class XmlDescriptor extends Descriptor
return $dom;
}
protected function describeInputArgument(InputArgument $argument, array $options = [])
protected function describeInputArgument(InputArgument $argument, array $options = []): void
{
$this->writeDocument($this->getInputArgumentDocument($argument));
}
protected function describeInputOption(InputOption $option, array $options = [])
protected function describeInputOption(InputOption $option, array $options = []): void
{
$this->writeDocument($this->getInputOptionDocument($option));
}
protected function describeInputDefinition(InputDefinition $definition, array $options = [])
protected function describeInputDefinition(InputDefinition $definition, array $options = []): void
{
$this->writeDocument($this->getInputDefinitionDocument($definition));
}
protected function describeCommand(Command $command, array $options = [])
protected function describeCommand(Command $command, array $options = []): void
{
$this->writeDocument($this->getCommandDocument($command, $options['short'] ?? false));
}
protected function describeApplication(Application $application, array $options = [])
protected function describeApplication(Application $application, array $options = []): void
{
$this->writeDocument($this->getApplicationDocument($application, $options['namespace'] ?? null, $options['short'] ?? false));
}
@@ -148,7 +148,7 @@ class XmlDescriptor extends Descriptor
/**
* Appends document children to parent node.
*/
private function appendDocument(\DOMNode $parentNode, \DOMNode $importedParent)
private function appendDocument(\DOMNode $parentNode, \DOMNode $importedParent): void
{
foreach ($importedParent->childNodes as $childNode) {
$parentNode->appendChild($parentNode->ownerDocument->importNode($childNode, true));
@@ -158,7 +158,7 @@ class XmlDescriptor extends Descriptor
/**
* Writes DOM document.
*/
private function writeDocument(\DOMDocument $dom)
private function writeDocument(\DOMDocument $dom): void
{
$dom->formatOutput = true;
$this->write($dom->saveXML());

View File

@@ -21,15 +21,36 @@ use Symfony\Component\Console\Output\OutputInterface;
final class ConsoleSignalEvent extends ConsoleEvent
{
private int $handlingSignal;
private int|false $exitCode;
public function __construct(Command $command, InputInterface $input, OutputInterface $output, int $handlingSignal)
public function __construct(Command $command, InputInterface $input, OutputInterface $output, int $handlingSignal, int|false $exitCode = 0)
{
parent::__construct($command, $input, $output);
$this->handlingSignal = $handlingSignal;
$this->exitCode = $exitCode;
}
public function getHandlingSignal(): int
{
return $this->handlingSignal;
}
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;
}
}

View File

@@ -31,6 +31,9 @@ class ErrorListener implements EventSubscriberInterface
$this->logger = $logger;
}
/**
* @return void
*/
public function onConsoleError(ConsoleErrorEvent $event)
{
if (null === $this->logger) {
@@ -48,6 +51,9 @@ class ErrorListener implements EventSubscriberInterface
$this->logger->critical('Error thrown while running command "{command}". Message: "{message}"', ['exception' => $error, 'command' => $inputString, 'message' => $error->getMessage()]);
}
/**
* @return void
*/
public function onConsoleTerminate(ConsoleTerminateEvent $event)
{
if (null === $this->logger) {

View File

@@ -81,6 +81,9 @@ class OutputFormatter implements WrappableOutputFormatterInterface
$this->styleStack = new OutputFormatterStyleStack();
}
/**
* @return void
*/
public function setDecorated(bool $decorated)
{
$this->decorated = $decorated;
@@ -91,6 +94,9 @@ class OutputFormatter implements WrappableOutputFormatterInterface
return $this->decorated;
}
/**
* @return void
*/
public function setStyle(string $name, OutputFormatterStyleInterface $style)
{
$this->styles[strtolower($name)] = $style;
@@ -115,6 +121,9 @@ class OutputFormatter implements WrappableOutputFormatterInterface
return $this->formatAndWrap($message, 0);
}
/**
* @return string
*/
public function formatAndWrap(?string $message, int $width)
{
if (null === $message) {

View File

@@ -20,6 +20,8 @@ interface OutputFormatterInterface
{
/**
* Sets the decorated flag.
*
* @return void
*/
public function setDecorated(bool $decorated);
@@ -30,6 +32,8 @@ interface OutputFormatterInterface
/**
* Sets a new style.
*
* @return void
*/
public function setStyle(string $name, OutputFormatterStyleInterface $style);

View File

@@ -38,6 +38,9 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
$this->color = new Color($this->foreground = $foreground ?: '', $this->background = $background ?: '', $this->options = $options);
}
/**
* @return void
*/
public function setForeground(string $color = null)
{
if (1 > \func_num_args()) {
@@ -46,6 +49,9 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
$this->color = new Color($this->foreground = $color ?: '', $this->background, $this->options);
}
/**
* @return void
*/
public function setBackground(string $color = null)
{
if (1 > \func_num_args()) {
@@ -59,12 +65,18 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
$this->href = $url;
}
/**
* @return void
*/
public function setOption(string $option)
{
$this->options[] = $option;
$this->color = new Color($this->foreground, $this->background, $this->options);
}
/**
* @return void
*/
public function unsetOption(string $option)
{
$pos = array_search($option, $this->options);
@@ -75,6 +87,9 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
$this->color = new Color($this->foreground, $this->background, $this->options);
}
/**
* @return void
*/
public function setOptions(array $options)
{
$this->color = new Color($this->foreground, $this->background, $this->options = $options);
@@ -83,7 +98,8 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface
public function apply(string $text): string
{
$this->handlesHrefGracefully ??= 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR')
&& (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100);
&& (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100)
&& !isset($_SERVER['IDEA_INITIAL_DIRECTORY']);
if (null !== $this->href && $this->handlesHrefGracefully) {
$text = "\033]8;;$this->href\033\\$text\033]8;;\033\\";

View File

@@ -20,26 +20,36 @@ interface OutputFormatterStyleInterface
{
/**
* Sets style foreground color.
*
* @return void
*/
public function setForeground(?string $color);
/**
* Sets style background color.
*
* @return void
*/
public function setBackground(?string $color);
/**
* Sets some specific style option.
*
* @return void
*/
public function setOption(string $option);
/**
* Unsets some specific style option.
*
* @return void
*/
public function unsetOption(string $option);
/**
* Sets multiple style options at once.
*
* @return void
*/
public function setOptions(array $options);

View File

@@ -34,6 +34,8 @@ class OutputFormatterStyleStack implements ResetInterface
/**
* Resets stack (ie. empty internal arrays).
*
* @return void
*/
public function reset()
{
@@ -42,6 +44,8 @@ class OutputFormatterStyleStack implements ResetInterface
/**
* Pushes a style in the stack.
*
* @return void
*/
public function push(OutputFormatterStyleInterface $style)
{

View File

@@ -14,6 +14,7 @@ namespace Symfony\Component\Console\Helper;
use Symfony\Component\Console\Descriptor\DescriptorInterface;
use Symfony\Component\Console\Descriptor\JsonDescriptor;
use Symfony\Component\Console\Descriptor\MarkdownDescriptor;
use Symfony\Component\Console\Descriptor\ReStructuredTextDescriptor;
use Symfony\Component\Console\Descriptor\TextDescriptor;
use Symfony\Component\Console\Descriptor\XmlDescriptor;
use Symfony\Component\Console\Exception\InvalidArgumentException;
@@ -38,6 +39,7 @@ class DescriptorHelper extends Helper
->register('xml', new XmlDescriptor())
->register('json', new JsonDescriptor())
->register('md', new MarkdownDescriptor())
->register('rst', new ReStructuredTextDescriptor())
;
}
@@ -48,6 +50,8 @@ class DescriptorHelper extends Helper
* * format: string, the output format name
* * raw_text: boolean, sets output type as raw
*
* @return void
*
* @throws InvalidArgumentException when the given format is not supported
*/
public function describe(OutputInterface $output, ?object $object, array $options = [])

View File

@@ -40,14 +40,12 @@ final class Dumper
return rtrim($dumper->dump(($this->cloner ??= new VarCloner())->cloneVar($var)->withRefHandles(false), true));
};
} else {
$this->handler = function ($var): string {
return match (true) {
null === $var => 'null',
true === $var => 'true',
false === $var => 'false',
\is_string($var) => '"'.$var.'"',
default => rtrim(print_r($var, true)),
};
$this->handler = fn ($var): string => match (true) {
null === $var => 'null',
true === $var => 'true',
false === $var => 'false',
\is_string($var) => '"'.$var.'"',
default => rtrim(print_r($var, true)),
};
}
}

View File

@@ -21,8 +21,11 @@ use Symfony\Component\String\UnicodeString;
*/
abstract class Helper implements HelperInterface
{
protected $helperSet = null;
protected $helperSet;
/**
* @return void
*/
public function setHelperSet(HelperSet $helperSet = null)
{
if (1 > \func_num_args()) {
@@ -88,6 +91,9 @@ abstract class Helper implements HelperInterface
return mb_substr($string, $from, $length, $encoding);
}
/**
* @return string
*/
public static function formatTime(int|float $secs)
{
static $timeFormats = [
@@ -117,6 +123,9 @@ abstract class Helper implements HelperInterface
}
}
/**
* @return string
*/
public static function formatMemory(int $memory)
{
if ($memory >= 1024 * 1024 * 1024) {
@@ -134,6 +143,9 @@ abstract class Helper implements HelperInterface
return sprintf('%d B', $memory);
}
/**
* @return string
*/
public static function removeDecoration(OutputFormatterInterface $formatter, ?string $string)
{
$isDecorated = $formatter->isDecorated();

View File

@@ -20,6 +20,8 @@ interface HelperInterface
{
/**
* Sets the helper set associated with this helper.
*
* @return void
*/
public function setHelperSet(?HelperSet $helperSet);

View File

@@ -35,6 +35,9 @@ class HelperSet implements \IteratorAggregate
}
}
/**
* @return void
*/
public function set(HelperInterface $helper, string $alias = null)
{
$this->helpers[$helper->getName()] = $helper;

View File

@@ -23,6 +23,9 @@ abstract class InputAwareHelper extends Helper implements InputAwareInterface
{
protected $input;
/**
* @return void
*/
public function setInput(InputInterface $input)
{
$this->input = $input;

View File

@@ -59,6 +59,7 @@ final class ProgressBar
private Terminal $terminal;
private ?string $previousMessage = null;
private Cursor $cursor;
private array $placeholders = [];
private static array $formatters;
private static array $formats;
@@ -94,12 +95,12 @@ final class ProgressBar
}
/**
* Sets a placeholder formatter for a given name.
* Sets a placeholder formatter for a given name, globally for all instances of ProgressBar.
*
* This method also allow you to override an existing placeholder.
*
* @param string $name The placeholder name (including the delimiter char like %)
* @param callable $callable A PHP callable
* @param string $name The placeholder name (including the delimiter char like %)
* @param callable(ProgressBar):string $callable A PHP callable
*/
public static function setPlaceholderFormatterDefinition(string $name, callable $callable): void
{
@@ -120,6 +121,26 @@ final class ProgressBar
return self::$formatters[$name] ?? null;
}
/**
* Sets a placeholder formatter for a given name, for this instance only.
*
* @param callable(ProgressBar):string $callable A PHP callable
*/
public function setPlaceholderFormatter(string $name, callable $callable): void
{
$this->placeholders[$name] = $callable;
}
/**
* Gets the placeholder formatter for a given name.
*
* @param string $name The placeholder name (including the delimiter char like %)
*/
public function getPlaceholderFormatter(string $name): ?callable
{
return $this->placeholders[$name] ?? $this::getPlaceholderFormatterDefinition($name);
}
/**
* Sets a format for a given name.
*
@@ -157,12 +178,12 @@ final class ProgressBar
* @param string $message The text to associate with the placeholder
* @param string $name The name of the placeholder
*/
public function setMessage(string $message, string $name = 'message')
public function setMessage(string $message, string $name = 'message'): void
{
$this->messages[$name] = $message;
}
public function getMessage(string $name = 'message')
public function getMessage(string $name = 'message'): string
{
return $this->messages[$name];
}
@@ -215,7 +236,7 @@ final class ProgressBar
return round((time() - $this->startTime) / ($this->step - $this->startingStep) * ($this->max - $this->step));
}
public function setBarWidth(int $size)
public function setBarWidth(int $size): void
{
$this->barWidth = max(1, $size);
}
@@ -225,7 +246,7 @@ final class ProgressBar
return $this->barWidth;
}
public function setBarCharacter(string $char)
public function setBarCharacter(string $char): void
{
$this->barChar = $char;
}
@@ -235,7 +256,7 @@ final class ProgressBar
return $this->barChar ?? ($this->max ? '=' : $this->emptyBarChar);
}
public function setEmptyBarCharacter(string $char)
public function setEmptyBarCharacter(string $char): void
{
$this->emptyBarChar = $char;
}
@@ -245,7 +266,7 @@ final class ProgressBar
return $this->emptyBarChar;
}
public function setProgressCharacter(string $char)
public function setProgressCharacter(string $char): void
{
$this->progressChar = $char;
}
@@ -255,7 +276,7 @@ final class ProgressBar
return $this->progressChar;
}
public function setFormat(string $format)
public function setFormat(string $format): void
{
$this->format = null;
$this->internalFormat = $format;
@@ -266,7 +287,7 @@ final class ProgressBar
*
* @param int|null $freq The frequency in steps
*/
public function setRedrawFrequency(?int $freq)
public function setRedrawFrequency(?int $freq): void
{
$this->redrawFreq = null !== $freq ? max(1, $freq) : null;
}
@@ -325,7 +346,7 @@ final class ProgressBar
*
* @param int $step Number of steps to advance
*/
public function advance(int $step = 1)
public function advance(int $step = 1): void
{
$this->setProgress($this->step + $step);
}
@@ -333,12 +354,12 @@ final class ProgressBar
/**
* Sets whether to overwrite the progressbar, false for new line.
*/
public function setOverwrite(bool $overwrite)
public function setOverwrite(bool $overwrite): void
{
$this->overwrite = $overwrite;
}
public function setProgress(int $step)
public function setProgress(int $step): void
{
if ($this->max && $step > $this->max) {
$this->max = $step;
@@ -371,7 +392,7 @@ final class ProgressBar
}
}
public function setMaxSteps(int $max)
public function setMaxSteps(int $max): void
{
$this->format = null;
$this->max = max(0, $max);
@@ -431,7 +452,7 @@ final class ProgressBar
$this->overwrite('');
}
private function setRealFormat(string $format)
private function setRealFormat(string $format): void
{
// try to use the _nomax variant if available
if (!$this->max && null !== self::getFormatDefinition($format.'_nomax')) {
@@ -513,9 +534,7 @@ final class ProgressBar
return $display;
},
'elapsed' => function (self $bar) {
return Helper::formatTime(time() - $bar->getStartTime());
},
'elapsed' => fn (self $bar) => Helper::formatTime(time() - $bar->getStartTime()),
'remaining' => function (self $bar) {
if (!$bar->getMaxSteps()) {
throw new LogicException('Unable to display the remaining time if the maximum number of steps is not set.');
@@ -530,18 +549,10 @@ final class ProgressBar
return Helper::formatTime($bar->getEstimated());
},
'memory' => function (self $bar) {
return Helper::formatMemory(memory_get_usage(true));
},
'current' => function (self $bar) {
return str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', \STR_PAD_LEFT);
},
'max' => function (self $bar) {
return $bar->getMaxSteps();
},
'percent' => function (self $bar) {
return floor($bar->getProgressPercent() * 100);
},
'memory' => fn (self $bar) => Helper::formatMemory(memory_get_usage(true)),
'current' => fn (self $bar) => str_pad($bar->getProgress(), $bar->getStepWidth(), ' ', \STR_PAD_LEFT),
'max' => fn (self $bar) => $bar->getMaxSteps(),
'percent' => fn (self $bar) => floor($bar->getProgressPercent() * 100),
];
}
@@ -568,7 +579,7 @@ final class ProgressBar
$regex = "{%([a-z\-_]+)(?:\:([^%]+))?%}i";
$callback = function ($matches) {
if ($formatter = $this::getPlaceholderFormatterDefinition($matches[1])) {
if ($formatter = $this->getPlaceholderFormatter($matches[1])) {
$text = $formatter($this, $this->output);
} elseif (isset($this->messages[$matches[1]])) {
$text = $this->messages[$matches[1]];
@@ -585,9 +596,7 @@ final class ProgressBar
$line = preg_replace_callback($regex, $callback, $this->format);
// gets string length for each sub line with multiline format
$linesLength = array_map(function ($subLine) {
return Helper::width(Helper::removeDecoration($this->output->getFormatter(), rtrim($subLine, "\r")));
}, explode("\n", $line));
$linesLength = array_map(fn ($subLine) => Helper::width(Helper::removeDecoration($this->output->getFormatter(), rtrim($subLine, "\r"))), explode("\n", $line));
$linesWidth = max($linesLength);

View File

@@ -70,6 +70,8 @@ class ProgressIndicator
/**
* Sets the current indicator message.
*
* @return void
*/
public function setMessage(?string $message)
{
@@ -80,6 +82,8 @@ class ProgressIndicator
/**
* Starts the indicator output.
*
* @return void
*/
public function start(string $message)
{
@@ -98,6 +102,8 @@ class ProgressIndicator
/**
* Advances the indicator.
*
* @return void
*/
public function advance()
{
@@ -123,6 +129,8 @@ class ProgressIndicator
/**
* Finish the indicator with message.
*
* @return void
*/
public function finish(string $message)
{
@@ -148,6 +156,8 @@ class ProgressIndicator
* Sets a placeholder formatter for a given name.
*
* This method also allow you to override an existing placeholder.
*
* @return void
*/
public static function setPlaceholderFormatterDefinition(string $name, callable $callable)
{
@@ -166,7 +176,7 @@ class ProgressIndicator
return self::$formatters[$name] ?? null;
}
private function display()
private function display(): void
{
if (OutputInterface::VERBOSITY_QUIET === $this->output->getVerbosity()) {
return;
@@ -195,7 +205,7 @@ class ProgressIndicator
/**
* Overwrites a previous message to the output.
*/
private function overwrite(string $message)
private function overwrite(string $message): void
{
if ($this->output->isDecorated()) {
$this->output->write("\x0D\x1B[2K");
@@ -216,18 +226,10 @@ class ProgressIndicator
private static function initPlaceholderFormatters(): array
{
return [
'indicator' => function (self $indicator) {
return $indicator->indicatorValues[$indicator->indicatorCurrent % \count($indicator->indicatorValues)];
},
'message' => function (self $indicator) {
return $indicator->message;
},
'elapsed' => function (self $indicator) {
return Helper::formatTime(time() - $indicator->startTime);
},
'memory' => function () {
return Helper::formatMemory(memory_get_usage(true));
},
'indicator' => fn (self $indicator) => $indicator->indicatorValues[$indicator->indicatorCurrent % \count($indicator->indicatorValues)],
'message' => fn (self $indicator) => $indicator->message,
'elapsed' => fn (self $indicator) => Helper::formatTime(time() - $indicator->startTime),
'memory' => fn () => Helper::formatMemory(memory_get_usage(true)),
];
}
}

View File

@@ -68,9 +68,7 @@ class QuestionHelper extends Helper
return $this->doAsk($output, $question);
}
$interviewer = function () use ($output, $question) {
return $this->doAsk($output, $question);
};
$interviewer = fn () => $this->doAsk($output, $question);
return $this->validateAttempts($interviewer, $output, $question);
} catch (MissingInputException $exception) {
@@ -91,6 +89,8 @@ class QuestionHelper extends Helper
/**
* Prevents usage of stty.
*
* @return void
*/
public static function disableStty()
{
@@ -123,7 +123,18 @@ class QuestionHelper extends Helper
}
if (false === $ret) {
$isBlocked = stream_get_meta_data($inputStream)['blocked'] ?? true;
if (!$isBlocked) {
stream_set_blocking($inputStream, true);
}
$ret = $this->readInput($inputStream, $question);
if (!$isBlocked) {
stream_set_blocking($inputStream, false);
}
if (false === $ret) {
throw new MissingInputException('Aborted.');
}
@@ -159,7 +170,7 @@ class QuestionHelper extends Helper
}
if ($validator = $question->getValidator()) {
return \call_user_func($question->getValidator(), $default);
return \call_user_func($validator, $default);
} elseif ($question instanceof ChoiceQuestion) {
$choices = $question->getChoices();
@@ -179,6 +190,8 @@ class QuestionHelper extends Helper
/**
* Outputs the question prompt.
*
* @return void
*/
protected function writePrompt(OutputInterface $output, Question $question)
{
@@ -215,6 +228,8 @@ class QuestionHelper extends Helper
/**
* Outputs an error message.
*
* @return void
*/
protected function writeError(OutputInterface $output, \Exception $error)
{
@@ -314,9 +329,7 @@ class QuestionHelper extends Helper
$matches = array_filter(
$autocomplete($ret),
function ($match) use ($ret) {
return '' === $ret || str_starts_with($match, $ret);
}
fn ($match) => '' === $ret || str_starts_with($match, $ret)
);
$numMatches = \count($matches);
$ofs = -1;
@@ -496,13 +509,11 @@ class QuestionHelper extends Helper
return self::$stdinIsInteractive = @posix_isatty(fopen('php://stdin', 'r'));
}
if (!\function_exists('exec')) {
if (!\function_exists('shell_exec')) {
return self::$stdinIsInteractive = true;
}
exec('stty 2> /dev/null', $output, $status);
return self::$stdinIsInteractive = 1 !== $status;
return self::$stdinIsInteractive = (bool) shell_exec('stty 2> '.('\\' === \DIRECTORY_SEPARATOR ? 'NUL' : '/dev/null'));
}
/**

View File

@@ -25,6 +25,9 @@ use Symfony\Component\Console\Style\SymfonyStyle;
*/
class SymfonyQuestionHelper extends QuestionHelper
{
/**
* @return void
*/
protected function writePrompt(OutputInterface $output, Question $question)
{
$text = OutputFormatter::escapeTrailingBackslash($question->getQuestion());
@@ -80,6 +83,9 @@ class SymfonyQuestionHelper extends QuestionHelper
$output->write($prompt);
}
/**
* @return void
*/
protected function writeError(OutputInterface $output, \Exception $error)
{
if ($output instanceof SymfonyStyle) {

View File

@@ -66,6 +66,8 @@ class Table
/**
* Sets a style definition.
*
* @return void
*/
public static function setStyleDefinition(string $name, TableStyle $style)
{
@@ -310,6 +312,8 @@ class Table
* | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
* | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
* +---------------+-----------------------+------------------+
*
* @return void
*/
public function render()
{
@@ -450,7 +454,7 @@ class Table
*
* +-----+-----------+-------+
*/
private function renderRowSeparator(int $type = self::SEPARATOR_MID, string $title = null, string $titleFormat = null)
private function renderRowSeparator(int $type = self::SEPARATOR_MID, string $title = null, string $titleFormat = null): void
{
if (!$count = $this->numberOfColumns) {
return;
@@ -515,7 +519,7 @@ class Table
*
* | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
*/
private function renderRow(array $row, string $cellFormat, string $firstCellFormat = null)
private function renderRow(array $row, string $cellFormat, string $firstCellFormat = null): void
{
$rowContent = $this->renderColumnSeparator(self::BORDER_OUTSIDE);
$columns = $this->getRowColumns($row);
@@ -588,7 +592,7 @@ class Table
/**
* Calculate number of columns for this table.
*/
private function calculateNumberOfColumns(array $rows)
private function calculateNumberOfColumns(array $rows): void
{
$columns = [0];
foreach ($rows as $row) {
@@ -727,7 +731,7 @@ class Table
/**
* fill cells for a row that contains colspan > 1.
*/
private function fillCells(iterable $row)
private function fillCells(iterable $row): iterable
{
$newRow = [];
@@ -789,7 +793,7 @@ class Table
/**
* Calculates columns widths.
*/
private function calculateColumnsWidth(iterable $groups)
private function calculateColumnsWidth(iterable $groups): void
{
for ($column = 0; $column < $this->numberOfColumns; ++$column) {
$lengths = [];
@@ -804,7 +808,7 @@ class Table
$textContent = Helper::removeDecoration($this->output->getFormatter(), $cell);
$textLength = Helper::width($textContent);
if ($textLength > 0) {
$contentColumns = str_split($textContent, ceil($textLength / $cell->getColspan()));
$contentColumns = mb_str_split($textContent, ceil($textLength / $cell->getColspan()));
foreach ($contentColumns as $position => $content) {
$row[$i + $position] = $content;
}
@@ -843,7 +847,7 @@ class Table
/**
* Called after rendering to cleanup cache data.
*/
private function cleanup()
private function cleanup(): void
{
$this->effectiveColumnWidths = [];
unset($this->numberOfColumns);

View File

@@ -67,9 +67,7 @@ class TableCellStyle
{
return array_filter(
$this->getOptions(),
function ($key) {
return \in_array($key, self::TAG_OPTIONS) && isset($this->options[$key]);
},
fn ($key) => \in_array($key, self::TAG_OPTIONS) && isset($this->options[$key]),
\ARRAY_FILTER_USE_KEY
);
}

View File

@@ -55,11 +55,17 @@ class ArgvInput extends Input
parent::__construct($definition);
}
/**
* @return void
*/
protected function setTokens(array $tokens)
{
$this->tokens = $tokens;
}
/**
* @return void
*/
protected function parse()
{
$parseOptions = true;
@@ -89,7 +95,7 @@ class ArgvInput extends Input
/**
* Parses a short option.
*/
private function parseShortOption(string $token)
private function parseShortOption(string $token): void
{
$name = substr($token, 1);
@@ -110,7 +116,7 @@ class ArgvInput extends Input
*
* @throws RuntimeException When option given doesn't exist
*/
private function parseShortOptionSet(string $name)
private function parseShortOptionSet(string $name): void
{
$len = \strlen($name);
for ($i = 0; $i < $len; ++$i) {
@@ -133,7 +139,7 @@ class ArgvInput extends Input
/**
* Parses a long option.
*/
private function parseLongOption(string $token)
private function parseLongOption(string $token): void
{
$name = substr($token, 2);
@@ -152,7 +158,7 @@ class ArgvInput extends Input
*
* @throws RuntimeException When too many arguments are given
*/
private function parseArgument(string $token)
private function parseArgument(string $token): void
{
$c = \count($this->arguments);
@@ -196,7 +202,7 @@ class ArgvInput extends Input
*
* @throws RuntimeException When option given doesn't exist
*/
private function addShortOption(string $shortcut, mixed $value)
private function addShortOption(string $shortcut, mixed $value): void
{
if (!$this->definition->hasShortcut($shortcut)) {
throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
@@ -210,7 +216,7 @@ class ArgvInput extends Input
*
* @throws RuntimeException When option given doesn't exist
*/
private function addLongOption(string $name, mixed $value)
private function addLongOption(string $name, mixed $value): void
{
if (!$this->definition->hasOption($name)) {
if (!$this->definition->hasNegation($name)) {

View File

@@ -113,6 +113,9 @@ class ArrayInput extends Input
return implode(' ', $params);
}
/**
* @return void
*/
protected function parse()
{
foreach ($this->parameters as $key => $value) {
@@ -134,7 +137,7 @@ class ArrayInput extends Input
*
* @throws InvalidOptionException When option given doesn't exist
*/
private function addShortOption(string $shortcut, mixed $value)
private function addShortOption(string $shortcut, mixed $value): void
{
if (!$this->definition->hasShortcut($shortcut)) {
throw new InvalidOptionException(sprintf('The "-%s" option does not exist.', $shortcut));
@@ -149,7 +152,7 @@ class ArrayInput extends Input
* @throws InvalidOptionException When option given doesn't exist
* @throws InvalidOptionException When a required value is missing
*/
private function addLongOption(string $name, mixed $value)
private function addLongOption(string $name, mixed $value): void
{
if (!$this->definition->hasOption($name)) {
if (!$this->definition->hasNegation($name)) {
@@ -182,7 +185,7 @@ class ArrayInput extends Input
*
* @throws InvalidArgumentException When argument given doesn't exist
*/
private function addArgument(string|int $name, mixed $value)
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));

View File

@@ -43,6 +43,9 @@ abstract class Input implements InputInterface, StreamableInputInterface
}
}
/**
* @return void
*/
public function bind(InputDefinition $definition)
{
$this->arguments = [];
@@ -54,17 +57,20 @@ abstract class Input implements InputInterface, StreamableInputInterface
/**
* Processes command line arguments.
*
* @return void
*/
abstract protected function parse();
/**
* @return void
*/
public function validate()
{
$definition = $this->definition;
$givenArguments = $this->arguments;
$missingArguments = array_filter(array_keys($definition->getArguments()), function ($argument) use ($definition, $givenArguments) {
return !\array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired();
});
$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)));
@@ -76,6 +82,9 @@ abstract class Input implements InputInterface, StreamableInputInterface
return $this->interactive;
}
/**
* @return void
*/
public function setInteractive(bool $interactive)
{
$this->interactive = $interactive;
@@ -95,6 +104,9 @@ abstract class Input implements InputInterface, StreamableInputInterface
return $this->arguments[$name] ?? $this->definition->getArgument($name)->getDefault();
}
/**
* @return void
*/
public function setArgument(string $name, mixed $value)
{
if (!$this->definition->hasArgument($name)) {
@@ -131,6 +143,9 @@ abstract class Input implements InputInterface, StreamableInputInterface
return \array_key_exists($name, $this->options) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
}
/**
* @return void
*/
public function setOption(string $name, mixed $value)
{
if ($this->definition->hasNegation($name)) {
@@ -157,11 +172,19 @@ abstract class Input implements InputInterface, StreamableInputInterface
return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token);
}
/**
* @param resource $stream
*
* @return void
*/
public function setStream($stream)
{
$this->stream = $stream;
}
/**
* @return resource
*/
public function getStream()
{
return $this->stream;

View File

@@ -37,7 +37,7 @@ class InputArgument
/**
* @param string $name The argument name
* @param int|null $mode The argument mode: self::REQUIRED or self::OPTIONAL
* @param int|null $mode The argument mode: a bit mask of self::REQUIRED, self::OPTIONAL and self::IS_ARRAY
* @param string $description A description text
* @param string|bool|int|float|array|null $default The default value (for self::OPTIONAL mode only)
* @param array|\Closure(CompletionInput,CompletionSuggestions):list<string|Suggestion> $suggestedValues The values used for input completion
@@ -91,6 +91,8 @@ class InputArgument
/**
* Sets the default value.
*
* @return void
*
* @throws LogicException When incorrect default value is given
*/
public function setDefault(string|bool|int|float|array $default = null)

View File

@@ -21,6 +21,8 @@ interface InputAwareInterface
{
/**
* Sets the Console Input.
*
* @return void
*/
public function setInput(InputInterface $input);
}

View File

@@ -46,6 +46,8 @@ class InputDefinition
/**
* Sets the definition of the input.
*
* @return void
*/
public function setDefinition(array $definition)
{
@@ -67,6 +69,8 @@ class InputDefinition
* Sets the InputArgument objects.
*
* @param InputArgument[] $arguments An array of InputArgument objects
*
* @return void
*/
public function setArguments(array $arguments = [])
{
@@ -81,6 +85,8 @@ class InputDefinition
* Adds an array of InputArgument objects.
*
* @param InputArgument[] $arguments An array of InputArgument objects
*
* @return void
*/
public function addArguments(?array $arguments = [])
{
@@ -92,6 +98,8 @@ class InputDefinition
}
/**
* @return void
*
* @throws LogicException When incorrect argument is given
*/
public function addArgument(InputArgument $argument)
@@ -190,6 +198,8 @@ class InputDefinition
* Sets the InputOption objects.
*
* @param InputOption[] $options An array of InputOption objects
*
* @return void
*/
public function setOptions(array $options = [])
{
@@ -203,6 +213,8 @@ class InputDefinition
* Adds an array of InputOption objects.
*
* @param InputOption[] $options An array of InputOption objects
*
* @return void
*/
public function addOptions(array $options = [])
{
@@ -212,6 +224,8 @@ class InputDefinition
}
/**
* @return void
*
* @throws LogicException When option given already exist
*/
public function addOption(InputOption $option)

View File

@@ -61,6 +61,8 @@ interface InputInterface
/**
* Binds the current Input instance with the given arguments and options.
*
* @return void
*
* @throws RuntimeException
*/
public function bind(InputDefinition $definition);
@@ -68,6 +70,8 @@ interface InputInterface
/**
* Validates the input.
*
* @return void
*
* @throws RuntimeException When not enough arguments are given
*/
public function validate();
@@ -91,6 +95,8 @@ interface InputInterface
/**
* Sets an argument value by name.
*
* @return void
*
* @throws InvalidArgumentException When argument given doesn't exist
*/
public function setArgument(string $name, mixed $value);
@@ -119,6 +125,8 @@ interface InputInterface
/**
* Sets an option value by name.
*
* @return void
*
* @throws InvalidArgumentException When option given doesn't exist
*/
public function setOption(string $name, mixed $value);
@@ -135,6 +143,8 @@ interface InputInterface
/**
* Sets the input interactivity.
*
* @return void
*/
public function setInteractive(bool $interactive);
}

View File

@@ -178,6 +178,9 @@ class InputOption
return self::VALUE_NEGATABLE === (self::VALUE_NEGATABLE & $this->mode);
}
/**
* @return void
*/
public function setDefault(string|bool|int|float|array $default = null)
{
if (1 > \func_num_args()) {

View File

@@ -25,6 +25,8 @@ interface StreamableInputInterface extends InputInterface
* This is mainly useful for testing purpose.
*
* @param resource $stream The input stream
*
* @return void
*/
public function setStream($stream);

View File

@@ -26,7 +26,7 @@ enum AnsiColorMode
case Ansi4;
/*
* 8-bit Ansi colors (240 differents colors + 16 duplicate color codes, ensuring backward compatibility).
* 8-bit Ansi colors (240 different colors + 16 duplicate color codes, ensuring backward compatibility).
* Output syntax is: "ESC[38;5;${foreGroundColorcode};48;5;${backGroundColorcode}m"
* Should be compatible with most terminals.
*/
@@ -78,25 +78,7 @@ enum AnsiColorMode
private function degradeHexColorToAnsi4(int $r, int $g, int $b): int
{
if (0 === round($this->getSaturation($r, $g, $b) / 50)) {
return 0;
}
return (int) ((round($b / 255) << 2) | (round($g / 255) << 1) | round($r / 255));
}
private function getSaturation(int $r, int $g, int $b): int
{
$r = $r / 255;
$g = $g / 255;
$b = $b / 255;
$v = max($r, $g, $b);
if (0 === $diff = $v - min($r, $g, $b)) {
return 0;
}
return (int) ((int) $diff * 100 / $v);
return round($b / 255) << 2 | (round($g / 255) << 1) | round($r / 255);
}
/**

View File

@@ -29,6 +29,9 @@ class BufferedOutput extends Output
return $content;
}
/**
* @return void
*/
protected function doWrite(string $message, bool $newline)
{
$this->buffer .= $message;

View File

@@ -64,18 +64,27 @@ class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
return new ConsoleSectionOutput($this->getStream(), $this->consoleSectionOutputs, $this->getVerbosity(), $this->isDecorated(), $this->getFormatter());
}
/**
* @return void
*/
public function setDecorated(bool $decorated)
{
parent::setDecorated($decorated);
$this->stderr->setDecorated($decorated);
}
/**
* @return void
*/
public function setFormatter(OutputFormatterInterface $formatter)
{
parent::setFormatter($formatter);
$this->stderr->setFormatter($formatter);
}
/**
* @return void
*/
public function setVerbosity(int $level)
{
parent::setVerbosity($level);
@@ -87,6 +96,9 @@ class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
return $this->stderr;
}
/**
* @return void
*/
public function setErrorOutput(OutputInterface $error)
{
$this->stderr = $error;

View File

@@ -24,6 +24,9 @@ interface ConsoleOutputInterface extends OutputInterface
*/
public function getErrorOutput(): OutputInterface;
/**
* @return void
*/
public function setErrorOutput(OutputInterface $error);
public function section(): ConsoleSectionOutput;

View File

@@ -60,6 +60,8 @@ class ConsoleSectionOutput extends StreamOutput
* Clears previous output for this section.
*
* @param int $lines Number of lines to clear. If null, then the entire output of this section is cleared
*
* @return void
*/
public function clear(int $lines = null)
{
@@ -81,6 +83,8 @@ class ConsoleSectionOutput extends StreamOutput
/**
* Overwrites the previous output with a new message.
*
* @return void
*/
public function overwrite(string|iterable $message)
{
@@ -153,12 +157,15 @@ class ConsoleSectionOutput extends StreamOutput
/**
* @internal
*/
public function addNewLineOfInputSubmit()
public function addNewLineOfInputSubmit(): void
{
$this->content[] = \PHP_EOL;
++$this->lines;
}
/**
* @return void
*/
protected function doWrite(string $message, bool $newline)
{
if (!$this->isDecorated()) {

View File

@@ -26,6 +26,9 @@ class NullOutput implements OutputInterface
{
private NullOutputFormatter $formatter;
/**
* @return void
*/
public function setFormatter(OutputFormatterInterface $formatter)
{
// do nothing
@@ -37,6 +40,9 @@ class NullOutput implements OutputInterface
return $this->formatter ??= new NullOutputFormatter();
}
/**
* @return void
*/
public function setDecorated(bool $decorated)
{
// do nothing
@@ -47,6 +53,9 @@ class NullOutput implements OutputInterface
return false;
}
/**
* @return void
*/
public function setVerbosity(int $level)
{
// do nothing
@@ -77,11 +86,17 @@ class NullOutput implements OutputInterface
return false;
}
/**
* @return void
*/
public function writeln(string|iterable $messages, int $options = self::OUTPUT_NORMAL)
{
// do nothing
}
/**
* @return void
*/
public function write(string|iterable $messages, bool $newline = false, int $options = self::OUTPUT_NORMAL)
{
// do nothing

View File

@@ -44,6 +44,9 @@ abstract class Output implements OutputInterface
$this->formatter->setDecorated($decorated);
}
/**
* @return void
*/
public function setFormatter(OutputFormatterInterface $formatter)
{
$this->formatter = $formatter;
@@ -54,6 +57,9 @@ abstract class Output implements OutputInterface
return $this->formatter;
}
/**
* @return void
*/
public function setDecorated(bool $decorated)
{
$this->formatter->setDecorated($decorated);
@@ -64,6 +70,9 @@ abstract class Output implements OutputInterface
return $this->formatter->isDecorated();
}
/**
* @return void
*/
public function setVerbosity(int $level)
{
$this->verbosity = $level;
@@ -94,11 +103,17 @@ abstract class Output implements OutputInterface
return self::VERBOSITY_DEBUG <= $this->verbosity;
}
/**
* @return void
*/
public function writeln(string|iterable $messages, int $options = self::OUTPUT_NORMAL)
{
$this->write($messages, true, $options);
}
/**
* @return void
*/
public function write(string|iterable $messages, bool $newline = false, int $options = self::OUTPUT_NORMAL)
{
if (!is_iterable($messages)) {
@@ -133,6 +148,8 @@ abstract class Output implements OutputInterface
/**
* Writes a message to the output.
*
* @return void
*/
abstract protected function doWrite(string $message, bool $newline);
}

View File

@@ -36,6 +36,8 @@ interface OutputInterface
* @param bool $newline Whether to add a newline
* @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants),
* 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
*
* @return void
*/
public function write(string|iterable $messages, bool $newline = false, int $options = 0);
@@ -44,11 +46,15 @@ interface OutputInterface
*
* @param int $options A bitmask of options (one of the OUTPUT or VERBOSITY constants),
* 0 is considered the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
*
* @return void
*/
public function writeln(string|iterable $messages, int $options = 0);
/**
* Sets the verbosity of the output.
*
* @return void
*/
public function setVerbosity(int $level);
@@ -79,6 +85,8 @@ interface OutputInterface
/**
* Sets the decorated flag.
*
* @return void
*/
public function setDecorated(bool $decorated);
@@ -87,6 +95,9 @@ interface OutputInterface
*/
public function isDecorated(): bool;
/**
* @return void
*/
public function setFormatter(OutputFormatterInterface $formatter);
/**

View File

@@ -62,6 +62,9 @@ class StreamOutput extends Output
return $this->stream;
}
/**
* @return void
*/
protected function doWrite(string $message, bool $newline)
{
if ($newline) {

View File

@@ -45,6 +45,9 @@ class TrimmedBufferOutput extends Output
return $content;
}
/**
* @return void
*/
protected function doWrite(string $message, bool $newline)
{
$this->buffer .= $message;

View File

@@ -146,12 +146,11 @@ class Question
if (\is_array($values)) {
$values = $this->isAssoc($values) ? array_merge(array_keys($values), array_values($values)) : array_values($values);
$callback = static function () use ($values) {
return $values;
};
$callback = static fn () => $values;
} elseif ($values instanceof \Traversable) {
$valueCache = null;
$callback = static function () use ($values, &$valueCache) {
$callback = static function () use ($values) {
static $valueCache;
return $valueCache ??= iterator_to_array($values, false);
};
} else {
@@ -267,6 +266,9 @@ class Question
return $this->normalizer;
}
/**
* @return bool
*/
protected function isAssoc(array $array)
{
return (bool) \count(array_filter(array_keys($array), 'is_string'));

View File

@@ -4,6 +4,18 @@ Console Component
The Console component eases the creation of beautiful and testable command line
interfaces.
Sponsor
-------
The Console component for Symfony 6.3 is [backed][1] by [Les-Tilleuls.coop][2].
Les-Tilleuls.coop is a team of 70+ Symfony experts who can help you design, develop and
fix your projects. They provide a wide range of professional services including development,
consulting, coaching, training and audits. They also are highly skilled in JS, Go and DevOps.
They are a worker cooperative!
Help Symfony by [sponsoring][3] its development!
Resources
---------
@@ -18,3 +30,7 @@ Credits
`Resources/bin/hiddeninput.exe` is a third party binary provided within this
component. Find sources and license at https://github.com/Seldaek/hidden-input.
[1]: https://symfony.com/backers
[2]: https://les-tilleuls.coop
[3]: https://symfony.com/sponsor

View File

@@ -6,6 +6,16 @@
# https://symfony.com/doc/current/contributing/code/license.html
_sf_{{ COMMAND_NAME }}() {
# Use the default completion for shell redirect operators.
for w in '>' '>>' '&>' '<'; do
if [[ $w = "${COMP_WORDS[COMP_CWORD-1]}" ]]; then
compopt -o filenames
COMPREPLY=($(compgen -f -- "${COMP_WORDS[COMP_CWORD]}"))
return 0
fi
done
# Use newline as only separator to allow space in completion values
IFS=$'\n'
local sf_cmd="${COMP_WORDS[0]}"

View File

@@ -1,3 +1,5 @@
#compdef {{ COMMAND_NAME }}
# This file is part of the Symfony package.
#
# (c) Fabien Potencier <fabien@symfony.com>

View File

@@ -30,6 +30,9 @@ abstract class OutputStyle implements OutputInterface, StyleInterface
$this->output = $output;
}
/**
* @return void
*/
public function newLine(int $count = 1)
{
$this->output->write(str_repeat(\PHP_EOL, $count));
@@ -40,16 +43,25 @@ abstract class OutputStyle implements OutputInterface, StyleInterface
return new ProgressBar($this->output, $max);
}
/**
* @return void
*/
public function write(string|iterable $messages, bool $newline = false, int $type = self::OUTPUT_NORMAL)
{
$this->output->write($messages, $newline, $type);
}
/**
* @return void
*/
public function writeln(string|iterable $messages, int $type = self::OUTPUT_NORMAL)
{
$this->output->writeln($messages, $type);
}
/**
* @return void
*/
public function setVerbosity(int $level)
{
$this->output->setVerbosity($level);
@@ -60,6 +72,9 @@ abstract class OutputStyle implements OutputInterface, StyleInterface
return $this->output->getVerbosity();
}
/**
* @return void
*/
public function setDecorated(bool $decorated)
{
$this->output->setDecorated($decorated);
@@ -70,6 +85,9 @@ abstract class OutputStyle implements OutputInterface, StyleInterface
return $this->output->isDecorated();
}
/**
* @return void
*/
public function setFormatter(OutputFormatterInterface $formatter)
{
$this->output->setFormatter($formatter);
@@ -100,6 +118,9 @@ abstract class OutputStyle implements OutputInterface, StyleInterface
return $this->output->isDebug();
}
/**
* @return OutputInterface
*/
protected function getErrorOutput()
{
if (!$this->output instanceof ConsoleOutputInterface) {

View File

@@ -20,51 +20,71 @@ interface StyleInterface
{
/**
* Formats a command title.
*
* @return void
*/
public function title(string $message);
/**
* Formats a section title.
*
* @return void
*/
public function section(string $message);
/**
* Formats a list.
*
* @return void
*/
public function listing(array $elements);
/**
* Formats informational text.
*
* @return void
*/
public function text(string|array $message);
/**
* Formats a success result bar.
*
* @return void
*/
public function success(string|array $message);
/**
* Formats an error result bar.
*
* @return void
*/
public function error(string|array $message);
/**
* Formats an warning result bar.
*
* @return void
*/
public function warning(string|array $message);
/**
* Formats a note admonition.
*
* @return void
*/
public function note(string|array $message);
/**
* Formats a caution admonition.
*
* @return void
*/
public function caution(string|array $message);
/**
* Formats a table.
*
* @return void
*/
public function table(array $headers, array $rows);
@@ -90,21 +110,29 @@ interface StyleInterface
/**
* Add newline(s).
*
* @return void
*/
public function newLine(int $count = 1);
/**
* Starts the progress output.
*
* @return void
*/
public function progressStart(int $max = 0);
/**
* Advances the progress output X steps.
*
* @return void
*/
public function progressAdvance(int $step = 1);
/**
* Finishes the progress output.
*
* @return void
*/
public function progressFinish();
}

View File

@@ -60,6 +60,8 @@ class SymfonyStyle extends OutputStyle
/**
* Formats a message as a block of text.
*
* @return void
*/
public function block(string|array $messages, string $type = null, string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = true)
{
@@ -70,6 +72,9 @@ class SymfonyStyle extends OutputStyle
$this->newLine();
}
/**
* @return void
*/
public function title(string $message)
{
$this->autoPrependBlock();
@@ -80,6 +85,9 @@ class SymfonyStyle extends OutputStyle
$this->newLine();
}
/**
* @return void
*/
public function section(string $message)
{
$this->autoPrependBlock();
@@ -90,17 +98,21 @@ class SymfonyStyle extends OutputStyle
$this->newLine();
}
/**
* @return void
*/
public function listing(array $elements)
{
$this->autoPrependText();
$elements = array_map(function ($element) {
return sprintf(' * %s', $element);
}, $elements);
$elements = array_map(fn ($element) => sprintf(' * %s', $element), $elements);
$this->writeln($elements);
$this->newLine();
}
/**
* @return void
*/
public function text(string|array $message)
{
$this->autoPrependText();
@@ -113,27 +125,41 @@ class SymfonyStyle extends OutputStyle
/**
* Formats a command comment.
*
* @return void
*/
public function comment(string|array $message)
{
$this->block($message, null, null, '<fg=default;bg=default> // </>', false, false);
}
/**
* @return void
*/
public function success(string|array $message)
{
$this->block($message, 'OK', 'fg=black;bg=green', ' ', true);
}
/**
* @return void
*/
public function error(string|array $message)
{
$this->block($message, 'ERROR', 'fg=white;bg=red', ' ', true);
}
/**
* @return void
*/
public function warning(string|array $message)
{
$this->block($message, 'WARNING', 'fg=black;bg=yellow', ' ', true);
}
/**
* @return void
*/
public function note(string|array $message)
{
$this->block($message, 'NOTE', 'fg=yellow', ' ! ');
@@ -141,17 +167,25 @@ class SymfonyStyle extends OutputStyle
/**
* Formats an info message.
*
* @return void
*/
public function info(string|array $message)
{
$this->block($message, 'INFO', 'fg=green', ' ', true);
}
/**
* @return void
*/
public function caution(string|array $message)
{
$this->block($message, 'CAUTION', 'fg=white;bg=red', ' ! ', true);
}
/**
* @return void
*/
public function table(array $headers, array $rows)
{
$this->createTable()
@@ -165,6 +199,8 @@ class SymfonyStyle extends OutputStyle
/**
* Formats a horizontal table.
*
* @return void
*/
public function horizontalTable(array $headers, array $rows)
{
@@ -185,6 +221,8 @@ class SymfonyStyle extends OutputStyle
* * 'A title'
* * ['key' => 'value']
* * new TableSeparator()
*
* @return void
*/
public function definitionList(string|array|TableSeparator ...$list)
{
@@ -247,17 +285,26 @@ class SymfonyStyle extends OutputStyle
return $this->askQuestion($questionChoice);
}
/**
* @return void
*/
public function progressStart(int $max = 0)
{
$this->progressBar = $this->createProgressBar($max);
$this->progressBar->start();
}
/**
* @return void
*/
public function progressAdvance(int $step = 1)
{
$this->getProgressBar()->advance($step);
}
/**
* @return void
*/
public function progressFinish()
{
$this->getProgressBar()->finish();
@@ -311,6 +358,9 @@ class SymfonyStyle extends OutputStyle
return $answer;
}
/**
* @return void
*/
public function writeln(string|iterable $messages, int $type = self::OUTPUT_NORMAL)
{
if (!is_iterable($messages)) {
@@ -323,6 +373,9 @@ class SymfonyStyle extends OutputStyle
}
}
/**
* @return void
*/
public function write(string|iterable $messages, bool $newline = false, int $type = self::OUTPUT_NORMAL)
{
if (!is_iterable($messages)) {
@@ -335,6 +388,9 @@ class SymfonyStyle extends OutputStyle
}
}
/**
* @return void
*/
public function newLine(int $count = 1)
{
parent::newLine($count);
@@ -381,7 +437,7 @@ class SymfonyStyle extends OutputStyle
{
$fetched = $this->bufferedOutput->fetch();
// Prepend new line if last char isn't EOL:
if (!str_ends_with($fetched, "\n")) {
if ($fetched && !str_ends_with($fetched, "\n")) {
$this->newLine();
}
}

View File

@@ -123,20 +123,19 @@ class Terminal
return self::$stty;
}
// skip check if exec function is disabled
if (!\function_exists('exec')) {
// skip check if shell_exec function is disabled
if (!\function_exists('shell_exec')) {
return false;
}
exec('stty 2>&1', $output, $exitcode);
return self::$stty = 0 === $exitcode;
return self::$stty = (bool) shell_exec('stty 2> '.('\\' === \DIRECTORY_SEPARATOR ? 'NUL' : '/dev/null'));
}
private static function initDimensions()
private static function initDimensions(): void
{
if ('\\' === \DIRECTORY_SEPARATOR) {
if (preg_match('/^(\d+)x(\d+)(?: \((\d+)x(\d+)\))?$/', trim(getenv('ANSICON')), $matches)) {
$ansicon = getenv('ANSICON');
if (false !== $ansicon && preg_match('/^(\d+)x(\d+)(?: \((\d+)x(\d+)\))?$/', trim($ansicon), $matches)) {
// extract [w, H] from "wxh (WxH)"
// or [w, h] from "wxh"
self::$width = (int) $matches[1];
@@ -166,7 +165,7 @@ class Terminal
/**
* Initializes dimensions using the output of an stty columns line.
*/
private static function initDimensionsUsingStty()
private static function initDimensionsUsingStty(): void
{
if ($sttyString = self::getSttyColumns()) {
if (preg_match('/rows.(\d+);.columns.(\d+);/is', $sttyString, $matches)) {
@@ -216,6 +215,8 @@ class Terminal
2 => ['pipe', 'w'],
];
$cp = \function_exists('sapi_windows_cp_set') ? sapi_windows_cp_get() : 0;
$process = proc_open($command, $descriptorspec, $pipes, null, null, ['suppress_errors' => true]);
if (!\is_resource($process)) {
return null;
@@ -226,6 +227,10 @@ class Terminal
fclose($pipes[2]);
proc_close($process);
if ($cp) {
sapi_windows_cp_set($cp);
}
return $info;
}
}

View File

@@ -128,7 +128,7 @@ trait TesterTrait
* * verbosity: Sets the output verbosity flag
* * capture_stderr_separately: Make output of stdOut and stdErr separately available
*/
private function initOutput(array $options)
private function initOutput(array $options): void
{
$this->captureStreamsIndependently = \array_key_exists('capture_stderr_separately', $options) && $options['capture_stderr_separately'];
if (!$this->captureStreamsIndependently) {

View File

@@ -2,7 +2,7 @@
"name": "symfony/console",
"type": "library",
"description": "Eases the creation of beautiful and testable command line interfaces",
"keywords": ["console", "cli", "command line", "terminal"],
"keywords": ["console", "cli", "command-line", "terminal"],
"homepage": "https://symfony.com",
"license": "MIT",
"authors": [
@@ -17,9 +17,9 @@
],
"require": {
"php": ">=8.1",
"symfony/deprecation-contracts": "^2.1|^3",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-mbstring": "~1.0",
"symfony/service-contracts": "^1.1|^2|^3",
"symfony/service-contracts": "^2.5|^3",
"symfony/string": "^5.4|^6.0"
},
"require-dev": {
@@ -34,12 +34,6 @@
"provide": {
"psr/log-implementation": "1.0|2.0|3.0"
},
"suggest": {
"symfony/event-dispatcher": "",
"symfony/lock": "",
"symfony/process": "",
"psr/log": "For using the console logger"
},
"conflict": {
"symfony/dependency-injection": "<5.4",
"symfony/dotenv": "<5.4",