modifs.mineures sans commentaires
This commit is contained in:
parent
2e3b290d84
commit
2efb0687f1
84
src/app/cli/AbstractArgsParser.php
Normal file
84
src/app/cli/AbstractArgsParser.php
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
namespace nulib\app\cli;
|
||||
|
||||
use stdClass;
|
||||
|
||||
abstract class AbstractArgsParser {
|
||||
/**
|
||||
* consommer les arguments de $src en avançant l'index $srci et provisionner
|
||||
* $dest à partir de $desti. si $desti est plus grand que 0, celà veut dire
|
||||
* que $dest a déjà commencé à être provisionné, et qu'il faut continuer.
|
||||
*
|
||||
* $destmin est le nombre minimum d'arguments à consommer. $destmax est le
|
||||
* nombre maximum d'arguments à consommer.
|
||||
*
|
||||
* $srci est la position de l'élément courant à consommer le cas échéant
|
||||
* retourner le nombre d'arguments qui manquent (ou 0 si tous les arguments
|
||||
* ont été consommés)
|
||||
*
|
||||
* pour les arguments optionnels, ils sont consommés tant qu'il y en a de
|
||||
* disponible, ou jusqu'à la présence de '--'. Si $keepsep, l'argument '--'
|
||||
* est gardé dans la liste des arguments optionnels.
|
||||
*/
|
||||
protected static function consume_args($src, &$srci, &$dest, $desti, $destmin, $destmax, bool $keepsep): int {
|
||||
$srcmax = count($src);
|
||||
# arguments obligatoires
|
||||
while ($desti < $destmin) {
|
||||
if ($srci < $srcmax) {
|
||||
$dest[] = $src[$srci];
|
||||
} else {
|
||||
# pas assez d'arguments
|
||||
return $destmin - $desti;
|
||||
}
|
||||
$srci++;
|
||||
$desti++;
|
||||
}
|
||||
# arguments facultatifs
|
||||
while ($desti < $destmax && $srci < $srcmax) {
|
||||
$opt = $src[$srci];
|
||||
if ($opt === "--") {
|
||||
# fin des options facultatives
|
||||
if ($keepsep) $dest[] = $opt;
|
||||
$srci++;
|
||||
break;
|
||||
}
|
||||
$dest[] = $opt;
|
||||
$srci++;
|
||||
$desti++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected static function check_missing(?string $option, int $count) {
|
||||
if ($count > 0) {
|
||||
throw new ArgException("$option: nombre d'arguments insuffisant (manque $count)");
|
||||
}
|
||||
}
|
||||
|
||||
abstract function normalize(array $args): array;
|
||||
|
||||
/** @var object|array objet destination */
|
||||
protected $dest;
|
||||
|
||||
protected function setDest(&$dest): void {
|
||||
$this->dest =& $dest;
|
||||
}
|
||||
|
||||
protected function unsetDest(): void {
|
||||
unset($this->dest);
|
||||
}
|
||||
|
||||
abstract function process(array $args);
|
||||
|
||||
function parse(&$dest, array $args=null): void {
|
||||
if ($args === null) {
|
||||
global $argv;
|
||||
$args = array_slice($argv, 1);
|
||||
}
|
||||
$args = $this->normalize($args);
|
||||
$dest ??= new stdClass();
|
||||
$this->setDest($dest);
|
||||
$this->process($args);
|
||||
$this->unsetDest();
|
||||
}
|
||||
}
|
@ -3,6 +3,9 @@ namespace nulib\app\cli;
|
||||
|
||||
use nulib\cl;
|
||||
|
||||
/**
|
||||
* Class ArgDefs: une liste d'objets ArgDef
|
||||
*/
|
||||
abstract class ArgDefs implements IArgo {
|
||||
function __construct(array $defs) {
|
||||
$this->defs = $defs;
|
||||
@ -64,61 +67,6 @@ abstract class ArgDefs implements IArgo {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* consommer les arguments de $src en avançant l'index $srci et provisionner
|
||||
* $dest à partir de $desti. si $desti est plus grand que 0, celà veut dire
|
||||
* que $dest a déjà commencé à être provisionné, et qu'il faut continuer.
|
||||
*
|
||||
* $destmin est le nombre minimum d'arguments à consommer. $destmax est le
|
||||
* nombre maximum d'arguments à consommer.
|
||||
*
|
||||
* $srci est la position de l'élément courant à consommer le cas échéant
|
||||
* retourner le nombre d'arguments qui manquent (ou 0 si tous les arguments
|
||||
* ont été consommés)
|
||||
*
|
||||
* pour les arguments optionnels, ils sont consommés tant qu'il y en a de
|
||||
* disponible, ou jusqu'à la présence de '--'. Si $keepsep, l'argument '--'
|
||||
* est gardé dans la liste des arguments optionnels.
|
||||
*/
|
||||
protected static function consume_args($src, &$srci, &$dest, $desti, $destmin, $destmax, bool $keepsep): int {
|
||||
$srcmax = count($src);
|
||||
# arguments obligatoires
|
||||
while ($desti < $destmin) {
|
||||
if ($srci < $srcmax) {
|
||||
$dest[] = $src[$srci];
|
||||
} else {
|
||||
# pas assez d'arguments
|
||||
return $destmin - $desti;
|
||||
}
|
||||
$srci++;
|
||||
$desti++;
|
||||
}
|
||||
# arguments facultatifs
|
||||
while ($desti < $destmax && $srci < $srcmax) {
|
||||
$opt = $src[$srci];
|
||||
if ($opt === "--") {
|
||||
# fin des options facultatives
|
||||
if ($keepsep) $dest[] = $opt;
|
||||
$srci++;
|
||||
break;
|
||||
}
|
||||
$dest[] = $opt;
|
||||
$srci++;
|
||||
$desti++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected static function check_missing(?string $option, int $count) {
|
||||
if ($count > 0) {
|
||||
throw new ArgException("$option: nombre d'arguments insuffisant (manque $count)");
|
||||
}
|
||||
}
|
||||
|
||||
function normalize(array $args): array {
|
||||
return $args;
|
||||
}
|
||||
|
||||
function debugInfos(): array {
|
||||
return array_map(function (IArgo $argo) {
|
||||
return $argo->debugInfos();
|
||||
|
@ -55,6 +55,11 @@ class SimpleArgDefs extends ArgDefs {
|
||||
|
||||
protected array $index;
|
||||
|
||||
/** @return string[] */
|
||||
function getOptions(): array {
|
||||
return array_keys($this->index);
|
||||
}
|
||||
|
||||
protected function setArgos(?array $argos): void {
|
||||
$argos ??= [];
|
||||
|
||||
@ -117,155 +122,6 @@ class SimpleArgDefs extends ArgDefs {
|
||||
return $this->index[$option] ?? null;
|
||||
}
|
||||
|
||||
function normalize(array $args): array {
|
||||
$i = 0;
|
||||
$max = count($args);
|
||||
$options = [];
|
||||
$remains = [];
|
||||
$parseOpts = true;
|
||||
while ($i < $max) {
|
||||
$arg = $args[$i++];
|
||||
if (!$parseOpts) {
|
||||
# le reste n'est que des arguments
|
||||
$remains[] = $arg;
|
||||
continue;
|
||||
}
|
||||
if ($arg === "--") {
|
||||
# fin des options
|
||||
$parseOpts = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (substr($arg, 0, 2) === "--") {
|
||||
#######################################################################
|
||||
# option longue
|
||||
$pos = strpos($arg, "=");
|
||||
if ($pos !== false) {
|
||||
# option avec valeur
|
||||
$option = substr($arg, 0, $pos);
|
||||
$value = substr($arg, $pos + 1);
|
||||
} else {
|
||||
# option sans valeur
|
||||
$option = $arg;
|
||||
$value = null;
|
||||
}
|
||||
/** @var ArgDef $argDef */
|
||||
$argDef = $this->index[$option] ?? null;
|
||||
if ($argDef === null) {
|
||||
# chercher une correspondance
|
||||
$len = strlen($option);
|
||||
$candidates = [];
|
||||
foreach (array_keys($this->index) as $candidate) {
|
||||
if (substr($candidate, 0, $len) === $option) {
|
||||
$candidates[] = $candidate;
|
||||
}
|
||||
}
|
||||
switch (count($candidates)) {
|
||||
case 0:
|
||||
throw new ArgException("$option: option invalide");
|
||||
case 1:
|
||||
$option = $candidates[0];
|
||||
break;
|
||||
default:
|
||||
$candidates = implode(", ", $candidates);
|
||||
throw new ArgException("$option: option ambigue (les options possibles sont $candidates)");
|
||||
}
|
||||
$argDef = $this->index[$option];
|
||||
}
|
||||
|
||||
if ($argDef->haveArgs) {
|
||||
$minArgs = $argDef->minArgs;
|
||||
$maxArgs = $argDef->maxArgs;
|
||||
$values = [];
|
||||
if ($value !== null) {
|
||||
$values[] = $value;
|
||||
$offset = 1;
|
||||
} elseif ($minArgs == 0) {
|
||||
# cas particulier: la première valeur doit être collée à l'option
|
||||
# si $maxArgs == 1
|
||||
$offset = $maxArgs == 1 ? 1 : 0;
|
||||
} else {
|
||||
$offset = 0;
|
||||
}
|
||||
$this->check_missing($option,
|
||||
self::consume_args($args, $i, $values, $offset, $minArgs, $maxArgs, true));
|
||||
|
||||
if ($minArgs == 0 && $maxArgs == 1) {
|
||||
# cas particulier: la première valeur doit être collée à l'option
|
||||
if (count($values) > 0) {
|
||||
$options[] = "$option=$values[0]";
|
||||
$values = array_slice($values, 1);
|
||||
} else {
|
||||
$options[] = $option;
|
||||
}
|
||||
} else {
|
||||
$options[] = $option;
|
||||
}
|
||||
$options = array_merge($options, $values);
|
||||
} elseif ($value !== null) {
|
||||
throw new ArgException("$option: cette option ne prend pas d'arguments");
|
||||
} else {
|
||||
$options[] = $option;
|
||||
}
|
||||
|
||||
} elseif (substr($arg, 0, 1) === "-") {
|
||||
#######################################################################
|
||||
# option courte
|
||||
$pos = 1;
|
||||
$len = strlen($arg);
|
||||
while ($pos < $len) {
|
||||
$option = "-".substr($arg, $pos, 1);
|
||||
/** @var ArgDef $argDef */
|
||||
$argDef = $this->index[$option] ?? null;
|
||||
if ($argDef === null) {
|
||||
throw new ArgException("$option: option invalide");
|
||||
}
|
||||
if ($argDef->haveArgs) {
|
||||
$minArgs = $argDef->minArgs;
|
||||
$maxArgs = $argDef->maxArgs;
|
||||
$values = [];
|
||||
if ($len > $pos + 1) {
|
||||
$values[] = substr($arg, $pos + 1);
|
||||
$offset = 1;
|
||||
$pos = $len;
|
||||
} elseif ($minArgs == 0) {
|
||||
# cas particulier: la première valeur doit être collée à l'option
|
||||
# si $maxArgs == 1
|
||||
$offset = $maxArgs == 1 ? 1 : 0;
|
||||
} else {
|
||||
$offset = 0;
|
||||
}
|
||||
$this->check_missing($option,
|
||||
self::consume_args($args, $i, $values, $offset, $minArgs, $maxArgs, true));
|
||||
|
||||
if ($minArgs == 0 && $maxArgs == 1) {
|
||||
# cas particulier: la première valeur doit être collée à l'option
|
||||
if (count($values) > 0) {
|
||||
$options[] = "$option$values[0]";
|
||||
$values = array_slice($values, 1);
|
||||
} else {
|
||||
$options[] = $option;
|
||||
}
|
||||
} else {
|
||||
$options[] = $option;
|
||||
}
|
||||
$options = array_merge($options, $values);
|
||||
} else {
|
||||
$options[] = $option;
|
||||
}
|
||||
$pos++;
|
||||
}
|
||||
} else {
|
||||
#XXX implémenter les commandes
|
||||
|
||||
#######################################################################
|
||||
# argument
|
||||
$remains[] = $arg;
|
||||
}
|
||||
}
|
||||
return array_merge($options, ["--"], $remains);
|
||||
}
|
||||
|
||||
function debugInfos(): array {
|
||||
return [
|
||||
"argos" => array_map(function (IArgo $argo) {
|
||||
|
170
src/app/cli/SimpleArgsParser.php
Normal file
170
src/app/cli/SimpleArgsParser.php
Normal file
@ -0,0 +1,170 @@
|
||||
<?php
|
||||
namespace nulib\app\cli;
|
||||
|
||||
use stdClass;
|
||||
|
||||
class SimpleArgsParser extends AbstractArgsParser {
|
||||
function __construct(array $defs) {
|
||||
$this->argDefs = new SimpleArgDefs($defs);
|
||||
}
|
||||
|
||||
protected SimpleArgDefs $argDefs;
|
||||
|
||||
protected function getArgDef(string $option): ?ArgDef {
|
||||
return $this->argDefs->get($option);
|
||||
}
|
||||
|
||||
protected function getOptions(): array {
|
||||
return $this->argDefs->getOptions();
|
||||
}
|
||||
|
||||
function normalize(array $args): array {
|
||||
$i = 0;
|
||||
$max = count($args);
|
||||
$options = [];
|
||||
$remains = [];
|
||||
$parseOpts = true;
|
||||
while ($i < $max) {
|
||||
$arg = $args[$i++];
|
||||
if (!$parseOpts) {
|
||||
# le reste n'est que des arguments
|
||||
$remains[] = $arg;
|
||||
continue;
|
||||
}
|
||||
if ($arg === "--") {
|
||||
# fin des options
|
||||
$parseOpts = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (substr($arg, 0, 2) === "--") {
|
||||
#######################################################################
|
||||
# option longue
|
||||
$pos = strpos($arg, "=");
|
||||
if ($pos !== false) {
|
||||
# option avec valeur
|
||||
$option = substr($arg, 0, $pos);
|
||||
$value = substr($arg, $pos + 1);
|
||||
} else {
|
||||
# option sans valeur
|
||||
$option = $arg;
|
||||
$value = null;
|
||||
}
|
||||
$argDef = $this->getArgDef($option);
|
||||
if ($argDef === null) {
|
||||
# chercher une correspondance
|
||||
$len = strlen($option);
|
||||
$candidates = [];
|
||||
foreach ($this->getOptions() as $candidate) {
|
||||
if (substr($candidate, 0, $len) === $option) {
|
||||
$candidates[] = $candidate;
|
||||
}
|
||||
}
|
||||
switch (count($candidates)) {
|
||||
case 0:
|
||||
throw new ArgException("$option: option invalide");
|
||||
case 1:
|
||||
$option = $candidates[0];
|
||||
break;
|
||||
default:
|
||||
$candidates = implode(", ", $candidates);
|
||||
throw new ArgException("$option: option ambigue (les options possibles sont $candidates)");
|
||||
}
|
||||
$argDef = $this->getArgDef($option);
|
||||
}
|
||||
|
||||
if ($argDef->haveArgs) {
|
||||
$minArgs = $argDef->minArgs;
|
||||
$maxArgs = $argDef->maxArgs;
|
||||
$values = [];
|
||||
if ($value !== null) {
|
||||
$values[] = $value;
|
||||
$offset = 1;
|
||||
} elseif ($minArgs == 0) {
|
||||
# cas particulier: la première valeur doit être collée à l'option
|
||||
# si $maxArgs == 1
|
||||
$offset = $maxArgs == 1 ? 1 : 0;
|
||||
} else {
|
||||
$offset = 0;
|
||||
}
|
||||
$this->check_missing($option,
|
||||
self::consume_args($args, $i, $values, $offset, $minArgs, $maxArgs, true));
|
||||
|
||||
if ($minArgs == 0 && $maxArgs == 1) {
|
||||
# cas particulier: la première valeur doit être collée à l'option
|
||||
if (count($values) > 0) {
|
||||
$options[] = "$option=$values[0]";
|
||||
$values = array_slice($values, 1);
|
||||
} else {
|
||||
$options[] = $option;
|
||||
}
|
||||
} else {
|
||||
$options[] = $option;
|
||||
}
|
||||
$options = array_merge($options, $values);
|
||||
} elseif ($value !== null) {
|
||||
throw new ArgException("$option: cette option ne prend pas d'arguments");
|
||||
} else {
|
||||
$options[] = $option;
|
||||
}
|
||||
|
||||
} elseif (substr($arg, 0, 1) === "-") {
|
||||
#######################################################################
|
||||
# option courte
|
||||
$pos = 1;
|
||||
$len = strlen($arg);
|
||||
while ($pos < $len) {
|
||||
$option = "-".substr($arg, $pos, 1);
|
||||
$argDef = $this->getArgDef($option);
|
||||
if ($argDef === null) {
|
||||
throw new ArgException("$option: option invalide");
|
||||
}
|
||||
if ($argDef->haveArgs) {
|
||||
$minArgs = $argDef->minArgs;
|
||||
$maxArgs = $argDef->maxArgs;
|
||||
$values = [];
|
||||
if ($len > $pos + 1) {
|
||||
$values[] = substr($arg, $pos + 1);
|
||||
$offset = 1;
|
||||
$pos = $len;
|
||||
} elseif ($minArgs == 0) {
|
||||
# cas particulier: la première valeur doit être collée à l'option
|
||||
# si $maxArgs == 1
|
||||
$offset = $maxArgs == 1 ? 1 : 0;
|
||||
} else {
|
||||
$offset = 0;
|
||||
}
|
||||
$this->check_missing($option,
|
||||
self::consume_args($args, $i, $values, $offset, $minArgs, $maxArgs, true));
|
||||
|
||||
if ($minArgs == 0 && $maxArgs == 1) {
|
||||
# cas particulier: la première valeur doit être collée à l'option
|
||||
if (count($values) > 0) {
|
||||
$options[] = "$option$values[0]";
|
||||
$values = array_slice($values, 1);
|
||||
} else {
|
||||
$options[] = $option;
|
||||
}
|
||||
} else {
|
||||
$options[] = $option;
|
||||
}
|
||||
$options = array_merge($options, $values);
|
||||
} else {
|
||||
$options[] = $option;
|
||||
}
|
||||
$pos++;
|
||||
}
|
||||
} else {
|
||||
#XXX implémenter les commandes
|
||||
|
||||
#######################################################################
|
||||
# argument
|
||||
$remains[] = $arg;
|
||||
}
|
||||
}
|
||||
return array_merge($options, ["--"], $remains);
|
||||
}
|
||||
|
||||
function process(array $args) {
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user