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)'); } } } }