Skip to content
Browse files

Moved Options class to 99designs/cliph

  • Loading branch information...
1 parent a98d8d8 commit eb3b35474e6b29d535226364d7a870b61f188fee @lox lox committed
Showing with 0 additions and 515 deletions.
  1. +0 −309 classes/Ergo/Console/Options.php
  2. +0 −206 tests/Console/OptionsTest.php
View
309 classes/Ergo/Console/Options.php
@@ -1,309 +0,0 @@
-<?php
-
-namespace Ergo\Console;
-
-/**
- * A command line options parser. Takes a specification and then an array of tokens from the
- * command line to parse. Errors are captured silently for missing parameters, flags that require
- * values, etc.
- */
-class Options
-{
- private
- $_args,
- $_options=array(),
- $_errors=array(),
- $_parsed=false
- ;
-
- /**
- * Constructor
- * @param array $argv
- * @param array params to pass to define, see define()
- */
- public function __construct($argv, $define=array())
- {
- $this->_args = $argv;
- $this->define($define);
- }
-
- /**
- * Adds options definitions to the parser. Either a string or an array of strings
- * can be provided, with keys like the following:
- *
- * -v
- * --flag
- * --flag=withvalue
- * --flag= (require an argument)
- * -f,--flag=value (either, or)
- *
- * Bare parameters without a prefix can be captured as :alias
- *
- * By default parameters can occur zero or one time, this can be controlled
- * with characters after the flag name:
- *
- * --flag? the default, zero or one
- * --flag! exactly one time
- * --flag+ one or more times
- * --flag* zero or more times
- *
- * @chainable
- */
- public function define($options)
- {
- if(!is_array($options)) $options = preg_split("/\s+/",$options);
-
- foreach($options as $option)
- {
- $option = $this->_parseOption($option);
- $this->_options[$option->name] = $option;
-
- foreach($option->aliases as $alias)
- $this->_options[$alias] =& $this->_options[$option->name];
- }
-
- return $this;
- }
-
- /**
- * Either returns the definition for the given option or returns false
- * @return mixed
- */
- private function _option($token)
- {
- if(!isset($this->_options[$token]))
- return false;
-
- return $this->_options[$token];
- }
-
- /**
- * Forces a re-parse of specific arguments
- * @chainable
- */
- public function parse($args=null)
- {
- $args = is_null($args) ? $this->_args : $args;
- $tokens = array_slice($args,1);
- $needsValue = false;
-
- // reset global state
- $this->_parsed = true;
- $this->_errors = array();
-
- // process a FIFO stack of tokens
- while($token = array_shift($tokens))
- {
- if(!$needsValue && !isset($this->_options[$token]))
- {
- // short arguments with multiple letters need expanding
- if(preg_match('/^-([a-z0-9]{2,})$/i', $token, $m))
- {
- foreach(str_split($m[1], 1) as $x) array_unshift($tokens, "-$x");
- continue;
- }
- // joined arguments like --arg=value need splitting
- else if(preg_match('/^(--?\w+)=(.+?)$/', $token, $m))
- {
- $tokens = array_merge(explode('=', $token, 2), $tokens);
- continue;
- }
- }
-
- if($option = $this->_option($token))
- {
- if($needsValue)
- {
- $this->_errors[] = "Option $needsValue needs a value";
- $needsValue = false;
- }
- else if($option->needsValue)
- $needsValue = $token;
- else
- $option->values []= NULL;
- }
- else
- {
- if($needsValue)
- $this->_options[$needsValue]->values []= $token;
- else if($param = $this->_nextParameter())
- $this->_options[$param]->values []= $token;
- else
- $this->_errors[] = "Unknown parameter $token";
-
- $needsValue = false;
- }
- }
-
- if($needsValue)
- $this->_errors[] = "Option $needsValue needs a value";
-
- // post-process to check recurrance
- foreach($this->_options as $option=>$config)
- {
- if(in_array($config->recurrance, array('+', '!')) && !$this->has($option))
- $this->_errors[] = sprintf("Parameter $option is required");
-
- if(in_array($config->recurrance, array('!', '?')) && count($this->values($option)) > 1)
- $this->_errors[] = sprintf("Multiple values for $option not allowed");
- }
-
- return $this;
- }
-
- /**
- * Returns an array of error messages related to validation, none implies valid
- * @return array
- */
- public function errors()
- {
- if(!$this->_parsed) $this->parse($this->_args);
-
- return $this->_errors;
- }
-
- /**
- * Prints the first error to the console
- * @chainable
- */
- public function printErrors()
- {
- if($errors = $this->errors())
- printf("\n%s\n", $errors[0]);
-
- return $this;
- }
-
- /**
- * Throws exceptions for parameter validation errors
- * @chainable
- */
- public function validate()
- {
- foreach($this->errors() as $error)
- throw new Exception($error);
-
- return $this;
- }
-
- /**
- * Retreives the value of the specified key, or returns the default if it
- * doesn't exist
- */
- public function fetch($key, $default)
- {
- return $this->has($key) ? $this->value($key) : $default;
- }
-
- /**
- * Determines if the specific key has been set. If multiple parameters are passed
- * it checks if at least one of the parameters is set.
- * @return bool
- */
- public function has($arg)
- {
- if(!$this->_parsed) $this->parse($this->_args);
-
- foreach(func_get_args() as $arg)
- {
- if(isset($this->_options[$arg])
- && count($this->_option($arg)->values)) return true;
- }
-
- return false;
- }
-
- /**
- * Returns a single option value if set, an exception otherwise
- * @throws Exception
- * @return mixed
- */
- public function value($key)
- {
- $values = $this->values($key);
- return count($values) ? $values[0] : null;
- }
-
- /**
- * Returns an array of option values
- * @throws Exception
- * @return array
- */
- public function values($arg)
- {
- if(!$this->_parsed) $this->parse($this->_args);
-
- if(($option = $this->_option($arg)) == false)
- throw new \InvalidArgumentException("Unknown argument $arg");
-
- return ($option->hasDefault && empty($option->values))
- ? array($option->default)
- : $option->values
- ;
- }
-
- // parses an options definition into a struct
- private function _parseOption($option)
- {
- $regex = '/^
- (?<option>(?:--?|\:)[\w-]+)
- (?<alias>,(?:--?)[\w-]+)*
- (?<recurrance>[*?+!])?
- (?<needsvalue>=(?<default>.*?))?
- $/x';
-
- if(preg_match($regex,$option,$m))
- {
- return (object) array(
- 'name' => $m['option'],
- 'recurrance' => !empty($m['recurrance']) ? $m['recurrance'] : '?',
- 'hasDefault' => !empty($m['default']),
- 'default' => isset($m['default']) ? $this->_parseOptionValue($m['default']) : null,
- 'needsValue' => !empty($m['needsvalue']),
- 'type' => $m['option'][0] == ':' ? 'param' : 'flag',
- 'aliases' => !empty($m['alias']) ? array_filter(explode(',', $m['alias'])) : array(),
- 'values' => array(),
- );
- }
- else
- {
- throw new \InvalidArgumentException("Failed to parse $option");
- }
- }
-
- // parses values like "true" and "false" into type php var
- private function _parseOptionValue($value)
- {
- if($value === 'true')
- return true;
- if($value === '' || strcasecmp($value,'null') === 0)
- return NULL;
- else if($value === 'false')
- return false;
- else if(ctype_digit($value))
- return (int) $value;
- else
- return $value;
- }
-
- // returns the next bare parameter to be captured, false if none
- private function _nextParameter()
- {
- foreach($this->_options as $option)
- {
- $recurring = $option->recurrance == '+' || $option->recurrance == '*';
- $optional = $option->recurrance == '?';
-
- if($option->type == 'param' && ($recurring || ($optional && !$this->has($option->name))))
- return $option->name;
- }
-
- return false;
- }
-
- // helper to allow shorthand access
- public function __get($prop)
- {
- return $this->value($prop);
- }
-}
View
206 tests/Console/OptionsTest.php
@@ -1,206 +0,0 @@
-<?php
-
-namespace Ergo\Tests\Console;
-use \Ergo\Console\Options;
-
-class OptionsTest extends \UnitTestCase
-{
- private function assertNoErrors($options)
- {
- $this->assertEqual(count($options->errors()), 0,
- "There shouldn't be any parsing errors");
- }
-
- public function testBasicApi()
- {
- $options = new Options(array('testscript.php','-v','--after','2008-01-01'));
- $options->define(array('--after=2009-01-01','-v','--flag'));
-
- $this->assertTrue($options->has('-v'));
- $this->assertFalse($options->has('--flag'));
- $this->assertTrue($options->has('--after'));
- $this->assertEqual($options->value('--after'), '2008-01-01');
- $this->assertEqual($options->values('--after'), array('2008-01-01'));
- $this->assertNoErrors($options);
- }
-
- public function testDefaultValue()
- {
- $options = new Options(array('z.php'));
- $options->define(array('--blargh=24'));
-
- $this->assertFalse($options->has('--blargh'));
- $this->assertEqual($options->value('--blargh'), '24');
- $this->assertEqual($options->values('--blargh'), array('24'));
- $this->assertNoErrors($options);
- }
-
- public function testBareParameters()
- {
- $options = new Options(array('testscript.php','-v','myfilename'));
- $options->define(array('-v','--flag', ':filename'));
-
- $this->assertTrue($options->has('-v'));
- $this->assertTrue($options->has(':filename'));
- $this->assertEqual($options->value(':filename'), 'myfilename');
- $this->assertNoErrors($options);
- }
-
- public function testShortParametersCanBeAggregated()
- {
- $options = new Options(array('testscript.php','-vz','-q'));
- $options->define(array('-v','-x','-z','-q'));
-
- $this->assertTrue($options->has('-v'));
- $this->assertTrue($options->has('-z'));
- $this->assertTrue($options->has('-q'));
- $this->assertFalse($options->has('-x'));
- $this->assertNoErrors($options);
- }
-
- public function testShortParametersWithValues()
- {
- $options = new Options(array('testscript.php','-v', 'blargh','-v=meep'));
- $options->define(array('-v*=null'));
-
- $this->assertTrue($options->has('-v'));
- $this->assertEqual($options->values('-v'), array('blargh','meep'));
- $this->assertNoErrors($options);
- }
-
- public function testShortParametersWithValuesAndAggregates()
- {
- $options = new Options(array('testscript.php','-vxz','-r=meep'));
- $options->define(array('-r=false*','-v','-x','-z'));
-
- $this->assertTrue($options->has('-v'));
- $this->assertTrue($options->has('-x'));
- $this->assertTrue($options->has('-z'));
- $this->assertEqual($options->values('-r'), array('meep'));
-
- $options = new Options(array('x.php','-vrz'));
- $options
- ->define(array('-v*=false','-x','-z','-r'))
- ->parse()
- ;
-
- $this->assertEqual(count($options->errors()), 1);
- }
-
- public function testMultipleParamsToHas()
- {
- $options = new Options(array('x.php','file'));
- $options->define(array('--blargh',':file'));
-
- $this->assertFalse($options->has('-v'));
- $this->assertTrue($options->has(':file'));
- $this->assertTrue($options->has('-v',':file'));
- $this->assertNoErrors($options);
- }
-
- public function testRequiredParameters()
- {
- $options = new Options(array('x.php','-v'));
- $options->define(array('--blargh+','-v'));
-
- $this->assertFalse($options->has('--blargh'));
- $this->assertTrue($options->has('-v'));
-
- $this->assertEqual($options->errors(), array(
- 'Parameter --blargh is required'
- ));
- }
-
- public function testOptionsWithEmptyDefaults()
- {
- $options = new Options(array('x.php'));
- $options->define(array('--blargh='));
-
- $this->assertFalse($options->has('--blargh'));
- $this->assertNull($options->value('--blargh'));
- $this->assertEqual($options->values('--blargh'), array());
- $this->assertNoErrors($options);
- }
-
- public function testOptionsWithNoValues()
- {
- $options = new Options(array('x.php'));
- $options->define(array('--blargh'));
-
- $this->assertFalse($options->has('--blargh'));
- $this->assertNull($options->value('--blargh'));
- $this->assertEqual($options->values('--blargh'), array());
- $this->assertNoErrors($options);
- }
-
- public function testOptionsValuesStartingWithDashes()
- {
- $options = new Options(array('x.php','-xv','-a','-3months'));
- $options->define(array('-x','-v','-a='));
-
- $this->assertTrue($options->has('-x'));
- $this->assertTrue($options->has('-v'));
- $this->assertTrue($options->has('-a'));
-
- $this->assertEqual($options->value('-a'), '-3months');
- $this->assertNoErrors($options);
- }
-
- public function testFetch()
- {
- $options = new Options(array('x.php', '-b=gralb'));
- $options->define(array('-b='));
-
- $this->assertEqual($options->fetch('-b', 'hello'), 'gralb');
- $this->assertEqual($options->fetch('-y', 'blarg'), 'blarg');
- }
-
- public function testAliases()
- {
- $options = new Options(array('x.php', '-s'));
- $options->define(array('-s,--long'));
-
- $this->assertTrue($options->has('-s'));
- $this->assertTrue($options->has('--long'));
-
- $options = new Options(array('x.php', '--long'));
- $options->define(array('--long,-s'));
-
- $this->assertTrue($options->has('-s'));
- $this->assertTrue($options->has('--long'));
- }
-
- public function testAliasesWithDefaults()
- {
- $options = new Options(array('x.php'));
- $options->define(array('-s,--long=llamas'));
-
- $this->assertFalse($options->has('-s'));
- $this->assertFalse($options->has('--long'));
- $this->assertEqual($options->value('-s'), 'llamas');
- $this->assertEqual($options->value('--long'),'llamas');
- }
-
- public function testMultipleOptionalParams()
- {
- $options = new Options(array('x.php', 'test1', 'test2'));
- $options->define(array(':param1', ':param2'));
-
- $this->assertTrue($options->has(':param1'));
- $this->assertTrue($options->has(':param2'));
- $this->assertEqual($options->value(':param1'), 'test1');
- $this->assertEqual($options->value(':param2'), 'test2');
- }
-
- public function testMultipleRecurringParamsAreGreedy()
- {
- $options = new Options(array('x.php', 'test1', 'test2'));
- $options->define(array(':param1+', ':param2'));
-
- $this->assertTrue($options->has(':param1'));
- $this->assertFalse($options->has(':param2'));
- $this->assertEqual($options->values(':param1'), array('test1', 'test2'));
- $this->assertEqual($options->values(':param2'), array());
- }
-}
-

0 comments on commit eb3b354

Please sign in to comment.
Something went wrong with that request. Please try again.