commit b89af5c6da57262211477ab301b738a3762375f4 Author: darkelfe14728 Date: Thu Sep 12 20:16:18 2019 +0200 Initial commit diff --git a/.idea/$CACHE_FILE$ b/.idea/$CACHE_FILE$ new file mode 100644 index 0000000..f2d6210 --- /dev/null +++ b/.idea/$CACHE_FILE$ @@ -0,0 +1,23 @@ + + + + + + + + + PHP + + + SQL + + + + + SqlResolveInspection + + + + + + \ No newline at end of file diff --git a/.idea/$PRODUCT_WORKSPACE_FILE$ b/.idea/$PRODUCT_WORKSPACE_FILE$ new file mode 100644 index 0000000..96ce66b --- /dev/null +++ b/.idea/$PRODUCT_WORKSPACE_FILE$ @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/CommandLine.iml b/.idea/CommandLine.iml new file mode 100644 index 0000000..c956989 --- /dev/null +++ b/.idea/CommandLine.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..a4810b6 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,115 @@ + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..a55e7a1 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/deployment.xml b/.idea/deployment.xml new file mode 100644 index 0000000..f99b3f9 --- /dev/null +++ b/.idea/deployment.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..0dab9d5 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..28a804d --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..acf5e25 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml new file mode 100644 index 0000000..877984f --- /dev/null +++ b/.idea/php.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..803f3ff --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1568311237973 + + + + + + \ No newline at end of file diff --git a/Argument/ArgumentAbstract.class.php b/Argument/ArgumentAbstract.class.php new file mode 100644 index 0000000..87e1fbd --- /dev/null +++ b/Argument/ArgumentAbstract.class.php @@ -0,0 +1,121 @@ +setName($name, true); + $this->setDescription($description); + } + + public function getVarName() { + return $this->_varName; + } + /** + * Définit le nom de la variable de retour de l'argument. + * + * @param string $varName Le nom + * + * @return $this + * + * @see $_name + */ + public function setVarName($varName) { + $this->_varName = $varName; + return $this; + } + + public function getName() { + return $this->_name; + } + /** + * Définit le nom de l'argument. + * + * @param string $name Le nom + * @param boolean $replaceVarName Remplacer également le nom de la variable de retour ? + * + * @return $this + * + * @see $_name + */ + public function setName($name, $replaceVarName = true) { + $this->_name = $name; + if($replaceVarName) + $this->setVarName($name); + return $this; + } + + public function getDescription() { + return $this->_description; + } + /** + * Définit la description de l'argument. + * + * @param string|null $description La description + * + * @return $this + * + * @see $_description + */ + public function setDescription($description) { + $this->_description = $description; + return $this; + } + + public function getDefault() { + return $this->_default; + } + /** + * Définit le valeur par défaut + * + * @param mixed|null $default La valeur par défaut. + * + * @return $this + * + * @see $_default + */ + public function setDefault($default = null) { + $this->_default = $default; + return $this; + } +} \ No newline at end of file diff --git a/Argument/IArgument.class.php b/Argument/IArgument.class.php new file mode 100644 index 0000000..4c4c0ed --- /dev/null +++ b/Argument/IArgument.class.php @@ -0,0 +1,51 @@ +setDefault($default); + } + + public function parse ($args) { + if($this->_parseTag($args[0])) + return new ParseResult(true, 1); + + return null; + } + + /** + * Définit le valeur par défaut + * + * @param boolean $default La valeur par défaut. + * + * @return $this + * + * @throws InvalidArgument Si la valeur n'est pas un booléen. + */ + public function setDefault ($default = null) { + if(is_null($default) || !is_bool($default)) + throw new InvalidArgument($default, 'La valeur par défaut DOIT être un booléen'); + + parent::setDefault($default); + return $this; + } +} \ No newline at end of file diff --git a/Argument/Option/FlagWithReverse.class.php b/Argument/Option/FlagWithReverse.class.php new file mode 100644 index 0000000..0fbbf1f --- /dev/null +++ b/Argument/Option/FlagWithReverse.class.php @@ -0,0 +1,48 @@ +hasTagShort()) { + if(substr($this->getTagShort(), 0, 1) == 'n') + $tagShort = substr($this->getTagShort(), 1); + else + $tagShort = 'n'.$this->getTagShort(); + } + + if(substr($this->getTagLong(), 0, 3) == 'no-') + $tagLong = substr($this->getTagLong(), 3); + else + $tagLong = 'no-'.$this->getTagLong(); + + $name = $this->getName(); + if(substr($name, 0, 3) == 'no') + $name = strtolower(substr($name, 2, 1)).substr($name, 3); + else + $name = 'no'.strtoupper(substr($name, 0, 1)).substr($name, 1); + + $description = '[INVERSE] '.$this->getDescription(); + + $reverse = new Flag($name, !$this->getDefault(), $description, $tagLong, $tagShort); + $reverse->setVarName($this->getVarName()); + + return array($reverse); + } +} \ No newline at end of file diff --git a/Argument/Option/IArgumentOption.class.php b/Argument/Option/IArgumentOption.class.php new file mode 100644 index 0000000..9d67878 --- /dev/null +++ b/Argument/Option/IArgumentOption.class.php @@ -0,0 +1,46 @@ +setTagLong($tagLong); + $this->setTagShort($tagShort); + } + + /** + * Est-ce la valeur correspond au tag court ou long ? + * + * Utilisé par les classes enfants pour savoir si le tag correspond. + * + * @param string $arg La valeur à examiner. + * + * @return boolean True si le tag correspond, sinon False. + */ + protected function _parseTag($arg) { + if($this->hasTagShort()) { + if ($arg == '-'.$this->getTagShort()) + return true; + } + + return $arg == '--'.$this->getTagLong(); + } + + public function getTagShort() { + return $this->_tagShort; + } + /** + * Définit le tag court. + * + * @param string|null $tagShort Le tage court. + * + * @return $this + * + * @see $_tagShort + */ + public function setTagShort($tagShort = null) { + $this->_tagShort = $tagShort; + return $this; + } + /** + * Est-ce que l'argument existe en forme courte ? + * + * @return boolean True si existe en forme courte, sinon False. + */ + public function hasTagShort() { + return !is_null($this->_tagShort); + } + + public function getTagLong() { + return $this->_tagLong; + } + /** + * Définit le tag long. + * + * Si non fourni, est déduit du {@see $_name nom de l'argument} : + * - Les "_" sont remplacés par "-" + * - Les lettres majuscules sont remplacées par "-" suivit de la lettre en minuscule + * + * @param string|null $tagLong Le tag long. + * + * @return $this + * + * @see $_tagLong + */ + public function setTagLong($tagLong = null) { + $this->_tagLong = $tagLong; + + if(is_null($this->_tagLong)) { + $this->_tagLong = preg_replace_callback( + '/[A-Z_]/', + function ($matches) { + if($matches[0] == '_') + return '-'; + else + return '-'.strtolower($matches[0]); + }, + $this->getName() + ); + } + + return $this; + } + + public function allowMultiple() { + return $this->_multiple; + } + /** + * Définit si l'argument est autorisé plusieurs fois ou non. + * + * @param boolean $multiple Argument autorisé plusieurs fois ? + * + * @return $this + * + * @throws InvalidArgument Si la valeur n'est pas un booléen + * + * @see $_optional + */ + public function setMultiple($multiple) { + if (!is_bool($multiple)) + throw new InvalidArgument($multiple, 'La valeur n\'est pas un booléen'); + + $this->_multiple = $multiple; + return $this; + } + + public function isStoppingParse() { + return $this->_isStoppingParse; + } + /** + * Définit si l'argument met fin au parsage ou non. + * + * @param boolean $stoppingParse Met fin au parsage ? + * + * @return $this + * + * @throws InvalidArgument Si la valeur n'est pas un booléen + * + * @see $_optional + */ + public function setStoppingParse($stoppingParse) { + if (!is_bool($stoppingParse)) + throw new InvalidArgument($stoppingParse, 'La valeur n\'est pas un booléen'); + + $this->_isStoppingParse = $stoppingParse; + return $this; + } +} \ No newline at end of file diff --git a/Argument/Option/Value.class.php b/Argument/Option/Value.class.php new file mode 100644 index 0000000..d2defb2 --- /dev/null +++ b/Argument/Option/Value.class.php @@ -0,0 +1,84 @@ +setValueParser($valueParser); + } + + public function parse ($args) { + try { + if(!$this->_parseTag($args[0])) + return null; + + if(count($args) < 2 || is_null($args[1])) + throw new IncorrectParse($this,null, 'Seconde valeur de l\'argument manquante'); + + return new ParseResult($this->_valueParser->parseValue($args[1]), 2); + } + catch (InvalidArgument $e) { + throw IncorrectParse::createFromInvalidArgument($this, $e); + } + } + + public function getValueDescription () { + return $this->_valueParser->getValueDescription(); + } + + /** + * Le parseur de valeur. + * + * @return IValueParser Le parseur. + * + * @see $_valueParser + */ + public function getValueParser() { + return $this->_valueParser; + } + /** + * Définit le parseur de valeur + * + * @param IValueParser $valueParser Le parseur + * + * @return $this + * + * @see $_valueParser + */ + public function setValueParser(IValueParser $valueParser) { + $this->_valueParser = $valueParser; + return $this; + } +} \ No newline at end of file diff --git a/Argument/ParseResult.class.php b/Argument/ParseResult.class.php new file mode 100644 index 0000000..1a30e13 --- /dev/null +++ b/Argument/ParseResult.class.php @@ -0,0 +1,85 @@ +setValue($value); + $this->setConsume($consume); + } + + /** + * Le nombre d'argument consumé. + * + * @return int Le nombre d'argument consumé. + * + * @see $_consume + */ + public function getConsume() { + return $this->_consume; + } + /** + * Définit le nombre d'argument consumé. + * + * @param $consume int Le nombre d'argument consumé. + * + * @return $this + * + * @see $_consume + */ + public function setConsume($consume) { + $this->_consume = $consume; + return $this; + } + + /** + * La valeur. + * + * @return mixed La valeur. + * + * @see $_value + */ + public function getValue() { + return $this->_value; + } + /** + * Définit les valeurs définies. + * + * @param mixed $value La valeur. + * + * @return $this + * + * @see $_value + */ + public function setValue($value) { + $this->_value = $value; + return $this; + } +} \ No newline at end of file diff --git a/Argument/Parser/BooleanParser.class.php b/Argument/Parser/BooleanParser.class.php new file mode 100644 index 0000000..5b7dac9 --- /dev/null +++ b/Argument/Parser/BooleanParser.class.php @@ -0,0 +1,32 @@ +setValueMin($valueMin); + $this->setValueMax($valueMax); + } + + public function parseValue ($arg) { + if (!$this->_isDecimal($arg)) + throw new InvalidArgument($arg, 'La valeur n\'est pas un réel valide'); + + $int = (int)$arg; + + if ($this->hasValueMin()) { + if ($int < $this->getValueMin()) + throw new InvalidArgument($int, 'La valeur est inférieure au minimum : ' . $this->getValueMin()); + } + if ($this->hasValueMax()) { + if ($int > $this->getValueMax()) + throw new InvalidArgument($int, 'La valeur est supérieur au maximum : ' . $this->getValueMax()); + } + + return $int; + } + + protected function _isDecimal($val) { + if(!is_numeric($val)) + return false; + + return true; + } + + public function getValueDescription () { + return ($this->hasValueMin() ? $this->getValueMin().' <= ' : '').'decimal'.($this->hasValueMax() ? ' <= '.$this->getValueMax() : ''); + } + + /** + * La valeur minimumm autorisée + * + * @return double|null La valeur minimum. + * + * @see $_valueMin + */ + public function getValueMin() { + return $this->_valueMin; + } + /** + * Définit la valeur minimum autorisée. + * + * @param double|null $valueMin La valeur minimum. + * + * @return $this + * + * @throws InvalidArgument Si la valeur minimum n'est ni null, ni un entier + */ + public function setValueMin($valueMin = null) { + if(!is_null($valueMin) && !$this->_isDecimal($valueMin)) + throw new InvalidArgument($valueMin, 'La valeur n\'est pas un entier ou null'); + + $this->_valueMin = (int)$valueMin; + return $this; + } + /** + * Est-ce qu'il existe une limite minimum ? + * + * @return boolean True s'il existe une limite, sinon False + */ + public function hasValueMin() { + return !is_null($this->getValueMin()); + } + + /** + * La valeur maximum autorisée + * + * @return double|null La valeur maximum. + * + * @see $_valueMax + */ + public function getValueMax() { + return $this->_valueMax; + } + /** + * Définit la valeur maximum autorisée. + * + * @param double|null $valueMax La valeur maximum. + * + * @return $this + * + * @throws InvalidArgument Si la valeur maximum n'est ni null, ni un entier + */ + public function setValueMax($valueMax = null) { + if(!is_null($valueMax) && !$this->_isDecimal($valueMax)) + throw new InvalidArgument($valueMax, 'La valeur n\'est pas un entier ou null'); + + $this->_valueMax = (int)$valueMax; + return $this; + } + /** + * Est-ce qu'il existe une limite maximum ? + * + * @return boolean True s'il existe une limite, sinon False + */ + public function hasValueMax() { + return !is_null($this->getValueMax()); + } +} \ No newline at end of file diff --git a/Argument/Parser/EnumParser.class.php b/Argument/Parser/EnumParser.class.php new file mode 100644 index 0000000..fc7db03 --- /dev/null +++ b/Argument/Parser/EnumParser.class.php @@ -0,0 +1,75 @@ +setValues($values); + } + + public function parseValue ($arg) { + if(!in_array($arg, $this->_values)) + throw new InvalidArgument($arg, 'La valeur ne fait partie de liste des valeurs autorisées : '.implode(', ', $this->getValues())); + + return $arg; + } + + public function getValueDescription () { + return 'enum('.implode(',', $this->getValues()).')'; + } + + /** + * La liste des valeurs autoriséees. + * + * @return string[] La liste des valeurs + * + * @see $_values + */ + public function getValues() { + return $this->_values; + } + /** + * Définit la liste des valeurs autorisées. + * + * @param string[] $values La liste des valeurs + * + * @return $this + * + * @throws InvalidArgument Si la liste n'est pas un tableau ou si celui-ci est vide + * + * @see $_values + */ + public function setValues($values) { + if(!is_array($values)) + throw new InvalidArgument($values, 'La liste de valeurs n\'est pas un tableau'); + + if(count($values) == 0) + throw new InvalidArgument($values, 'La liste de valeurs doit avoir au moins un élément'); + + $this->_values = $values; + return $this; + } +} \ No newline at end of file diff --git a/Argument/Parser/IValueParser.class.php b/Argument/Parser/IValueParser.class.php new file mode 100644 index 0000000..49427ac --- /dev/null +++ b/Argument/Parser/IValueParser.class.php @@ -0,0 +1,32 @@ +setValueMin($valueMin); + $this->setValueMax($valueMax); + } + + public function parseValue ($arg) { + if(!$this->_isInt($arg)) + throw new InvalidArgument($arg, 'La valeur n\'est pas un entier valide'); + + $int = (int)$arg; + + if($this->hasValueMin()) { + if($int < $this->getValueMin()) + throw new InvalidArgument($int, 'La valeur est inférieure au minimum : '.$this->getValueMin()); + } + if($this->hasValueMax()) { + if($int > $this->getValueMax()) + throw new InvalidArgument($int, 'La valeur est supérieur au maximum : '.$this->getValueMax()); + } + + return $int; + } + + protected function _isInt($val) { + if(!is_numeric($val)) + return false; + + if(floor($val) != $val) + return false; + + return true; + } + + public function getValueDescription () { + return ($this->hasValueMin() ? $this->getValueMin().' <= ' : '').'integer'.($this->hasValueMax() ? ' <= '.$this->getValueMax() : ''); + } + + /** + * La valeur minimumm autorisée + * + * @return int|null La valeur minimum. + * + * @see $_valueMin + */ + public function getValueMin() { + return $this->_valueMin; + } + /** + * Définit la valeur minimum autorisée. + * + * @param int|null $valueMin La valeur minimum. + * + * @return $this + * + * @throws InvalidArgument Si la valeur minimum n'est ni null, ni un entier + */ + public function setValueMin($valueMin = null) { + if(!is_null($valueMin) && !$this->_isInt($valueMin)) + throw new InvalidArgument($valueMin, 'La valeur n\'est pas un entier ou null'); + + $this->_valueMin = (int)$valueMin; + return $this; + } + /** + * Est-ce qu'il existe une limite minimum ? + * + * @return boolean True s'il existe une limite, sinon False + */ + public function hasValueMin() { + return !is_null($this->getValueMin()); + } + + /** + * La valeur maximum autorisée + * + * @return int|null La valeur maximum. + * + * @see $_valueMax + */ + public function getValueMax() { + return $this->_valueMax; + } + /** + * Définit la valeur maximum autorisée. + * + * @param int|null $valueMax La valeur maximum. + * + * @return $this + * + * @throws InvalidArgument Si la valeur maximum n'est ni null, ni un entier + */ + public function setValueMax($valueMax = null) { + if(!is_null($valueMax) && !$this->_isInt($valueMax)) + throw new InvalidArgument($valueMax, 'La valeur n\'est pas un entier ou null'); + + $this->_valueMax = (int)$valueMax; + return $this; + } + /** + * Est-ce qu'il existe une limite maximum ? + * + * @return boolean True s'il existe une limite, sinon False + */ + public function hasValueMax() { + return !is_null($this->getValueMax()); + } +} \ No newline at end of file diff --git a/Argument/Parser/RegexParser.class.php b/Argument/Parser/RegexParser.class.php new file mode 100644 index 0000000..3007a5b --- /dev/null +++ b/Argument/Parser/RegexParser.class.php @@ -0,0 +1,65 @@ +setRegex($regex); + } + + public function parseValue ($arg) { + if(!preg_match($this->regex, $arg, $matches)) + throw new InvalidArgument($arg, 'La valeur ne correspond pas au motif attendu'); + + return $matches; + } + + public function getValueDescription () { + return $this->getRegex(); + } + + /** + * Le motif à respecter + * + * @return string Le motif + * + * @see $_regex + */ + public function getRegex() { + return $this->_regex; + } + /** + * Modifie le motif à respecter + * + * @param string $regex Le nouveau motif + * + * @return $this + */ + public function setRegex($regex = null) { + $this->_regex = $regex; + return $this; + } +} \ No newline at end of file diff --git a/Argument/Parser/StringParser.class.php b/Argument/Parser/StringParser.class.php new file mode 100644 index 0000000..0402761 --- /dev/null +++ b/Argument/Parser/StringParser.class.php @@ -0,0 +1,20 @@ +setVarName($varName); + $this->setValue($value); + } + + public function parse ($args) { + if($args[0] == $this->getValue()) + return new ParseResult($this->getValue(), 1); + + return null; + } + + public function getValueDescription () { + return '"'.$this->getValue().'"'; + } + + /** + * La valeur. + * + * @return string La valeur. + * + * @see $_value + */ + public function getValue() { + return $this->_value; + } + /** + * Définit la valeur + * + * @param string $value La valeur + * + * @return $this + * + * @see $_value + */ + public function setValue($value) { + $this->_value = $value; + return $this; + } +} \ No newline at end of file diff --git a/Argument/Value/IArgumentValue.class.php b/Argument/Value/IArgumentValue.class.php new file mode 100644 index 0000000..9ed437e --- /dev/null +++ b/Argument/Value/IArgumentValue.class.php @@ -0,0 +1,31 @@ +setValueParser($valueParser); + } + + public function parse ($args) { + try { + return new ParseResult($this->_valueParser->parseValue($args[0]), 1); + } + catch(InvalidArgument $e) { + throw IncorrectParse::createFromInvalidArgument($this, $e); + } + } + + public function getValueDescription () { + return $this->_valueParser->getValueDescription(); + } + + /** + * Le parseur de valeur. + * + * @return IValueParser Le parseur. + * + * @see $_valueParser + */ + public function getValueParser() { + return $this->_valueParser; + } + /** + * Définit le parseur de valeur + * + * @param IValueParser $valueParser Le parseur + * + * @return $this + * + * @see $_valueParser + */ + public function setValueParser(IValueParser $valueParser) { + $this->_valueParser = $valueParser; + return $this; + } +} \ No newline at end of file diff --git a/Argument/Value/ValueAbstract.class.php b/Argument/Value/ValueAbstract.class.php new file mode 100644 index 0000000..4ecaadf --- /dev/null +++ b/Argument/Value/ValueAbstract.class.php @@ -0,0 +1,117 @@ +setOptional($optional); + } + + public function getOccurMin() { + return $this->_occurMin; + } + /** + * Définit le nombre minimum d'occurence. + * + * @param int $occurMin Le nombre minimum + * + * @return $this + * + * @throws InvalidArgument Si la valeur n'est pas un entier positif. + * + * @see $_occurMin + */ + public function setOccurMin($occurMin) { + if (!is_numeric($occurMin)) + throw new InvalidArgument($occurMin, 'La valeur n\'est pas un entier'); + + if (floor($occurMin) != $occurMin) + throw new InvalidArgument($occurMin, 'La valeur n\'est pas un entier'); + + $int = (int)$occurMin; + if($int < 0) + throw new InvalidArgument($occurMin, 'La valeur n\'est pas un entier positif'); + + $this->_occurMin = $int; + return $this; + } + /** + * Définit si l'argument est facultatif ou non. + * + * @param bool $optional Argument facultatif ? + * + * @return $this + */ + public function setOptional($optional = false) { + try { + $this->setOccurMin($optional ? 0 : 1); + } + catch(InvalidArgument $e) {} + + return $this; + } + + public function getOccurMax() { + return $this->_occurMax; + } + /** + * Définit le nombre mawimum d'occurence. + * + * @param int|null $occurMax Le nombre maximum + * + * @return $this + * + * @throws InvalidArgument Si la valeur n'est pas Null ou un entier strictement positif. + * + * @see $_occurMax + */ + public function setOccurMax($occurMax) { + if (!is_null($occurMax)) { + if (!is_numeric($occurMax)) + throw new InvalidArgument($occurMax, 'La valeur n\'est pas un entier'); + + if (floor($occurMax) != $occurMax) + throw new InvalidArgument($occurMax, 'La valeur n\'est pas un entier'); + + $occurMax = (int)$occurMax; + if ($occurMax <= 0) + throw new InvalidArgument($occurMax, 'La valeur n\'est pas un entier strictement positif'); + } + + $this->_occurMax = $occurMax; + return $this; + } +} \ No newline at end of file diff --git a/CommandLine.class.php b/CommandLine.class.php new file mode 100644 index 0000000..715100c --- /dev/null +++ b/CommandLine.class.php @@ -0,0 +1,871 @@ +setProgramName($programName); + $this->setDescription($mainDescription); + $this->setCommand($command); + $this->setVersion($version); + } + + /** + * Ajoute les options communes à la plupart des programmes + * + * Voici la liste des options générées : + * - Aide : --help ou -h + * - Version : --version ou -v (voir $shortForVersion) + * + * @param bool $shortForVersion L'option pour la version existe également au format "court" (-v) + * + * @return $this + */ + public function addDefaultArguments ($shortForVersion = true) { + if(!$this->existsOption(self::ARGUMENT_OPTION_HELP)) { + $this->addOption( + (new Argument\Option\Flag(self::ARGUMENT_OPTION_HELP, false, 'Affiche cette aide', 'help', 'h')) + ->setStoppingParse(true) + ); + } + + if (!$this->existsOption(self::ARGUMENT_OPTION_HELP) && !is_null($this->getVersion())) { + $this->addOption( + (new Argument\Option\Flag(self::ARGUMENT_OPTION_VERSION,false,'Affiche la version','version',$shortForVersion ? 'v' : null)) + ->setStoppingParse(true) + ); + } + + return $this; + } + /** + * Auto-lance les fonctions correspondantes aux options communes. + * + * @param stdClass $values Les valeurs du parsage + * @param boolean $exitAtEnd Terminer le script à la fin de la fonction correspondante ? + */ + public function treatDefaultArguments ($values, $exitAtEnd = true) { + if ($values->{self::ARGUMENT_OPTION_HELP} === true) { + $this->showHelp($exitAtEnd); + } + + if ($values->{self::ARGUMENT_OPTION_VERSION} === true) { + $this->showVersion($exitAtEnd); + } + } + + /** + * Traite la ligne de commande du script actuel pour en sortir les valeurs. + * + * @return stdClass Les valeurs extraites. + * @throws IException Quand toutes les solutions ne correspond pas + * @throws Exception Autre erreur + */ + public function parse () { + $argv = $_SERVER['argv']; + array_shift($argv); // Supprime le 1er paramètre : le nom du script PHP + + return $this->parseExplicit($argv); + } + /** + * Traite une ligne de commande pour en sortir les valeurs. + * + * @param string[] $argv La liste des arguments. + * + * @return stdClass Les valeurs extraites. + * @throws IException Quand toutes les solutions ne correspond pas + * @throws Exception Autre erreur + */ + public function parseExplicit ($argv) { + ksort($this->_solutions, SORT_NUMERIC); + foreach ($this->_solutions as $ordre => $solution) { + $argv_loop = $argv; + try { + $values = $solution->parseExplicit($argv_loop, $this->_optionsCommon); + } + catch (IException $e) { + if ($ordre >= ($this->getNextSolutionOrder() - 1)) { + throw $e; + } + + continue; + } + catch (Exception $e) { + throw $e; + } + + return $values; + } + + if (count($argv) > 0) { + throw new TooMuchValues($argv, 'Trop de valeurs'); + } + + return null; + } + + /** + * Affiche la version du programme + * + * @param boolean $exitAtEnd Terminer le script à la fin de la fonction ? + */ + public function showVersion ($exitAtEnd = true) { + echo $this->getVersion() . "\n"; + + if ($exitAtEnd) { + exit(0); + } + } + /** + * Affiche l'aide du programme. + * + * @param boolean $exitAtEnd Terminer le script à la fin de la fonction ? + */ + public function showHelp ($exitAtEnd = true) { + echo $this->generateHelp() . "\n"; + + if ($exitAtEnd) { + exit(0); + } + } + /** + * Génère l'aide du programme. + * + * @return string Le texte de l'aide. + */ + public function generateHelp () { + ksort($this->_solutions); + + $help = array(); + + $help[] = $this->getProgramName() . (is_null($this->getVersion()) ? '' : ', version ' . $this->getVersion()); + $help[] = $this->getDescription(); + $help[] = ''; + + foreach ($this->_solutions as $solution) { + $syntax = array( + $this->getCommand(), + count($this->_optionsCommon) > 0 || count($solution->getOptions()) > 0 ? '[OPTIONS]' : '', + ); + $syntax = array_merge($syntax, array_map(array(__CLASS__, '_getSyntaxOfArgument'), $solution->getArguments())); + + $help[] = implode(' ', $syntax); + + $help[] = self::TAB . 'Arguments :'; + $help = array_merge($help, self::_getArgumentsListing($solution->getArguments())); + $help[] = ''; + + $help[] = self::TAB . 'Options :'; + $help = array_merge($help, self::_getOptionsListing(array_merge($this->_optionsCommon, $solution->getOptions()))); + $help[] = ''; + } + $help[] = ''; + + return implode("\n", $help); + } + + /** + * La syntax d'un argument + * + * @param IArgumentValue $argument L'argument + * + * @return string La syntax de l'argument + * @see generateHelp() + */ + protected static function _getSyntaxOfArgument (IArgumentValue $argument) { + $syntax = ''; + + $min = $argument->getOccurMin(); + $max = $argument->getOccurMax(); + if (is_null($max)) { + $max = -1; + } + + if ($min == 0) { + $syntax .= '['; + } + + $syntax .= $argument->getName(); + for ($curr = 2; $curr <= $min; $curr++) { + $syntax .= ' ' . $argument->getName() . $curr; + } + + if ($max === -1 || $max > 1) { + if ($min < 2) { + $syntax .= ' [' . $argument->getName() . '2]'; + } + + $syntax .= ' ... [' . $argument->getName() . ($max === -1 ? 'N' : $max) . ']'; + } + + if ($min == 0) { + $syntax .= ']'; + } + + return $syntax; + } + /** + * Transforme une liste d'argument en un liste de leur descriptif complet (pour l'aide). + * + * @param IArgumentValue[] $arguments La liste des arguments + * + * @return string[] Les descriptifs complet des arguments + */ + protected static function _getArgumentsListing ($arguments) { + /* + * Calcul des différents padding + */ + // Initialisation + $pads = new stdClass; + $pads->name = 0; + $pads->occurMin = 0; + $pads->occurMax = 0; + $pads->valueDescription = 0; + + // Lecture des arguments + foreach ($arguments as $argument) { + $pads->name = max($pads->name, strlen($argument->getName())); + + $max = $argument->getOccurMax(); + $pads->occurMin = max($pads->occurMin, strlen($argument->getOccurMin())); + $pads->occurMax = max($pads->occurMax, strlen(is_null($max) ? 'N' : $max)); + + if ($argument instanceof IArgumentValueDescription) { + $pads->valueDescription = max($pads->valueDescription, strlen($argument->getValueDescription())); + } + } + + /* + * Génération des descriptifs + */ + $entries = array(); + foreach ($arguments as $argument) { + $label = str_pad($argument->getName(), $pads->name, ' ', STR_PAD_RIGHT); + + $max = $argument->getOccurMax(); + + $occur = ''; + $occur .= str_pad($argument->getOccurMin(), $pads->occurMin, ' ', STR_PAD_LEFT); + $occur .= ' => '; + $occur .= str_pad(is_null($max) ? 'N' : $max, $pads->occurMax, ' ', STR_PAD_RIGHT); + + $valueDescription = str_pad( + $argument instanceof IArgumentValueDescription ? $argument->getValueDescription() : '', + $pads->valueDescription, + ' ', + STR_PAD_RIGHT + ); + + $entries[] = self::TAB . self::TAB . implode(self::TAB, array( + $label, + $occur, + $valueDescription, + preg_replace( + /** @lang RegExp */ '@\r?\n@', + '$0' . self::TAB . self::TAB . implode(self::TAB, array( + str_pad('', $pads->name, ' '), + str_pad('', + $pads->occurMin + 4 + $pads->occurMax, + ' ' + ), + str_pad('', $pads->valueDescription, ' '), + '', + ) + ), + $argument->getDescription() + ), + ) + ); + } + + return $entries; + } + /** + * Transforme une liste d'options en un liste de leur descriptif complet (pour l'aide). + * + * @param IArgumentOption[] $options La liste des options + * + * @return string[] Les descriptifs complet des options + */ + protected static function _getOptionsListing ($options) { + /* + * Calcul des différents padding + */ + // Initialisation + $pads = new stdClass; + $pads->tagShort = 0; + $pads->tagLong = 0; + $pads->valueDescription = 0; + + // Lecture des arguments + foreach ($options as $option) { + $short = $option->getTagShort(); + if (!is_null($short)) { + $pads->tagShort = max($pads->tagShort, strlen($short)); + } + $pads->tagLong = max($pads->tagLong, strlen($option->getTagLong())); + + if ($option instanceof IArgumentValueDescription) { + $pads->valueDescription = max($pads->valueDescription, strlen($option->getValueDescription())); + } + } + + // Les tags ont une taille suplémentaire incompressible + $pads->tagShort += 1; + $pads->tagLong += 2; + + /* + * Génération des descriptifs + */ + $entries = array(); + foreach ($options as $option) { + $short = $option->getTagShort(); + + $label = ''; + $label .= str_pad(is_null($short) ? '' : '-' . $short, $pads->tagShort, ' ', STR_PAD_RIGHT); + $label .= ' '; + $label .= str_pad('--' . $option->getTagLong(), $pads->tagLong, ' ', STR_PAD_RIGHT); + $label .= $option->allowMultiple() ? ' *' : ($option->isStoppingParse() ? ' X' : ' '); + + $valueDescription = str_pad( + $option instanceof IArgumentValueDescription ? $option->getValueDescription() : '', + $pads->valueDescription, + ' ', + STR_PAD_RIGHT + ); + + $entries[] = self::TAB . self::TAB . implode(self::TAB, array( + $label, + $valueDescription, + preg_replace( + /** @lang RegExp */ '@\r?\n@', + '$0' . self::TAB . self::TAB . implode(self::TAB, array( + str_pad('', $pads->tagShort + 1 + $pads->tagLong + + 2, ' ' + ), + str_pad('', $pads->valueDescription, ' '), + '', + ) + ), + $option->getDescription() + ), + ) + ); + } + + return $entries; + } + + /** + * Le nom du programme. + * + * @return string Le nom. + * @see CommandLine::$_programName + */ + public function getProgramName () { + return $this->_programName; + } + /** + * Modifie le nom du programme. + * + * @param string $programName Le nouveau nom + * + * @return $this + * @see CommandLine::$_programName + */ + public function setProgramName ($programName) { + $this->_programName = $programName; + return $this; + } + + /** + * La commande de lancement du programme + * + * NULL = {@see CommandLine::$_programName Nom du programme} + * + * @return string|null La commande + * @see CommandLine::$_command + */ + public function getCommand () { + return $this->_command; + } + /** + * Modifie la commande de lancement du programme + * + * @param string|null $command La nouvelle commande + * + * @return $this + * @see CommandLine::$_command + */ + public function setCommand ($command) { + $this->_command = $command; + return $this; + } + + /** + * La description du programme + * + * @return string La description + * @see CommandLine::$_description + */ + public function getDescription () { + return $this->_description; + } + /** + * Modifie la description du programme. + * + * @param string $description La nouvelle description. + * + * @return $this + * @see CommandLine::$_description + */ + public function setDescription ($description) { + $this->_description = $description; + return $this; + } + + /** + * La version du programme + * + * @return string|null La version + * @see CommandLine::$_version + */ + public function getVersion () { + return $this->_version; + } + /** + * Modifie la version du programme. + * + * @param string|null $version La nouvelle version. + * + * @return $this + * @see CommandLine::$_version + */ + public function setVersion ($version) { + $this->_version = $version; + return $this; + } + + /** + * La liste des arguments "option" + * + * @return IArgumentOption[] La liste des options + * @see CommandLine::$_arguments_options + */ + public function getOptions () { + return $this->_arguments_options; + } + /** + * Modifie la liste des arguments "option" + * + * *Attention* : cette fonction _remplace_ la liste des options. + * Pour seulement en ajouter, utiliser {@see CommandLine::addOptions()} + * + * @param IArgumentOption[] $options La nouvelle liste d'options + * + * @return $this + * @see CommandLine::$_arguments_options + */ + public function setOptions ($options) { + self::_checkOptionList($options); + + $this->_arguments_options = $options; + return $this; + } + /** + * Ajoute des arguments "option" à ceux existants + * + * Pour ajouter une seule option, il est conseillé d'utiliser {@see CommandLine::addOption()} + * + * @param IArgumentOption[]|IArgumentOption $options La liste d'options à ajouter + * + * @return $this + * @see CommandLine::$_arguments_options + */ + public function addOptions ($options) { + if (!is_array($options) && $options instanceof IArgumentOption) { + $options = array($options); + } + self::_checkOptionList($options); + + $this->_arguments_options = array_merge($this->_arguments_options, $options); + return $this; + } + /** + * Ajoute un seul argument "option" à ceux existants + * + * Pour ajouter plusieurs options à la fois, il est conseillé d'utiliser {@see CommandLine::addOptions()} + * + * @param IArgumentOption $option L'options à ajouter + * + * @return $this + * @see CommandLine::$_arguments_options + */ + public function addOption ($option) { + return $this->addOptions(array($option)); + } + /** + * Est-ce que l'argument "option" existe déjà ? + * + * @param string $optionName Le nom de l'option + * + * @return bool True si l'option existe déjà, sinon False + */ + public function existsOption ($optionName) { + foreach ($this->_arguments_options as $option) { + if ($option->getName() == $optionName) { + return true; + } + } + + return false; + } + /** + * Est-ce que c'est un argument "option" ? + * + * @param mixed $argument L'argument à tester + * + * @return bool True si c'est bien un argument "option", sinon False + */ + public static function isOption ($argument) { + return $argument instanceof IArgumentOption; + } + /** + * Vérifie que la liste d'arguments "option" est valide + * + * Doit être un tableau et chaque élément doit implémenter {@see IArgumentOption} + * + * @param IArgumentOption[] $options Les options à vérifier + */ + protected static function _checkOptionList ($options) { + if (!is_array($options)) { + throw new InvalidArgumentException('La liste des options n\'est pas un tableau'); + } + foreach ($options as $key => $option) { + if (!self::isOption($option)) { + throw new InvalidArgumentException('L\'option ' . $key . ' n\'est pas valide (n\'implémente pas IArgumentOption)'); + } + } + } + + /** + * La liste des arguments "value" + * + * @return IArgumentValue[] La liste des valeurs + * @see CommandLine::$_arguments_values + */ + public function getValues () { + return $this->_arguments_values; + } + /** + * Modifie la liste des arguments "value" + * + * *Attention* : cette fonction _remplace_ la liste des valeurs. + * Pour seulement en ajouter, utiliser {@see CommandLine::addValues()} + * + * @param IArgumentValue[] $values La nouvelle liste de valeurs + * + * @return $this + * @see CommandLine::$_arguments_values + */ + public function setValues ($values) { + self::_checkValueList($values); + + $this->_arguments_values = $values; + return $this; + } + /** + * Ajoute des arguments "value" à celles existantes + * + * Pour ajouter une seule valeur, il est conseillé d'utiliser {@see CommandLine::addValue()} + * + * @param IArgumentValue[]|IArgumentValue $values La liste des valeurs à ajouter + * + * @return $this + * @see CommandLine::$_arguments_values + */ + public function addValues ($values) { + if (!is_array($values) && $values instanceof IArgumentValue) { + $values = array($values); + } + self::_checkValueList($values); + + $this->_arguments_values = array_merge($this->_arguments_values, $values); + return $this; + } + /** + * Ajoute un seul argument "value" à celles existantes + * + * Pour ajouter plusieurs valeurs à la fois, il est conseillé d'utiliser {@see CommandLine::addValues()} + * + * @param IArgumentValue $value La valeur à ajouter + * + * @return $this + * @see CommandLine::$_arguments_values + */ + public function addValue ($value) { + return $this->addValues(array($value)); + } + /** + * Est-ce que l'argument "value" existe déjà ? + * + * @param string $valueName Le nom de la valeur + * + * @return bool True si la valeur existe déjà, sinon False + */ + public function existsValue ($valueName) { + foreach ($this->_arguments_values as $option) { + if ($option->getName() == $valueName) { + return true; + } + } + + return false; + } + /** + * Est-ce que c'est un argument "value" ? + * + * @param mixed $argument L'argument à tester + * + * @return bool True si c'est bien un argument "value", sinon False + */ + public static function isValue ($argument) { + return $argument instanceof IArgumentValue; + } + /** + * Vérifie que la liste d'arguments "value" est valide + * + * Doit être un tableau et chaque élément doit implémenter {@see IArgumentValue} + * + * @param IArgumentValue[] $values Les valeurs à vérifier + */ + protected static function _checkValueList ($values) { + if (!is_array($values)) { + throw new InvalidArgumentException('La liste des valeurs n\'est pas un tableau'); + } + foreach ($values as $key => $value) { + if (!self::isValue($value)) { + throw new InvalidArgumentException('La valeur ' . $key . ' n\'est pas valide (n\'implémente pas IArgumentValue)'); + } + } + } + + /** + * La liste de tous les arguments ("value" et "option") + * + * @return IArgument[] La liste des arguments + */ + public function getArguments () { + return array_merge($this->getValues(), $this->getOptions()); + } + /** + * Modifie la liste de tous les arguments ("value" et "option") + * + * *Attention* : cette fonction _remplace_ la liste des arguments. + * Pour seulement en ajouter, utiliser {@see CommandLine::addArguments()} + * + * @param IArgumentValue[] $arguments La nouvelle liste d'arguments + * + * @return $this + */ + public function setArguments ($arguments) { + $this->setOptions(array()); + $this->setValues(array()); + + return $this->addArguments($arguments); + } + /** + * Ajoute des arguments "value" à celles existantes + * + * Pour ajouter un seul argument, il est conseillé d'utiliser {@see CommandLine::addArgument()} + * + * @param IArgument[]|IArgument $arguments La liste des arguments à ajouter + * + * @return $this + */ + public function addArguments ($arguments) { + if (!is_array($arguments) && $arguments instanceof IArgumentValue) { + $arguments = array($arguments); + } + self::_checkValueList($arguments); + + foreach ($arguments as $argument) { + $this->addArgument($argument); + } + + return $this; + } + /** + * Ajoute un seul argument ("value" ou "option") à ceux existants + * + * Pour ajouter plusieurs arguments à la fois, il est conseillé d'utiliser {@see CommandLine::addArguments()} + * + * @param IArgument $argument L'argument à ajouter + * + * @return $this + */ + public function addArgument ($argument) { + if (!self::isArgument($argument)) { + throw new InvalidArgumentException('L\'argument n\'est pas valide (n\'implémente pas IArgument)'); + } + + if (self::isOption($argument)) { + $this->addOption($argument); + } + elseif (self::isValue($argument)) { + $this->addValue($argument); + } + else { + $type = ''; + try { + $reflex = new ReflectionClass($argument); + foreach ($reflex->getInterfaces() as $interface) { + if ($interface->implementsInterface(IArgument::class)) { + $type = $interface->getName(); + break; + } + } + } + catch (ReflectionException $e) { + $type = ''; + } + + throw new InvalidArgumentException('L\'argument n\'est pas d\'un type géré : ' . $type); + } + + return $this; + } + /** + * Est-ce que l'argument ("value" ou "option") existe déjà ? + * + * @param string $argumentName Le nom de l'argument + * + * @return bool True si l'argument existe déjà, sinon False + */ + public function existsArgument ($argumentName) { + $arguments = $this->getArguments(); + foreach ($arguments as $argument) { + if ($argument->getName() == $argumentName) { + return true; + } + } + + return false; + } + /** + * Est-ce que c'est un argument ("value" ou "option") ? + * + * @param mixed $argument L'argument à tester + * + * @return bool True si c'est bien un argument, sinon False + */ + public static function isArgument ($argument) { + return $argument instanceof IArgument; + } + /** + * Vérifie que la liste d'arguments ("value" et "option") est valide + * + * Doit être un tableau et chaque élément doit implémenter {@see IArgument} + * + * @param IArgument[] $arguments Les arguments à vérifier + */ + protected static function _checkArgumentList ($arguments) { + if (!is_array($arguments)) { + throw new InvalidArgumentException('La liste des arguments n\'est pas un tableau'); + } + foreach ($arguments as $key => $argument) { + if (!self::isArgument($argument)) { + throw new InvalidArgumentException('L\'argument ' . $key . ' n\'est pas valide (n\'implémente pas IArgument)'); + } + } + } +} \ No newline at end of file diff --git a/Exception/IException.class.php b/Exception/IException.class.php new file mode 100644 index 0000000..b19c2c3 --- /dev/null +++ b/Exception/IException.class.php @@ -0,0 +1,12 @@ +_arguments)) + throw new InvalidArgument($order, 'Cet ordre est déjà utilisé pour un autre argument'); + + $this->_arguments[$order] = $argument; + return $this; + } + /** + * Liste des arguments de la solution. + * + * @return IArgumentValue[] Les arguments. + */ + public function getArguments () { + ksort($this->_arguments); + return $this->_arguments; + } + /** + * Le prochain ordre disponible pour un argument + * + * @return int La prochaine position + */ + public function getNextOrder () { + $keys = array_keys($this->_arguments); + if(count($keys) < 1) + return 1; + + return max($keys) + 1; + } + + /** + * Ajoute une option + * + * @param IArgumentOption $option L'option + * + * @return $this + * + * @throws InvalidArgument Si les arguments secondaires (le cas échéant) sont invalides. + * + * @see $_options + */ + public function addOption (IArgumentOption $option) { + if(array_key_exists($option->getName(), $this->_options)) + throw new InvalidArgument($option, 'Il existe déjà une autre option avec le même nom'); + + $this->_options[$option->getName()] = $option; + + if($option instanceof IArgumentOptionSecondary) { + foreach($option->getOthersOptions() as $option_second) + $this->addOption($option_second); + } + + return $this; + } + /** + * Liste des options de la solution. + * + * @return IArgumentOption[] Les options. + */ + public function getOptions () { + return $this->_options; + } + + /** + * Traite la solution pour en sortir les valeurs. + * + * @param string[] $argv La liste des arguments + * @param IArgumentOption[] $common_options La liste des options communes + * + * @return stdClass Les valeurs extraites. + * @throws MissingArgument Quand un argument est manquant ou en quantité insuffisante + * @throws InvalidArgument Quand un argument n'arrive pas à parser sa valeur + * @throws TooMuchValues Quand la solution ne consumme pas la totalité des arguments + */ + public function parseExplicit ($argv, $common_options) { + $stop = false; + /** @var IArgumentOption[] $options */$options = array_merge($common_options, $this->getOptions()); + + $values = $this->_parseOptions($argv, $options, $stop); + if($stop) + return $values; + + $arguments = $this->getArguments(); + foreach($arguments as $ordre => $argument) { + $min = $argument->getOccurMin(); + self::_parseXTimes($argv, $values, $argument, $min); + + $max = $argument->getOccurMax(); + if(is_null($max) || $max > $min) { + $nbArgs = $this->_getNbArgumentRestant($ordre); + if($nbArgs >= 0 && count($argv) > $nbArgs) { + $nbParse = count($argv) - $nbArgs; + if(!is_null($max)) + $nbParse = min($nbParse, $max - $min); + + self::_parseXTimes($argv, $values, $argument, $nbParse, $min); + } + } + } + + if(count($argv)) + throw new TooMuchValues($argv, 'Trop de paramètres'); + + // Valeurs par défaut + foreach($options as $option) { + if(!isset($values->{$option->getVarName()})) + $values->{$option->getVarName()} = $option->getDefault(); + } + foreach($arguments as $argument) { + if(!isset($values->{$argument->getVarName()})) + $values->{$argument->getVarName()} = $argument->getDefault(); + } + + return $values; + } + /** + * Parse des options. + * + * @param string[] $argv La liste des arguments + * @param IArgumentOption[] $options La liste des options + * @param boolean $stop Arrêt du parsage ? + * + * @return stdClass Les options extraites + * @throws IncorrectParse Si le parsage d'une des options échoue + */ + protected static function _parseOptions (&$argv, $options, &$stop) { + $values = new stdClass(); + $stop = false; + + foreach($options as $option) { + do { + $argv_option = $argv; + + $find = false; + while(count($argv_option) > 0) { + $result = $option->parse($argv_option); + + if(!is_null($result)) { + if($option->isStoppingParse()) + $values = new stdClass(); + + self::_setValue($values, $option, $result, $option->allowMultiple() ? 2 : 1); + + if($option->isStoppingParse()) { + $stop = true; + return $values; + } + + array_splice($argv, count($argv) - count($argv_option), $result->getConsume()); + $find = true; + break; + } + + array_shift($argv_option); + } + } while(count($argv_option) > 1 && (!$find || $option->allowMultiple())); + } + + return $values; + } + + /** + * Parse un argument X fois + * + * @param string[] $argv Liste des arguments + * @param stdClass $values Les valeurs de sortie + * @param IArgumentValue $argument L'argument à parser + * @param int $xTimes Le nombre de fois à parser + * @param int $offset L'offset pour le n° d'occurence + * + * @throws MissingArgument Si l'argument n'est pas en quantité suffisante + * @throws IncorrectParse Si l'argument échoue à se parser + */ + protected static function _parseXTimes (&$argv, &$values, $argument, $xTimes, $offset = 0) { + if($xTimes > count($argv)) + throw new MissingArgument($argument, 'L\'argument est en quantité insuffisante (' . count($argv) . ' / ' . $xTimes . ')'); + + for($curr = 1; $curr <= $xTimes; $curr++) { + $result = $argument->parse($argv); + if(is_null($result)) + throw new MissingArgument($argument, 'L\'occurence n° ' . ($offset + $curr) . ' de l\'argument ne correspond pas'); + + self::_setValue($values, $argument, $result, $argument->getOccurMax()); + $argv = array_slice($argv, $result->getConsume()); + } + } + /** + * Ajoute le résultat d'un argument aux valeurs de sortie + * + * @param stdClass $values Les valeurs de sortie, auxquelles ajouter la résultat + * @param IArgument $argument L'argument actuel + * @param ParseResult $result Le résultat de l'argument + * @param int|null $max Le nombre max de valeur autorisé + */ + protected static function _setValue (&$values, $argument, $result, $max) { + if(is_null($max) || $max > 1) { + if(!isset($values->{$argument->getVarName()})) + $values->{$argument->getVarName()} = array(); + + $values->{$argument->getVarName()}[] = $result->getValue(); + } + else + $values->{$argument->getVarName()} = $result->getValue(); + } + /** + * Calcule le nombre d'argument restant à honorer. + * + * @param int $ordre_start L'ordre de l'argument en cours + * + * @return int Le nombre d'argument restant, ou -1 si un argument "illimité" + */ + protected function _getNbArgumentRestant ($ordre_start) { + $nb = 0; + foreach($this->getArguments() as $ordre => $argument) { + if($ordre <= $ordre_start) + continue; + + $max = $argument->getOccurMax(); + if(is_null($max)) + return -1; + + $nb += $max; + } + + return $nb; + } +} \ No newline at end of file