diff --git a/Application.php b/Application.php index dc710e8cc..113778b43 100644 --- a/Application.php +++ b/Application.php @@ -75,8 +75,6 @@ class Application implements ResetInterface private array $commands = []; private bool $wantHelps = false; private ?Command $runningCommand = null; - private string $name; - private string $version; private ?CommandLoaderInterface $commandLoader = null; private bool $catchExceptions = true; private bool $catchErrors = false; @@ -91,15 +89,15 @@ class Application implements ResetInterface private ?SignalRegistry $signalRegistry = null; private array $signalsToDispatchEvent = []; - public function __construct(string $name = 'UNKNOWN', string $version = 'UNKNOWN') - { - $this->name = $name; - $this->version = $version; + public function __construct( + private string $name = 'UNKNOWN', + private string $version = 'UNKNOWN', + ) { $this->terminal = new Terminal(); $this->defaultCommand = 'list'; if (\defined('SIGINT') && SignalRegistry::isSupported()) { $this->signalRegistry = new SignalRegistry(); - $this->signalsToDispatchEvent = [\SIGINT, \SIGTERM, \SIGUSR1, \SIGUSR2]; + $this->signalsToDispatchEvent = [\SIGINT, \SIGQUIT, \SIGTERM, \SIGUSR1, \SIGUSR2]; } } @@ -111,10 +109,7 @@ public function setDispatcher(EventDispatcherInterface $dispatcher): void $this->dispatcher = $dispatcher; } - /** - * @return void - */ - public function setCommandLoader(CommandLoaderInterface $commandLoader) + public function setCommandLoader(CommandLoaderInterface $commandLoader): void { $this->commandLoader = $commandLoader; } @@ -128,10 +123,7 @@ public function getSignalRegistry(): SignalRegistry return $this->signalRegistry; } - /** - * @return void - */ - public function setSignalsToDispatchEvent(int ...$signalsToDispatchEvent) + public function setSignalsToDispatchEvent(int ...$signalsToDispatchEvent): void { $this->signalsToDispatchEvent = $signalsToDispatchEvent; } @@ -224,7 +216,7 @@ public function run(?InputInterface $input = null, ?OutputInterface $output = nu * * @return int 0 if everything went fine, or an error code */ - public function doRun(InputInterface $input, OutputInterface $output) + public function doRun(InputInterface $input, OutputInterface $output): int { if (true === $input->hasParameterOption(['--version', '-V'], true)) { $output->writeln($this->getLongVersion()); @@ -327,17 +319,11 @@ public function doRun(InputInterface $input, OutputInterface $output) return $exitCode; } - /** - * @return void - */ - public function reset() + public function reset(): void { } - /** - * @return void - */ - public function setHelperSet(HelperSet $helperSet) + public function setHelperSet(HelperSet $helperSet): void { $this->helperSet = $helperSet; } @@ -350,10 +336,7 @@ public function getHelperSet(): HelperSet return $this->helperSet ??= $this->getDefaultHelperSet(); } - /** - * @return void - */ - public function setDefinition(InputDefinition $definition) + public function setDefinition(InputDefinition $definition): void { $this->definition = $definition; } @@ -423,10 +406,8 @@ public function areExceptionsCaught(): bool /** * Sets whether to catch exceptions or not during commands execution. - * - * @return void */ - public function setCatchExceptions(bool $boolean) + public function setCatchExceptions(bool $boolean): void { $this->catchExceptions = $boolean; } @@ -449,10 +430,8 @@ public function isAutoExitEnabled(): bool /** * Sets whether to automatically exit after a command execution or not. - * - * @return void */ - public function setAutoExit(bool $boolean) + public function setAutoExit(bool $boolean): void { $this->autoExit = $boolean; } @@ -467,10 +446,8 @@ public function getName(): string /** * Sets the application name. - * - * @return void */ - public function setName(string $name) + public function setName(string $name): void { $this->name = $name; } @@ -485,20 +462,16 @@ public function getVersion(): string /** * Sets the application version. - * - * @return void */ - public function setVersion(string $version) + public function setVersion(string $version): void { $this->version = $version; } /** * Returns the long version of the application. - * - * @return string */ - public function getLongVersion() + public function getLongVersion(): string { if ('UNKNOWN' !== $this->getName()) { if ('UNKNOWN' !== $this->getVersion()) { @@ -525,10 +498,8 @@ public function register(string $name): Command * 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) + public function addCommands(array $commands): void { foreach ($commands as $command) { $this->add($command); @@ -540,10 +511,8 @@ public function addCommands(array $commands) * * If a command with the same name already exists, it will be overridden. * If the command is not enabled it will not be added. - * - * @return Command|null */ - public function add(Command $command) + public function add(Command $command): ?Command { $this->init(); @@ -576,11 +545,9 @@ public function add(Command $command) /** * Returns a registered command by name or alias. * - * @return Command - * * @throws CommandNotFoundException When given command name does not exist */ - public function get(string $name) + public function get(string $name): Command { $this->init(); @@ -653,7 +620,7 @@ public function findNamespace(string $namespace): string $expr = implode('[^:]*:', array_map('preg_quote', explode(':', $namespace))).'[^:]*'; $namespaces = preg_grep('{^'.$expr.'}', $allNamespaces); - if (empty($namespaces)) { + if (!$namespaces) { $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace); if ($alternatives = $this->findAlternatives($namespace, $allNamespaces)) { @@ -683,11 +650,9 @@ public function findNamespace(string $namespace): string * Contrary to get, this command tries to find the best * match if you give it an abbreviation of a name or alias. * - * @return Command - * * @throws CommandNotFoundException When command name is incorrect or ambiguous */ - public function find(string $name) + public function find(string $name): Command { $this->init(); @@ -709,12 +674,12 @@ public function find(string $name) $expr = implode('[^:]*:', array_map('preg_quote', explode(':', $name))).'[^:]*'; $commands = preg_grep('{^'.$expr.'}', $allCommands); - if (empty($commands)) { + if (!$commands) { $commands = preg_grep('{^'.$expr.'}i', $allCommands); } // if no commands matched or we just matched namespaces - if (empty($commands) || \count(preg_grep('{^'.$expr.'$}i', $commands)) < 1) { + if (!$commands || \count(preg_grep('{^'.$expr.'$}i', $commands)) < 1) { if (false !== $pos = strrpos($name, ':')) { // check if a namespace exists and contains commands $this->findNamespace(substr($name, 0, $pos)); @@ -749,7 +714,7 @@ public function find(string $name) $aliases[$nameOrAlias] = $commandName; - return $commandName === $nameOrAlias || !\in_array($commandName, $commands); + return $commandName === $nameOrAlias || !\in_array($commandName, $commands, true); })); } @@ -795,7 +760,7 @@ public function find(string $name) * * @return Command[] */ - public function all(?string $namespace = null) + public function all(?string $namespace = null): array { $this->init(); @@ -936,10 +901,8 @@ protected function doRenderThrowable(\Throwable $e, OutputInterface $output): vo /** * Configures the input and output instances based on the user arguments and options. - * - * @return void */ - protected function configureIO(InputInterface $input, OutputInterface $output) + protected function configureIO(InputInterface $input, OutputInterface $output): void { if (true === $input->hasParameterOption(['--ansi'], true)) { $output->setDecorated(true); @@ -1004,7 +967,7 @@ protected function configureIO(InputInterface $input, OutputInterface $output) * * @return int 0 if everything went fine, or an error code */ - protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output) + protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output): int { foreach ($command->getHelperSet() as $helper) { if ($helper instanceof InputAwareInterface) { @@ -1021,7 +984,7 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI if (Terminal::hasSttyAvailable()) { $sttyMode = shell_exec('stty -g'); - foreach ([\SIGINT, \SIGTERM] as $signal) { + foreach ([\SIGINT, \SIGQUIT, \SIGTERM] as $signal) { $this->signalRegistry->register($signal, static fn () => shell_exec('stty '.$sttyMode)); } } @@ -1038,11 +1001,6 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI // 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) { @@ -1060,14 +1018,7 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI foreach ($commandSignals as $signal) { $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) { + if (false !== $exitCode = $command->handleSignal($signal)) { exit($exitCode); } }); diff --git a/Attribute/AsCommand.php b/Attribute/AsCommand.php index b337f548f..6066d7c53 100644 --- a/Attribute/AsCommand.php +++ b/Attribute/AsCommand.php @@ -17,6 +17,12 @@ #[\Attribute(\Attribute::TARGET_CLASS)] class AsCommand { + /** + * @param string $name The name of the command, used when calling it (i.e. "cache:clear") + * @param string|null $description The description of the command, displayed with the help page + * @param string[] $aliases The list of aliases of the command. The command will be executed when using one of them (i.e. "cache:clean") + * @param bool $hidden If true, the command won't be shown when listing all the available commands, but it can still be run as any other command + */ public function __construct( public string $name, public ?string $description = null, diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ccb41d94..25d7f7179 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,19 @@ CHANGELOG ========= +7.1 +--- + + * Add `ArgvInput::getRawTokens()` + +7.0 +--- + + * Add method `__toString()` to `InputInterface` + * Remove `Command::$defaultName` and `Command::$defaultDescription`, use the `AsCommand` attribute instead + * Require explicit argument when calling `*Command::setApplication()`, `*FormatterStyle::setForeground/setBackground()`, `Helper::setHelpSet()`, `Input*::setDefault()` and `Question::setAutocompleterCallback/setValidator()` + * Remove `StringInput::REGEX_STRING` + 6.4 --- diff --git a/Command/Command.php b/Command/Command.php index 9f9cb2f53..03da6db43 100644 --- a/Command/Command.php +++ b/Command/Command.php @@ -39,20 +39,6 @@ class Command public const FAILURE = 1; public const INVALID = 2; - /** - * @var string|null The default command name - * - * @deprecated since Symfony 6.1, use the AsCommand attribute instead - */ - protected static $defaultName; - - /** - * @var string|null The default command description - * - * @deprecated since Symfony 6.1, use the AsCommand attribute instead - */ - protected static $defaultDescription; - private ?Application $application = null; private ?string $name = null; private ?string $processTitle = null; @@ -70,40 +56,20 @@ class Command public static function getDefaultName(): ?string { - $class = static::class; - - if ($attribute = (new \ReflectionClass($class))->getAttributes(AsCommand::class)) { + if ($attribute = (new \ReflectionClass(static::class))->getAttributes(AsCommand::class)) { return $attribute[0]->newInstance()->name; } - $r = new \ReflectionProperty($class, 'defaultName'); - - if ($class !== $r->class || null === static::$defaultName) { - return null; - } - - trigger_deprecation('symfony/console', '6.1', 'Relying on the static property "$defaultName" for setting a command name is deprecated. Add the "%s" attribute to the "%s" class instead.', AsCommand::class, static::class); - - return static::$defaultName; + return null; } public static function getDefaultDescription(): ?string { - $class = static::class; - - if ($attribute = (new \ReflectionClass($class))->getAttributes(AsCommand::class)) { + if ($attribute = (new \ReflectionClass(static::class))->getAttributes(AsCommand::class)) { return $attribute[0]->newInstance()->description; } - $r = new \ReflectionProperty($class, 'defaultDescription'); - - if ($class !== $r->class || null === static::$defaultDescription) { - return null; - } - - trigger_deprecation('symfony/console', '6.1', 'Relying on the static property "$defaultDescription" for setting a command description is deprecated. Add the "%s" attribute to the "%s" class instead.', AsCommand::class, static::class); - - return static::$defaultDescription; + return null; } /** @@ -141,22 +107,14 @@ public function __construct(?string $name = null) * Ignores validation errors. * * This is mainly useful for the help command. - * - * @return void */ - public function ignoreValidationErrors() + public function ignoreValidationErrors(): void { $this->ignoreValidationErrors = true; } - /** - * @return void - */ - public function setApplication(?Application $application = null) + public function setApplication(?Application $application): void { - if (1 > \func_num_args()) { - trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); - } $this->application = $application; if ($application) { $this->setHelperSet($application->getHelperSet()); @@ -167,10 +125,7 @@ public function setApplication(?Application $application = null) $this->fullDefinition = null; } - /** - * @return void - */ - public function setHelperSet(HelperSet $helperSet) + public function setHelperSet(HelperSet $helperSet): void { $this->helperSet = $helperSet; } @@ -196,10 +151,8 @@ public function getApplication(): ?Application * * Override this to check for x or y and return false if the command cannot * run properly under the current conditions. - * - * @return bool */ - public function isEnabled() + public function isEnabled(): bool { return true; } @@ -227,7 +180,7 @@ protected function configure() * * @see setCode() */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { throw new LogicException('You must override the execute() method in the concrete command class.'); } @@ -324,17 +277,13 @@ public function run(InputInterface $input, OutputInterface $output): int $statusCode = ($this->code)($input, $output); } else { $statusCode = $this->execute($input, $output); - - if (!\is_int($statusCode)) { - throw new \TypeError(sprintf('Return value of "%s::execute()" must be of the type int, "%s" returned.', static::class, get_debug_type($statusCode))); - } } return is_numeric($statusCode) ? (int) $statusCode : 0; } /** - * Adds suggestions to $suggestions for the current completion input (e.g. option or argument). + * Supplies suggestions when resolving possible completion options for input (e.g. option or argument). */ public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { @@ -452,20 +401,16 @@ public function getNativeDefinition(): InputDefinition /** * Adds an argument. * - * @param $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL - * @param $default The default value (for InputArgument::OPTIONAL mode only) + * @param $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL + * @param $default The default value (for InputArgument::OPTIONAL mode only) * @param array|\Closure(CompletionInput,CompletionSuggestions):list $suggestedValues The values used for input completion * * @return $this * * @throws InvalidArgumentException When argument mode is not valid */ - public function addArgument(string $name, ?int $mode = null, string $description = '', mixed $default = null /* array|\Closure $suggestedValues = null */): static + public function addArgument(string $name, ?int $mode = null, string $description = '', mixed $default = null, array|\Closure $suggestedValues = []): static { - $suggestedValues = 5 <= \func_num_args() ? func_get_arg(4) : []; - if (!\is_array($suggestedValues) && !$suggestedValues instanceof \Closure) { - throw new \TypeError(sprintf('Argument 5 passed to "%s()" must be array or \Closure, "%s" given.', __METHOD__, get_debug_type($suggestedValues))); - } $this->definition->addArgument(new InputArgument($name, $mode, $description, $default, $suggestedValues)); $this->fullDefinition?->addArgument(new InputArgument($name, $mode, $description, $default, $suggestedValues)); @@ -475,21 +420,17 @@ public function addArgument(string $name, ?int $mode = null, string $description /** * Adds an option. * - * @param $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts - * @param $mode The option mode: One of the InputOption::VALUE_* constants - * @param $default The default value (must be null for InputOption::VALUE_NONE) + * @param $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts + * @param $mode The option mode: One of the InputOption::VALUE_* constants + * @param $default The default value (must be null for InputOption::VALUE_NONE) * @param array|\Closure(CompletionInput,CompletionSuggestions):list $suggestedValues The values used for input completion * * @return $this * * @throws InvalidArgumentException If option mode is invalid or incompatible */ - public function addOption(string $name, string|array|null $shortcut = null, ?int $mode = null, string $description = '', mixed $default = null /* array|\Closure $suggestedValues = [] */): static + public function addOption(string $name, string|array|null $shortcut = null, ?int $mode = null, string $description = '', mixed $default = null, array|\Closure $suggestedValues = []): static { - $suggestedValues = 6 <= \func_num_args() ? func_get_arg(5) : []; - if (!\is_array($suggestedValues) && !$suggestedValues instanceof \Closure) { - throw new \TypeError(sprintf('Argument 5 passed to "%s()" must be array or \Closure, "%s" given.', __METHOD__, get_debug_type($suggestedValues))); - } $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default, $suggestedValues)); $this->fullDefinition?->addOption(new InputOption($name, $shortcut, $mode, $description, $default, $suggestedValues)); @@ -695,12 +636,10 @@ public function getUsages(): array /** * Gets a helper instance by name. * - * @return HelperInterface - * * @throws LogicException if no HelperSet is defined * @throws InvalidArgumentException if the helper is not defined */ - public function getHelper(string $name): mixed + public function getHelper(string $name): HelperInterface { if (null === $this->helperSet) { throw new LogicException(sprintf('Cannot retrieve helper "%s" because there is no HelperSet defined. Did you forget to add your command to the application or to set the application on the command using the setApplication() method? You can also set the HelperSet directly using the setHelperSet() method.', $name)); diff --git a/Command/CompleteCommand.php b/Command/CompleteCommand.php index 23be5577b..38aa737f0 100644 --- a/Command/CompleteCommand.php +++ b/Command/CompleteCommand.php @@ -34,18 +34,7 @@ final class CompleteCommand extends Command { public const COMPLETION_API_VERSION = '1'; - /** - * @deprecated since Symfony 6.1 - */ - protected static $defaultName = '|_complete'; - - /** - * @deprecated since Symfony 6.1 - */ - protected static $defaultDescription = 'Internal command to provide shell completion suggestions'; - private array $completionOutputs; - private bool $isDebug = false; /** diff --git a/Command/DumpCompletionCommand.php b/Command/DumpCompletionCommand.php index 51b613a14..be6f54592 100644 --- a/Command/DumpCompletionCommand.php +++ b/Command/DumpCompletionCommand.php @@ -27,16 +27,6 @@ #[AsCommand(name: 'completion', description: 'Dump the shell completion script')] final class DumpCompletionCommand extends Command { - /** - * @deprecated since Symfony 6.1 - */ - protected static $defaultName = 'completion'; - - /** - * @deprecated since Symfony 6.1 - */ - protected static $defaultDescription = 'Dump the shell completion script'; - private array $supportedShells; protected function configure(): void diff --git a/Command/HelpCommand.php b/Command/HelpCommand.php index e6447b050..a2a72dab4 100644 --- a/Command/HelpCommand.php +++ b/Command/HelpCommand.php @@ -27,10 +27,7 @@ class HelpCommand extends Command { private Command $command; - /** - * @return void - */ - protected function configure() + protected function configure(): void { $this->ignoreValidationErrors(); @@ -57,10 +54,7 @@ protected function configure() ; } - /** - * @return void - */ - public function setCommand(Command $command) + public function setCommand(Command $command): void { $this->command = $command; } diff --git a/Command/LazyCommand.php b/Command/LazyCommand.php index b94da6665..fd2c300d7 100644 --- a/Command/LazyCommand.php +++ b/Command/LazyCommand.php @@ -27,17 +27,21 @@ final class LazyCommand extends Command { private \Closure|Command $command; - private ?bool $isEnabled; - public function __construct(string $name, array $aliases, string $description, bool $isHidden, \Closure $commandFactory, ?bool $isEnabled = true) - { + public function __construct( + string $name, + array $aliases, + string $description, + bool $isHidden, + \Closure $commandFactory, + private ?bool $isEnabled = true, + ) { $this->setName($name) ->setAliases($aliases) ->setHidden($isHidden) ->setDescription($description); $this->command = $commandFactory; - $this->isEnabled = $isEnabled; } public function ignoreValidationErrors(): void @@ -45,11 +49,8 @@ public function ignoreValidationErrors(): void $this->getCommand()->ignoreValidationErrors(); } - public function setApplication(?Application $application = null): void + public function setApplication(?Application $application): void { - if (1 > \func_num_args()) { - trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); - } if ($this->command instanceof parent) { $this->command->setApplication($application); } @@ -116,9 +117,8 @@ public function getNativeDefinition(): InputDefinition /** * @param array|\Closure(CompletionInput,CompletionSuggestions):list $suggestedValues The values used for input completion */ - public function addArgument(string $name, ?int $mode = null, string $description = '', mixed $default = null /* array|\Closure $suggestedValues = [] */): static + public function addArgument(string $name, ?int $mode = null, string $description = '', mixed $default = null, array|\Closure $suggestedValues = []): static { - $suggestedValues = 5 <= \func_num_args() ? func_get_arg(4) : []; $this->getCommand()->addArgument($name, $mode, $description, $default, $suggestedValues); return $this; @@ -127,9 +127,8 @@ public function addArgument(string $name, ?int $mode = null, string $description /** * @param array|\Closure(CompletionInput,CompletionSuggestions):list $suggestedValues The values used for input completion */ - public function addOption(string $name, string|array|null $shortcut = null, ?int $mode = null, string $description = '', mixed $default = null /* array|\Closure $suggestedValues = [] */): static + public function addOption(string $name, string|array|null $shortcut = null, ?int $mode = null, string $description = '', mixed $default = null, array|\Closure $suggestedValues = []): static { - $suggestedValues = 6 <= \func_num_args() ? func_get_arg(5) : []; $this->getCommand()->addOption($name, $shortcut, $mode, $description, $default, $suggestedValues); return $this; diff --git a/Command/ListCommand.php b/Command/ListCommand.php index 5850c3d7b..61b4b1b3e 100644 --- a/Command/ListCommand.php +++ b/Command/ListCommand.php @@ -25,10 +25,7 @@ */ class ListCommand extends Command { - /** - * @return void - */ - protected function configure() + protected function configure(): void { $this ->setName('list') diff --git a/Command/LockableTrait.php b/Command/LockableTrait.php index cd7548f02..f0001cc52 100644 --- a/Command/LockableTrait.php +++ b/Command/LockableTrait.php @@ -26,6 +26,8 @@ trait LockableTrait { private ?LockInterface $lock = null; + private ?LockFactory $lockFactory = null; + /** * Locks a command. */ @@ -39,13 +41,17 @@ private function lock(?string $name = null, bool $blocking = false): bool throw new LogicException('A lock is already in place.'); } - if (SemaphoreStore::isSupported()) { - $store = new SemaphoreStore(); - } else { - $store = new FlockStore(); + if (null === $this->lockFactory) { + if (SemaphoreStore::isSupported()) { + $store = new SemaphoreStore(); + } else { + $store = new FlockStore(); + } + + $this->lockFactory = (new LockFactory($store)); } - $this->lock = (new LockFactory($store))->createLock($name ?: $this->getName()); + $this->lock = $this->lockFactory->createLock($name ?: $this->getName()); if (!$this->lock->acquire($blocking)) { $this->lock = null; diff --git a/Command/SignalableCommandInterface.php b/Command/SignalableCommandInterface.php index f8eb8e522..40b301d18 100644 --- a/Command/SignalableCommandInterface.php +++ b/Command/SignalableCommandInterface.php @@ -26,9 +26,7 @@ public function getSubscribedSignals(): array; /** * 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, /* int|false $previousExitCode = 0 */); + public function handleSignal(int $signal, int|false $previousExitCode = 0): int|false; } diff --git a/CommandLoader/ContainerCommandLoader.php b/CommandLoader/ContainerCommandLoader.php index bfa0ac467..84e25be76 100644 --- a/CommandLoader/ContainerCommandLoader.php +++ b/CommandLoader/ContainerCommandLoader.php @@ -22,16 +22,13 @@ */ class ContainerCommandLoader implements CommandLoaderInterface { - private ContainerInterface $container; - private array $commandMap; - /** * @param array $commandMap An array with command names as keys and service ids as values */ - public function __construct(ContainerInterface $container, array $commandMap) - { - $this->container = $container; - $this->commandMap = $commandMap; + public function __construct( + private ContainerInterface $container, + private array $commandMap, + ) { } public function get(string $name): Command diff --git a/CommandLoader/FactoryCommandLoader.php b/CommandLoader/FactoryCommandLoader.php index 9ced75aeb..ae16bf6f1 100644 --- a/CommandLoader/FactoryCommandLoader.php +++ b/CommandLoader/FactoryCommandLoader.php @@ -21,14 +21,12 @@ */ class FactoryCommandLoader implements CommandLoaderInterface { - private array $factories; - /** * @param callable[] $factories Indexed by command names */ - public function __construct(array $factories) - { - $this->factories = $factories; + public function __construct( + private array $factories, + ) { } public function has(string $name): bool diff --git a/Completion/Output/FishCompletionOutput.php b/Completion/Output/FishCompletionOutput.php index d2c414e48..356a974ea 100644 --- a/Completion/Output/FishCompletionOutput.php +++ b/Completion/Output/FishCompletionOutput.php @@ -21,11 +21,14 @@ class FishCompletionOutput implements CompletionOutputInterface { public function write(CompletionSuggestions $suggestions, OutputInterface $output): void { - $values = $suggestions->getValueSuggestions(); + $values = []; + foreach ($suggestions->getValueSuggestions() as $value) { + $values[] = $value->getValue().($value->getDescription() ? "\t".$value->getDescription() : ''); + } foreach ($suggestions->getOptionSuggestions() as $option) { - $values[] = '--'.$option->getName(); + $values[] = '--'.$option->getName().($option->getDescription() ? "\t".$option->getDescription() : ''); if ($option->isNegatable()) { - $values[] = '--no-'.$option->getName(); + $values[] = '--no-'.$option->getName().($option->getDescription() ? "\t".$option->getDescription() : ''); } } $output->write(implode("\n", $values)); diff --git a/Completion/Suggestion.php b/Completion/Suggestion.php index 7392965a2..3251b079f 100644 --- a/Completion/Suggestion.php +++ b/Completion/Suggestion.php @@ -20,7 +20,7 @@ class Suggestion implements \Stringable { public function __construct( private readonly string $value, - private readonly string $description = '' + private readonly string $description = '', ) { } diff --git a/Cursor.php b/Cursor.php index 69fd3821c..965f996e0 100644 --- a/Cursor.php +++ b/Cursor.php @@ -18,16 +18,16 @@ */ final class Cursor { - private OutputInterface $output; /** @var resource */ private $input; /** * @param resource|null $input */ - public function __construct(OutputInterface $output, $input = null) - { - $this->output = $output; + public function __construct( + private OutputInterface $output, + $input = null, + ) { $this->input = $input ?? (\defined('STDIN') ? \STDIN : fopen('php://input', 'r+')); } diff --git a/DependencyInjection/AddConsoleCommandPass.php b/DependencyInjection/AddConsoleCommandPass.php index 27705ddb6..f712c614a 100644 --- a/DependencyInjection/AddConsoleCommandPass.php +++ b/DependencyInjection/AddConsoleCommandPass.php @@ -29,10 +29,7 @@ */ class AddConsoleCommandPass implements CompilerPassInterface { - /** - * @return void - */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { $commandServices = $container->findTaggedServiceIds('console.command', true); $lazyCommandMap = []; diff --git a/Descriptor/ApplicationDescription.php b/Descriptor/ApplicationDescription.php index ef9e8a63b..5149fde4c 100644 --- a/Descriptor/ApplicationDescription.php +++ b/Descriptor/ApplicationDescription.php @@ -24,9 +24,6 @@ class ApplicationDescription { public const GLOBAL_NAMESPACE = '_global'; - private Application $application; - private ?string $namespace; - private bool $showHidden; private array $namespaces; /** @@ -39,11 +36,11 @@ class ApplicationDescription */ private array $aliases = []; - public function __construct(Application $application, ?string $namespace = null, bool $showHidden = false) - { - $this->application = $application; - $this->namespace = $namespace; - $this->showHidden = $showHidden; + public function __construct( + private Application $application, + private ?string $namespace = null, + private bool $showHidden = false, + ) { } public function getNamespaces(): array diff --git a/Descriptor/DescriptorInterface.php b/Descriptor/DescriptorInterface.php index ab468a256..04e5a7c86 100644 --- a/Descriptor/DescriptorInterface.php +++ b/Descriptor/DescriptorInterface.php @@ -20,8 +20,5 @@ */ interface DescriptorInterface { - /** - * @return void - */ - public function describe(OutputInterface $output, object $object, array $options = []); + public function describe(OutputInterface $output, object $object, array $options = []): void; } diff --git a/Descriptor/ReStructuredTextDescriptor.php b/Descriptor/ReStructuredTextDescriptor.php index d4423fd34..f12fecb67 100644 --- a/Descriptor/ReStructuredTextDescriptor.php +++ b/Descriptor/ReStructuredTextDescriptor.php @@ -226,7 +226,7 @@ private function getNonDefaultOptions(InputDefinition $definition): array $nonDefaultOptions = []; foreach ($definition->getOptions() as $option) { // Skip global options. - if (!\in_array($option->getName(), $globalOptions)) { + if (!\in_array($option->getName(), $globalOptions, true)) { $nonDefaultOptions[] = $option; } } diff --git a/Descriptor/XmlDescriptor.php b/Descriptor/XmlDescriptor.php index 866c71856..8e44c88c4 100644 --- a/Descriptor/XmlDescriptor.php +++ b/Descriptor/XmlDescriptor.php @@ -208,7 +208,7 @@ private function getInputOptionDocument(InputOption $option): \DOMDocument $defaults = \is_array($option->getDefault()) ? $option->getDefault() : (\is_bool($option->getDefault()) ? [var_export($option->getDefault(), true)] : ($option->getDefault() ? [$option->getDefault()] : [])); $objectXML->appendChild($defaultsXML = $dom->createElement('defaults')); - if (!empty($defaults)) { + if ($defaults) { foreach ($defaults as $default) { $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); $defaultXML->appendChild($dom->createTextNode($default)); diff --git a/Event/ConsoleErrorEvent.php b/Event/ConsoleErrorEvent.php index 7be2ff83e..1c0d62652 100644 --- a/Event/ConsoleErrorEvent.php +++ b/Event/ConsoleErrorEvent.php @@ -22,14 +22,15 @@ */ final class ConsoleErrorEvent extends ConsoleEvent { - private \Throwable $error; private int $exitCode; - public function __construct(InputInterface $input, OutputInterface $output, \Throwable $error, ?Command $command = null) - { + public function __construct( + InputInterface $input, + OutputInterface $output, + private \Throwable $error, + ?Command $command = null, + ) { parent::__construct($command, $input, $output); - - $this->error = $error; } public function getError(): \Throwable diff --git a/Event/ConsoleEvent.php b/Event/ConsoleEvent.php index 6ba1615fe..2f9f0778e 100644 --- a/Event/ConsoleEvent.php +++ b/Event/ConsoleEvent.php @@ -23,16 +23,11 @@ */ class ConsoleEvent extends Event { - protected $command; - - private InputInterface $input; - private OutputInterface $output; - - public function __construct(?Command $command, InputInterface $input, OutputInterface $output) - { - $this->command = $command; - $this->input = $input; - $this->output = $output; + public function __construct( + protected ?Command $command, + private InputInterface $input, + private OutputInterface $output, + ) { } /** diff --git a/Event/ConsoleSignalEvent.php b/Event/ConsoleSignalEvent.php index 95af1f915..b27f08a18 100644 --- a/Event/ConsoleSignalEvent.php +++ b/Event/ConsoleSignalEvent.php @@ -20,14 +20,14 @@ */ final class ConsoleSignalEvent extends ConsoleEvent { - private int $handlingSignal; - private int|false $exitCode; - - public function __construct(Command $command, InputInterface $input, OutputInterface $output, int $handlingSignal, int|false $exitCode = 0) - { + public function __construct( + Command $command, + InputInterface $input, + OutputInterface $output, + private int $handlingSignal, + private int|false $exitCode = 0, + ) { parent::__construct($command, $input, $output); - $this->handlingSignal = $handlingSignal; - $this->exitCode = $exitCode; } public function getHandlingSignal(): int diff --git a/EventListener/ErrorListener.php b/EventListener/ErrorListener.php index c9ec24434..49915a498 100644 --- a/EventListener/ErrorListener.php +++ b/EventListener/ErrorListener.php @@ -24,17 +24,12 @@ */ class ErrorListener implements EventSubscriberInterface { - private ?LoggerInterface $logger; - - public function __construct(?LoggerInterface $logger = null) - { - $this->logger = $logger; + public function __construct( + private ?LoggerInterface $logger = null, + ) { } - /** - * @return void - */ - public function onConsoleError(ConsoleErrorEvent $event) + public function onConsoleError(ConsoleErrorEvent $event): void { if (null === $this->logger) { return; @@ -51,10 +46,7 @@ public function onConsoleError(ConsoleErrorEvent $event) $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) + public function onConsoleTerminate(ConsoleTerminateEvent $event): void { if (null === $this->logger) { return; diff --git a/Exception/CommandNotFoundException.php b/Exception/CommandNotFoundException.php index 541b32b23..246f04fa2 100644 --- a/Exception/CommandNotFoundException.php +++ b/Exception/CommandNotFoundException.php @@ -18,19 +18,19 @@ */ class CommandNotFoundException extends \InvalidArgumentException implements ExceptionInterface { - private array $alternatives; - /** * @param string $message Exception message to throw * @param string[] $alternatives List of similar defined names * @param int $code Exception code * @param \Throwable|null $previous Previous exception used for the exception chaining */ - public function __construct(string $message, array $alternatives = [], int $code = 0, ?\Throwable $previous = null) - { + public function __construct( + string $message, + private array $alternatives = [], + int $code = 0, + ?\Throwable $previous = null, + ) { parent::__construct($message, $code, $previous); - - $this->alternatives = $alternatives; } /** diff --git a/Formatter/NullOutputFormatterStyle.php b/Formatter/NullOutputFormatterStyle.php index ae23decb1..06fa6e40b 100644 --- a/Formatter/NullOutputFormatterStyle.php +++ b/Formatter/NullOutputFormatterStyle.php @@ -21,19 +21,13 @@ public function apply(string $text): string return $text; } - public function setBackground(?string $color = null): void + public function setBackground(?string $color): void { - if (1 > \func_num_args()) { - trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); - } // do nothing } - public function setForeground(?string $color = null): void + public function setForeground(?string $color): void { - if (1 > \func_num_args()) { - trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); - } // do nothing } diff --git a/Formatter/OutputFormatter.php b/Formatter/OutputFormatter.php index 3e4897c33..8e81e5900 100644 --- a/Formatter/OutputFormatter.php +++ b/Formatter/OutputFormatter.php @@ -83,10 +83,7 @@ public function __construct(bool $decorated = false, array $styles = []) $this->styleStack = new OutputFormatterStyleStack(); } - /** - * @return void - */ - public function setDecorated(bool $decorated) + public function setDecorated(bool $decorated): void { $this->decorated = $decorated; } @@ -96,10 +93,7 @@ public function isDecorated(): bool return $this->decorated; } - /** - * @return void - */ - public function setStyle(string $name, OutputFormatterStyleInterface $style) + public function setStyle(string $name, OutputFormatterStyleInterface $style): void { $this->styles[strtolower($name)] = $style; } @@ -123,10 +117,7 @@ public function format(?string $message): ?string return $this->formatAndWrap($message, 0); } - /** - * @return string - */ - public function formatAndWrap(?string $message, int $width) + public function formatAndWrap(?string $message, int $width): string { if (null === $message) { return ''; diff --git a/Formatter/OutputFormatterInterface.php b/Formatter/OutputFormatterInterface.php index 433cd4197..947347fa7 100644 --- a/Formatter/OutputFormatterInterface.php +++ b/Formatter/OutputFormatterInterface.php @@ -20,10 +20,8 @@ interface OutputFormatterInterface { /** * Sets the decorated flag. - * - * @return void */ - public function setDecorated(bool $decorated); + public function setDecorated(bool $decorated): void; /** * Whether the output will decorate messages. @@ -32,10 +30,8 @@ public function isDecorated(): bool; /** * Sets a new style. - * - * @return void */ - public function setStyle(string $name, OutputFormatterStyleInterface $style); + public function setStyle(string $name, OutputFormatterStyleInterface $style): void; /** * Checks if output formatter has style with specified name. diff --git a/Formatter/OutputFormatterStyle.php b/Formatter/OutputFormatterStyle.php index 21e7f5ab0..20a65b517 100644 --- a/Formatter/OutputFormatterStyle.php +++ b/Formatter/OutputFormatterStyle.php @@ -38,25 +38,13 @@ public function __construct(?string $foreground = null, ?string $background = nu $this->color = new Color($this->foreground = $foreground ?: '', $this->background = $background ?: '', $this->options = $options); } - /** - * @return void - */ - public function setForeground(?string $color = null) + public function setForeground(?string $color): void { - if (1 > \func_num_args()) { - trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); - } $this->color = new Color($this->foreground = $color ?: '', $this->background, $this->options); } - /** - * @return void - */ - public function setBackground(?string $color = null) + public function setBackground(?string $color): void { - if (1 > \func_num_args()) { - trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); - } $this->color = new Color($this->foreground, $this->background = $color ?: '', $this->options); } @@ -65,19 +53,13 @@ public function setHref(string $url): void $this->href = $url; } - /** - * @return void - */ - public function setOption(string $option) + public function setOption(string $option): void { $this->options[] = $option; $this->color = new Color($this->foreground, $this->background, $this->options); } - /** - * @return void - */ - public function unsetOption(string $option) + public function unsetOption(string $option): void { $pos = array_search($option, $this->options); if (false !== $pos) { @@ -87,10 +69,7 @@ public function unsetOption(string $option) $this->color = new Color($this->foreground, $this->background, $this->options); } - /** - * @return void - */ - public function setOptions(array $options) + public function setOptions(array $options): void { $this->color = new Color($this->foreground, $this->background, $this->options = $options); } diff --git a/Formatter/OutputFormatterStyleInterface.php b/Formatter/OutputFormatterStyleInterface.php index 3b15098cb..037419277 100644 --- a/Formatter/OutputFormatterStyleInterface.php +++ b/Formatter/OutputFormatterStyleInterface.php @@ -20,38 +20,28 @@ interface OutputFormatterStyleInterface { /** * Sets style foreground color. - * - * @return void */ - public function setForeground(?string $color); + public function setForeground(?string $color): void; /** * Sets style background color. - * - * @return void */ - public function setBackground(?string $color); + public function setBackground(?string $color): void; /** * Sets some specific style option. - * - * @return void */ - public function setOption(string $option); + public function setOption(string $option): void; /** * Unsets some specific style option. - * - * @return void */ - public function unsetOption(string $option); + public function unsetOption(string $option): void; /** * Sets multiple style options at once. - * - * @return void */ - public function setOptions(array $options); + public function setOptions(array $options): void; /** * Applies the style to a given text. diff --git a/Formatter/OutputFormatterStyleStack.php b/Formatter/OutputFormatterStyleStack.php index 62d2ca0e7..4985213ab 100644 --- a/Formatter/OutputFormatterStyleStack.php +++ b/Formatter/OutputFormatterStyleStack.php @@ -34,20 +34,16 @@ public function __construct(?OutputFormatterStyleInterface $emptyStyle = null) /** * Resets stack (ie. empty internal arrays). - * - * @return void */ - public function reset() + public function reset(): void { $this->styles = []; } /** * Pushes a style in the stack. - * - * @return void */ - public function push(OutputFormatterStyleInterface $style) + public function push(OutputFormatterStyleInterface $style): void { $this->styles[] = $style; } diff --git a/Formatter/WrappableOutputFormatterInterface.php b/Formatter/WrappableOutputFormatterInterface.php index 746cd27e7..412d9976f 100644 --- a/Formatter/WrappableOutputFormatterInterface.php +++ b/Formatter/WrappableOutputFormatterInterface.php @@ -20,8 +20,6 @@ interface WrappableOutputFormatterInterface extends OutputFormatterInterface { /** * Formats a message according to the given styles, wrapping at `$width` (0 means no wrapping). - * - * @return string */ - public function formatAndWrap(?string $message, int $width); + public function formatAndWrap(?string $message, int $width): string; } diff --git a/Helper/DescriptorHelper.php b/Helper/DescriptorHelper.php index eb32bce8f..300c7b102 100644 --- a/Helper/DescriptorHelper.php +++ b/Helper/DescriptorHelper.php @@ -50,11 +50,9 @@ public function __construct() * * 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 = []) + public function describe(OutputInterface $output, ?object $object, array $options = []): void { $options = array_merge([ 'raw_text' => false, diff --git a/Helper/Dumper.php b/Helper/Dumper.php index a3b8e3952..0cd01e616 100644 --- a/Helper/Dumper.php +++ b/Helper/Dumper.php @@ -21,17 +21,13 @@ */ final class Dumper { - private OutputInterface $output; - private ?CliDumper $dumper; - private ?ClonerInterface $cloner; private \Closure $handler; - public function __construct(OutputInterface $output, ?CliDumper $dumper = null, ?ClonerInterface $cloner = null) - { - $this->output = $output; - $this->dumper = $dumper; - $this->cloner = $cloner; - + public function __construct( + private OutputInterface $output, + private ?CliDumper $dumper = null, + private ?ClonerInterface $cloner = null, + ) { if (class_exists(CliDumper::class)) { $this->handler = function ($var): string { $dumper = $this->dumper ??= new CliDumper(null, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR); diff --git a/Helper/Helper.php b/Helper/Helper.php index 05be64787..de090063a 100644 --- a/Helper/Helper.php +++ b/Helper/Helper.php @@ -21,16 +21,10 @@ */ abstract class Helper implements HelperInterface { - protected $helperSet; + protected ?HelperSet $helperSet = null; - /** - * @return void - */ - public function setHelperSet(?HelperSet $helperSet = null) + public function setHelperSet(?HelperSet $helperSet): void { - if (1 > \func_num_args()) { - trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); - } $this->helperSet = $helperSet; } @@ -91,10 +85,7 @@ public static function substr(?string $string, int $from, ?int $length = null): return mb_substr($string, $from, $length, $encoding); } - /** - * @return string - */ - public static function formatTime(int|float $secs, int $precision = 1) + public static function formatTime(int|float $secs, int $precision = 1): string { $secs = (int) floor($secs); @@ -134,10 +125,7 @@ public static function formatTime(int|float $secs, int $precision = 1) return implode(', ', array_reverse($times)); } - /** - * @return string - */ - public static function formatMemory(int $memory) + public static function formatMemory(int $memory): string { if ($memory >= 1024 * 1024 * 1024) { return sprintf('%.1f GiB', $memory / 1024 / 1024 / 1024); @@ -154,10 +142,7 @@ public static function formatMemory(int $memory) return sprintf('%d B', $memory); } - /** - * @return string - */ - public static function removeDecoration(OutputFormatterInterface $formatter, ?string $string) + public static function removeDecoration(OutputFormatterInterface $formatter, ?string $string): string { $isDecorated = $formatter->isDecorated(); $formatter->setDecorated(false); diff --git a/Helper/HelperInterface.php b/Helper/HelperInterface.php index ab626c938..8c4da3c91 100644 --- a/Helper/HelperInterface.php +++ b/Helper/HelperInterface.php @@ -20,10 +20,8 @@ interface HelperInterface { /** * Sets the helper set associated with this helper. - * - * @return void */ - public function setHelperSet(?HelperSet $helperSet); + public function setHelperSet(?HelperSet $helperSet): void; /** * Gets the helper set associated with this helper. @@ -32,8 +30,6 @@ public function getHelperSet(): ?HelperSet; /** * Returns the canonical name of this helper. - * - * @return string */ - public function getName(); + public function getName(): string; } diff --git a/Helper/HelperSet.php b/Helper/HelperSet.php index f8c74ca2c..30df9f958 100644 --- a/Helper/HelperSet.php +++ b/Helper/HelperSet.php @@ -35,10 +35,7 @@ public function __construct(array $helpers = []) } } - /** - * @return void - */ - public function set(HelperInterface $helper, ?string $alias = null) + public function set(HelperInterface $helper, ?string $alias = null): void { $this->helpers[$helper->getName()] = $helper; if (null !== $alias) { diff --git a/Helper/InputAwareHelper.php b/Helper/InputAwareHelper.php index 6f8225973..47126bdaa 100644 --- a/Helper/InputAwareHelper.php +++ b/Helper/InputAwareHelper.php @@ -21,12 +21,9 @@ */ abstract class InputAwareHelper extends Helper implements InputAwareInterface { - protected $input; + protected InputInterface $input; - /** - * @return void - */ - public function setInput(InputInterface $input) + public function setInput(InputInterface $input): void { $this->input = $input; } diff --git a/Helper/OutputWrapper.php b/Helper/OutputWrapper.php index 2ec819c74..0ea2b7056 100644 --- a/Helper/OutputWrapper.php +++ b/Helper/OutputWrapper.php @@ -49,7 +49,7 @@ final class OutputWrapper private const URL_PATTERN = 'https?://\S+'; public function __construct( - private bool $allowCutUrls = false + private bool $allowCutUrls = false, ) { } diff --git a/Helper/ProgressBar.php b/Helper/ProgressBar.php index 23157e3c7..619eafea2 100644 --- a/Helper/ProgressBar.php +++ b/Helper/ProgressBar.php @@ -195,7 +195,7 @@ public function getStartTime(): int public function getMaxSteps(): int { - return $this->max; + return $this->max ?? 0; } public function getProgress(): int @@ -215,7 +215,7 @@ public function getProgressPercent(): float public function getBarOffset(): float { - return floor($this->max ? $this->percent * $this->barWidth : (null === $this->redrawFreq ? (int) (min(5, $this->barWidth / 15) * $this->writeCount) : $this->step) % $this->barWidth); + return floor(null !== $this->max ? $this->percent * $this->barWidth : (null === $this->redrawFreq ? (int) (min(5, $this->barWidth / 15) * $this->writeCount) : $this->step) % $this->barWidth); } public function getEstimated(): float @@ -253,7 +253,7 @@ public function setBarCharacter(string $char): void public function getBarCharacter(): string { - return $this->barChar ?? ($this->max ? '=' : $this->emptyBarChar); + return $this->barChar ?? (null !== $this->max ? '=' : $this->emptyBarChar); } public function setEmptyBarCharacter(string $char): void @@ -315,7 +315,21 @@ public function maxSecondsBetweenRedraws(float $seconds): void */ public function iterate(iterable $iterable, ?int $max = null): iterable { - $this->start($max ?? (is_countable($iterable) ? \count($iterable) : 0)); + if (0 === $max) { + $max = null; + } + + $max ??= is_countable($iterable) ? \count($iterable) : null; + + if (0 === $max) { + $this->max = 0; + $this->stepWidth = 2; + $this->finish(); + + return; + } + + $this->start($max); foreach ($iterable as $key => $value) { yield $key => $value; @@ -373,11 +387,15 @@ public function setProgress(int $step): void $step = 0; } - $redrawFreq = $this->redrawFreq ?? (($this->max ?: 10) / 10); - $prevPeriod = (int) ($this->step / $redrawFreq); - $currPeriod = (int) ($step / $redrawFreq); + $redrawFreq = $this->redrawFreq ?? (($this->max ?? 10) / 10); + $prevPeriod = $redrawFreq ? (int) ($this->step / $redrawFreq) : 0; + $currPeriod = $redrawFreq ? (int) ($step / $redrawFreq) : 0; $this->step = $step; - $this->percent = $this->max ? (float) $this->step / $this->max : 0; + $this->percent = match ($this->max) { + null => 0, + 0 => 1, + default => (float) $this->step / $this->max, + }; $timeInterval = microtime(true) - $this->lastWriteTime; // Draw regardless of other limits @@ -398,11 +416,20 @@ public function setProgress(int $step): void } } - public function setMaxSteps(int $max): void + public function setMaxSteps(?int $max): void { + if (0 === $max) { + $max = null; + } + $this->format = null; - $this->max = max(0, $max); - $this->stepWidth = $this->max ? Helper::width((string) $this->max) : 4; + if (null === $max) { + $this->max = null; + $this->stepWidth = 4; + } else { + $this->max = max(0, $max); + $this->stepWidth = Helper::width((string) $this->max); + } } /** @@ -410,16 +437,16 @@ public function setMaxSteps(int $max): void */ public function finish(): void { - if (!$this->max) { + if (null === $this->max) { $this->max = $this->step; } - if ($this->step === $this->max && !$this->overwrite) { + if (($this->step === $this->max || null === $this->max) && !$this->overwrite) { // prevent double 100% output return; } - $this->setProgress($this->max); + $this->setProgress($this->max ?? $this->step); } /** @@ -542,14 +569,14 @@ private static function initPlaceholderFormatters(): array }, 'elapsed' => fn (self $bar) => Helper::formatTime(time() - $bar->getStartTime(), 2), 'remaining' => function (self $bar) { - if (!$bar->getMaxSteps()) { + if (null === $bar->getMaxSteps()) { throw new LogicException('Unable to display the remaining time if the maximum number of steps is not set.'); } return Helper::formatTime($bar->getRemaining(), 2); }, 'estimated' => function (self $bar) { - if (!$bar->getMaxSteps()) { + if (null === $bar->getMaxSteps()) { throw new LogicException('Unable to display the estimated time if the maximum number of steps is not set.'); } diff --git a/Helper/ProgressIndicator.php b/Helper/ProgressIndicator.php index 92106caf6..969d83539 100644 --- a/Helper/ProgressIndicator.php +++ b/Helper/ProgressIndicator.php @@ -31,13 +31,11 @@ class ProgressIndicator 'very_verbose_no_ansi' => ' %message% (%elapsed:6s%, %memory:6s%)', ]; - private OutputInterface $output; private int $startTime; private ?string $format = null; private ?string $message = null; private array $indicatorValues; private int $indicatorCurrent; - private int $indicatorChangeInterval; private float $indicatorUpdateTime; private bool $started = false; @@ -50,9 +48,12 @@ class ProgressIndicator * @param int $indicatorChangeInterval Change interval in milliseconds * @param array|null $indicatorValues Animated indicator characters */ - public function __construct(OutputInterface $output, ?string $format = null, int $indicatorChangeInterval = 100, ?array $indicatorValues = null) - { - $this->output = $output; + public function __construct( + private OutputInterface $output, + ?string $format = null, + private int $indicatorChangeInterval = 100, + ?array $indicatorValues = null, + ) { $format ??= $this->determineBestFormat(); $indicatorValues ??= ['-', '\\', '|', '/']; @@ -63,17 +64,14 @@ public function __construct(OutputInterface $output, ?string $format = null, int } $this->format = self::getFormatDefinition($format); - $this->indicatorChangeInterval = $indicatorChangeInterval; $this->indicatorValues = $indicatorValues; $this->startTime = time(); } /** * Sets the current indicator message. - * - * @return void */ - public function setMessage(?string $message) + public function setMessage(?string $message): void { $this->message = $message; @@ -82,10 +80,8 @@ public function setMessage(?string $message) /** * Starts the indicator output. - * - * @return void */ - public function start(string $message) + public function start(string $message): void { if ($this->started) { throw new LogicException('Progress indicator already started.'); @@ -102,10 +98,8 @@ public function start(string $message) /** * Advances the indicator. - * - * @return void */ - public function advance() + public function advance(): void { if (!$this->started) { throw new LogicException('Progress indicator has not yet been started.'); @@ -129,10 +123,8 @@ public function advance() /** * Finish the indicator with message. - * - * @return void */ - public function finish(string $message) + public function finish(string $message): void { if (!$this->started) { throw new LogicException('Progress indicator has not yet been started.'); @@ -156,10 +148,8 @@ public static function getFormatDefinition(string $name): ?string * 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) + public static function setPlaceholderFormatterDefinition(string $name, callable $callable): void { self::$formatters ??= self::initPlaceholderFormatters(); diff --git a/Helper/QuestionHelper.php b/Helper/QuestionHelper.php index b40b13191..54825c6cd 100644 --- a/Helper/QuestionHelper.php +++ b/Helper/QuestionHelper.php @@ -34,11 +34,6 @@ */ class QuestionHelper extends Helper { - /** - * @var resource|null - */ - private $inputStream; - private static bool $stty = true; private static bool $stdinIsInteractive; @@ -59,16 +54,15 @@ public function ask(InputInterface $input, OutputInterface $output, Question $qu return $this->getDefaultAnswer($question); } - if ($input instanceof StreamableInputInterface && $stream = $input->getStream()) { - $this->inputStream = $stream; - } + $inputStream = $input instanceof StreamableInputInterface ? $input->getStream() : null; + $inputStream ??= STDIN; try { if (!$question->getValidator()) { - return $this->doAsk($output, $question); + return $this->doAsk($inputStream, $output, $question); } - $interviewer = fn () => $this->doAsk($output, $question); + $interviewer = fn () => $this->doAsk($inputStream, $output, $question); return $this->validateAttempts($interviewer, $output, $question); } catch (MissingInputException $exception) { @@ -89,10 +83,8 @@ public function getName(): string /** * Prevents usage of stty. - * - * @return void */ - public static function disableStty() + public static function disableStty(): void { self::$stty = false; } @@ -100,13 +92,14 @@ public static function disableStty() /** * Asks the question to the user. * + * @param resource $inputStream + * * @throws RuntimeException In case the fallback is deactivated and the response cannot be hidden */ - private function doAsk(OutputInterface $output, Question $question): mixed + private function doAsk($inputStream, OutputInterface $output, Question $question): mixed { $this->writePrompt($output, $question); - $inputStream = $this->inputStream ?: \STDIN; $autocomplete = $question->getAutocompleterCallback(); if (null === $autocomplete || !self::$stty || !Terminal::hasSttyAvailable()) { @@ -190,10 +183,8 @@ private function getDefaultAnswer(Question $question): mixed /** * Outputs the question prompt. - * - * @return void */ - protected function writePrompt(OutputInterface $output, Question $question) + protected function writePrompt(OutputInterface $output, Question $question): void { $message = $question->getQuestion(); @@ -228,10 +219,8 @@ protected function formatChoiceQuestionChoices(ChoiceQuestion $question, string /** * Outputs an error message. - * - * @return void */ - protected function writeError(OutputInterface $output, \Exception $error) + protected function writeError(OutputInterface $output, \Exception $error): void { if (null !== $this->getHelperSet() && $this->getHelperSet()->has('formatter')) { $message = $this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error'); diff --git a/Helper/SymfonyQuestionHelper.php b/Helper/SymfonyQuestionHelper.php index 8ebc84376..48d947b75 100644 --- a/Helper/SymfonyQuestionHelper.php +++ b/Helper/SymfonyQuestionHelper.php @@ -25,10 +25,7 @@ */ class SymfonyQuestionHelper extends QuestionHelper { - /** - * @return void - */ - protected function writePrompt(OutputInterface $output, Question $question) + protected function writePrompt(OutputInterface $output, Question $question): void { $text = OutputFormatter::escapeTrailingBackslash($question->getQuestion()); $default = $question->getDefault(); @@ -83,10 +80,7 @@ protected function writePrompt(OutputInterface $output, Question $question) $output->write($prompt); } - /** - * @return void - */ - protected function writeError(OutputInterface $output, \Exception $error) + protected function writeError(OutputInterface $output, \Exception $error): void { if ($output instanceof SymfonyStyle) { $output->newLine(); diff --git a/Helper/Table.php b/Helper/Table.php index 1f026dc50..09709a28e 100644 --- a/Helper/Table.php +++ b/Helper/Table.php @@ -45,7 +45,6 @@ class Table private array $rows = []; private array $effectiveColumnWidths = []; private int $numberOfColumns; - private OutputInterface $output; private TableStyle $style; private array $columnStyles = []; private array $columnWidths = []; @@ -55,10 +54,9 @@ class Table private static array $styles; - public function __construct(OutputInterface $output) - { - $this->output = $output; - + public function __construct( + private OutputInterface $output, + ) { self::$styles ??= self::initStyles(); $this->setStyle('default'); @@ -66,10 +64,8 @@ public function __construct(OutputInterface $output) /** * Sets a style definition. - * - * @return void */ - public static function setStyleDefinition(string $name, TableStyle $style) + public static function setStyleDefinition(string $name, TableStyle $style): void { self::$styles ??= self::initStyles(); @@ -194,7 +190,7 @@ public function setHeaders(array $headers): static /** * @return $this */ - public function setRows(array $rows) + public function setRows(array $rows): static { $this->rows = []; @@ -312,10 +308,8 @@ public function setVertical(bool $vertical = true): static * | 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() + public function render(): void { $divider = new TableSeparator(); $isCellWithColspan = static fn ($cell) => $cell instanceof TableCell && $cell->getColspan() >= 2; @@ -725,7 +719,7 @@ private function fillNextRows(array $rows, int $line): array foreach ($unmergedRows as $unmergedRowKey => $unmergedRow) { // we need to know if $unmergedRow will be merged or inserted into $rows - if (isset($rows[$unmergedRowKey]) && \is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRows[$unmergedRowKey]) <= $this->numberOfColumns)) { + if (isset($rows[$unmergedRowKey]) && \is_array($rows[$unmergedRowKey]) && ($this->getNumberOfColumns($rows[$unmergedRowKey]) + $this->getNumberOfColumns($unmergedRow) <= $this->numberOfColumns)) { foreach ($unmergedRow as $cellKey => $cell) { // insert cell into row at cellKey position array_splice($rows[$unmergedRowKey], $cellKey, 0, [$cell]); @@ -733,8 +727,8 @@ private function fillNextRows(array $rows, int $line): array } else { $row = $this->copyRow($rows, $unmergedRowKey - 1); foreach ($unmergedRow as $column => $cell) { - if (!empty($cell)) { - $row[$column] = $unmergedRow[$column]; + if ($cell) { + $row[$column] = $cell; } } array_splice($rows, $unmergedRowKey, 0, [$row]); diff --git a/Helper/TableCell.php b/Helper/TableCell.php index 394b2bc95..1c4eeea20 100644 --- a/Helper/TableCell.php +++ b/Helper/TableCell.php @@ -18,17 +18,16 @@ */ class TableCell { - private string $value; private array $options = [ 'rowspan' => 1, 'colspan' => 1, 'style' => null, ]; - public function __construct(string $value = '', array $options = []) - { - $this->value = $value; - + public function __construct( + private string $value = '', + array $options = [], + ) { // check option names if ($diff = array_diff(array_keys($options), array_keys($this->options))) { throw new InvalidArgumentException(sprintf('The TableCell does not support the following options: \'%s\'.', implode('\', \'', $diff))); diff --git a/Helper/TableCellStyle.php b/Helper/TableCellStyle.php index 9419dcb40..49b97f853 100644 --- a/Helper/TableCellStyle.php +++ b/Helper/TableCellStyle.php @@ -67,7 +67,7 @@ public function getTagOptions(): array { return array_filter( $this->getOptions(), - fn ($key) => \in_array($key, self::TAG_OPTIONS) && isset($this->options[$key]), + fn ($key) => \in_array($key, self::TAG_OPTIONS, true) && isset($this->options[$key]), \ARRAY_FILTER_USE_KEY ); } diff --git a/Helper/TableRows.php b/Helper/TableRows.php index 97d07726e..fb2dc2789 100644 --- a/Helper/TableRows.php +++ b/Helper/TableRows.php @@ -16,11 +16,9 @@ */ class TableRows implements \IteratorAggregate { - private \Closure $generator; - - public function __construct(\Closure $generator) - { - $this->generator = $generator; + public function __construct( + private \Closure $generator, + ) { } public function getIterator(): \Traversable diff --git a/Input/ArgvInput.php b/Input/ArgvInput.php index ab9f28c54..95703ba5f 100644 --- a/Input/ArgvInput.php +++ b/Input/ArgvInput.php @@ -40,9 +40,11 @@ */ class ArgvInput extends Input { + /** @var list */ private array $tokens; private array $parsed; + /** @param list|null $argv */ public function __construct(?array $argv = null, ?InputDefinition $definition = null) { $argv ??= $_SERVER['argv'] ?? []; @@ -55,18 +57,13 @@ public function __construct(?array $argv = null, ?InputDefinition $definition = parent::__construct($definition); } - /** - * @return void - */ - protected function setTokens(array $tokens) + /** @param list $tokens */ + protected function setTokens(array $tokens): void { $this->tokens = $tokens; } - /** - * @return void - */ - protected function parse() + protected function parse(): void { $parseOptions = true; $this->parsed = $this->tokens; @@ -348,6 +345,35 @@ public function getParameterOption(string|array $values, string|bool|int|float|a return $default; } + /** + * Returns un-parsed and not validated tokens. + * + * @param bool $strip Whether to return the raw parameters (false) or the values after the command name (true) + * + * @return list + */ + public function getRawTokens(bool $strip = false): array + { + if (!$strip) { + return $this->tokens; + } + + $parameters = []; + $keep = false; + foreach ($this->tokens as $value) { + if (!$keep && $value === $this->getFirstArgument()) { + $keep = true; + + continue; + } + if ($keep) { + $parameters[] = $value; + } + } + + return $parameters; + } + /** * Returns a stringified representation of the args passed to the command. */ diff --git a/Input/ArrayInput.php b/Input/ArrayInput.php index c1bc914ca..d27ff411e 100644 --- a/Input/ArrayInput.php +++ b/Input/ArrayInput.php @@ -25,12 +25,10 @@ */ class ArrayInput extends Input { - private array $parameters; - - public function __construct(array $parameters, ?InputDefinition $definition = null) - { - $this->parameters = $parameters; - + public function __construct( + private array $parameters, + ?InputDefinition $definition = null, + ) { parent::__construct($definition); } @@ -113,10 +111,7 @@ public function __toString(): string return implode(' ', $params); } - /** - * @return void - */ - protected function parse() + protected function parse(): void { foreach ($this->parameters as $key => $value) { if ('--' === $key) { diff --git a/Input/Input.php b/Input/Input.php index 1c21573bc..5a8b9a27a 100644 --- a/Input/Input.php +++ b/Input/Input.php @@ -27,12 +27,12 @@ */ abstract class Input implements InputInterface, StreamableInputInterface { - protected $definition; + protected InputDefinition $definition; /** @var resource */ protected $stream; - protected $options = []; - protected $arguments = []; - protected $interactive = true; + protected array $options = []; + protected array $arguments = []; + protected bool $interactive = true; public function __construct(?InputDefinition $definition = null) { @@ -44,10 +44,7 @@ public function __construct(?InputDefinition $definition = null) } } - /** - * @return void - */ - public function bind(InputDefinition $definition) + public function bind(InputDefinition $definition): void { $this->arguments = []; $this->options = []; @@ -58,15 +55,10 @@ public function bind(InputDefinition $definition) /** * Processes command line arguments. - * - * @return void */ - abstract protected function parse(); + abstract protected function parse(): void; - /** - * @return void - */ - public function validate() + public function validate(): void { $definition = $this->definition; $givenArguments = $this->arguments; @@ -83,10 +75,7 @@ public function isInteractive(): bool return $this->interactive; } - /** - * @return void - */ - public function setInteractive(bool $interactive) + public function setInteractive(bool $interactive): void { $this->interactive = $interactive; } @@ -105,10 +94,7 @@ public function getArgument(string $name): mixed return $this->arguments[$name] ?? $this->definition->getArgument($name)->getDefault(); } - /** - * @return void - */ - public function setArgument(string $name, mixed $value) + public function setArgument(string $name, mixed $value): void { if (!$this->definition->hasArgument($name)) { throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); @@ -144,10 +130,7 @@ public function getOption(string $name): mixed return \array_key_exists($name, $this->options) ? $this->options[$name] : $this->definition->getOption($name)->getDefault(); } - /** - * @return void - */ - public function setOption(string $name, mixed $value) + public function setOption(string $name, mixed $value): void { if ($this->definition->hasNegation($name)) { $this->options[$this->definition->negationToName($name)] = !$value; @@ -175,10 +158,8 @@ public function escapeToken(string $token): string /** * @param resource $stream - * - * @return void */ - public function setStream($stream) + public function setStream($stream): void { $this->stream = $stream; } diff --git a/Input/InputArgument.php b/Input/InputArgument.php index 4ef79feb7..a5d949277 100644 --- a/Input/InputArgument.php +++ b/Input/InputArgument.php @@ -25,37 +25,47 @@ */ class InputArgument { + /** + * Providing an argument is required (e.g. just 'app:foo' is not allowed). + */ public const REQUIRED = 1; + + /** + * Providing an argument is optional (e.g. 'app:foo' and 'app:foo bar' are both allowed). This is the default behavior of arguments. + */ public const OPTIONAL = 2; + + /** + * The argument accepts multiple values and turn them into an array (e.g. 'app:foo bar baz' will result in value ['bar', 'baz']). + */ public const IS_ARRAY = 4; - private string $name; private int $mode; - private string|int|bool|array|null|float $default; - private array|\Closure $suggestedValues; - private string $description; + private string|int|bool|array|float|null $default; /** * @param string $name The argument name - * @param int|null $mode The argument mode: a bit mask of self::REQUIRED, self::OPTIONAL and self::IS_ARRAY + * @param int-mask-of|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 $suggestedValues The values used for input completion * * @throws InvalidArgumentException When argument mode is not valid */ - public function __construct(string $name, ?int $mode = null, string $description = '', string|bool|int|float|array|null $default = null, \Closure|array $suggestedValues = []) - { + public function __construct( + private string $name, + ?int $mode = null, + private string $description = '', + string|bool|int|float|array|null $default = null, + private \Closure|array $suggestedValues = [], + ) { if (null === $mode) { $mode = self::OPTIONAL; - } elseif ($mode > 7 || $mode < 1) { + } elseif ($mode >= (self::IS_ARRAY << 1) || $mode < 1) { throw new InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode)); } - $this->name = $name; $this->mode = $mode; - $this->description = $description; - $this->suggestedValues = $suggestedValues; $this->setDefault($default); } @@ -90,16 +100,9 @@ public function isArray(): bool /** * Sets the default value. - * - * @return void - * - * @throws LogicException When incorrect default value is given */ - public function setDefault(string|bool|int|float|array|null $default = null) + public function setDefault(string|bool|int|float|array|null $default): void { - if (1 > \func_num_args()) { - trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); - } if ($this->isRequired() && null !== $default) { throw new LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.'); } @@ -123,13 +126,16 @@ public function getDefault(): string|bool|int|float|array|null return $this->default; } + /** + * Returns true if the argument has values for input completion. + */ public function hasCompletion(): bool { return [] !== $this->suggestedValues; } /** - * Adds suggestions to $suggestions for the current completion input. + * Supplies suggestions when command resolves possible completion options for input. * * @see Command::complete() */ diff --git a/Input/InputAwareInterface.php b/Input/InputAwareInterface.php index 0ad27b455..ba4664cdb 100644 --- a/Input/InputAwareInterface.php +++ b/Input/InputAwareInterface.php @@ -21,8 +21,6 @@ interface InputAwareInterface { /** * Sets the Console Input. - * - * @return void */ - public function setInput(InputInterface $input); + public function setInput(InputInterface $input): void; } diff --git a/Input/InputDefinition.php b/Input/InputDefinition.php index b7162d770..f27e29748 100644 --- a/Input/InputDefinition.php +++ b/Input/InputDefinition.php @@ -46,10 +46,8 @@ public function __construct(array $definition = []) /** * Sets the definition of the input. - * - * @return void */ - public function setDefinition(array $definition) + public function setDefinition(array $definition): void { $arguments = []; $options = []; @@ -69,10 +67,8 @@ public function setDefinition(array $definition) * Sets the InputArgument objects. * * @param InputArgument[] $arguments An array of InputArgument objects - * - * @return void */ - public function setArguments(array $arguments = []) + public function setArguments(array $arguments = []): void { $this->arguments = []; $this->requiredCount = 0; @@ -85,10 +81,8 @@ public function setArguments(array $arguments = []) * Adds an array of InputArgument objects. * * @param InputArgument[] $arguments An array of InputArgument objects - * - * @return void */ - public function addArguments(?array $arguments = []) + public function addArguments(?array $arguments = []): void { if (null !== $arguments) { foreach ($arguments as $argument) { @@ -98,11 +92,9 @@ public function addArguments(?array $arguments = []) } /** - * @return void - * * @throws LogicException When incorrect argument is given */ - public function addArgument(InputArgument $argument) + public function addArgument(InputArgument $argument): void { if (isset($this->arguments[$argument->getName()])) { throw new LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName())); @@ -198,10 +190,8 @@ public function getArgumentDefaults(): array * Sets the InputOption objects. * * @param InputOption[] $options An array of InputOption objects - * - * @return void */ - public function setOptions(array $options = []) + public function setOptions(array $options = []): void { $this->options = []; $this->shortcuts = []; @@ -213,10 +203,8 @@ public function setOptions(array $options = []) * Adds an array of InputOption objects. * * @param InputOption[] $options An array of InputOption objects - * - * @return void */ - public function addOptions(array $options = []) + public function addOptions(array $options = []): void { foreach ($options as $option) { $this->addOption($option); @@ -224,11 +212,9 @@ public function addOptions(array $options = []) } /** - * @return void - * * @throws LogicException When option given already exist */ - public function addOption(InputOption $option) + public function addOption(InputOption $option): void { if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) { throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName())); diff --git a/Input/InputInterface.php b/Input/InputInterface.php index aaed5fd01..c177d960b 100644 --- a/Input/InputInterface.php +++ b/Input/InputInterface.php @@ -18,9 +18,6 @@ * InputInterface is the interface implemented by all input classes. * * @author Fabien Potencier - * - * @method string __toString() Returns a stringified representation of the args passed to the command. - * InputArguments MUST be escaped as well as the InputOption values passed to the command. */ interface InputInterface { @@ -53,28 +50,22 @@ public function hasParameterOption(string|array $values, bool $onlyParams = fals * @param string|array $values The value(s) to look for in the raw parameters (can be an array) * @param string|bool|int|float|array|null $default The default value to return if no result is found * @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal - * - * @return mixed */ - public function getParameterOption(string|array $values, string|bool|int|float|array|null $default = false, bool $onlyParams = false); + public function getParameterOption(string|array $values, string|bool|int|float|array|null $default = false, bool $onlyParams = false): mixed; /** * Binds the current Input instance with the given arguments and options. * - * @return void - * * @throws RuntimeException */ - public function bind(InputDefinition $definition); + public function bind(InputDefinition $definition): void; /** * Validates the input. * - * @return void - * * @throws RuntimeException When not enough arguments are given */ - public function validate(); + public function validate(): void; /** * Returns all the given arguments merged with the default values. @@ -86,20 +77,16 @@ public function getArguments(): array; /** * Returns the argument value for a given argument name. * - * @return mixed - * * @throws InvalidArgumentException When argument given doesn't exist */ - public function getArgument(string $name); + public function getArgument(string $name): mixed; /** * Sets an argument value by name. * - * @return void - * * @throws InvalidArgumentException When argument given doesn't exist */ - public function setArgument(string $name, mixed $value); + public function setArgument(string $name, mixed $value): void; /** * Returns true if an InputArgument object exists by name or position. @@ -116,20 +103,16 @@ public function getOptions(): array; /** * Returns the option value for a given option name. * - * @return mixed - * * @throws InvalidArgumentException When option given doesn't exist */ - public function getOption(string $name); + public function getOption(string $name): mixed; /** * Sets an option value by name. * - * @return void - * * @throws InvalidArgumentException When option given doesn't exist */ - public function setOption(string $name, mixed $value); + public function setOption(string $name, mixed $value): void; /** * Returns true if an InputOption object exists by name. @@ -143,8 +126,13 @@ public function isInteractive(): bool; /** * Sets the input interactivity. + */ + public function setInteractive(bool $interactive): void; + + /** + * Returns a stringified representation of the args passed to the command. * - * @return void + * InputArguments MUST be escaped as well as the InputOption values passed to the command. */ - public function setInteractive(bool $interactive); + public function __toString(): string; } diff --git a/Input/InputOption.php b/Input/InputOption.php index bb533801f..617c348d5 100644 --- a/Input/InputOption.php +++ b/Input/InputOption.php @@ -46,32 +46,36 @@ class InputOption public const VALUE_IS_ARRAY = 8; /** - * The option may have either positive or negative value (e.g. --ansi or --no-ansi). + * The option allows passing a negated variant (e.g. --ansi or --no-ansi). */ public const VALUE_NEGATABLE = 16; private string $name; - private string|array|null $shortcut; + private ?string $shortcut; private int $mode; - private string|int|bool|array|null|float $default; - private array|\Closure $suggestedValues; - private string $description; + private string|int|bool|array|float|null $default; /** * @param string|array|null $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts - * @param int|null $mode The option mode: One of the VALUE_* constants + * @param int-mask-of|null $mode The option mode: One of the VALUE_* constants * @param string|bool|int|float|array|null $default The default value (must be null for self::VALUE_NONE) * @param array|\Closure(CompletionInput,CompletionSuggestions):list $suggestedValues The values used for input completion * * @throws InvalidArgumentException If option mode is invalid or incompatible */ - public function __construct(string $name, string|array|null $shortcut = null, ?int $mode = null, string $description = '', string|bool|int|float|array|null $default = null, array|\Closure $suggestedValues = []) - { + public function __construct( + string $name, + string|array|null $shortcut = null, + ?int $mode = null, + private string $description = '', + string|bool|int|float|array|null $default = null, + private array|\Closure $suggestedValues = [], + ) { if (str_starts_with($name, '--')) { $name = substr($name, 2); } - if (empty($name)) { + if (!$name) { throw new InvalidArgumentException('An option name cannot be empty.'); } @@ -101,8 +105,6 @@ public function __construct(string $name, string|array|null $shortcut = null, ?i $this->name = $name; $this->shortcut = $shortcut; $this->mode = $mode; - $this->description = $description; - $this->suggestedValues = $suggestedValues; if ($suggestedValues && !$this->acceptValue()) { throw new LogicException('Cannot set suggested values if the option does not accept a value.'); @@ -173,19 +175,21 @@ public function isArray(): bool return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode); } + /** + * Returns true if the option allows passing a negated variant. + * + * @return bool true if mode is self::VALUE_NEGATABLE, false otherwise + */ public function isNegatable(): bool { return self::VALUE_NEGATABLE === (self::VALUE_NEGATABLE & $this->mode); } /** - * @return void + * Sets the default value. */ - public function setDefault(string|bool|int|float|array|null $default = null) + public function setDefault(string|bool|int|float|array|null $default): void { - if (1 > \func_num_args()) { - trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); - } if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) { throw new LogicException('Cannot set a default value when using InputOption::VALUE_NONE mode.'); } @@ -217,13 +221,16 @@ public function getDescription(): string return $this->description; } + /** + * Returns true if the option has values for input completion. + */ public function hasCompletion(): bool { return [] !== $this->suggestedValues; } /** - * Adds suggestions to $suggestions for the current completion input. + * Supplies suggestions when command resolves possible completion options for input. * * @see Command::complete() */ diff --git a/Input/StreamableInputInterface.php b/Input/StreamableInputInterface.php index 4b95fcb11..4a0dc017f 100644 --- a/Input/StreamableInputInterface.php +++ b/Input/StreamableInputInterface.php @@ -25,10 +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); + public function setStream($stream): void; /** * Returns the input stream. diff --git a/Input/StringInput.php b/Input/StringInput.php index 82bd21440..835700143 100644 --- a/Input/StringInput.php +++ b/Input/StringInput.php @@ -24,10 +24,6 @@ */ class StringInput extends ArgvInput { - /** - * @deprecated since Symfony 6.1 - */ - public const REGEX_STRING = '([^\s]+?)(?:\s|(? + * * @throws InvalidArgumentException When unable to parse input (should never happen) */ private function tokenize(string $input): array diff --git a/Logger/ConsoleLogger.php b/Logger/ConsoleLogger.php index fddef50cd..ad6e49ce1 100644 --- a/Logger/ConsoleLogger.php +++ b/Logger/ConsoleLogger.php @@ -29,7 +29,6 @@ class ConsoleLogger extends AbstractLogger public const INFO = 'info'; public const ERROR = 'error'; - private OutputInterface $output; private array $verbosityLevelMap = [ LogLevel::EMERGENCY => OutputInterface::VERBOSITY_NORMAL, LogLevel::ALERT => OutputInterface::VERBOSITY_NORMAL, @@ -52,9 +51,11 @@ class ConsoleLogger extends AbstractLogger ]; private bool $errored = false; - public function __construct(OutputInterface $output, array $verbosityLevelMap = [], array $formatLevelMap = []) - { - $this->output = $output; + public function __construct( + private OutputInterface $output, + array $verbosityLevelMap = [], + array $formatLevelMap = [], + ) { $this->verbosityLevelMap = $verbosityLevelMap + $this->verbosityLevelMap; $this->formatLevelMap = $formatLevelMap + $this->formatLevelMap; } diff --git a/Messenger/RunCommandMessageHandler.php b/Messenger/RunCommandMessageHandler.php index 14f9c1764..1bc499494 100644 --- a/Messenger/RunCommandMessageHandler.php +++ b/Messenger/RunCommandMessageHandler.php @@ -22,8 +22,9 @@ */ final class RunCommandMessageHandler { - public function __construct(private readonly Application $application) - { + public function __construct( + private readonly Application $application, + ) { } public function __invoke(RunCommandMessage $message): RunCommandContext diff --git a/Output/AnsiColorMode.php b/Output/AnsiColorMode.php index 5f9f744fe..ca40ffb79 100644 --- a/Output/AnsiColorMode.php +++ b/Output/AnsiColorMode.php @@ -63,7 +63,7 @@ public function convertFromHexToAnsiColorCode(string $hexColor): string return match ($this) { self::Ansi4 => (string) $this->convertFromRGB($r, $g, $b), self::Ansi8 => '8;5;'.((string) $this->convertFromRGB($r, $g, $b)), - self::Ansi24 => sprintf('8;2;%d;%d;%d', $r, $g, $b) + self::Ansi24 => sprintf('8;2;%d;%d;%d', $r, $g, $b), }; } @@ -72,7 +72,7 @@ private function convertFromRGB(int $r, int $g, int $b): int return match ($this) { self::Ansi4 => $this->degradeHexColorToAnsi4($r, $g, $b), self::Ansi8 => $this->degradeHexColorToAnsi8($r, $g, $b), - default => throw new InvalidArgumentException("RGB cannot be converted to {$this->name}.") + default => throw new InvalidArgumentException("RGB cannot be converted to {$this->name}."), }; } diff --git a/Output/BufferedOutput.php b/Output/BufferedOutput.php index ef5099bfd..3c8d3906f 100644 --- a/Output/BufferedOutput.php +++ b/Output/BufferedOutput.php @@ -29,10 +29,7 @@ public function fetch(): string return $content; } - /** - * @return void - */ - protected function doWrite(string $message, bool $newline) + protected function doWrite(string $message, bool $newline): void { $this->buffer .= $message; diff --git a/Output/ConsoleOutput.php b/Output/ConsoleOutput.php index 5837e74a3..2ad3dbcf3 100644 --- a/Output/ConsoleOutput.php +++ b/Output/ConsoleOutput.php @@ -64,28 +64,19 @@ public function section(): ConsoleSectionOutput return new ConsoleSectionOutput($this->getStream(), $this->consoleSectionOutputs, $this->getVerbosity(), $this->isDecorated(), $this->getFormatter()); } - /** - * @return void - */ - public function setDecorated(bool $decorated) + public function setDecorated(bool $decorated): void { parent::setDecorated($decorated); $this->stderr->setDecorated($decorated); } - /** - * @return void - */ - public function setFormatter(OutputFormatterInterface $formatter) + public function setFormatter(OutputFormatterInterface $formatter): void { parent::setFormatter($formatter); $this->stderr->setFormatter($formatter); } - /** - * @return void - */ - public function setVerbosity(int $level) + public function setVerbosity(int $level): void { parent::setVerbosity($level); $this->stderr->setVerbosity($level); @@ -96,10 +87,7 @@ public function getErrorOutput(): OutputInterface return $this->stderr; } - /** - * @return void - */ - public function setErrorOutput(OutputInterface $error) + public function setErrorOutput(OutputInterface $error): void { $this->stderr = $error; } diff --git a/Output/ConsoleOutputInterface.php b/Output/ConsoleOutputInterface.php index 9c0049c8f..1f8f147ce 100644 --- a/Output/ConsoleOutputInterface.php +++ b/Output/ConsoleOutputInterface.php @@ -24,10 +24,7 @@ interface ConsoleOutputInterface extends OutputInterface */ public function getErrorOutput(): OutputInterface; - /** - * @return void - */ - public function setErrorOutput(OutputInterface $error); + public function setErrorOutput(OutputInterface $error): void; public function section(): ConsoleSectionOutput; } diff --git a/Output/ConsoleSectionOutput.php b/Output/ConsoleSectionOutput.php index f2d7933bb..09aa7fe99 100644 --- a/Output/ConsoleSectionOutput.php +++ b/Output/ConsoleSectionOutput.php @@ -60,12 +60,10 @@ public function setMaxHeight(int $maxHeight): void * 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) + public function clear(?int $lines = null): void { - if (empty($this->content) || !$this->isDecorated()) { + if (!$this->content || !$this->isDecorated()) { return; } @@ -83,10 +81,8 @@ public function clear(?int $lines = null) /** * Overwrites the previous output with a new message. - * - * @return void */ - public function overwrite(string|iterable $message) + public function overwrite(string|iterable $message): void { $this->clear(); $this->writeln($message); @@ -162,10 +158,7 @@ public function addNewLineOfInputSubmit(): void ++$this->lines; } - /** - * @return void - */ - protected function doWrite(string $message, bool $newline) + protected function doWrite(string $message, bool $newline): void { // Simulate newline behavior for consistent output formatting, avoiding extra logic if (!$newline && str_ends_with($message, \PHP_EOL)) { diff --git a/Output/NullOutput.php b/Output/NullOutput.php index f3aa15b1d..40ae33282 100644 --- a/Output/NullOutput.php +++ b/Output/NullOutput.php @@ -26,10 +26,7 @@ class NullOutput implements OutputInterface { private NullOutputFormatter $formatter; - /** - * @return void - */ - public function setFormatter(OutputFormatterInterface $formatter) + public function setFormatter(OutputFormatterInterface $formatter): void { // do nothing } @@ -40,10 +37,7 @@ public function getFormatter(): OutputFormatterInterface return $this->formatter ??= new NullOutputFormatter(); } - /** - * @return void - */ - public function setDecorated(bool $decorated) + public function setDecorated(bool $decorated): void { // do nothing } @@ -53,10 +47,7 @@ public function isDecorated(): bool return false; } - /** - * @return void - */ - public function setVerbosity(int $level) + public function setVerbosity(int $level): void { // do nothing } @@ -86,18 +77,12 @@ public function isDebug(): bool return false; } - /** - * @return void - */ - public function writeln(string|iterable $messages, int $options = self::OUTPUT_NORMAL) + public function writeln(string|iterable $messages, int $options = self::OUTPUT_NORMAL): void { // do nothing } - /** - * @return void - */ - public function write(string|iterable $messages, bool $newline = false, int $options = self::OUTPUT_NORMAL) + public function write(string|iterable $messages, bool $newline = false, int $options = self::OUTPUT_NORMAL): void { // do nothing } diff --git a/Output/Output.php b/Output/Output.php index 00f481e03..2bb105748 100644 --- a/Output/Output.php +++ b/Output/Output.php @@ -44,10 +44,7 @@ public function __construct(?int $verbosity = self::VERBOSITY_NORMAL, bool $deco $this->formatter->setDecorated($decorated); } - /** - * @return void - */ - public function setFormatter(OutputFormatterInterface $formatter) + public function setFormatter(OutputFormatterInterface $formatter): void { $this->formatter = $formatter; } @@ -57,10 +54,7 @@ public function getFormatter(): OutputFormatterInterface return $this->formatter; } - /** - * @return void - */ - public function setDecorated(bool $decorated) + public function setDecorated(bool $decorated): void { $this->formatter->setDecorated($decorated); } @@ -70,10 +64,7 @@ public function isDecorated(): bool return $this->formatter->isDecorated(); } - /** - * @return void - */ - public function setVerbosity(int $level) + public function setVerbosity(int $level): void { $this->verbosity = $level; } @@ -103,18 +94,12 @@ public function isDebug(): bool return self::VERBOSITY_DEBUG <= $this->verbosity; } - /** - * @return void - */ - public function writeln(string|iterable $messages, int $options = self::OUTPUT_NORMAL) + public function writeln(string|iterable $messages, int $options = self::OUTPUT_NORMAL): void { $this->write($messages, true, $options); } - /** - * @return void - */ - public function write(string|iterable $messages, bool $newline = false, int $options = self::OUTPUT_NORMAL) + public function write(string|iterable $messages, bool $newline = false, int $options = self::OUTPUT_NORMAL): void { if (!is_iterable($messages)) { $messages = [$messages]; @@ -148,8 +133,6 @@ public function write(string|iterable $messages, bool $newline = false, int $opt /** * Writes a message to the output. - * - * @return void */ - abstract protected function doWrite(string $message, bool $newline); + abstract protected function doWrite(string $message, bool $newline): void; } diff --git a/Output/OutputInterface.php b/Output/OutputInterface.php index 19a817901..41315fbf2 100644 --- a/Output/OutputInterface.php +++ b/Output/OutputInterface.php @@ -36,29 +36,23 @@ 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); + public function write(string|iterable $messages, bool $newline = false, int $options = 0): void; /** * Writes a message to the output and adds a newline at the end. * * @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); + public function writeln(string|iterable $messages, int $options = 0): void; /** * Sets the verbosity of the output. * * @param self::VERBOSITY_* $level - * - * @return void */ - public function setVerbosity(int $level); + public function setVerbosity(int $level): void; /** * Gets the current verbosity of the output. @@ -89,20 +83,15 @@ public function isDebug(): bool; /** * Sets the decorated flag. - * - * @return void */ - public function setDecorated(bool $decorated); + public function setDecorated(bool $decorated): void; /** * Gets the decorated flag. */ public function isDecorated(): bool; - /** - * @return void - */ - public function setFormatter(OutputFormatterInterface $formatter); + public function setFormatter(OutputFormatterInterface $formatter): void; /** * Returns current output formatter instance. diff --git a/Output/StreamOutput.php b/Output/StreamOutput.php index f51d03763..e94a5eb8b 100644 --- a/Output/StreamOutput.php +++ b/Output/StreamOutput.php @@ -63,10 +63,7 @@ public function getStream() return $this->stream; } - /** - * @return void - */ - protected function doWrite(string $message, bool $newline) + protected function doWrite(string $message, bool $newline): void { if ($newline) { $message .= \PHP_EOL; diff --git a/Output/TrimmedBufferOutput.php b/Output/TrimmedBufferOutput.php index 23a2be8c3..c1862a2bd 100644 --- a/Output/TrimmedBufferOutput.php +++ b/Output/TrimmedBufferOutput.php @@ -45,10 +45,7 @@ public function fetch(): string return $content; } - /** - * @return void - */ - protected function doWrite(string $message, bool $newline) + protected function doWrite(string $message, bool $newline): void { $this->buffer .= $message; diff --git a/Question/ChoiceQuestion.php b/Question/ChoiceQuestion.php index 465f3184f..d062ce78d 100644 --- a/Question/ChoiceQuestion.php +++ b/Question/ChoiceQuestion.php @@ -20,7 +20,6 @@ */ class ChoiceQuestion extends Question { - private array $choices; private bool $multiselect = false; private string $prompt = ' > '; private string $errorMessage = 'Value "%s" is invalid'; @@ -30,15 +29,17 @@ class ChoiceQuestion extends Question * @param array $choices The list of available choices * @param string|bool|int|float|null $default The default answer to return */ - public function __construct(string $question, array $choices, string|bool|int|float|null $default = null) - { + public function __construct( + string $question, + private array $choices, + string|bool|int|float|null $default = null, + ) { if (!$choices) { throw new \LogicException('Choice question must have at least 1 choice available.'); } parent::__construct($question, $default); - $this->choices = $choices; $this->setValidator($this->getDefaultValidator()); $this->setAutocompleterValues($choices); } diff --git a/Question/ConfirmationQuestion.php b/Question/ConfirmationQuestion.php index 40eab2429..951d68140 100644 --- a/Question/ConfirmationQuestion.php +++ b/Question/ConfirmationQuestion.php @@ -18,18 +18,18 @@ */ class ConfirmationQuestion extends Question { - private string $trueAnswerRegex; - /** * @param string $question The question to ask to the user * @param bool $default The default answer to return, true or false * @param string $trueAnswerRegex A regex to match the "yes" answer */ - public function __construct(string $question, bool $default = true, string $trueAnswerRegex = '/^y/i') - { + public function __construct( + string $question, + bool $default = true, + private string $trueAnswerRegex = '/^y/i', + ) { parent::__construct($question, $default); - $this->trueAnswerRegex = $trueAnswerRegex; $this->setNormalizer($this->getDefaultNormalizer()); } diff --git a/Question/Question.php b/Question/Question.php index 94c688fa8..46a60c798 100644 --- a/Question/Question.php +++ b/Question/Question.php @@ -21,13 +21,11 @@ */ class Question { - private string $question; private ?int $attempts = null; private bool $hidden = false; private bool $hiddenFallback = true; private ?\Closure $autocompleterCallback = null; private ?\Closure $validator = null; - private string|int|bool|null|float $default; private ?\Closure $normalizer = null; private bool $trimmable = true; private bool $multiline = false; @@ -36,10 +34,10 @@ class Question * @param string $question The question to ask to the user * @param string|bool|int|float|null $default The default answer to return if the user enters nothing */ - public function __construct(string $question, string|bool|int|float|null $default = null) - { - $this->question = $question; - $this->default = $default; + public function __construct( + private string $question, + private string|bool|int|float|null $default = null, + ) { } /** @@ -175,11 +173,8 @@ public function getAutocompleterCallback(): ?callable * * @return $this */ - public function setAutocompleterCallback(?callable $callback = null): static + public function setAutocompleterCallback(?callable $callback): static { - if (1 > \func_num_args()) { - trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); - } if ($this->hidden && null !== $callback) { throw new LogicException('A hidden question cannot use the autocompleter.'); } @@ -194,11 +189,8 @@ public function setAutocompleterCallback(?callable $callback = null): static * * @return $this */ - public function setValidator(?callable $validator = null): static + public function setValidator(?callable $validator): static { - if (1 > \func_num_args()) { - trigger_deprecation('symfony/console', '6.2', 'Calling "%s()" without any arguments is deprecated, pass null explicitly instead.', __METHOD__); - } $this->validator = null === $validator ? null : $validator(...); return $this; @@ -266,10 +258,7 @@ public function getNormalizer(): ?callable return $this->normalizer; } - /** - * @return bool - */ - protected function isAssoc(array $array) + protected function isAssoc(array $array): bool { return (bool) \count(array_filter(array_keys($array), 'is_string')); } diff --git a/README.md b/README.md index e9013182a..92f70e714 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,7 @@ interfaces. Sponsor ------- -The Console component for Symfony 6.4 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! +Help Symfony by [sponsoring][1] its development! Resources --------- @@ -31,6 +24,4 @@ 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 +[1]: https://symfony.com/sponsor diff --git a/Resources/completion.fish b/Resources/completion.fish index 1c34292ae..1853dd80f 100644 --- a/Resources/completion.fish +++ b/Resources/completion.fish @@ -19,11 +19,7 @@ function _sf_{{ COMMAND_NAME }} set completecmd $completecmd "-c$c" - set sfcomplete ($completecmd) - - for i in $sfcomplete - echo $i - end + $completecmd end complete -c '{{ COMMAND_NAME }}' -a '(_sf_{{ COMMAND_NAME }})' -f diff --git a/Style/OutputStyle.php b/Style/OutputStyle.php index ddfa8decc..9f62ea312 100644 --- a/Style/OutputStyle.php +++ b/Style/OutputStyle.php @@ -23,17 +23,12 @@ */ abstract class OutputStyle implements OutputInterface, StyleInterface { - private OutputInterface $output; - - public function __construct(OutputInterface $output) - { - $this->output = $output; + public function __construct( + private OutputInterface $output, + ) { } - /** - * @return void - */ - public function newLine(int $count = 1) + public function newLine(int $count = 1): void { $this->output->write(str_repeat(\PHP_EOL, $count)); } @@ -43,26 +38,17 @@ public function createProgressBar(int $max = 0): ProgressBar return new ProgressBar($this->output, $max); } - /** - * @return void - */ - public function write(string|iterable $messages, bool $newline = false, int $type = self::OUTPUT_NORMAL) + public function write(string|iterable $messages, bool $newline = false, int $type = self::OUTPUT_NORMAL): void { $this->output->write($messages, $newline, $type); } - /** - * @return void - */ - public function writeln(string|iterable $messages, int $type = self::OUTPUT_NORMAL) + public function writeln(string|iterable $messages, int $type = self::OUTPUT_NORMAL): void { $this->output->writeln($messages, $type); } - /** - * @return void - */ - public function setVerbosity(int $level) + public function setVerbosity(int $level): void { $this->output->setVerbosity($level); } @@ -72,10 +58,7 @@ public function getVerbosity(): int return $this->output->getVerbosity(); } - /** - * @return void - */ - public function setDecorated(bool $decorated) + public function setDecorated(bool $decorated): void { $this->output->setDecorated($decorated); } @@ -85,10 +68,7 @@ public function isDecorated(): bool return $this->output->isDecorated(); } - /** - * @return void - */ - public function setFormatter(OutputFormatterInterface $formatter) + public function setFormatter(OutputFormatterInterface $formatter): void { $this->output->setFormatter($formatter); } @@ -118,10 +98,7 @@ public function isDebug(): bool return $this->output->isDebug(); } - /** - * @return OutputInterface - */ - protected function getErrorOutput() + protected function getErrorOutput(): OutputInterface { if (!$this->output instanceof ConsoleOutputInterface) { return $this->output; diff --git a/Style/StyleInterface.php b/Style/StyleInterface.php index 6bced158a..fcc5bc775 100644 --- a/Style/StyleInterface.php +++ b/Style/StyleInterface.php @@ -20,73 +20,53 @@ interface StyleInterface { /** * Formats a command title. - * - * @return void */ - public function title(string $message); + public function title(string $message): void; /** * Formats a section title. - * - * @return void */ - public function section(string $message); + public function section(string $message): void; /** * Formats a list. - * - * @return void */ - public function listing(array $elements); + public function listing(array $elements): void; /** * Formats informational text. - * - * @return void */ - public function text(string|array $message); + public function text(string|array $message): void; /** * Formats a success result bar. - * - * @return void */ - public function success(string|array $message); + public function success(string|array $message): void; /** * Formats an error result bar. - * - * @return void */ - public function error(string|array $message); + public function error(string|array $message): void; /** * Formats an warning result bar. - * - * @return void */ - public function warning(string|array $message); + public function warning(string|array $message): void; /** * Formats a note admonition. - * - * @return void */ - public function note(string|array $message); + public function note(string|array $message): void; /** * Formats a caution admonition. - * - * @return void */ - public function caution(string|array $message); + public function caution(string|array $message): void; /** * Formats a table. - * - * @return void */ - public function table(array $headers, array $rows); + public function table(array $headers, array $rows): void; /** * Asks a question. @@ -110,29 +90,21 @@ public function choice(string $question, array $choices, mixed $default = null): /** * Add newline(s). - * - * @return void */ - public function newLine(int $count = 1); + public function newLine(int $count = 1): void; /** * Starts the progress output. - * - * @return void */ - public function progressStart(int $max = 0); + public function progressStart(int $max = 0): void; /** * Advances the progress output X steps. - * - * @return void */ - public function progressAdvance(int $step = 1); + public function progressAdvance(int $step = 1): void; /** * Finishes the progress output. - * - * @return void */ - public function progressFinish(); + public function progressFinish(): void; } diff --git a/Style/SymfonyStyle.php b/Style/SymfonyStyle.php index 03bda8784..19ad892ef 100644 --- a/Style/SymfonyStyle.php +++ b/Style/SymfonyStyle.php @@ -40,30 +40,27 @@ class SymfonyStyle extends OutputStyle { public const MAX_LINE_LENGTH = 120; - private InputInterface $input; - private OutputInterface $output; private SymfonyQuestionHelper $questionHelper; private ProgressBar $progressBar; private int $lineLength; private TrimmedBufferOutput $bufferedOutput; - public function __construct(InputInterface $input, OutputInterface $output) - { - $this->input = $input; + public function __construct( + private InputInterface $input, + private OutputInterface $output, + ) { $this->bufferedOutput = new TrimmedBufferOutput(\DIRECTORY_SEPARATOR === '\\' ? 4 : 2, $output->getVerbosity(), false, clone $output->getFormatter()); // Windows cmd wraps lines as soon as the terminal width is reached, whether there are following chars or not. $width = (new Terminal())->getWidth() ?: self::MAX_LINE_LENGTH; $this->lineLength = min($width - (int) (\DIRECTORY_SEPARATOR === '\\'), self::MAX_LINE_LENGTH); - parent::__construct($this->output = $output); + parent::__construct($output); } /** * 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) + public function block(string|array $messages, ?string $type = null, ?string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = true): void { $messages = \is_array($messages) ? array_values($messages) : [$messages]; @@ -72,10 +69,7 @@ public function block(string|array $messages, ?string $type = null, ?string $sty $this->newLine(); } - /** - * @return void - */ - public function title(string $message) + public function title(string $message): void { $this->autoPrependBlock(); $this->writeln([ @@ -85,10 +79,7 @@ public function title(string $message) $this->newLine(); } - /** - * @return void - */ - public function section(string $message) + public function section(string $message): void { $this->autoPrependBlock(); $this->writeln([ @@ -98,10 +89,7 @@ public function section(string $message) $this->newLine(); } - /** - * @return void - */ - public function listing(array $elements) + public function listing(array $elements): void { $this->autoPrependText(); $elements = array_map(fn ($element) => sprintf(' * %s', $element), $elements); @@ -110,10 +98,7 @@ public function listing(array $elements) $this->newLine(); } - /** - * @return void - */ - public function text(string|array $message) + public function text(string|array $message): void { $this->autoPrependText(); @@ -125,68 +110,46 @@ public function text(string|array $message) /** * Formats a command comment. - * - * @return void */ - public function comment(string|array $message) + public function comment(string|array $message): void { $this->block($message, null, null, ' // ', false, false); } - /** - * @return void - */ - public function success(string|array $message) + public function success(string|array $message): void { $this->block($message, 'OK', 'fg=black;bg=green', ' ', true); } - /** - * @return void - */ - public function error(string|array $message) + public function error(string|array $message): void { $this->block($message, 'ERROR', 'fg=white;bg=red', ' ', true); } - /** - * @return void - */ - public function warning(string|array $message) + public function warning(string|array $message): void { $this->block($message, 'WARNING', 'fg=black;bg=yellow', ' ', true); } - /** - * @return void - */ - public function note(string|array $message) + public function note(string|array $message): void { $this->block($message, 'NOTE', 'fg=yellow', ' ! '); } /** * Formats an info message. - * - * @return void */ - public function info(string|array $message) + public function info(string|array $message): void { $this->block($message, 'INFO', 'fg=green', ' ', true); } - /** - * @return void - */ - public function caution(string|array $message) + public function caution(string|array $message): void { $this->block($message, 'CAUTION', 'fg=white;bg=red', ' ! ', true); } - /** - * @return void - */ - public function table(array $headers, array $rows) + public function table(array $headers, array $rows): void { $this->createTable() ->setHeaders($headers) @@ -199,10 +162,8 @@ public function table(array $headers, array $rows) /** * Formats a horizontal table. - * - * @return void */ - public function horizontalTable(array $headers, array $rows) + public function horizontalTable(array $headers, array $rows): void { $this->createTable() ->setHorizontal(true) @@ -221,10 +182,8 @@ public function horizontalTable(array $headers, array $rows) * * 'A title' * * ['key' => 'value'] * * new TableSeparator() - * - * @return void */ - public function definitionList(string|array|TableSeparator ...$list) + public function definitionList(string|array|TableSeparator ...$list): void { $headers = []; $row = []; @@ -285,27 +244,18 @@ public function choice(string $question, array $choices, mixed $default = null, return $this->askQuestion($questionChoice); } - /** - * @return void - */ - public function progressStart(int $max = 0) + public function progressStart(int $max = 0): void { $this->progressBar = $this->createProgressBar($max); $this->progressBar->start(); } - /** - * @return void - */ - public function progressAdvance(int $step = 1) + public function progressAdvance(int $step = 1): void { $this->getProgressBar()->advance($step); } - /** - * @return void - */ - public function progressFinish() + public function progressFinish(): void { $this->getProgressBar()->finish(); $this->newLine(2); @@ -366,10 +316,7 @@ public function askQuestion(Question $question): mixed return $answer; } - /** - * @return void - */ - public function writeln(string|iterable $messages, int $type = self::OUTPUT_NORMAL) + public function writeln(string|iterable $messages, int $type = self::OUTPUT_NORMAL): void { if (!is_iterable($messages)) { $messages = [$messages]; @@ -381,10 +328,7 @@ public function writeln(string|iterable $messages, int $type = self::OUTPUT_NORM } } - /** - * @return void - */ - public function write(string|iterable $messages, bool $newline = false, int $type = self::OUTPUT_NORMAL) + public function write(string|iterable $messages, bool $newline = false, int $type = self::OUTPUT_NORMAL): void { if (!is_iterable($messages)) { $messages = [$messages]; @@ -396,10 +340,7 @@ public function write(string|iterable $messages, bool $newline = false, int $typ } } - /** - * @return void - */ - public function newLine(int $count = 1) + public function newLine(int $count = 1): void { parent::newLine($count); $this->bufferedOutput->write(str_repeat("\n", $count)); diff --git a/Terminal.php b/Terminal.php index f094adedc..80f254434 100644 --- a/Terminal.php +++ b/Terminal.php @@ -140,7 +140,7 @@ private static function initDimensions(): void // or [w, h] from "wxh" self::$width = (int) $matches[1]; self::$height = isset($matches[4]) ? (int) $matches[4] : (int) $matches[2]; - } elseif (!self::hasVt100Support() && self::hasSttyAvailable()) { + } elseif (!sapi_windows_vt100_support(fopen('php://stdout', 'w')) && self::hasSttyAvailable()) { // only use stty on Windows if the terminal does not support vt100 (e.g. Windows 7 + git-bash) // testing for stty in a Windows 10 vt100-enabled console will implicitly disable vt100 support on STDOUT self::initDimensionsUsingStty(); @@ -154,14 +154,6 @@ private static function initDimensions(): void } } - /** - * Returns whether STDOUT has vt100 support (some Windows 10+ configurations). - */ - private static function hasVt100Support(): bool - { - return \function_exists('sapi_windows_vt100_support') && sapi_windows_vt100_support(fopen('php://stdout', 'w')); - } - /** * Initializes dimensions using the output of an stty columns line. */ diff --git a/Tester/ApplicationTester.php b/Tester/ApplicationTester.php index 58aee54d6..cebb6f8eb 100644 --- a/Tester/ApplicationTester.php +++ b/Tester/ApplicationTester.php @@ -28,11 +28,9 @@ class ApplicationTester { use TesterTrait; - private Application $application; - - public function __construct(Application $application) - { - $this->application = $application; + public function __construct( + private Application $application, + ) { } /** diff --git a/Tester/CommandCompletionTester.php b/Tester/CommandCompletionTester.php index a90fe52ef..76cbaf14f 100644 --- a/Tester/CommandCompletionTester.php +++ b/Tester/CommandCompletionTester.php @@ -22,11 +22,9 @@ */ class CommandCompletionTester { - private Command $command; - - public function __construct(Command $command) - { - $this->command = $command; + public function __construct( + private Command $command, + ) { } /** diff --git a/Tester/CommandTester.php b/Tester/CommandTester.php index 2ff813b7d..d39cde7f6 100644 --- a/Tester/CommandTester.php +++ b/Tester/CommandTester.php @@ -24,11 +24,9 @@ class CommandTester { use TesterTrait; - private Command $command; - - public function __construct(Command $command) - { - $this->command = $command; + public function __construct( + private Command $command, + ) { } /** diff --git a/Tests/Command/CommandTest.php b/Tests/Command/CommandTest.php index 76dacfadb..f3e4b51d1 100644 --- a/Tests/Command/CommandTest.php +++ b/Tests/Command/CommandTest.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Console\Tests\Command; use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\Console\Application; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; @@ -29,8 +28,6 @@ class CommandTest extends TestCase { - use ExpectDeprecationTrait; - protected static string $fixturesPath; public static function setUpBeforeClass(): void @@ -441,37 +438,6 @@ public function testCommandAttribute() $this->assertSame(['f'], $command->getAliases()); } - /** - * @group legacy - */ - public function testDefaultNameProperty() - { - $this->expectDeprecation('Since symfony/console 6.1: Relying on the static property "$defaultName" for setting a command name is deprecated. Add the "Symfony\Component\Console\Attribute\AsCommand" attribute to the "Symfony\Component\Console\Tests\Command\MyCommand" class instead.'); - - $this->assertSame('my:command', MyCommand::getDefaultName()); - } - - /** - * @group legacy - */ - public function testDefaultDescriptionProperty() - { - $this->expectDeprecation('Since symfony/console 6.1: Relying on the static property "$defaultDescription" for setting a command description is deprecated. Add the "Symfony\Component\Console\Attribute\AsCommand" attribute to the "Symfony\Component\Console\Tests\Command\MyCommand" class instead.'); - - $this->assertSame('This is a command I wrote all by myself', MyCommand::getDefaultDescription()); - } - - /** - * @group legacy - */ - public function testStaticDefaultProperties() - { - $command = new MyCommand(); - - $this->assertSame('my:command', $command->getName()); - $this->assertSame('This is a command I wrote all by myself', $command->getDescription()); - } - public function testAttributeOverridesProperty() { $this->assertSame('my:command', MyAnnotatedCommand::getDefaultName()); @@ -517,29 +483,10 @@ class Php8Command2 extends Command { } -class MyCommand extends Command -{ - /** - * @deprecated since Symfony 6.1 - */ - protected static $defaultName = 'my:command'; - - /** - * @deprecated since Symfony 6.1 - */ - protected static $defaultDescription = 'This is a command I wrote all by myself'; -} - #[AsCommand(name: 'my:command', description: 'This is a command I wrote all by myself')] class MyAnnotatedCommand extends Command { - /** - * @deprecated since Symfony 6.1 - */ protected static $defaultName = 'i-shall-be-ignored'; - /** - * @deprecated since Symfony 6.1 - */ protected static $defaultDescription = 'This description should be ignored.'; } diff --git a/Tests/Command/LockableTraitTest.php b/Tests/Command/LockableTraitTest.php index 77b54f9ee..0268d9681 100644 --- a/Tests/Command/LockableTraitTest.php +++ b/Tests/Command/LockableTraitTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\Lock\LockFactory; +use Symfony\Component\Lock\SharedLockInterface; use Symfony\Component\Lock\Store\FlockStore; use Symfony\Component\Lock\Store\SemaphoreStore; @@ -26,6 +27,7 @@ public static function setUpBeforeClass(): void self::$fixturesPath = __DIR__.'/../Fixtures/'; require_once self::$fixturesPath.'/FooLockCommand.php'; require_once self::$fixturesPath.'/FooLock2Command.php'; + require_once self::$fixturesPath.'/FooLock3Command.php'; } public function testLockIsReleased() @@ -64,4 +66,18 @@ public function testMultipleLockCallsThrowLogicException() $tester = new CommandTester($command); $this->assertSame(1, $tester->execute([])); } + + public function testCustomLockFactoryIsUsed() + { + $lockFactory = $this->createMock(LockFactory::class); + $command = new \FooLock3Command($lockFactory); + + $tester = new CommandTester($command); + + $lock = $this->createMock(SharedLockInterface::class); + $lock->method('acquire')->willReturn(false); + + $lockFactory->expects(static::once())->method('createLock')->willReturn($lock); + $this->assertSame(1, $tester->execute([])); + } } diff --git a/Tests/Completion/Output/FishCompletionOutputTest.php b/Tests/Completion/Output/FishCompletionOutputTest.php index 2e615d040..93456e138 100644 --- a/Tests/Completion/Output/FishCompletionOutputTest.php +++ b/Tests/Completion/Output/FishCompletionOutputTest.php @@ -23,11 +23,11 @@ public function getCompletionOutput(): CompletionOutputInterface public function getExpectedOptionsOutput(): string { - return "--option1\n--negatable\n--no-negatable"; + return "--option1\tFirst Option\n--negatable\tCan be negative\n--no-negatable\tCan be negative"; } public function getExpectedValuesOutput(): string { - return "Green\nRed\nYellow"; + return "Green\tBeans are green\nRed\tRose are red\nYellow\tCanaries are yellow"; } } diff --git a/Tests/EventListener/ErrorListenerTest.php b/Tests/EventListener/ErrorListenerTest.php index 40020baee..e26109851 100644 --- a/Tests/EventListener/ErrorListenerTest.php +++ b/Tests/EventListener/ErrorListenerTest.php @@ -107,19 +107,6 @@ public function testAllKindsOfInputCanBeLogged() $listener->onConsoleTerminate($this->getConsoleTerminateEvent(new StringInput('test:run --foo=bar'), 255)); } - public function testCommandNameIsDisplayedForNonStringableInput() - { - $logger = $this->createMock(LoggerInterface::class); - $logger - ->expects($this->once()) - ->method('debug') - ->with('Command "{command}" exited with code "{code}"', ['command' => 'test:run', 'code' => 255]) - ; - - $listener = new ErrorListener($logger); - $listener->onConsoleTerminate($this->getConsoleTerminateEvent($this->createMock(InputInterface::class), 255)); - } - private function getConsoleTerminateEvent(InputInterface $input, $exitCode) { return new ConsoleTerminateEvent(new Command('test:run'), $input, $this->createMock(OutputInterface::class), $exitCode); diff --git a/Tests/Fixtures/FooLock3Command.php b/Tests/Fixtures/FooLock3Command.php new file mode 100644 index 000000000..78492de69 --- /dev/null +++ b/Tests/Fixtures/FooLock3Command.php @@ -0,0 +1,35 @@ +lockFactory = $lockFactory; + } + + protected function configure(): void + { + $this->setName('foo:lock3'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + if (!$this->lock()) { + return 1; + } + + $this->release(); + + return 2; + } +} diff --git a/Tests/Helper/ProgressBarTest.php b/Tests/Helper/ProgressBarTest.php index a1db94583..3d1bfa48f 100644 --- a/Tests/Helper/ProgressBarTest.php +++ b/Tests/Helper/ProgressBarTest.php @@ -1102,6 +1102,20 @@ public function testIterateUncountable() ); } + public function testEmptyInputWithDebugFormat() + { + $bar = new ProgressBar($output = $this->getOutputStream()); + $bar->setFormat('%current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%'); + + $this->assertEquals([], iterator_to_array($bar->iterate([]))); + + rewind($output->getStream()); + $this->assertEquals( + ' 0/0 [============================] 100% < 1 sec/< 1 sec', + stream_get_contents($output->getStream()) + ); + } + protected function getOutputStream($decorated = true, $verbosity = StreamOutput::VERBOSITY_NORMAL) { return new StreamOutput(fopen('php://memory', 'r+', false), $verbosity, $decorated); @@ -1273,7 +1287,7 @@ public function testMultiLineFormatIsFullyCorrectlyWithManuallyCleanup() 'Foo!'.\PHP_EOL. $this->generateOutput('[--->------------------------]'). "\nProcessing \"foobar\"...". - $this->generateOutput("[----->----------------------]\nProcessing \"foobar\"..."), + $this->generateOutput("[============================]\nProcessing \"foobar\"..."), stream_get_contents($output->getStream()) ); } diff --git a/Tests/Input/ArgvInputTest.php b/Tests/Input/ArgvInputTest.php index a47d557b7..80ea06d67 100644 --- a/Tests/Input/ArgvInputTest.php +++ b/Tests/Input/ArgvInputTest.php @@ -562,4 +562,31 @@ public function testParseOptionWithValueOptionalGivenEmptyAndOptionalArgument() $this->assertEquals(['foo' => '0'], $input->getOptions(), '->parse() parses optional options with empty value as null'); $this->assertEquals(['name' => 'bar'], $input->getArguments(), '->parse() parses optional arguments'); } + + public function testGetRawTokensFalse() + { + $input = new ArgvInput(['cli.php', '--foo', 'bar']); + $this->assertSame(['--foo', 'bar'], $input->getRawTokens()); + } + + /** + * @dataProvider provideGetRawTokensTrueTests + */ + public function testGetRawTokensTrue(array $argv, array $expected) + { + $input = new ArgvInput($argv); + $this->assertSame($expected, $input->getRawTokens(true)); + } + + public static function provideGetRawTokensTrueTests(): iterable + { + yield [['app/console', 'foo:bar'], []]; + yield [['app/console', 'foo:bar', '--env=prod'], ['--env=prod']]; + yield [['app/console', 'foo:bar', '--env', 'prod'], ['--env', 'prod']]; + yield [['app/console', '--no-ansi', 'foo:bar', '--env', 'prod'], ['--env', 'prod']]; + yield [['app/console', '--no-ansi', 'foo:bar', '--env', 'prod'], ['--env', 'prod']]; + yield [['app/console', '--no-ansi', 'foo:bar', 'argument'], ['argument']]; + yield [['app/console', '--no-ansi', 'foo:bar', 'foo:bar'], ['foo:bar']]; + yield [['app/console', '--no-ansi', 'foo:bar', '--', 'argument'], ['--', 'argument']]; + } } diff --git a/composer.json b/composer.json index 1610f7341..0ed1bd9af 100644 --- a/composer.json +++ b/composer.json @@ -16,34 +16,33 @@ } ], "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=8.2", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^5.4|^6.0|^7.0" + "symfony/string": "^6.4|^7.0" }, "require-dev": { - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/event-dispatcher": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", "symfony/http-foundation": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^5.4|^6.0|^7.0", - "symfony/messenger": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/stopwatch": "^5.4|^6.0|^7.0", - "symfony/var-dumper": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0", "psr/log": "^1|^2|^3" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" }, "autoload": { "psr-4": { "Symfony\\Component\\Console\\": "" },