From d4f858834679736fc8f0daef3aa078939db02666 Mon Sep 17 00:00:00 2001 From: Devon Bagley Date: Thu, 19 Jul 2018 14:01:07 -0700 Subject: [PATCH 1/3] Refactor (Option::type) More consistant constants Add class for holding constants which are common accross classes and performing simple value operations and comparisons. DEPRECATE the current constants, which are similar but use different naming and values to mean the same thing in other classes. BREAKING: The original constant values used in the Command class have changed. - Add enumerator class to contain Option type constant values along with some utility functions for comparing types. - Replace constant values with those from TypeEnum in Option - Replace constant values with those from TypeEnum in Command - Add dependency for systems without SplTypes PECL extension - Tests for new class *_Deprecated:_* - Option::TYPE_SHORT - Option::TYPE_VERBOSE - Option::TYPE_NAMED - Option::TYPE_ANONYMOUS - Command::OPTION_TYPE_SHORT - Command::OPTION_TYPE_VERBOSE - Command::OPTION_TYPE_ARGUMENT --- composer.json | 3 +- src/Commando/Command.php | 33 +++++-------- src/Commando/Option.php | 26 +++++----- src/Commando/Option/TypeEnum.php | 48 ++++++++++++++++++ src/Commando/Util/Enum.php | 83 ++++++++++++++++++++++++++++++++ tests/Commando/TypeEnumTest.php | 63 ++++++++++++++++++++++++ 6 files changed, 221 insertions(+), 35 deletions(-) create mode 100644 src/Commando/Option/TypeEnum.php create mode 100644 src/Commando/Util/Enum.php create mode 100644 tests/Commando/TypeEnumTest.php 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..306f7fa 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_NAMED = 3; // 1|2 - const TYPE_ANONYMOUS = 4; + const TYPE_SHORT = TypeEnum::SHORT; + const TYPE_VERBOSE = TypeEnum::LONG; + const TYPE_NAMED = (TypeEnum::SHORT | TypeEnum::LONG); // 1|2 + 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, self::class) ? static::extractValueFrom($value) : $value; + $realType = (int) is_a($type, self::class) ? 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 From c24897f352be34f917be7ab717e2ec8d52a0489c Mon Sep 17 00:00:00 2001 From: Devon Bagley Date: Thu, 19 Jul 2018 14:11:31 -0700 Subject: [PATCH 2/3] Refactor (Option::type) Backward compatibility with PHP 5.4+ - Replace ::class since this was introduced in 5.5 - Constants could not be declared with expressions until 5.6 --- src/Commando/Option.php | 2 +- src/Commando/Util/Enum.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Commando/Option.php b/src/Commando/Option.php index 306f7fa..6ad3882 100755 --- a/src/Commando/Option.php +++ b/src/Commando/Option.php @@ -61,7 +61,7 @@ class Option const TYPE_SHORT = TypeEnum::SHORT; const TYPE_VERBOSE = TypeEnum::LONG; - const TYPE_NAMED = (TypeEnum::SHORT | TypeEnum::LONG); // 1|2 + const TYPE_NAMED = 3; // 1|2 const TYPE_ANONYMOUS = TypeEnum::ARGUMENT; /** diff --git a/src/Commando/Util/Enum.php b/src/Commando/Util/Enum.php index efd3b7d..e5663b8 100644 --- a/src/Commando/Util/Enum.php +++ b/src/Commando/Util/Enum.php @@ -48,8 +48,8 @@ public function isType($type) */ public static function isValueType($value, $type) { - $realValue = (int) is_subclass_of($value, self::class) ? static::extractValueFrom($value) : $value; - $realType = (int) is_a($type, self::class) ? static::extractValueFrom($type) : $type; + $realValue = (int) is_subclass_of($value, \get_class(self)) ? static::extractValueFrom($value) : $value; + $realType = (int) is_subclass_of($type, \get_class(self)) ? static::extractValueFrom($type) : $type; return (bool) ($realValue & $realType); } From b2a987b32635777f386c555d65b8df9615c5d5bf Mon Sep 17 00:00:00 2001 From: Devon Bagley Date: Thu, 19 Jul 2018 14:20:53 -0700 Subject: [PATCH 3/3] Refactor (Option::type) get_class expects object Use explicit class name as string for is_subclass_of --- src/Commando/Util/Enum.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Commando/Util/Enum.php b/src/Commando/Util/Enum.php index e5663b8..833d175 100644 --- a/src/Commando/Util/Enum.php +++ b/src/Commando/Util/Enum.php @@ -48,8 +48,8 @@ public function isType($type) */ public static function isValueType($value, $type) { - $realValue = (int) is_subclass_of($value, \get_class(self)) ? static::extractValueFrom($value) : $value; - $realType = (int) is_subclass_of($type, \get_class(self)) ? static::extractValueFrom($type) : $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); }