diff --git a/src/Input/AbstractInputHandler.php b/src/Input/AbstractInputHandler.php index d35eb47..89d4c00 100644 --- a/src/Input/AbstractInputHandler.php +++ b/src/Input/AbstractInputHandler.php @@ -8,8 +8,7 @@ use pointybeard\Helpers\Functions\Flags; abstract class AbstractInputHandler implements Interfaces\InputHandlerInterface { - protected $options = []; - protected $arguments = []; + protected $input = []; protected $collection = null; abstract protected function parse(): bool; @@ -17,8 +16,7 @@ abstract class AbstractInputHandler implements Interfaces\InputHandlerInterface public function bind(InputCollection $inputCollection, bool $skipValidation = false): bool { // Do the binding stuff here - $this->options = []; - $this->arguments = []; + $this->input = []; $this->collection = $inputCollection; $this->parse(); @@ -43,71 +41,56 @@ abstract class AbstractInputHandler implements Interfaces\InputHandlerInterface public function validate(): void { - // Do basic missing option and value checking here - foreach ($this->collection->getOptions() as $input) { - self::checkRequiredAndRequiredValue($input, $this->options); - } + foreach ($this->collection->getItems() as $type => $items) { + foreach($items as $input) { + self::checkRequiredAndRequiredValue($input, $this->input); - // Option validation. - foreach ($this->collection->getoptions() as $o) { - $result = false; + if( + null !== $input->default() && + null === $this->find($input->name()) && + null === $input->validator() + ) { + $result = $input->default(); + } elseif(null !== $input->validator()) { + $validator = $input->validator(); - if (!array_key_exists($o->name(), $this->options)) { - $result = $o->default(); - } else { - if (null === $o->validator()) { - $result = $o->default(); - continue; - } elseif ($o->validator() instanceof \Closure) { - $validator = new Validator($o->validator()); - } elseif ($o->validator() instanceof Validator) { - $validator = $o->validator(); + if ($validator instanceof \Closure) { + $validator = new Validator($validator); + } elseif (!($validator instanceof Validator)) { + throw new \Exception("Validator for '{$input->name()}' must be NULL or an instance of either Closure or Input\Validator."); + } + + $result = $validator->validate($input, $this); } else { - throw new \Exception("Validator for option {$o->name()} must be NULL or an instance of either Closure or Input\Validator."); + $result = $this->find($input->name()); } - $result = $validator->validate($o, $this); - } - - $this->options[$o->name()] = $result; - } - - // Argument validation. - foreach ($this->collection->getArguments() as $a) { - self::checkRequiredAndRequiredValue($a, $this->arguments); - - if (isset($this->arguments[$a->name()]) && null !== $a->validator()) { - if ($a->validator() instanceof \Closure) { - $validator = new Validator($a->validator()); - } elseif ($a->validator() instanceof Validator) { - $validator = $a->validator(); - } else { - throw new \Exception("Validator for argument {$a->name()} must be NULL or an instance of either Closure or Input\Validator."); - } - - $validator->validate($a, $this); + $this->input[$input->name()] = $result; } } } - public function getArgument(string $name): ?string + public function find(string $name) { - return $this->arguments[$name] ?? null; + if(isset($this->input[$name])) { + return $this->input[$name]; + } + + // Check the collection to see if anything responds to $name + foreach($this->collection->getItems() as $type => $items) { + foreach($items as $ii) { + if($ii->respondsTo($name) && isset($this->input[$ii->name()])) { + return $this->input[$ii->name()]; + } + } + } + + throw new Exceptions\InputNotFoundException($name); } - public function getOption(string $name) + public function getInput(): array { - return $this->options[$name] ?? null; - } - - public function getArguments(): array - { - return $this->arguments; - } - - public function getOptions(): array - { - return $this->options; + return $this->input; } public function getCollection(): ?InputCollection diff --git a/src/Input/AbstractInputType.php b/src/Input/AbstractInputType.php index 9fadc5e..be3999c 100644 --- a/src/Input/AbstractInputType.php +++ b/src/Input/AbstractInputType.php @@ -12,22 +12,40 @@ abstract class AbstractInputType implements Interfaces\InputTypeInterface protected $flags; protected $description; protected $validator; + protected $default; protected $value; - public function __construct($name, int $flags = null, string $description = null, object $validator = null) + public function __construct(string $name = null, int $flags = null, string $description = null, object $validator = null, $default = null) { $this->name = $name; $this->flags = $flags; $this->description = $description; $this->validator = $validator; + $this->default = $default; } - public function __call($name, array $args = []) + public function __call($name, array $args=[]) + { + if (empty($args)) { + return $this->$name; + } + + $this->$name = $args[0]; + + return $this; + } + + public function __get($name) { return $this->$name; } + public function respondsTo(string $name): bool + { + return ($name == $this->name); + } + public function getType(): string { return strtolower((new \ReflectionClass(static::class))->getShortName()); diff --git a/src/Input/Exceptions/InputNotFoundException.php b/src/Input/Exceptions/InputNotFoundException.php index 475cc66..f262aaa 100644 --- a/src/Input/Exceptions/InputNotFoundException.php +++ b/src/Input/Exceptions/InputNotFoundException.php @@ -4,10 +4,10 @@ declare(strict_types=1); namespace pointybeard\Helpers\Cli\Input\Exceptions; -class InputHandlerNotFoundException extends \Exception +class InputNotFoundException extends \Exception { - public function __construct(string $handler, string $command, $code = 0, \Exception $previous = null) + public function __construct(string $name, $code = 0, \Exception $previous = null) { - return parent::__construct(sprintf('The input handler %s could not be located.', $handler), $code, $previous); + return parent::__construct(sprintf('Input %s could not be found.', $name), $code, $previous); } } diff --git a/src/Input/Handlers/Argv.php b/src/Input/Handlers/Argv.php index 75f30dd..536284c 100644 --- a/src/Input/Handlers/Argv.php +++ b/src/Input/Handlers/Argv.php @@ -67,6 +67,7 @@ class Argv extends Input\AbstractInputHandler $it = new \ArrayIterator($this->argv); $position = 0; + $argumentCount = 0; while ($it->valid()) { $token = $it->current(); @@ -82,9 +83,9 @@ class Argv extends Input\AbstractInputHandler $value = true; } - $o = $this->collection->findOption($name); + $o = $this->collection->find($name); - $this->options[ + $this->input[ $o instanceof Input\AbstractInputType ? $o->name() : $name @@ -98,14 +99,14 @@ class Argv extends Input\AbstractInputHandler // Determine if we're expecting a value. // It also might have a long option equivalent, so we need // to look for that too. - $o = $this->collection->findOption($name); + $o = $this->collection->find($name); // This could also be an incrementing value // and needs to be added up. E.g. e.g. -vvv or -v -v -v // would be -v => 3 if ($o instanceof Input\AbstractInputType && Flags\is_flag_set($o->flags(), Input\AbstractInputType::FLAG_TYPE_INCREMENTING)) { - $value = isset($this->options[$name]) - ? $this->options[$name] + 1 + $value = isset($this->input[$name]) + ? $this->input[$name] + 1 : 1 ; @@ -128,7 +129,7 @@ class Argv extends Input\AbstractInputHandler } } - $this->options[ + $this->input[ $o instanceof Input\AbstractInputType ? $o->name() : $name @@ -141,12 +142,13 @@ class Argv extends Input\AbstractInputHandler // Arguments are positional, so we need to keep a track // of the index and look at the collection for an argument // with the same index - $a = $this->collection->getArgumentsByIndex(count($this->arguments)); - $this->arguments[ + $a = $this->collection->getItemByIndex('Argument', $argumentCount); + $this->input[ $a instanceof Input\AbstractInputType ? $a->name() - : count($this->arguments) + : $argumentCount ] = $token; + $argumentCount++; break; } $it->next(); diff --git a/src/Input/InputCollection.php b/src/Input/InputCollection.php index ddf99ba..d307217 100644 --- a/src/Input/InputCollection.php +++ b/src/Input/InputCollection.php @@ -6,8 +6,7 @@ namespace pointybeard\Helpers\Cli\Input; class InputCollection { - private $arguments = []; - private $options = []; + private $items = []; // Prevents the class from being instanciated public function __construct() @@ -17,96 +16,77 @@ class InputCollection public function append(Interfaces\InputTypeInterface $input, bool $replace = false): self { $class = new \ReflectionClass($input); - $this->{'append'.$class->getShortName()}($input, $replace); + + $index = null; + $type = null; + + if (!$replace && null !== $this->find($input->name(), null, null, $index, $type)) { + throw new \Exception("{$class->getShortName()} '{$input->name()}' already exists in this collection"); + } + + if (true == $replace && null !== $index) { + $this->items[$class->getShortName()][$index] = $argument; + } else { + $this->items[$class->getShortName()][] = $input; + } return $this; } - public function findArgument(string $name, ?int &$index = null): ?AbstractInputType + public function find(string $name, array $restrictToType=null, array $excludeType=null, &$type = null, &$index = null): ?AbstractInputType { - foreach ($this->arguments as $index => $a) { - if ($a->name() == $name) { - return $a; + foreach($this->items as $type => $items) { + + // Check if we're restricting to or excluding specific types + if(null !== $restrictToType && !in_array($type, $restrictToType)) { + continue; + + } elseif(null !== $excludeType && in_array($type, $excludeType)) { + continue; + } + + foreach($items as $index => $item) { + if($item->respondsTo($name)) { + return $item; + } } } - + $type = null; $index = null; - return null; } - public function findOption(string $name, ?int &$index = null): ?AbstractInputType - { - $type = 1 == strlen($name) ? 'name' : 'long'; - - foreach ($this->options as $index => $o) { - if ($o->$type() == $name) { - return $o; - } - } - - $index = null; - - return null; + public function getTypes(): array { + return array_keys($this->items); } - private function appendArgument(Interfaces\InputTypeInterface $argument, bool $replace = false): void - { - if (null !== $this->findArgument($argument->name(), $index) && !$replace) { - throw new \Exception("Argument {$argument->name()} already exists in collection"); - } - - if (true == $replace && null !== $index) { - $this->arguments[$index] = $argument; - } else { - $this->arguments[] = $argument; - } + public function getItems(): array { + return $this->items; } - private function appendOption(Interfaces\InputTypeInterface $option, bool $replace = false): void - { - if (null !== $this->findOption($option->name(), $index) && !$replace) { - throw new \Exception("Option -{$option->name()} already exists in collection"); - } - if (true == $replace && null !== $index) { - $this->options[$index] = $option; - } else { - $this->options[] = $option; - } + public function getItemsByType(string $type): array { + return $this->items[$type] ?? []; } - public function getArgumentsByIndex(int $index): ?AbstractInputType - { - return $this->arguments[$index] ?? null; - } - - public function getArguments(): array - { - return $this->arguments; - } - - public function getOptions(): array - { - return $this->options; + public function getItemByIndex(string $type, int $index): ?AbstractInputType { + return $this->items[$type][$index] ?? null; } public static function merge(self ...$collections): self { - $arguments = []; - $options = []; + $items = []; foreach ($collections as $c) { - $arguments = array_merge($arguments, $c->getArguments()); - $options = array_merge($options, $c->getOptions()); + foreach($c->items() as $type => $items) { + foreach($items as $item) { + $items[] = $item; + } + } } $mergedCollection = new self(); - $it = new \AppendIterator(); - $it->append(new \ArrayIterator($arguments)); - $it->append(new \ArrayIterator($options)); - - foreach ($it as $input) { + foreach ($items as $input) { try { $mergedCollection->append($input, true); } catch (\Exception $ex) { diff --git a/src/Input/InputTypeFactory.php b/src/Input/InputTypeFactory.php new file mode 100644 index 0000000..6d942dc --- /dev/null +++ b/src/Input/InputTypeFactory.php @@ -0,0 +1,23 @@ +