getDesc();
echo implode("\n", $desc["message"])."\n";
$ec = $desc["exitcode"] ?? 0;
break;
case "dump":
case "d":
yaml::dump($runfile->read());
break;
case "reset":
case "z":
if (!$runfile->isRunning()) $runfile->reset();
else $ec = self::_error("cannot reset while running");
break;
case "release":
case "rl":
$runfile->release();
break;
case "start":
case "s":
array_splice($argv, 1, 1); $argc--;
return;
case "kill":
case "k":
if ($runfile->isRunning()) $runfile->wfKill();
else $ec = self::_error("not running");
break;
default:
$ec = self::_error("$argv[1]: unexpected command", app::EC_BAD_COMMAND);
}
exit($ec);
}
static function run(?Application $app=null): void {
$unlock = false;
$stop = false;
$shutdown = function () use (&$unlock, &$stop) {
if ($unlock) {
app::get()->getRunfile()->release();
$unlock = false;
}
if ($stop) {
app::get()->getRunfile()->wfStop();
$stop = false;
}
};
register_shutdown_function($shutdown);
app::install_signal_handler(static::INSTALL_SIGNAL_HANDLER);
try {
static::_initialize_app();
$useRunfile = static::USE_RUNFILE;
$useRunlock = static::USE_RUNLOCK;
if ($useRunfile) {
$runfile = app::get()->getRunfile();
global $argc, $argv;
self::_manage_runfile($argc, $argv, $runfile);
if ($useRunlock && $runfile->warnIfLocked()) exit(app::EC_LOCKED);
$runfile->wfStart();
$stop = true;
if ($useRunlock) {
$runfile->lock();
$unlock = true;
}
}
if ($app === null) $app = new static();
static::_configure_app($app);
static::_start_app($app);
} catch (ExitError $e) {
if ($e->haveUserMessage()) msg::error($e->getUserMessage());
exit($e->getCode());
} catch (Exception $e) {
msg::error($e);
exit(app::EC_UNEXPECTED);
}
}
protected static function _initialize_app(): void {
app::init(static::class);
app::set_fact(app::FACT_CLI_APP);
msg::set_messenger(new StdMessenger([
"min_level" => msg::DEBUG,
]));
}
protected static function _configure_app(Application $app): void {
config::configure(config::CONFIGURE_INITIAL_ONLY);
$msgs = null;
$msgs["console"] = new StdMessenger([
"min_level" => msg::NORMAL,
]);
if (static::USE_LOGFILE) {
$msgs["log"] = new StdMessenger([
"output" => app::get()->getLogfile(),
"min_level" => msg::MINOR,
"add_date" => true,
]);
}
msg::init($msgs);
$app->parseArgs();
config::configure();
}
protected static function _start_app(Application $app): void {
$retcode = $app->main();
if (is_int($retcode)) exit($retcode);
elseif (is_bool($retcode)) exit($retcode? 0: 1);
elseif ($retcode !== null) exit(strval($retcode));
}
/**
* sortir de l'application avec un code d'erreur, qui est 0 par défaut (i.e
* pas d'erreur)
*
* équivalent à lancer l'exception {@link ExitError}
*/
protected static final function exit(int $exitcode=0, $message=null) {
throw new ExitError($exitcode, $message);
}
/**
* sortir de l'application avec un code d'erreur, qui vaut 1 par défaut (i.e
* une erreur s'est produite)
*
* équivalent à lancer l'exception {@link ExitError}
*/
protected static final function die($message=null, int $exitcode=1) {
throw new ExitError($exitcode, $message);
}
const PROFILE_SECTION = [
"title" => "PROFIL D'EXECUTION",
"show" => false,
["-c", "--config", "--app-config",
"args" => "file", "argsdesc" => "CONFIG.yml",
"action" => [config::class, "load_config"],
"help" => "spécifier un fichier de configuration",
],
["group",
["-g", "--profile", "--app-profile",
"args" => 1, "argsdesc" => "PROFILE",
"action" => [app::class, "set_profile"],
"help" => "spécifier le profil d'exécution",
],
["-P", "--prod", "action" => [app::class, "set_profile", ref_profiles::PROD]],
["-T", "--test", "action" => [app::class, "set_profile", ref_profiles::TEST]],
["--devel", "action" => [app::class, "set_profile", ref_profiles::DEVEL]],
],
];
const VERBOSITY_SECTION = [
"title" => "NIVEAU D'INFORMATION",
"show" => false,
["group",
["--verbosity",
"args" => "verbosity", "argsdesc" => "silent|quiet|verbose|debug",
"action" => [console::class, "set_verbosity"],
"help" => "spécifier le niveau d'informations affiché",
],
["-q", "--quiet", "action" => [console::class, "set_verbosity", "quiet"]],
["-v", "--verbose", "action" => [console::class, "set_verbosity", "verbose"]],
["-D", "--debug", "action" => [console::class, "set_verbosity", "debug"]],
],
["-L", "--logfile",
"args" => "output",
"action" => [log::class, "set_output"],
"help" => "Logger les messages de l'application dans le fichier spécifié",
],
["group",
["--color",
"action" => [console::class, "set_color", true],
"help" => "Afficher (resp. ne pas afficher) la sortie en couleur par défaut",
],
["--no-color", "action" => [console::class, "set_color", false]],
],
];
const ARGS = [
"sections" => [
self::PROFILE_SECTION,
self::VERBOSITY_SECTION,
],
];
protected function getArgsParser(): AbstractArgsParser {
return new SimpleArgsParser(static::ARGS);
}
/** @throws ArgsException */
function parseArgs(array $args=null): void {
$this->getArgsParser()->parse($this, $args);
}
const PROFILE_COLORS = [
ref_profiles::PROD => "@r",
ref_profiles::TEST => "@g",
ref_profiles::DEVEL => "@w",
];
const DEFAULT_PROFILE_COLOR = "y";
/** retourner le profil courant en couleur */
static function get_profile(?string $profile=null): string {
if ($profile === null) $profile = app::get_profile();
foreach (static::PROFILE_COLORS as $text => $color) {
if (strpos($profile, $text) !== false) {
return $color? "$profile": $profile;
}
}
$color = static::DEFAULT_PROFILE_COLOR;
return $color? "$profile": $profile;
}
protected ?array $args = null;
abstract function main();
static function runfile(): RunFile {
return app::with(static::class)->getRunfile();
}
}