Upgrade framework

This commit is contained in:
2023-11-14 16:54:35 +01:00
parent 1648a5cd42
commit 4fcf6fffcc
10548 changed files with 693138 additions and 466698 deletions

View File

@@ -40,20 +40,12 @@ use Symfony\Component\Console\Exception\RuntimeException;
*/
class ArgvInput extends Input
{
private $tokens;
private $parsed;
private array $tokens;
private array $parsed;
/**
* Constructor.
*
* @param array|null $argv An array of parameters from the CLI (in the argv format)
* @param InputDefinition|null $definition A InputDefinition instance
*/
public function __construct(array $argv = null, InputDefinition $definition = null)
{
if (null === $argv) {
$argv = $_SERVER['argv'];
}
$argv = $argv ?? $_SERVER['argv'] ?? [];
// strip the application name
array_shift($argv);
@@ -76,30 +68,35 @@ class ArgvInput extends Input
$parseOptions = true;
$this->parsed = $this->tokens;
while (null !== $token = array_shift($this->parsed)) {
if ($parseOptions && '' == $token) {
$this->parseArgument($token);
} elseif ($parseOptions && '--' == $token) {
$parseOptions = false;
} elseif ($parseOptions && 0 === strpos($token, '--')) {
$this->parseLongOption($token);
} elseif ($parseOptions && '-' === $token[0] && '-' !== $token) {
$this->parseShortOption($token);
} else {
$this->parseArgument($token);
}
$parseOptions = $this->parseToken($token, $parseOptions);
}
}
protected function parseToken(string $token, bool $parseOptions): bool
{
if ($parseOptions && '' == $token) {
$this->parseArgument($token);
} elseif ($parseOptions && '--' == $token) {
return false;
} elseif ($parseOptions && str_starts_with($token, '--')) {
$this->parseLongOption($token);
} elseif ($parseOptions && '-' === $token[0] && '-' !== $token) {
$this->parseShortOption($token);
} else {
$this->parseArgument($token);
}
return $parseOptions;
}
/**
* Parses a short option.
*
* @param string $token The current token
*/
private function parseShortOption($token)
private function parseShortOption(string $token)
{
$name = substr($token, 1);
if (strlen($name) > 1) {
if (\strlen($name) > 1) {
if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) {
// an option with a value (with no space)
$this->addShortOption($name[0], substr($name, 1));
@@ -114,16 +111,15 @@ class ArgvInput extends Input
/**
* Parses a short option set.
*
* @param string $name The current token
*
* @throws RuntimeException When option given doesn't exist
*/
private function parseShortOptionSet($name)
private function parseShortOptionSet(string $name)
{
$len = strlen($name);
$len = \strlen($name);
for ($i = 0; $i < $len; ++$i) {
if (!$this->definition->hasShortcut($name[$i])) {
throw new RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i]));
$encoding = mb_detect_encoding($name, null, true);
throw new RuntimeException(sprintf('The "-%s" option does not exist.', false === $encoding ? $name[$i] : mb_substr($name, $i, 1, $encoding)));
}
$option = $this->definition->getOptionForShortcut($name[$i]);
@@ -139,20 +135,13 @@ class ArgvInput extends Input
/**
* Parses a long option.
*
* @param string $token The current token
*/
private function parseLongOption($token)
private function parseLongOption(string $token)
{
$name = substr($token, 2);
if (false !== $pos = strpos($name, '=')) {
if (0 === strlen($value = substr($name, $pos + 1))) {
// if no value after "=" then substr() returns "" since php7 only, false before
// see http://php.net/manual/fr/migration70.incompatible.php#119151
if (\PHP_VERSION_ID < 70000 && false === $value) {
$value = '';
}
if ('' === $value = substr($name, $pos + 1)) {
array_unshift($this->parsed, $value);
}
$this->addLongOption(substr($name, 0, $pos), $value);
@@ -164,18 +153,16 @@ class ArgvInput extends Input
/**
* Parses an argument.
*
* @param string $token The current token
*
* @throws RuntimeException When too many arguments are given
*/
private function parseArgument($token)
private function parseArgument(string $token)
{
$c = count($this->arguments);
$c = \count($this->arguments);
// if input is expecting another argument, add it
if ($this->definition->hasArgument($c)) {
$arg = $this->definition->getArgument($c);
$this->arguments[$arg->getName()] = $arg->isArray() ? array($token) : $token;
$this->arguments[$arg->getName()] = $arg->isArray() ? [$token] : $token;
// if last argument isArray(), append token to last argument
} elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) {
@@ -185,23 +172,34 @@ class ArgvInput extends Input
// unexpected argument
} else {
$all = $this->definition->getArguments();
if (count($all)) {
throw new RuntimeException(sprintf('Too many arguments, expected arguments "%s".', implode('" "', array_keys($all))));
$symfonyCommandName = null;
if (($inputArgument = $all[$key = array_key_first($all)] ?? null) && 'command' === $inputArgument->getName()) {
$symfonyCommandName = $this->arguments['command'] ?? null;
unset($all[$key]);
}
throw new RuntimeException(sprintf('No arguments expected, got "%s".', $token));
if (\count($all)) {
if ($symfonyCommandName) {
$message = sprintf('Too many arguments to "%s" command, expected arguments "%s".', $symfonyCommandName, implode('" "', array_keys($all)));
} else {
$message = sprintf('Too many arguments, expected arguments "%s".', implode('" "', array_keys($all)));
}
} elseif ($symfonyCommandName) {
$message = sprintf('No arguments expected for "%s" command, got "%s".', $symfonyCommandName, $token);
} else {
$message = sprintf('No arguments expected, got "%s".', $token);
}
throw new RuntimeException($message);
}
}
/**
* Adds a short option value.
*
* @param string $shortcut The short option key
* @param mixed $value The value for the option
*
* @throws RuntimeException When option given doesn't exist
*/
private function addShortOption($shortcut, $value)
private function addShortOption(string $shortcut, mixed $value)
{
if (!$this->definition->hasShortcut($shortcut)) {
throw new RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut));
@@ -213,15 +211,22 @@ class ArgvInput extends Input
/**
* Adds a long option value.
*
* @param string $name The long option key
* @param mixed $value The value for the option
*
* @throws RuntimeException When option given doesn't exist
*/
private function addLongOption($name, $value)
private function addLongOption(string $name, mixed $value)
{
if (!$this->definition->hasOption($name)) {
throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name));
if (!$this->definition->hasNegation($name)) {
throw new RuntimeException(sprintf('The "--%s" option does not exist.', $name));
}
$optionName = $this->definition->negationToName($name);
if (null !== $value) {
throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name));
}
$this->options[$optionName] = false;
return;
}
$option = $this->definition->getOption($name);
@@ -230,11 +235,11 @@ class ArgvInput extends Input
throw new RuntimeException(sprintf('The "--%s" option does not accept a value.', $name));
}
if (in_array($value, array('', null), true) && $option->acceptValue() && count($this->parsed)) {
if (\in_array($value, ['', null], true) && $option->acceptValue() && \count($this->parsed)) {
// if option accepts an optional or mandatory argument
// let's see if there is one provided
$next = array_shift($this->parsed);
if ((isset($next[0]) && '-' !== $next[0]) || in_array($next, array('', null), true)) {
if ((isset($next[0]) && '-' !== $next[0]) || \in_array($next, ['', null], true)) {
$value = $next;
} else {
array_unshift($this->parsed, $next);
@@ -261,30 +266,55 @@ class ArgvInput extends Input
/**
* {@inheritdoc}
*/
public function getFirstArgument()
public function getFirstArgument(): ?string
{
foreach ($this->tokens as $token) {
$isOption = false;
foreach ($this->tokens as $i => $token) {
if ($token && '-' === $token[0]) {
if (str_contains($token, '=') || !isset($this->tokens[$i + 1])) {
continue;
}
// If it's a long option, consider that everything after "--" is the option name.
// Otherwise, use the last char (if it's a short option set, only the last one can take a value with space separator)
$name = '-' === $token[1] ? substr($token, 2) : substr($token, -1);
if (!isset($this->options[$name]) && !$this->definition->hasShortcut($name)) {
// noop
} elseif ((isset($this->options[$name]) || isset($this->options[$name = $this->definition->shortcutToName($name)])) && $this->tokens[$i + 1] === $this->options[$name]) {
$isOption = true;
}
continue;
}
if ($isOption) {
$isOption = false;
continue;
}
return $token;
}
return null;
}
/**
* {@inheritdoc}
*/
public function hasParameterOption($values, $onlyParams = false)
public function hasParameterOption(string|array $values, bool $onlyParams = false): bool
{
$values = (array) $values;
foreach ($this->tokens as $token) {
if ($onlyParams && $token === '--') {
if ($onlyParams && '--' === $token) {
return false;
}
foreach ($values as $value) {
if ($token === $value || 0 === strpos($token, $value.'=')) {
// Options with values:
// For long options, test for '--option=' at beginning
// For short options, test for '-o' at beginning
$leading = str_starts_with($value, '--') ? $value.'=' : $value;
if ($token === $value || '' !== $leading && str_starts_with($token, $leading)) {
return true;
}
}
@@ -296,25 +326,28 @@ class ArgvInput extends Input
/**
* {@inheritdoc}
*/
public function getParameterOption($values, $default = false, $onlyParams = false)
public function getParameterOption(string|array $values, string|bool|int|float|array|null $default = false, bool $onlyParams = false): mixed
{
$values = (array) $values;
$tokens = $this->tokens;
while (0 < count($tokens)) {
while (0 < \count($tokens)) {
$token = array_shift($tokens);
if ($onlyParams && $token === '--') {
return false;
if ($onlyParams && '--' === $token) {
return $default;
}
foreach ($values as $value) {
if ($token === $value || 0 === strpos($token, $value.'=')) {
if (false !== $pos = strpos($token, '=')) {
return substr($token, $pos + 1);
}
if ($token === $value) {
return array_shift($tokens);
}
// Options with values:
// For long options, test for '--option=' at beginning
// For short options, test for '-o' at beginning
$leading = str_starts_with($value, '--') ? $value.'=' : $value;
if ('' !== $leading && str_starts_with($token, $leading)) {
return substr($token, \strlen($leading));
}
}
}
@@ -323,17 +356,15 @@ class ArgvInput extends Input
/**
* Returns a stringified representation of the args passed to the command.
*
* @return string
*/
public function __toString()
public function __toString(): string
{
$tokens = array_map(function ($token) {
if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) {
return $match[1].$this->escapeToken($match[2]);
}
if ($token && $token[0] !== '-') {
if ($token && '-' !== $token[0]) {
return $this->escapeToken($token);
}

View File

@@ -19,20 +19,14 @@ use Symfony\Component\Console\Exception\InvalidOptionException;
*
* Usage:
*
* $input = new ArrayInput(array('name' => 'foo', '--bar' => 'foobar'));
* $input = new ArrayInput(['command' => 'foo:bar', 'foo' => 'bar', '--bar' => 'foobar']);
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ArrayInput extends Input
{
private $parameters;
private array $parameters;
/**
* Constructor.
*
* @param array $parameters An array of parameters
* @param InputDefinition|null $definition A InputDefinition instance
*/
public function __construct(array $parameters, InputDefinition $definition = null)
{
$this->parameters = $parameters;
@@ -43,34 +37,36 @@ class ArrayInput extends Input
/**
* {@inheritdoc}
*/
public function getFirstArgument()
public function getFirstArgument(): ?string
{
foreach ($this->parameters as $key => $value) {
if ($key && '-' === $key[0]) {
foreach ($this->parameters as $param => $value) {
if ($param && \is_string($param) && '-' === $param[0]) {
continue;
}
return $value;
}
return null;
}
/**
* {@inheritdoc}
*/
public function hasParameterOption($values, $onlyParams = false)
public function hasParameterOption(string|array $values, bool $onlyParams = false): bool
{
$values = (array) $values;
foreach ($this->parameters as $k => $v) {
if (!is_int($k)) {
if (!\is_int($k)) {
$v = $k;
}
if ($onlyParams && $v === '--') {
if ($onlyParams && '--' === $v) {
return false;
}
if (in_array($v, $values)) {
if (\in_array($v, $values)) {
return true;
}
}
@@ -81,20 +77,20 @@ class ArrayInput extends Input
/**
* {@inheritdoc}
*/
public function getParameterOption($values, $default = false, $onlyParams = false)
public function getParameterOption(string|array $values, string|bool|int|float|array|null $default = false, bool $onlyParams = false): mixed
{
$values = (array) $values;
foreach ($this->parameters as $k => $v) {
if ($onlyParams && ($k === '--' || (is_int($k) && $v === '--'))) {
return false;
if ($onlyParams && ('--' === $k || (\is_int($k) && '--' === $v))) {
return $default;
}
if (is_int($k)) {
if (in_array($v, $values)) {
if (\is_int($k)) {
if (\in_array($v, $values)) {
return true;
}
} elseif (in_array($k, $values)) {
} elseif (\in_array($k, $values)) {
return $v;
}
}
@@ -104,17 +100,22 @@ class ArrayInput extends Input
/**
* Returns a stringified representation of the args passed to the command.
*
* @return string
*/
public function __toString()
public function __toString(): string
{
$params = array();
$params = [];
foreach ($this->parameters as $param => $val) {
if ($param && '-' === $param[0]) {
$params[] = $param.('' != $val ? '='.$this->escapeToken($val) : '');
if ($param && \is_string($param) && '-' === $param[0]) {
$glue = ('-' === $param[1]) ? '=' : ' ';
if (\is_array($val)) {
foreach ($val as $v) {
$params[] = $param.('' != $v ? $glue.$this->escapeToken($v) : '');
}
} else {
$params[] = $param.('' != $val ? $glue.$this->escapeToken($val) : '');
}
} else {
$params[] = $this->escapeToken($val);
$params[] = \is_array($val) ? implode(' ', array_map([$this, 'escapeToken'], $val)) : $this->escapeToken($val);
}
}
@@ -127,12 +128,12 @@ class ArrayInput extends Input
protected function parse()
{
foreach ($this->parameters as $key => $value) {
if ($key === '--') {
if ('--' === $key) {
return;
}
if (0 === strpos($key, '--')) {
if (str_starts_with($key, '--')) {
$this->addLongOption(substr($key, 2), $value);
} elseif ('-' === $key[0]) {
} elseif (str_starts_with($key, '-')) {
$this->addShortOption(substr($key, 1), $value);
} else {
$this->addArgument($key, $value);
@@ -143,12 +144,9 @@ class ArrayInput extends Input
/**
* Adds a short option value.
*
* @param string $shortcut The short option key
* @param mixed $value The value for the option
*
* @throws InvalidOptionException When option given doesn't exist
*/
private function addShortOption($shortcut, $value)
private function addShortOption(string $shortcut, mixed $value)
{
if (!$this->definition->hasShortcut($shortcut)) {
throw new InvalidOptionException(sprintf('The "-%s" option does not exist.', $shortcut));
@@ -160,16 +158,20 @@ class ArrayInput extends Input
/**
* Adds a long option value.
*
* @param string $name The long option key
* @param mixed $value The value for the option
*
* @throws InvalidOptionException When option given doesn't exist
* @throws InvalidOptionException When a required value is missing
*/
private function addLongOption($name, $value)
private function addLongOption(string $name, mixed $value)
{
if (!$this->definition->hasOption($name)) {
throw new InvalidOptionException(sprintf('The "--%s" option does not exist.', $name));
if (!$this->definition->hasNegation($name)) {
throw new InvalidOptionException(sprintf('The "--%s" option does not exist.', $name));
}
$optionName = $this->definition->negationToName($name);
$this->options[$optionName] = false;
return;
}
$option = $this->definition->getOption($name);
@@ -190,12 +192,9 @@ class ArrayInput extends Input
/**
* Adds an argument value.
*
* @param string $name The argument name
* @param mixed $value The value for the argument
*
* @throws InvalidArgumentException When argument given doesn't exist
*/
private function addArgument($name, $value)
private function addArgument(string|int $name, mixed $value)
{
if (!$this->definition->hasArgument($name)) {
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));

View File

@@ -27,20 +27,12 @@ use Symfony\Component\Console\Exception\RuntimeException;
*/
abstract class Input implements InputInterface, StreamableInputInterface
{
/**
* @var InputDefinition
*/
protected $definition;
protected $stream;
protected $options = array();
protected $arguments = array();
protected $options = [];
protected $arguments = [];
protected $interactive = true;
/**
* Constructor.
*
* @param InputDefinition|null $definition A InputDefinition instance
*/
public function __construct(InputDefinition $definition = null)
{
if (null === $definition) {
@@ -56,8 +48,8 @@ abstract class Input implements InputInterface, StreamableInputInterface
*/
public function bind(InputDefinition $definition)
{
$this->arguments = array();
$this->options = array();
$this->arguments = [];
$this->options = [];
$this->definition = $definition;
$this->parse();
@@ -77,10 +69,10 @@ abstract class Input implements InputInterface, StreamableInputInterface
$givenArguments = $this->arguments;
$missingArguments = array_filter(array_keys($definition->getArguments()), function ($argument) use ($definition, $givenArguments) {
return !array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired();
return !\array_key_exists($argument, $givenArguments) && $definition->getArgument($argument)->isRequired();
});
if (count($missingArguments) > 0) {
if (\count($missingArguments) > 0) {
throw new RuntimeException(sprintf('Not enough arguments (missing: "%s").', implode(', ', $missingArguments)));
}
}
@@ -88,7 +80,7 @@ abstract class Input implements InputInterface, StreamableInputInterface
/**
* {@inheritdoc}
*/
public function isInteractive()
public function isInteractive(): bool
{
return $this->interactive;
}
@@ -96,15 +88,15 @@ abstract class Input implements InputInterface, StreamableInputInterface
/**
* {@inheritdoc}
*/
public function setInteractive($interactive)
public function setInteractive(bool $interactive)
{
$this->interactive = (bool) $interactive;
$this->interactive = $interactive;
}
/**
* {@inheritdoc}
*/
public function getArguments()
public function getArguments(): array
{
return array_merge($this->definition->getArgumentDefaults(), $this->arguments);
}
@@ -112,19 +104,19 @@ abstract class Input implements InputInterface, StreamableInputInterface
/**
* {@inheritdoc}
*/
public function getArgument($name)
public function getArgument(string $name): mixed
{
if (!$this->definition->hasArgument($name)) {
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)->getDefault();
return $this->arguments[$name] ?? $this->definition->getArgument($name)->getDefault();
}
/**
* {@inheritdoc}
*/
public function setArgument($name, $value)
public function setArgument(string $name, mixed $value)
{
if (!$this->definition->hasArgument($name)) {
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
@@ -136,7 +128,7 @@ abstract class Input implements InputInterface, StreamableInputInterface
/**
* {@inheritdoc}
*/
public function hasArgument($name)
public function hasArgument(string $name): bool
{
return $this->definition->hasArgument($name);
}
@@ -144,7 +136,7 @@ abstract class Input implements InputInterface, StreamableInputInterface
/**
* {@inheritdoc}
*/
public function getOptions()
public function getOptions(): array
{
return array_merge($this->definition->getOptionDefaults(), $this->options);
}
@@ -152,21 +144,33 @@ abstract class Input implements InputInterface, StreamableInputInterface
/**
* {@inheritdoc}
*/
public function getOption($name)
public function getOption(string $name): mixed
{
if ($this->definition->hasNegation($name)) {
if (null === $value = $this->getOption($this->definition->negationToName($name))) {
return $value;
}
return !$value;
}
if (!$this->definition->hasOption($name)) {
throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
}
return array_key_exists($name, $this->options) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
return \array_key_exists($name, $this->options) ? $this->options[$name] : $this->definition->getOption($name)->getDefault();
}
/**
* {@inheritdoc}
*/
public function setOption($name, $value)
public function setOption(string $name, mixed $value)
{
if (!$this->definition->hasOption($name)) {
if ($this->definition->hasNegation($name)) {
$this->options[$this->definition->negationToName($name)] = !$value;
return;
} elseif (!$this->definition->hasOption($name)) {
throw new InvalidArgumentException(sprintf('The "%s" option does not exist.', $name));
}
@@ -176,19 +180,15 @@ abstract class Input implements InputInterface, StreamableInputInterface
/**
* {@inheritdoc}
*/
public function hasOption($name)
public function hasOption(string $name): bool
{
return $this->definition->hasOption($name);
return $this->definition->hasOption($name) || $this->definition->hasNegation($name);
}
/**
* Escapes a token through escapeshellarg if it contains unsafe chars.
*
* @param string $token
*
* @return string
*/
public function escapeToken($token)
public function escapeToken(string $token): string
{
return preg_match('{^[\w-]+$}', $token) ? $token : escapeshellarg($token);
}

View File

@@ -21,30 +21,28 @@ use Symfony\Component\Console\Exception\LogicException;
*/
class InputArgument
{
const REQUIRED = 1;
const OPTIONAL = 2;
const IS_ARRAY = 4;
public const REQUIRED = 1;
public const OPTIONAL = 2;
public const IS_ARRAY = 4;
private $name;
private $mode;
private $default;
private $description;
private string $name;
private int $mode;
private string|int|bool|array|null|float $default;
private string $description;
/**
* Constructor.
*
* @param string $name The argument name
* @param int $mode The argument mode: self::REQUIRED or self::OPTIONAL
* @param string $description A description text
* @param mixed $default The default value (for self::OPTIONAL mode only)
* @param string $name The argument name
* @param int|null $mode The argument mode: self::REQUIRED or self::OPTIONAL
* @param string $description A description text
* @param string|bool|int|float|array|null $default The default value (for self::OPTIONAL mode only)
*
* @throws InvalidArgumentException When argument mode is not valid
*/
public function __construct($name, $mode = null, $description = '', $default = null)
public function __construct(string $name, int $mode = null, string $description = '', string|bool|int|float|array $default = null)
{
if (null === $mode) {
$mode = self::OPTIONAL;
} elseif (!is_int($mode) || $mode > 7 || $mode < 1) {
} elseif ($mode > 7 || $mode < 1) {
throw new InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode));
}
@@ -57,10 +55,8 @@ class InputArgument
/**
* Returns the argument name.
*
* @return string The argument name
*/
public function getName()
public function getName(): string
{
return $this->name;
}
@@ -70,7 +66,7 @@ class InputArgument
*
* @return bool true if parameter mode is self::REQUIRED, false otherwise
*/
public function isRequired()
public function isRequired(): bool
{
return self::REQUIRED === (self::REQUIRED & $this->mode);
}
@@ -80,7 +76,7 @@ class InputArgument
*
* @return bool true if mode is self::IS_ARRAY, false otherwise
*/
public function isArray()
public function isArray(): bool
{
return self::IS_ARRAY === (self::IS_ARRAY & $this->mode);
}
@@ -88,20 +84,18 @@ class InputArgument
/**
* Sets the default value.
*
* @param mixed $default The default value
*
* @throws LogicException When incorrect default value is given
*/
public function setDefault($default = null)
public function setDefault(string|bool|int|float|array $default = null)
{
if (self::REQUIRED === $this->mode && null !== $default) {
if ($this->isRequired() && null !== $default) {
throw new LogicException('Cannot set a default value except for InputArgument::OPTIONAL mode.');
}
if ($this->isArray()) {
if (null === $default) {
$default = array();
} elseif (!is_array($default)) {
$default = [];
} elseif (!\is_array($default)) {
throw new LogicException('A default value for an array argument must be an array.');
}
}
@@ -111,20 +105,16 @@ class InputArgument
/**
* Returns the default value.
*
* @return mixed The default value
*/
public function getDefault()
public function getDefault(): string|bool|int|float|array|null
{
return $this->default;
}
/**
* Returns the description text.
*
* @return string The description text
*/
public function getDescription()
public function getDescription(): string
{
return $this->description;
}

View File

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

View File

@@ -19,41 +19,38 @@ use Symfony\Component\Console\Exception\LogicException;
*
* Usage:
*
* $definition = new InputDefinition(array(
* new InputArgument('name', InputArgument::REQUIRED),
* new InputOption('foo', 'f', InputOption::VALUE_REQUIRED),
* ));
* $definition = new InputDefinition([
* new InputArgument('name', InputArgument::REQUIRED),
* new InputOption('foo', 'f', InputOption::VALUE_REQUIRED),
* ]);
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class InputDefinition
{
private $arguments;
private $requiredCount;
private $hasAnArrayArgument = false;
private $hasOptional;
private $options;
private $shortcuts;
private array $arguments = [];
private int $requiredCount = 0;
private $lastArrayArgument = null;
private $lastOptionalArgument = null;
private array $options = [];
private array $negations = [];
private array $shortcuts = [];
/**
* Constructor.
*
* @param array $definition An array of InputArgument and InputOption instance
*/
public function __construct(array $definition = array())
public function __construct(array $definition = [])
{
$this->setDefinition($definition);
}
/**
* Sets the definition of the input.
*
* @param array $definition The definition array
*/
public function setDefinition(array $definition)
{
$arguments = array();
$options = array();
$arguments = [];
$options = [];
foreach ($definition as $item) {
if ($item instanceof InputOption) {
$options[] = $item;
@@ -71,12 +68,12 @@ class InputDefinition
*
* @param InputArgument[] $arguments An array of InputArgument objects
*/
public function setArguments($arguments = array())
public function setArguments(array $arguments = [])
{
$this->arguments = array();
$this->arguments = [];
$this->requiredCount = 0;
$this->hasOptional = false;
$this->hasAnArrayArgument = false;
$this->lastOptionalArgument = null;
$this->lastArrayArgument = null;
$this->addArguments($arguments);
}
@@ -85,7 +82,7 @@ class InputDefinition
*
* @param InputArgument[] $arguments An array of InputArgument objects
*/
public function addArguments($arguments = array())
public function addArguments(?array $arguments = [])
{
if (null !== $arguments) {
foreach ($arguments as $argument) {
@@ -95,10 +92,6 @@ class InputDefinition
}
/**
* Adds an InputArgument object.
*
* @param InputArgument $argument An InputArgument object
*
* @throws LogicException When incorrect argument is given
*/
public function addArgument(InputArgument $argument)
@@ -107,22 +100,22 @@ class InputDefinition
throw new LogicException(sprintf('An argument with name "%s" already exists.', $argument->getName()));
}
if ($this->hasAnArrayArgument) {
throw new LogicException('Cannot add an argument after an array argument.');
if (null !== $this->lastArrayArgument) {
throw new LogicException(sprintf('Cannot add a required argument "%s" after an array argument "%s".', $argument->getName(), $this->lastArrayArgument->getName()));
}
if ($argument->isRequired() && $this->hasOptional) {
throw new LogicException('Cannot add a required argument after an optional one.');
if ($argument->isRequired() && null !== $this->lastOptionalArgument) {
throw new LogicException(sprintf('Cannot add a required argument "%s" after an optional one "%s".', $argument->getName(), $this->lastOptionalArgument->getName()));
}
if ($argument->isArray()) {
$this->hasAnArrayArgument = true;
$this->lastArrayArgument = $argument;
}
if ($argument->isRequired()) {
++$this->requiredCount;
} else {
$this->hasOptional = true;
$this->lastOptionalArgument = $argument;
}
$this->arguments[$argument->getName()] = $argument;
@@ -131,33 +124,25 @@ class InputDefinition
/**
* Returns an InputArgument by name or by position.
*
* @param string|int $name The InputArgument name or position
*
* @return InputArgument An InputArgument object
*
* @throws InvalidArgumentException When argument given doesn't exist
*/
public function getArgument($name)
public function getArgument(string|int $name): InputArgument
{
if (!$this->hasArgument($name)) {
throw new InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name));
}
$arguments = is_int($name) ? array_values($this->arguments) : $this->arguments;
$arguments = \is_int($name) ? array_values($this->arguments) : $this->arguments;
return $arguments[$name];
}
/**
* Returns true if an InputArgument object exists by name or position.
*
* @param string|int $name The InputArgument name or position
*
* @return bool true if the InputArgument object exists, false otherwise
*/
public function hasArgument($name)
public function hasArgument(string|int $name): bool
{
$arguments = is_int($name) ? array_values($this->arguments) : $this->arguments;
$arguments = \is_int($name) ? array_values($this->arguments) : $this->arguments;
return isset($arguments[$name]);
}
@@ -165,41 +150,35 @@ class InputDefinition
/**
* Gets the array of InputArgument objects.
*
* @return InputArgument[] An array of InputArgument objects
* @return InputArgument[]
*/
public function getArguments()
public function getArguments(): array
{
return $this->arguments;
}
/**
* Returns the number of InputArguments.
*
* @return int The number of InputArguments
*/
public function getArgumentCount()
public function getArgumentCount(): int
{
return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments);
return null !== $this->lastArrayArgument ? \PHP_INT_MAX : \count($this->arguments);
}
/**
* Returns the number of required InputArguments.
*
* @return int The number of required InputArguments
*/
public function getArgumentRequiredCount()
public function getArgumentRequiredCount(): int
{
return $this->requiredCount;
}
/**
* Gets the default values.
*
* @return array An array of default values
* @return array<string|bool|int|float|array|null>
*/
public function getArgumentDefaults()
public function getArgumentDefaults(): array
{
$values = array();
$values = [];
foreach ($this->arguments as $argument) {
$values[$argument->getName()] = $argument->getDefault();
}
@@ -212,10 +191,11 @@ class InputDefinition
*
* @param InputOption[] $options An array of InputOption objects
*/
public function setOptions($options = array())
public function setOptions(array $options = [])
{
$this->options = array();
$this->shortcuts = array();
$this->options = [];
$this->shortcuts = [];
$this->negations = [];
$this->addOptions($options);
}
@@ -224,7 +204,7 @@ class InputDefinition
*
* @param InputOption[] $options An array of InputOption objects
*/
public function addOptions($options = array())
public function addOptions(array $options = [])
{
foreach ($options as $option) {
$this->addOption($option);
@@ -232,10 +212,6 @@ class InputDefinition
}
/**
* Adds an InputOption object.
*
* @param InputOption $option An InputOption object
*
* @throws LogicException When option given already exist
*/
public function addOption(InputOption $option)
@@ -243,6 +219,9 @@ class InputDefinition
if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) {
throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName()));
}
if (isset($this->negations[$option->getName()])) {
throw new LogicException(sprintf('An option named "%s" already exists.', $option->getName()));
}
if ($option->getShortcut()) {
foreach (explode('|', $option->getShortcut()) as $shortcut) {
@@ -258,18 +237,22 @@ class InputDefinition
$this->shortcuts[$shortcut] = $option->getName();
}
}
if ($option->isNegatable()) {
$negatedName = 'no-'.$option->getName();
if (isset($this->options[$negatedName])) {
throw new LogicException(sprintf('An option named "%s" already exists.', $negatedName));
}
$this->negations[$negatedName] = $option->getName();
}
}
/**
* Returns an InputOption by name.
*
* @param string $name The InputOption name
*
* @return InputOption A InputOption object
*
* @throws InvalidArgumentException When option given doesn't exist
*/
public function getOption($name)
public function getOption(string $name): InputOption
{
if (!$this->hasOption($name)) {
throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name));
@@ -283,12 +266,8 @@ class InputDefinition
*
* This method can't be used to check if the user included the option when
* executing the command (use getOption() instead).
*
* @param string $name The InputOption name
*
* @return bool true if the InputOption object exists, false otherwise
*/
public function hasOption($name)
public function hasOption(string $name): bool
{
return isset($this->options[$name]);
}
@@ -296,45 +275,43 @@ class InputDefinition
/**
* Gets the array of InputOption objects.
*
* @return InputOption[] An array of InputOption objects
* @return InputOption[]
*/
public function getOptions()
public function getOptions(): array
{
return $this->options;
}
/**
* Returns true if an InputOption object exists by shortcut.
*
* @param string $name The InputOption shortcut
*
* @return bool true if the InputOption object exists, false otherwise
*/
public function hasShortcut($name)
public function hasShortcut(string $name): bool
{
return isset($this->shortcuts[$name]);
}
/**
* Gets an InputOption by shortcut.
*
* @param string $shortcut the Shortcut name
*
* @return InputOption An InputOption object
* Returns true if an InputOption object exists by negated name.
*/
public function getOptionForShortcut($shortcut)
public function hasNegation(string $name): bool
{
return isset($this->negations[$name]);
}
/**
* Gets an InputOption by shortcut.
*/
public function getOptionForShortcut(string $shortcut): InputOption
{
return $this->getOption($this->shortcutToName($shortcut));
}
/**
* Gets an array of default values.
*
* @return array An array of all default values
* @return array<string|bool|int|float|array|null>
*/
public function getOptionDefaults()
public function getOptionDefaults(): array
{
$values = array();
$values = [];
foreach ($this->options as $option) {
$values[$option->getName()] = $option->getDefault();
}
@@ -345,13 +322,11 @@ class InputDefinition
/**
* Returns the InputOption name given a shortcut.
*
* @param string $shortcut The shortcut
*
* @return string The InputOption name
*
* @throws InvalidArgumentException When option given does not exist
*
* @internal
*/
private function shortcutToName($shortcut)
public function shortcutToName(string $shortcut): string
{
if (!isset($this->shortcuts[$shortcut])) {
throw new InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut));
@@ -361,15 +336,27 @@ class InputDefinition
}
/**
* Gets the synopsis.
* Returns the InputOption name given a negation.
*
* @param bool $short Whether to return the short version (with options folded) or not
* @throws InvalidArgumentException When option given does not exist
*
* @return string The synopsis
* @internal
*/
public function getSynopsis($short = false)
public function negationToName(string $negation): string
{
$elements = array();
if (!isset($this->negations[$negation])) {
throw new InvalidArgumentException(sprintf('The "--%s" option does not exist.', $negation));
}
return $this->negations[$negation];
}
/**
* Gets the synopsis.
*/
public function getSynopsis(bool $short = false): string
{
$elements = [];
if ($short && $this->getOptions()) {
$elements[] = '[options]';
@@ -386,29 +373,30 @@ class InputDefinition
}
$shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : '';
$elements[] = sprintf('[%s--%s%s]', $shortcut, $option->getName(), $value);
$negation = $option->isNegatable() ? sprintf('|--no-%s', $option->getName()) : '';
$elements[] = sprintf('[%s--%s%s%s]', $shortcut, $option->getName(), $value, $negation);
}
}
if (count($elements) && $this->getArguments()) {
if (\count($elements) && $this->getArguments()) {
$elements[] = '[--]';
}
$tail = '';
foreach ($this->getArguments() as $argument) {
$element = '<'.$argument->getName().'>';
if (!$argument->isRequired()) {
$element = '['.$element.']';
} elseif ($argument->isArray()) {
$element = $element.' ('.$element.')';
}
if ($argument->isArray()) {
$element .= '...';
}
if (!$argument->isRequired()) {
$element = '['.$element;
$tail .= ']';
}
$elements[] = $element;
}
return implode(' ', $elements);
return implode(' ', $elements).$tail;
}
}

View File

@@ -23,42 +23,42 @@ interface InputInterface
{
/**
* Returns the first argument from the raw parameters (not parsed).
*
* @return string The value of the first argument or null otherwise
*/
public function getFirstArgument();
public function getFirstArgument(): ?string;
/**
* Returns true if the raw parameters (not parsed) contain a value.
*
* This method is to be used to introspect the input parameters
* before they have been validated. It must be used carefully.
* Does not necessarily return the correct result for short options
* when multiple flags are combined in the same option.
*
* @param string|array $values The values to look for in the raw parameters (can be an array)
* @param bool $onlyParams Only check real parameters, skip those following an end of options (--) signal
*
* @return bool true if the value is contained in the raw parameters
*/
public function hasParameterOption($values, $onlyParams = false);
public function hasParameterOption(string|array $values, bool $onlyParams = false): bool;
/**
* Returns the value of a raw option (not parsed).
*
* This method is to be used to introspect the input parameters
* before they have been validated. It must be used carefully.
* Does not necessarily return the correct result for short options
* when multiple flags are combined in the same option.
*
* @param string|array $values The value(s) to look for in the raw parameters (can be an array)
* @param mixed $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
* @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 The option value
* @return mixed
*/
public function getParameterOption($values, $default = false, $onlyParams = false);
public function getParameterOption(string|array $values, string|bool|int|float|array|null $default = false, bool $onlyParams = false);
/**
* Binds the current Input instance with the given arguments and options.
*
* @param InputDefinition $definition A InputDefinition instance
* @throws RuntimeException
*/
public function bind(InputDefinition $definition);
@@ -72,88 +72,66 @@ interface InputInterface
/**
* Returns all the given arguments merged with the default values.
*
* @return array
* @return array<string|bool|int|float|array|null>
*/
public function getArguments();
public function getArguments(): array;
/**
* Returns the argument value for a given argument name.
*
* @param string $name The argument name
*
* @return mixed The argument value
* @return mixed
*
* @throws InvalidArgumentException When argument given doesn't exist
*/
public function getArgument($name);
public function getArgument(string $name);
/**
* Sets an argument value by name.
*
* @param string $name The argument name
* @param string $value The argument value
*
* @throws InvalidArgumentException When argument given doesn't exist
*/
public function setArgument($name, $value);
public function setArgument(string $name, mixed $value);
/**
* Returns true if an InputArgument object exists by name or position.
*
* @param string|int $name The InputArgument name or position
*
* @return bool true if the InputArgument object exists, false otherwise
*/
public function hasArgument($name);
public function hasArgument(string $name): bool;
/**
* Returns all the given options merged with the default values.
*
* @return array
* @return array<string|bool|int|float|array|null>
*/
public function getOptions();
public function getOptions(): array;
/**
* Returns the option value for a given option name.
*
* @param string $name The option name
*
* @return mixed The option value
* @return mixed
*
* @throws InvalidArgumentException When option given doesn't exist
*/
public function getOption($name);
public function getOption(string $name);
/**
* Sets an option value by name.
*
* @param string $name The option name
* @param string|bool $value The option value
*
* @throws InvalidArgumentException When option given doesn't exist
*/
public function setOption($name, $value);
public function setOption(string $name, mixed $value);
/**
* Returns true if an InputOption object exists by name.
*
* @param string $name The InputOption name
*
* @return bool true if the InputOption object exists, false otherwise
*/
public function hasOption($name);
public function hasOption(string $name): bool;
/**
* Is this input means interactive?
*
* @return bool
*/
public function isInteractive();
public function isInteractive(): bool;
/**
* Sets the input interactivity.
*
* @param bool $interactive If the input should be interactive
*/
public function setInteractive($interactive);
public function setInteractive(bool $interactive);
}

View File

@@ -21,31 +21,47 @@ use Symfony\Component\Console\Exception\LogicException;
*/
class InputOption
{
const VALUE_NONE = 1;
const VALUE_REQUIRED = 2;
const VALUE_OPTIONAL = 4;
const VALUE_IS_ARRAY = 8;
private $name;
private $shortcut;
private $mode;
private $default;
private $description;
/**
* Do not accept input for the option (e.g. --yell). This is the default behavior of options.
*/
public const VALUE_NONE = 1;
/**
* Constructor.
*
* @param string $name The option name
* @param string|array $shortcut The shortcuts, can be null, a string of shortcuts delimited by | or an array of shortcuts
* @param int $mode The option mode: One of the VALUE_* constants
* @param string $description A description text
* @param mixed $default The default value (must be null for self::VALUE_NONE)
* A value must be passed when the option is used (e.g. --iterations=5 or -i5).
*/
public const VALUE_REQUIRED = 2;
/**
* The option may or may not have a value (e.g. --yell or --yell=loud).
*/
public const VALUE_OPTIONAL = 4;
/**
* The option accepts multiple values (e.g. --dir=/foo --dir=/bar).
*/
public const VALUE_IS_ARRAY = 8;
/**
* The option may have either positive or negative value (e.g. --ansi or --no-ansi).
*/
public const VALUE_NEGATABLE = 16;
private string $name;
private string|array|null $shortcut;
private int $mode;
private string|int|bool|array|null|float $default;
private string $description;
/**
* @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 string|bool|int|float|array|null $default The default value (must be null for self::VALUE_NONE)
*
* @throws InvalidArgumentException If option mode is invalid or incompatible
*/
public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null)
public function __construct(string $name, string|array $shortcut = null, int $mode = null, string $description = '', string|bool|int|float|array $default = null)
{
if (0 === strpos($name, '--')) {
if (str_starts_with($name, '--')) {
$name = substr($name, 2);
}
@@ -58,7 +74,7 @@ class InputOption
}
if (null !== $shortcut) {
if (is_array($shortcut)) {
if (\is_array($shortcut)) {
$shortcut = implode('|', $shortcut);
}
$shortcuts = preg_split('{(\|)-?}', ltrim($shortcut, '-'));
@@ -72,7 +88,7 @@ class InputOption
if (null === $mode) {
$mode = self::VALUE_NONE;
} elseif (!is_int($mode) || $mode > 15 || $mode < 1) {
} elseif ($mode >= (self::VALUE_NEGATABLE << 1) || $mode < 1) {
throw new InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode));
}
@@ -84,26 +100,25 @@ class InputOption
if ($this->isArray() && !$this->acceptValue()) {
throw new InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.');
}
if ($this->isNegatable() && $this->acceptValue()) {
throw new InvalidArgumentException('Impossible to have an option mode VALUE_NEGATABLE if the option also accepts a value.');
}
$this->setDefault($default);
}
/**
* Returns the option shortcut.
*
* @return string The shortcut
*/
public function getShortcut()
public function getShortcut(): ?string
{
return $this->shortcut;
}
/**
* Returns the option name.
*
* @return string The name
*/
public function getName()
public function getName(): string
{
return $this->name;
}
@@ -113,7 +128,7 @@ class InputOption
*
* @return bool true if value mode is not self::VALUE_NONE, false otherwise
*/
public function acceptValue()
public function acceptValue(): bool
{
return $this->isValueRequired() || $this->isValueOptional();
}
@@ -123,7 +138,7 @@ class InputOption
*
* @return bool true if value mode is self::VALUE_REQUIRED, false otherwise
*/
public function isValueRequired()
public function isValueRequired(): bool
{
return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode);
}
@@ -133,7 +148,7 @@ class InputOption
*
* @return bool true if value mode is self::VALUE_OPTIONAL, false otherwise
*/
public function isValueOptional()
public function isValueOptional(): bool
{
return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode);
}
@@ -143,19 +158,17 @@ class InputOption
*
* @return bool true if mode is self::VALUE_IS_ARRAY, false otherwise
*/
public function isArray()
public function isArray(): bool
{
return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode);
}
/**
* Sets the default value.
*
* @param mixed $default The default value
*
* @throws LogicException When incorrect default value is given
*/
public function setDefault($default = null)
public function isNegatable(): bool
{
return self::VALUE_NEGATABLE === (self::VALUE_NEGATABLE & $this->mode);
}
public function setDefault(string|bool|int|float|array $default = null)
{
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.');
@@ -163,47 +176,40 @@ class InputOption
if ($this->isArray()) {
if (null === $default) {
$default = array();
} elseif (!is_array($default)) {
$default = [];
} elseif (!\is_array($default)) {
throw new LogicException('A default value for an array option must be an array.');
}
}
$this->default = $this->acceptValue() ? $default : false;
$this->default = $this->acceptValue() || $this->isNegatable() ? $default : false;
}
/**
* Returns the default value.
*
* @return mixed The default value
*/
public function getDefault()
public function getDefault(): string|bool|int|float|array|null
{
return $this->default;
}
/**
* Returns the description text.
*
* @return string The description text
*/
public function getDescription()
public function getDescription(): string
{
return $this->description;
}
/**
* Checks whether the given option equals this one.
*
* @param InputOption $option option to compare
*
* @return bool
*/
public function equals(InputOption $option)
public function equals(self $option): bool
{
return $option->getName() === $this->getName()
&& $option->getShortcut() === $this->getShortcut()
&& $option->getDefault() === $this->getDefault()
&& $option->isNegatable() === $this->isNegatable()
&& $option->isArray() === $this->isArray()
&& $option->isValueRequired() === $this->isValueRequired()
&& $option->isValueOptional() === $this->isValueOptional()

View File

@@ -24,17 +24,16 @@ use Symfony\Component\Console\Exception\InvalidArgumentException;
*/
class StringInput extends ArgvInput
{
const REGEX_STRING = '([^\s]+?)(?:\s|(?<!\\\\)"|(?<!\\\\)\'|$)';
const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')';
public const REGEX_STRING = '([^\s]+?)(?:\s|(?<!\\\\)"|(?<!\\\\)\'|$)';
public const REGEX_UNQUOTED_STRING = '([^\s\\\\]+?)';
public const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')';
/**
* Constructor.
*
* @param string $input An array of parameters from the CLI (in the argv format)
* @param string $input A string representing the parameters from the CLI
*/
public function __construct($input)
public function __construct(string $input)
{
parent::__construct(array());
parent::__construct([]);
$this->setTokens($this->tokenize($input));
}
@@ -42,31 +41,42 @@ class StringInput extends ArgvInput
/**
* Tokenizes a string.
*
* @param string $input The input to tokenize
*
* @return array An array of tokens
*
* @throws InvalidArgumentException When unable to parse input (should never happen)
*/
private function tokenize($input)
private function tokenize(string $input): array
{
$tokens = array();
$length = strlen($input);
$tokens = [];
$length = \strlen($input);
$cursor = 0;
$token = null;
while ($cursor < $length) {
if (preg_match('/\s+/A', $input, $match, null, $cursor)) {
} elseif (preg_match('/([^="\'\s]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, null, $cursor)) {
$tokens[] = $match[1].$match[2].stripcslashes(str_replace(array('"\'', '\'"', '\'\'', '""'), '', substr($match[3], 1, strlen($match[3]) - 2)));
} elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, null, $cursor)) {
$tokens[] = stripcslashes(substr($match[0], 1, strlen($match[0]) - 2));
} elseif (preg_match('/'.self::REGEX_STRING.'/A', $input, $match, null, $cursor)) {
$tokens[] = stripcslashes($match[1]);
} else {
// should never happen
throw new InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10)));
if ('\\' === $input[$cursor]) {
$token .= $input[++$cursor] ?? '';
++$cursor;
continue;
}
$cursor += strlen($match[0]);
if (preg_match('/\s+/A', $input, $match, 0, $cursor)) {
if (null !== $token) {
$tokens[] = $token;
$token = null;
}
} elseif (preg_match('/([^="\'\s]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, 0, $cursor)) {
$token .= $match[1].$match[2].stripcslashes(str_replace(['"\'', '\'"', '\'\'', '""'], '', substr($match[3], 1, -1)));
} elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, 0, $cursor)) {
$token .= stripcslashes(substr($match[0], 1, -1));
} elseif (preg_match('/'.self::REGEX_UNQUOTED_STRING.'/A', $input, $match, 0, $cursor)) {
$token .= $match[1];
} else {
// should never happen
throw new InvalidArgumentException(sprintf('Unable to parse input near "... %s ...".', substr($input, $cursor, 10)));
}
$cursor += \strlen($match[0]);
}
if (null !== $token) {
$tokens[] = $token;
}
return $tokens;