diff --git a/composer.json b/composer.json index 6c83bd7..36806ac 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,8 @@ ], "require": { "php": ">=5.4", - "kevinlebrun/colors.php": "~0.2" + "kevinlebrun/colors.php": "~0.2", + "ducks-project/spl-types": "^1.0" }, "version": "0.3.0", diff --git a/src/Commando/Command.php b/src/Commando/Command.php index 26b494e..bd0d5bf 100755 --- a/src/Commando/Command.php +++ b/src/Commando/Command.php @@ -18,6 +18,8 @@ namespace Commando; +use Commando\Option\TypeEnum; + /** * Here are all the methods available through __call. For accurate method documentation, see the actual method. * @@ -55,9 +57,9 @@ class Command implements \ArrayAccess, \Iterator { - const OPTION_TYPE_ARGUMENT = 1; // e.g. foo - const OPTION_TYPE_SHORT = 2; // e.g. -u - const OPTION_TYPE_VERBOSE = 4; // e.g. --username + const OPTION_TYPE_ARGUMENT = TypeEnum::ARGUMENT; // e.g. foo + const OPTION_TYPE_SHORT = TypeEnum::SHORT; // e.g. -u + const OPTION_TYPE_VERBOSE = TypeEnum::LONG; // e.g. --username private $current_option = null, @@ -427,7 +429,7 @@ public function parse() list($name, $type) = $this->_parseOption($token); // We allow short groups - if (strlen($name) > 1 && $type === self::OPTION_TYPE_SHORT) { + if (strlen($name) > 1 && $type === TypeEnum::SHORT) { $group = str_split($name); // correct option name @@ -441,7 +443,7 @@ public function parse() } } - if ($type === self::OPTION_TYPE_ARGUMENT) { + if ($type === TypeEnum::ARGUMENT) { // its an argument, use an int as the index $keyvals[$count] = $name; @@ -473,7 +475,7 @@ public function parse() // the next token MUST be an "argument" and not another flag/option $token = array_shift($tokens); list($val, $type) = $this->_parseOption($token); - if ($type !== self::OPTION_TYPE_ARGUMENT) + if ($type !== TypeEnum::ARGUMENT) throw new \Exception(sprintf('Unable to parse option %s: Expected an argument', $token)); $keyvals[$name] = $val; } @@ -490,8 +492,8 @@ public function parse() foreach ($this->options as $option) { if (is_null($option->getValue()) && $option->isRequired()) { throw new \Exception(sprintf('Required %s %s must be specified', - $option->getType() & Option::TYPE_NAMED ? - 'option' : 'argument', $option->getName())); + $option->getType() === TypeEnum::ARGUMENT ? + 'argument' : 'option', $option->getName())); } } @@ -521,15 +523,6 @@ public function parse() $this->sorted_keys = array_keys($this->options); natsort($this->sorted_keys); - // // See if our options have what they require - // foreach ($this->options as $option) { - // $needs = $option->hasNeeds($keyvals); - // if ($needs !== true) { - // throw new \InvalidArgumentException( - // 'Option "'.$option->getName().'" does not have required option(s): '.implode(', ', $needs) - // ); - // } - // } } catch(\Exception $e) { $this->error($e); } @@ -581,12 +574,12 @@ private function _parseOption($token) if (!empty($matches['hyphen'])) { $type = (strlen($matches['hyphen']) === 1) ? - self::OPTION_TYPE_SHORT: - self::OPTION_TYPE_VERBOSE; + TypeEnum::SHORT: + TypeEnum::LONG; return array($matches['name'], $type); } - return array($token, self::OPTION_TYPE_ARGUMENT); + return array($token, TypeEnum::ARGUMENT); } diff --git a/src/Commando/Option.php b/src/Commando/Option.php index aad7def..6ad3882 100755 --- a/src/Commando/Option.php +++ b/src/Commando/Option.php @@ -2,6 +2,7 @@ namespace Commando; use \Commando\Util\Terminal; +use \Commando\Option\TypeEnum; /** * Here are all the methods available through __call. For accurate method documentation, see the actual method. @@ -48,7 +49,7 @@ class Option $required = false, /* bool */ $needs = array(), /* set of other required options for this option */ $boolean = false, /* bool */ - $type = 0, /* int see constants */ + $type = 0, /* TypeEnum(int) */ $rule, /* closure */ $map, /* closure */ $increment = false, /* bool */ @@ -58,10 +59,10 @@ class Option $file_require_exists, /* bool require that the file path is valid */ $file_allow_globbing; /* bool allow globbing for files */ - const TYPE_SHORT = 1; - const TYPE_VERBOSE = 2; + const TYPE_SHORT = TypeEnum::SHORT; + const TYPE_VERBOSE = TypeEnum::LONG; const TYPE_NAMED = 3; // 1|2 - const TYPE_ANONYMOUS = 4; + const TYPE_ANONYMOUS = TypeEnum::ARGUMENT; /** * @param string|int $name single char name or int index for this option @@ -75,10 +76,9 @@ public function __construct($name) } if (!is_int($name)) { - $this->type = mb_strlen($name, 'UTF-8') === 1 ? - self::TYPE_SHORT : self::TYPE_VERBOSE; + $this->type = new TypeEnum(mb_strlen($name, 'UTF-8') > 1 ? TypeEnum::LONG : TypeEnum::SHORT); } else { - $this->type = self::TYPE_ANONYMOUS; + $this->type = new TypeEnum(TypeEnum::ARGUMENT); } $this->name = $name; @@ -301,7 +301,7 @@ public function getDescription() */ public function getType() { - return $this->type; + return $this->type->value; } /** @@ -429,15 +429,13 @@ public function getHelp() $color = new \Colors\Color(); $help = ''; - $isNamed = ($this->type & self::TYPE_NAMED); + $isNamed = $this->type->isNamed(); if ($isNamed) { - $help .= PHP_EOL . (mb_strlen($this->name, 'UTF-8') === 1 ? - '-' : '--') . $this->name; + $help .= PHP_EOL . ($this->type->isType(TypeEnum::SHORT) ? '-' : '--') . $this->name; if (!empty($this->aliases)) { foreach($this->aliases as $alias) { - $help .= (mb_strlen($alias, 'UTF-8') === 1 ? - '/-' : '/--') . $alias; + $help .= (mb_strlen($alias, 'UTF-8') === 1 ? '/-' : '/--') . $alias; } } if (!$this->isBoolean()) { diff --git a/src/Commando/Option/TypeEnum.php b/src/Commando/Option/TypeEnum.php new file mode 100644 index 0000000..b974cad --- /dev/null +++ b/src/Commando/Option/TypeEnum.php @@ -0,0 +1,48 @@ +__default; + } + } + + /** + * Is the instance value equal to $type? + * + * @param mixed $type + * @return boolean + */ + public function isType($type) + { + return static::isValueType($this, $type); + } + + /** + * Is the $value equal to $type? + * + * @param mixed $value + * @param mixed $type + * @return boolean + */ + public static function isValueType($value, $type) + { + $realValue = (int) is_subclass_of($value, "\\Commando\\Util\\Enum") ? static::extractValueFrom($value) : $value; + $realType = (int) is_subclass_of($type, "\\Commando\\Util\\Enum") ? static::extractValueFrom($type) : $type; + + return (bool) ($realValue & $realType); + } + + /** + * Get value of Enum + * + * @param Enum $value + * @return int + */ + public static function extractValueFrom(Enum $value) + { + return $value->value; + } +} + +if (class_exists("\\SplEnum")) { + /** + * Enum extending SPL_TYPES SplEnum + */ + class Enum extends \SplEnum { + use EnumUtilitiesTrait; + } +} else { + /** + * Enum extending ducks-project/spl-types SplEnum + */ + class Enum extends \Ducks\Component\SplTypes\SplEnum { + use EnumUtilitiesTrait; + } +} diff --git a/tests/Commando/TypeEnumTest.php b/tests/Commando/TypeEnumTest.php new file mode 100644 index 0000000..9bb9a31 --- /dev/null +++ b/tests/Commando/TypeEnumTest.php @@ -0,0 +1,63 @@ +assertEquals(TypeEnum::extractValueFrom($type), TypeEnum::SHORT); + } + + function testIsValueType() { + $type1 = new TypeEnum(TypeEnum::SHORT); + $type2 = new TypeEnum(TypeEnum::LONG); + $this->assertFalse(TypeEnum::isValueType(TypeEnum::SHORT, TypeEnum::LONG)); + $this->assertFalse(TypeEnum::isValueType($type1, $type2)); + $this->assertTrue(TypeEnum::isValueType($type1, TypeEnum::SHORT)); + $this->assertTrue(TypeEnum::isValueType(TypeEnum::LONG, $type2)); + } + + function test__getValue() { + $type = new TypeEnum(TypeEnum::SHORT); + $this->assertEquals($type->value, TypeEnum::SHORT); + } + + function testIsType() { + $type = new TypeEnum(TypeEnum::SHORT); + $this->assertTrue($type->isType(TypeEnum::SHORT)); + $this->assertFalse($type->isType(new TypeEnum(2))); // TypeEnum::LONG + } + + function testIsValueNamed() { + $type = new TypeEnum(TypeEnum::SHORT); + $this->assertTrue(TypeEnum::isValueNamed($type)); + $this->assertTrue(TypeEnum::isValueNamed(TypeEnum::LONG)); + $this->assertFalse(TypeEnum::isValueNamed(4)); // TypeEnum::ARGUMENT + } + + function testIsNamed() { + $type1 = new TypeEnum(TypeEnum::SHORT); + $type2 = new TypeEnum(TypeEnum::LONG); + $type3 = new TypeEnum(TypeEnum::ARGUMENT); + $this->assertTrue($type1->isNamed()); + $this->assertTrue($type2->isNamed()); + $this->assertFalse($type3->isNamed()); + } + +} \ No newline at end of file