Skip to content
This repository has been archived by the owner on Jan 8, 2020. It is now read-only.

Adding Zend\Console\Getopt option callback hooks #5713

Merged
merged 2 commits into from Mar 4, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
79 changes: 59 additions & 20 deletions library/Zend/Console/Getopt.php
Expand Up @@ -66,17 +66,6 @@
*
* Example: 'abc:' means options '-a', '-b', and '-c'
* are legal, and the latter requires a string parameter.
*
* @todo Handle flags that implicitly print usage message, e.g. --help
*
* @todo Enable user to specify header and footer content in the help message.
*
* @todo Feature request to handle option interdependencies.
* e.g. if -b is specified, -a must be specified or else the
* usage is invalid.
*
* @todo Feature request to implement callbacks.
* e.g. if -a is specified, run function 'handleOptionA'().
*/
class Getopt
{
Expand Down Expand Up @@ -191,6 +180,13 @@ class Getopt
*/
protected $parsed = false;

/**
* A list of callbacks to call when a particular option is present.
*
* @var array
*/
protected $optionCallbacks = array();

/**
* The constructor takes one to three parameters.
*
Expand Down Expand Up @@ -389,7 +385,7 @@ public function addRules($rules)
$this->_addRulesModeZend($rules);
break;
}
// intentional fallthrough
// intentional fallthrough
case self::MODE_GNU:
$this->_addRulesModeGnu($rules);
break;
Expand Down Expand Up @@ -591,8 +587,8 @@ public function getUsageMessage()
}
foreach ($lines as $linepart) {
$usage .= sprintf("%s %s\n",
str_pad($linepart['name'], $maxLen),
$linepart['help']);
str_pad($linepart['name'], $maxLen),
$linepart['help']);
}
return $usage;
}
Expand Down Expand Up @@ -676,7 +672,7 @@ public function parse()
}
if (substr($argv[0], 0, 2) == '--') {
$this->_parseLongOption($argv);
} elseif (substr($argv[0], 0, 1) == '-' && ('-' != $argv[0] || count($argv) >1)) {
} elseif (substr($argv[0], 0, 1) == '-' && ('-' != $argv[0] || count($argv) >1)) {
$this->_parseShortOptionCluster($argv);
} elseif ($this->getoptConfig[self::CONFIG_PARSEALL]) {
$this->remainingArgs[] = array_shift($argv);
Expand All @@ -690,9 +686,52 @@ public function parse()
}
}
$this->parsed = true;

//go through parsed args and process callbacks
$this->triggerCallbacks();

return $this;
}

/**
* @param string $option The name of the property which, if present, will call the passed
* callback with the value of this parameter.
* @param callable $callback The callback that will be called for this option. The first
* parameter will be the value of getOption($option), the second
* parameter will be a reference to $this object. If the callback returns
* false then an Exception\RuntimeException will be thrown indicating that
* there is a parse issue with this option.
*
* @return \Zend\Console\Getopt Fluent interface.
*/
public function setOptionCallback($option, \Closure $callback)
{
$this->optionCallbacks[$option] = $callback;

return $this;
}

/**
* Triggers all the registered callbacks.
*/
protected function triggerCallbacks()
{
foreach ($this->optionCallbacks as $option => $callback) {
if (null === $this->getOption($option)) {
continue;
}
//make sure we've resolved the alias, if using one
if (isset($this->ruleMap[$option]) && $option = $this->ruleMap[$option]) {
if (false === $callback($this->getOption($option), $this)) {
throw new Exception\RuntimeException(
"The option $option is invalid. See usage.",
$this->getUsageMessage()
);
}
}
}
}

/**
* Parse command-line arguments for a single long option.
* A long option is preceded by a double '--' character.
Expand Down Expand Up @@ -754,7 +793,7 @@ protected function _parseSingleOption($flag, &$argv)
throw new Exception\RuntimeException(
"Option \"$flag\" is not recognized.",
$this->getUsageMessage()
);
);
}

// Magic methods in future will use this mark as real flag value
Expand All @@ -777,7 +816,7 @@ protected function _parseSingleOption($flag, &$argv)
throw new Exception\RuntimeException(
"Option \"$flag\" requires a parameter.",
$this->getUsageMessage()
);
);
}
break;
case 'optional':
Expand Down Expand Up @@ -864,8 +903,8 @@ protected function _setSingleOptionValue($flag, $value)
protected function _setBooleanFlagValue($flag)
{
$this->options[$flag] = array_key_exists($flag, $this->options)
? (int) $this->options[$flag] + 1
: true;
? (int) $this->options[$flag] + 1
: true;
}

/**
Expand Down Expand Up @@ -1018,4 +1057,4 @@ protected function _addRulesModeZend($rules)
$this->rules[$mainFlag] = $rule;
}
}
}
}
70 changes: 70 additions & 0 deletions tests/ZendTest/Console/GetoptTest.php
Expand Up @@ -647,4 +647,74 @@ public function testGetoptRaiseExceptionForNumericOptionsIfAneHandlerIsSpecified
$this->setExpectedException('\Zend\Console\Exception\RuntimeException');
$opts->parse();
}

public function testOptionCallback()
{
$opts = new Getopt('a', array('-a'));
$testVal = null;
$opts->setOptionCallback('a', function($val, $opts) use (&$testVal) {
$testVal = $val;
});
$opts->parse();

$this->assertTrue($testVal);
}

public function testOptionCallbackAddedByAlias()
{
$opts = new Getopt(array(
'a|apples|apple=s' => "APPLES",
'b|bears|bear=s' => "BEARS"
), array(
'--apples=Gala',
'--bears=Grizzly'
));

$appleCallbackCalled = null;
$bearCallbackCalled = null;

$opts->setOptionCallback('a', function($val) use (&$appleCallbackCalled){
$appleCallbackCalled = $val;
});

$opts->setOptionCallback('bear', function($val) use (&$bearCallbackCalled){
$bearCallbackCalled = $val;
});

$opts->parse();

$this->assertSame('Gala', $appleCallbackCalled);
$this->assertSame('Grizzly', $bearCallbackCalled);
}

public function testOptionCallbackNotCalled()
{
$opts = new Getopt(array(
'a|apples|apple' => "APPLES",
'b|bears|bear' => "BEARS"
), array(
'--apples=Gala'
));

$bearCallbackCalled = null;

$opts->setOptionCallback('bear', function($val) use (&$bearCallbackCalled){
$bearCallbackCalled = $val;
});

$opts->parse();

$this->assertNull($bearCallbackCalled);
}

/**
* @expectedException \Zend\Console\Exception\RuntimeException
* @expectedExceptionMessage The option x is invalid. See usage.
*/
public function testOptionCallbackReturnsFallsAndThrowException()
{
$opts = new Getopt('x', array('-x'));
$opts->setOptionCallback('x', function(){return false;});
$opts->parse();
}
}