8000 [Console] Added three state long option by ocke · Pull Request #11883 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Console] Added three state long option #11883

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/Symfony/Component/Console/Input/ArgvInput.php
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ private function parseArgument($token)
// 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() ? array($token) : $token;

// if last argument isArray(), append token to last argument
} elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) {
Expand Down Expand Up @@ -249,6 +249,8 @@ private function addLongOption($name, $value)

if ($option->isArray()) {
$this->options[$name][] = $value;
} elseif ($option->isValueTernary() && true === $value) {
$this->options[$name] = $option->getFlagValue();
} else {
$this->options[$name] = $value;
}
Expand Down Expand Up @@ -336,7 +338,7 @@ public function __toString()
$self = $this;
$tokens = array_map(function ($token) use ($self) {
if (preg_match('{^(-[^=]+=)(.+)}', $token, $match)) {
return $match[1] . $self->escapeToken($match[2]);
return $match[1].$self->escapeToken($match[2]);
}

if ($token && $token[0] !== '-') {
Expand Down
38 changes: 36 additions & 2 deletions src/Symfony/Component/Console/Input/InputOption.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ class InputOption
const VALUE_REQUIRED = 2;
const VALUE_OPTIONAL = 4;
const VALUE_IS_ARRAY = 8;
const VALUE_TERNARY = 16;

private $name;
private $shortcut;
private $mode;
private $default;
private $flagValue;
private $description;

/**
Expand Down Expand Up @@ -73,7 +75,7 @@ public function __construct($name, $shortcut = null, $mode = null, $description

if (null === $mode) {
$mode = self::VALUE_NONE;
} elseif (!is_int($mode) || $mode > 15 || $mode < 1) {
} elseif (!is_int($mode) || $mode > 31 || $mode < 1) {
throw new \InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode));
}

Expand Down Expand Up @@ -116,7 +118,7 @@ public function getName()
*/
public function acceptValue()
{
return $this->isValueRequired() || $this->isValueOptional();
return $this->isValueRequired() || $this->isValueOptional() || $this->isValueTernary();
}

/**
Expand Down Expand Up @@ -149,9 +151,22 @@ public function isArray()
return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode);
}

/**
* Returns true if the option takes an optional value with false as default and flagValue as default
*
* @return bool true if mode is self::VALUE_TERNARY, false otherwise
*/
public function isValueTernary()
{
return self::VALUE_TERNARY === (self::VALUE_TERNARY & $this->mode);
}

/**
* Sets the default value.
*
* For VALUE_TERNARY the $default will always be set to false (for when the long option is not used)
* and $flagValue will be used to store the default (for when the long option is used without a value)
*
* @param mixed $default The default value
*
* @throws \LogicException When incorrect default value is given
Expand All @@ -170,6 +185,14 @@ public function setDefault($default = null)
}
}

$flagValue = false;

if ($this->isValueTernary()) {
$flagValue = $default;
$default = false;
}

$this->flagValue = $flagValue;
$this->default = $this->acceptValue() ? $default : false;
}

Expand All @@ -183,6 +206,16 @@ public function getDefault()
return $this->default;
}

/**
* Returns the default value for a longoption without a value
*
* @return mixed The default value
*/
public function getFlagValue()
{
return $this->flagValue;
}

/**
* Returns the description text.
*
Expand All @@ -207,6 +240,7 @@ public function equals(InputOption $option)
&& $option->isArray() === $this->isArray()
&& $option->isValueRequired() === $this->isValueRequired()
&& $option->isValueOptional() === $this->isValueOptional()
&& $option->isValueTernary() === $this->isValueTernary()
;
}
}
68 changes: 43 additions & 25 deletions src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,92 +57,110 @@ public function provideOptions()
array('cli.php', '--foo'),
array(new InputOption('foo')),
array('foo' => true),
'->parse() parses long options without a value'
'->parse() parses long options without a value',
),
array(
array('cli.php', '--foo=bar'),
array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)),
array('foo' => 'bar'),
'->parse() parses long options with a required value (with a = separator)'
'->parse() parses long options with a required value (with a = separator)',
),
array(
array('cli.php', '--foo', 'bar'),
array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)),
array('foo' => 'bar'),
'->parse() parses long options with a required value (with a space separator)'
'->parse() parses long options with a required value (with a space separator)',
),
array(
array('cli.php', '-f'),
array(new InputOption('foo', 'f')),
array('foo' => true),
'->parse() parses short options without a value'
'->parse() parses short options without a value',
),
array(
array('cli.php', '-fbar'),
array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)),
array('foo' => 'bar'),
'->parse() parses short options with a required value (with no separator)'
'->parse() parses short options with a required value (with no separator)',
),
array(
array('cli.php', '-f', 'bar'),
array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)),
array('foo' => 'bar'),
'->parse() parses short options with a required value (with a space separator)'
'->parse() parses short options with a required value (with a space separator)',
),
array(
array('cli.php', '-f', ''),
array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)),
array('foo' => ''),
'->parse() parses short options with an optional empty value'
'->parse() parses short options with an optional empty value',
),
array(
array('cli.php', '-f', '', 'foo'),
array(new InputArgument('name'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)),
array('foo' => ''),
'->parse() parses short options with an optional empty value followed by an argument'
'->parse() parses short options with an optional empty value followed by an argument',
),
array(
array('cli.php', '-f', '', '-b'),
array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b')),
array('foo' => '', 'bar' => true),
'->parse() parses short options with an optional empty value followed by an option'
'->parse() parses short options with an optional empty value followed by an option',
),
array(
array('cli.php', '-f', '-b', 'foo'),
array(new InputArgument('name'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b')),
array('foo' => null, 'bar' => true),
'->parse() parses short options with an optional value which is not present'
'->parse() parses short options with an optional value which is not present',
),
array(
array('cli.php', '-fb'),
array(new InputOption('foo', 'f'), new InputOption('bar', 'b')),
array('foo' => true, 'bar' => true),
'->parse() parses short options when they are aggregated as a single one'
'->parse() parses short options when they are aggregated as a single one',
),
array(
array('cli.php', '-fb', 'bar'),
array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_REQUIRED)),
array('foo' => true, 'bar' => 'bar'),
'->parse() parses short options when they are aggregated as a single one and the last one has a required value'
'->parse() parses short options when they are aggregated as a single one and the last one has a required value',
),
array(
array('cli.php', '-fb', 'bar'),
array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)),
array('foo' => true, 'bar' => 'bar'),
'->parse() parses short options when they are aggregated as a single one and the last one has an optional value'
'->parse() parses short options when they are aggregated as a single one and the last one has an optional value',
),
array(
array('cli.php', '-fbbar'),
array(new InputOption('foo', 'f'), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)),
array('foo' => true, 'bar' => 'bar'),
'->parse() parses short options when they are aggregated as a single one and the last one has an optional value with no separator'
'->parse() parses short options when they are aggregated as a single one and the last one has an optional value with no separator',
),
array(
array('cli.php', '-fbbar'),
array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b', InputOption::VALUE_OPTIONAL)),
array('foo' => 'bbar', 'bar' => null),
'->parse() parses short options when they are aggregated as a single one and one of them takes a value'
)
'->parse() parses short options when they are aggregated as a single one and one of them takes a value',
),
array(
array('cli.php'),
array(new InputOption('foo', 'f', InputOption::VALUE_TERNARY)),
array('foo' => false),
'->parse() parses undefined ternary long option',
),
array(
array('cli.php', '--foo'),
array(new InputOption('foo', 'f', InputOption::VALUE_TERNARY)),
array('foo' => null),
'->parse() parses ternary long option without a value',
),
array(
array('cli.php', '--foo=test'),
array(new InputOption('foo', 'f', InputOption::VALUE_TERNARY)),
array('foo' => 'test'),
'->parse() parses ternary long option with a value',
),
);
}

Expand All @@ -163,43 +181,43 @@ public function provideInvalidInput()
array(
array('cli.php', '--foo'),
new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))),
'The "--foo" option requires a value.'
'The "--foo" option requires a value.',
),
array(
array('cli.php', '-f'),
new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED))),
'The "--foo" option requires a value.'
'The "--foo" option requires a value.',
),
array(
array('cli.php', '-ffoo'),
new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_NONE))),
'The "-o" option does not exist.'
'The "-o" option does not exist.',
),
array(
array('cli.php', '--foo=bar'),
new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_NONE))),
'The "--foo" option does not accept a value.'
'The "--foo" option does not accept a value.',
),
array(
array('cli.php', 'foo', 'bar'),
new InputDefinition(),
'Too many arguments.'
'Too many arguments.',
),
array(
array('cli.php', '--foo'),
new InputDefinition(),
'The "--foo" option does not exist.'
'The "--foo" option does not exist.',
),
array(
array('cli.php', '-f'),
new InputDefinition(),
'The "-f" option does not exist.'
'The "-f" option does not exist.',
),
array(
array('cli.php', '-1'),
new InputDefinition(array(new InputArgument('number'))),
'The "-1" option does not exist.'
)
'The "-1" option does not exist.',
),
);
}

Expand Down
28 changes: 27 additions & 1 deletion src/Symfony/Component/Console/Tests/Input/InputOptionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ public function testModes()
$this->assertTrue($option->acceptValue(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode');
$this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode');
$this->assertTrue($option->isValueOptional(), '__construct() can take "InputOption::VALUE_OPTIONAL" as its mode');

$option = new InputOption('foo', 'f', InputOption::VALUE_TERNARY);
$this->assertTrue($option->acceptValue(), '__construct() can take "InputOption::VALUE_TERNARY" as its mode');
$this->assertFalse($option->isValueRequired(), '__construct() can take "InputOption::VALUE_TERNARY" as its mode');
$this->assertTrue($option->isValueTernary(), '__construct() can take "InputOption::VALUE_TERNARY" as its mode');
}

/**
Expand All @@ -86,7 +91,7 @@ public function provideInvalidModes()
{
return array(
array('ANOTHER_ONE'),
array(-1)
array(-1),
);
}

Expand Down Expand Up @@ -144,6 +149,27 @@ public function testGetDefault()

$option = new InputOption('foo', null, InputOption::VALUE_NONE);
$this->assertFalse($option->getDefault(), '->getDefault() returns false if the option does not take a value');

$option = new InputOption('foo', null, InputOption::VALUE_TERNARY);
$this->assertFalse($option->getDefault(), '->getDefault() returns false if the option does not take a value');

$option = new InputOption('foo', null, InputOption::VALUE_TERNARY);
$this->assertFalse($option->getDefault(), '->getDefault() returns false');

$option = new InputOption('foo', null, InputOption::VALUE_TERNARY, '', 'default');
$this->assertFalse($option->getDefault(), '->getDefault() returns false');
}

public function testGetFlagValue()
{
$option = new InputOption('foo');
$this->assertFalse($option->getFlagValue(), '->getDefault() returns false');

$option = new InputOption('foo', null, InputOption::VALUE_TERNARY);
$this->assertNull($option->getFlagValue(), '->getDefault() returns null if the option does not have a value');

$option = new InputOption('foo', null, InputOption::VALUE_TERNARY, '', 'default');
$this->assertEquals('default', $option->getFlagValue(), '->getDefault() returns the default value');
}

public function testSetDefault()
Expand Down
0