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;
|
use nulib\cl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ArgDefs: une liste d'objets ArgDef
|
||||||
|
*/
|
||||||
abstract class ArgDefs implements IArgo {
|
abstract class ArgDefs implements IArgo {
|
||||||
function __construct(array $defs) {
|
function __construct(array $defs) {
|
||||||
$this->defs = $defs;
|
$this->defs = $defs;
|
||||||
@ -64,61 +67,6 @@ abstract class ArgDefs implements IArgo {
|
|||||||
return null;
|
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 {
|
function debugInfos(): array {
|
||||||
return array_map(function (IArgo $argo) {
|
return array_map(function (IArgo $argo) {
|
||||||
return $argo->debugInfos();
|
return $argo->debugInfos();
|
||||||
|
@ -55,6 +55,11 @@ class SimpleArgDefs extends ArgDefs {
|
|||||||
|
|
||||||
protected array $index;
|
protected array $index;
|
||||||
|
|
||||||
|
/** @return string[] */
|
||||||
|
function getOptions(): array {
|
||||||
|
return array_keys($this->index);
|
||||||
|
}
|
||||||
|
|
||||||
protected function setArgos(?array $argos): void {
|
protected function setArgos(?array $argos): void {
|
||||||
$argos ??= [];
|
$argos ??= [];
|
||||||
|
|
||||||
@ -117,155 +122,6 @@ class SimpleArgDefs extends ArgDefs {
|
|||||||
return $this->index[$option] ?? null;
|
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 {
|
function debugInfos(): array {
|
||||||
return [
|
return [
|
||||||
"argos" => array_map(function (IArgo $argo) {
|
"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