From 71963f98fb15d6da2eaa9ccce9b6c3a87c507a03 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Sat, 8 Sep 2018 15:13:17 +0100 Subject: [PATCH] Import argument types from PhpGt/Installer --- src/Application.php | 77 ++++++++ src/Argument/Argument.php | 22 +++ src/Argument/ArgumentList.php | 158 +++++++++++++++++ src/Argument/ArgumentValueList.php | 14 ++ src/Argument/CommandArgument.php | 12 ++ src/Argument/LongOptionArgument.php | 8 + src/Argument/NamedArgument.php | 12 ++ src/Argument/ShortOptionArgument.php | 8 + src/CliException.php | 6 + src/Command.php | 254 +++++++++++++++++++++++++++ src/Parameter/NamedParameter.php | 13 ++ src/Parameter/Parameter.php | 49 ++++++ src/Stream.php | 80 +++++++++ 13 files changed, 713 insertions(+) create mode 100644 src/Application.php create mode 100644 src/Argument/Argument.php create mode 100644 src/Argument/ArgumentList.php create mode 100644 src/Argument/ArgumentValueList.php create mode 100644 src/Argument/CommandArgument.php create mode 100644 src/Argument/LongOptionArgument.php create mode 100644 src/Argument/NamedArgument.php create mode 100644 src/Argument/ShortOptionArgument.php create mode 100644 src/CliException.php create mode 100644 src/Command.php create mode 100644 src/Parameter/NamedParameter.php create mode 100644 src/Parameter/Parameter.php create mode 100644 src/Stream.php diff --git a/src/Application.php b/src/Application.php new file mode 100644 index 0000000..5434210 --- /dev/null +++ b/src/Application.php @@ -0,0 +1,77 @@ +applicationName = $applicationName; + $this->arguments = $arguments; + $this->commands = $commands; + + $this->commands []= new HelpCommand( + $this->applicationName, + $this->commands + ); + + $this->streams = new Stream( + "php://stdin", + "php://stdout", + "php://stderr" + ); + } + + public function setStreams($in, $out, $error) { + $this->streams->setStreams($in, $out, $error); + } + + public function run():void { + $commandName = $this->arguments->getCommandName(); + $command = $this->findCommandByName($commandName); + $command->setStream($this->stream); + $argumentValueList = $command->getArgumentValueList($this->arguments); + + try { + $command->checkArguments($this->arguments); + } + catch(NotEnoughArgumentsException $exception) { + $this->streams->writeLine( + "Not enough arguments passed.", + Stream::ERROR + ); + $this->streams->writeLine( + $command->getUsage(), + Stream::ERROR + ); + } + catch(MissingRequiredParameterException $exception) { + + } + catch(MissingRequiredParameterValueException $exception) { + + } + + $command->run($argumentValueList); + } + + protected function findCommandByName(string $name):Command { + foreach($this->commands as $command) { + if($command->getName() !== $name) { + continue; + } + + return $command; + } + + throw new InvalidCommandException($name); + } +} \ No newline at end of file diff --git a/src/Argument/Argument.php b/src/Argument/Argument.php new file mode 100644 index 0000000..1cf3c26 --- /dev/null +++ b/src/Argument/Argument.php @@ -0,0 +1,22 @@ +key = $this->processRawKey($rawKey); + $this->value = $value; + } + + abstract protected function processRawKey(string $rawKey):string; + + public function getKey():string { + return $this->key; + } + + public function getValue():?string { + return $this->value; + } +} \ No newline at end of file diff --git a/src/Argument/ArgumentList.php b/src/Argument/ArgumentList.php new file mode 100644 index 0000000..a7aaff1 --- /dev/null +++ b/src/Argument/ArgumentList.php @@ -0,0 +1,158 @@ +script = $script; + $this->buildArgumentList($arguments); + } + + public function getCommandName():string { + return $this->argumentList[0]->getValue(); + } + + protected function buildArgumentList(array $arguments):void { + $commandArgument = array_shift($arguments); + $this->argumentList []= new CommandArgument( + $commandArgument ?? self::DEFAULT_COMMAND + ); + + $skipNextArgument = false; + + foreach ($arguments as $i => $arg) { + if($skipNextArgument) { + $skipNextArgument = false; + continue; + } + + if ($arg[0] === "-") { + if(strstr($arg, "=")) { + $name = substr( + $arg, + 0, + strpos( + $arg, + "=" + ) + ); + + $value = substr( + $arg, + strpos( + $arg, + "=" + ) + 1 + ); + } + else { + $skipNextArgument = true; + $name = $arg; + $value = $arguments[$i + 1]; + } + + if ($arg[1] === "-") { + $this->argumentList []= new LongOptionArgument( + $name, + $value + ); + } + else { + $this->argumentList []= new ShortOptionArgument( + $arg, + $value + ); + } + } else { + $this->argumentList []= new NamedArgument($arg); + } + } + } + + /** + * @link http://php.net/manual/en/iterator.current.php + */ + public function current():Argument { + return $this->argumentList[$this->iteratorIndex]; + } + + /** + * @link http://php.net/manual/en/iterator.next.php + */ + public function next():void { + $this->iteratorIndex++; + } + + /** + * @link http://php.net/manual/en/iterator.key.php + */ + public function key():int { + return $this->iteratorIndex; + } + + /** + * @link http://php.net/manual/en/iterator.valid.php + */ + public function valid():bool { + return isset($this->argumentList[$this->iteratorIndex]); + } + + /** + * @link http://php.net/manual/en/iterator.rewind.php + */ + public function rewind() { + $this->iteratorIndex = 0; + } + + public function contains(Parameter $parameter):bool { + $longOption = $parameter->getLongOption(); + $shortOption = $parameter->getShortOption(); + + foreach($this->argumentList as $argument) { + $key = $argument->getKey(); + + if($argument instanceof LongOptionArgument) { + if($key === $longOption) { + return true; + } + } + elseif($argument instanceof ShortOptionArgument) { + if($key === $shortOption) { + return true; + } + } + } + + return false; + } + + public function getValueForParameter(Parameter $parameter):?string { + $longOption = $parameter->getLongOption(); + $shortOption = $parameter->getShortOption(); + + foreach($this->argumentList as $argument) { + $key = $argument->getKey(); + + if($argument instanceof LongOptionArgument) { + if($key === $longOption) { + return $argument->getValue(); + } + } + elseif($argument instanceof ShortOptionArgument) { + if($key === $shortOption) { + return $argument->getValue(); + } + } + } + + return null; + } +} \ No newline at end of file diff --git a/src/Argument/ArgumentValueList.php b/src/Argument/ArgumentValueList.php new file mode 100644 index 0000000..524d545 --- /dev/null +++ b/src/Argument/ArgumentValueList.php @@ -0,0 +1,14 @@ +valueMap[$key] = $value; + } + + public function get(string $key):string { + return $this->valueMap[$key]; + } +} \ No newline at end of file diff --git a/src/Argument/CommandArgument.php b/src/Argument/CommandArgument.php new file mode 100644 index 0000000..2edb7c2 --- /dev/null +++ b/src/Argument/CommandArgument.php @@ -0,0 +1,12 @@ +stream = $stream; + } + + abstract public function run(ArgumentValueList $arguments):void; + + public function getName():string { + return $this->name; + } + + protected function setName(string $name):void { + $this->name = $name; + } + + public function getDescription():string { + return $this->description; + } + + protected function setDescription(string $description):void { + $this->description = $description; + } + + + public function checkArguments(ArgumentList $argumentList):void { + $numRequiredNamedParameters = count( + $this->requiredNamedParameterList + ); + + $passedNamedArguments = 0; + foreach($argumentList as $argument) { + if($argument instanceof NamedArgument) { + $passedNamedArguments ++; + } + } + + if($passedNamedArguments < $numRequiredNamedParameters) { + throw new NotEnoughArgumentsException(); + } + + foreach($this->requiredParameterList as $parameter) { + if(!$argumentList->contains($parameter)) { + throw new MissingRequiredParameterException( + $parameter + ); + } + + if($parameter->isValueRequired()) { + $value = $argumentList->getValueForParameter( + $parameter + ); + if(is_null($value)) { + throw new MissingRequiredParameterValueException( + $parameter + ); + } + } + } + } + + /** + * @return NamedParameter[] + */ + public function getRequiredNamedParameterList():array { + return $this->requiredNamedParameterList; + } + + public function getUsage():string { + $message = ""; + + $message .= "Usage: "; + $message .= $this->getName(); + + foreach($this->requiredNamedParameterList as $parameter) { + $message .= " "; + $message .= $parameter->getOptionName(); + } + + foreach($this->optionalNamedParameterList as $parameter) { + $message .= " ["; + $message .= $parameter->getOptionName(); + $message .= "]"; + } + + foreach($this->requiredParameterList as $parameter) { + $message .= " --"; + $message .= $parameter->getLongOption(); + + if($short = $parameter->getShortOption()) { + $message .= "|-$short"; + } + + if($parameter->isValueRequired()) { + $message .= " "; + $message .= $parameter->getExample(); + } + } + + foreach($this->optionalParameterList as $parameter) { + $message .= " [--"; + $message .= $parameter->getLongOption(); + + if($short = $parameter->getShortOption()) { + $message .= "|-$short"; + } + + if($parameter->isValueRequired()) { + $message .= " "; + $message .= $parameter->getExample(); + $message .= "]"; + } + } + + return $message; + } + + public function getArgumentValueList( + ArgumentList $arguments + ):ArgumentValueList { + $namedParameterIndex = 0; + /** @var NamedParameter[] */ + $namedParameterList = array_merge( + $this->requiredNamedParameterList, + $this->optionalNamedParameterList + ); + + $parameterIndex = 0; + /** @var Parameter[] $parameterList */ + $parameterList = array_merge( + $this->requiredParameterList, + $this->optionalParameterList + ); + + $argumentValueList = new ArgumentValueList(); + + foreach($arguments as $argument) { + if($argument instanceof CommandArgument) { + continue; + } + + if($argument instanceof NamedArgument) { + /** @var NamedParameter $parameter */ + $parameter = $namedParameterList[ + $namedParameterIndex + ]; + + $argumentValueList->set( + $parameter->getOptionName(), + $argument->getValue() + ); + $namedParameterIndex++; + } + + if($argument instanceof Argument) { + /** @var Parameter $parameter */ + $parameter = $parameterList[ + $parameterIndex + ]; + + $argumentValueList->set( + $parameter->getLongOption(), + $argument->getValue() + ); + } + } + + return $argumentValueList; + } + + protected function setRequiredNamedParameter(string $name):void { + $this->requiredNamedParameterList []= new NamedParameter( + $name + ); + } + + /** + * @return NamedParameter[] + */ + public function getOptionalNamedParameterList():array { + return $this->optionalNamedParameterList; + } + + protected function setOptionalNamedParameter(string $name):void { + $this->optionalNamedParameterList []= new NamedParameter( + $name + ); + } + + /** + * @return Parameter[] + */ + public function getRequiredParameterList():array { + return $this->requiredParameterList; + } + + protected function setRequiredParameter( + bool $requireValue, + string $longOption, + string $shortOption = null, + string $example = null + ):void { + $this->requiredParameterList []= new Parameter( + $requireValue, + $longOption, + $shortOption, + $example + ); + } + + /** + * @return Parameter[] + */ + public function getOptionalParameterList():array { + return $this->optionalParameterList; + } + + protected function setOptionalParameter( + bool $requireValue, + string $longOption, + string $shortOption = null, + string $example = null + ):void { + $this->optionalParameterList []= new Parameter( + $requireValue, + $longOption, + $shortOption, + $example + ); + } +} \ No newline at end of file diff --git a/src/Parameter/NamedParameter.php b/src/Parameter/NamedParameter.php new file mode 100644 index 0000000..d2b5e2f --- /dev/null +++ b/src/Parameter/NamedParameter.php @@ -0,0 +1,13 @@ +longOption = $optionName; + } + + public function getOptionName():string { + return $this->longOption; + } +} \ No newline at end of file diff --git a/src/Parameter/Parameter.php b/src/Parameter/Parameter.php new file mode 100644 index 0000000..36e1e22 --- /dev/null +++ b/src/Parameter/Parameter.php @@ -0,0 +1,49 @@ +requireValue = $requireValue; + $this->longOption = $longOption; + $this->shortOption = $shortOption; + $this->example = $example; + } + + public function __toString():string { + $message = $this->longOption; + + if(!is_null($this->shortOption)) { + $message .= " ("; + $message .= $this->shortOption; + $message .= ")"; + } + + return $message; + } + + public function getLongOption():string { + return $this->longOption; + } + + public function getShortOption():?string { + return $this->shortOption; + } + + public function isValueRequired():bool { + return $this->requireValue; + } + + public function getExample():string { + return $this->example ?? "value"; + } +} \ No newline at end of file diff --git a/src/Stream.php b/src/Stream.php new file mode 100644 index 0000000..1f11dfc --- /dev/null +++ b/src/Stream.php @@ -0,0 +1,80 @@ +setStreams($in, $out, $error); + } + + public function setStreams(string $in, string $out, string $error) { + $this->in = new SplFileObject( + $in, + "r" + ); + $this->out = new SplFileObject( + $out, + "w" + ); + $this->error = new SplFileObject( + $error, + "w" + ); + } + + public function getInStream():SplFileObject { + return $this->in; + } + + public function getOutStream():SplFileObject { + return $this->out; + } + + public function getErrorStream():SplFileObject { + return $this->error; + } + + public function write( + string $message, + string $streamName = self::OUT + ):void { + $this->getNamedStream( + $streamName + )->fwrite($message); + } + + public function writeLine( + string $message = "", + string $streamName = self::OUT + ):void { + $this->write( + $message . PHP_EOL, + $streamName + ); + } + + protected function getNamedStream(string $streamName):SplFileObject { + switch($streamName) { + case self::IN: + return $this->in; + case self::OUT: + return $this->out; + case self::ERROR: + return $this->error; + } + } +} \ No newline at end of file