2023-12-03 22:10:18 +04:00
|
|
|
<?php
|
|
|
|
namespace nur\cli;
|
|
|
|
|
|
|
|
use Exception;
|
|
|
|
use nur\b\ExitException;
|
|
|
|
use nur\b\ValueException;
|
|
|
|
use nur\config;
|
|
|
|
use nur\config\ArrayConfig;
|
|
|
|
use nur\msg;
|
|
|
|
use nur\os;
|
|
|
|
use nur\path;
|
2024-05-02 10:01:48 +04:00
|
|
|
use nur\sery\output\say as nsay;
|
|
|
|
use nur\sery\output\log as nlog;
|
|
|
|
use nur\sery\output\msg as nmsg;
|
|
|
|
use nur\sery\output\std\StdMessenger as nStdMessenger;
|
2023-12-03 22:10:18 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Class Application: application de base
|
|
|
|
*/
|
|
|
|
abstract class Application {
|
|
|
|
protected static function _app_init(): void {
|
|
|
|
config::set_fact(config::FACT_CLI_APP);
|
2024-05-03 08:07:18 +04:00
|
|
|
|
2024-05-02 10:01:48 +04:00
|
|
|
nmsg::set_messenger_class(nStdMessenger::class);
|
2024-05-03 08:07:18 +04:00
|
|
|
nmsg::reset_params(["min_level" => nmsg::DEBUG]);
|
2024-04-04 22:57:10 +04:00
|
|
|
|
2023-12-03 22:10:18 +04:00
|
|
|
msg::set_messenger_class(Console::class);
|
|
|
|
msg::get()->setParametrableParams([
|
|
|
|
# En ligne de commande, on peut afficher les messages loggués
|
|
|
|
"display_log" => true,
|
|
|
|
]);
|
|
|
|
# avant que l'application soit configurée, rester en mode debug
|
|
|
|
msg::get()->setLevels(msg::DEBUG_LEVELS, msg::DEBUG_LEVELS, msg::DEBUG);
|
|
|
|
# si un fichier nommé .default-profile-devel existe dans le répertoire de
|
|
|
|
# l'application ou du projet, alors le profil par défaut est devel
|
|
|
|
global $argv;
|
|
|
|
$homedir = os::homedir();
|
|
|
|
$projdir = path::abspath(path::dirname($argv[0]));
|
|
|
|
while (true) {
|
|
|
|
if (file_exists("$projdir/.default-profile-devel")) {
|
|
|
|
config::set_default_profile(config::DEVEL);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
# s'arrêter au répertoire du projet, ou à $HOMEDIR, ou à la racine
|
|
|
|
if (file_exists("$projdir/composer.json")) break;
|
|
|
|
if ($projdir == $homedir) break;
|
|
|
|
$projdir = path::dirname($projdir);
|
|
|
|
if ($projdir == "/") break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected static function _app_configure(Application $app): void {
|
|
|
|
config::configure(config::CONFIGURE_INITIAL_ONLY);
|
|
|
|
# revenir à la configuration par défaut une fois que la configuration
|
|
|
|
# initiale est faite
|
|
|
|
msg::get()->setLevels(msg::PRINT_LEVELS, msg::LOG_LEVELS, msg::TYPE_LEVELS);
|
2024-05-03 08:07:18 +04:00
|
|
|
nsay::reset_params(["min_level" => nmsg::NORMAL]);
|
2023-12-03 22:10:18 +04:00
|
|
|
|
|
|
|
$app->parseArgs();
|
|
|
|
config::configure();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected static function _app_main(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));
|
|
|
|
}
|
|
|
|
|
|
|
|
static function run(?Application $app=null): void {
|
|
|
|
try {
|
|
|
|
static::_app_init();
|
|
|
|
if ($app === null) $app = new static();
|
|
|
|
static::_app_configure($app);
|
|
|
|
static::_app_main($app);
|
|
|
|
} catch (ExitException $e) {
|
|
|
|
msg::error($e);
|
|
|
|
exit($e->getCode());
|
|
|
|
} catch (Exception $e) {
|
|
|
|
msg::error($e);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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 ExitException}
|
|
|
|
*/
|
|
|
|
protected static final function exit(int $exitcode=0, $message=null) {
|
|
|
|
throw new ExitException($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 ExitException}
|
|
|
|
*/
|
|
|
|
protected static final function die($message=null, int $exitcode=1) {
|
|
|
|
throw new ExitException($exitcode, $message);
|
|
|
|
}
|
|
|
|
|
|
|
|
const PROFILE_SECTION = [
|
|
|
|
"title" => "PROFILS D'EXECUTION",
|
|
|
|
["group",
|
2024-05-02 10:01:48 +04:00
|
|
|
["-p", "--profile", "--app-profile",
|
|
|
|
"args" => 1, "argsdesc" => "PROFILE",
|
2023-12-03 22:10:18 +04:00
|
|
|
"action" => [null, "set_application_profile"],
|
|
|
|
"help" => "spécifier le profil d'exécution",
|
|
|
|
],
|
|
|
|
["-P", "--prod", "action" => [config::class, "set_profile", config::PROD]],
|
|
|
|
["-T", "--test", "action" => [config::class, "set_profile", config::TEST]],
|
|
|
|
["--devel", "action" => [config::class, "set_profile", config::DEVEL]],
|
|
|
|
],
|
|
|
|
];
|
|
|
|
|
|
|
|
static function set_application_profile(string $profile): void {
|
|
|
|
config::set_profile($profile);
|
|
|
|
}
|
|
|
|
|
|
|
|
const VERBOSITY_SECTION = [
|
|
|
|
"title" => "NIVEAU D'INFORMATION",
|
|
|
|
"show" => false,
|
|
|
|
["group",
|
2024-05-02 10:01:48 +04:00
|
|
|
["--verbosity",
|
|
|
|
"args" => 1, "argsdesc" => "silent|very-quiet|quiet|verbose|debug|trace",
|
2023-12-03 22:10:18 +04:00
|
|
|
"action" => [null, "set_application_verbosity"],
|
|
|
|
"help" => "spécifier le niveau d'informations affiché",
|
|
|
|
],
|
|
|
|
["-q", "--quiet", "action" => [null, "set_application_verbosity", "quiet"]],
|
|
|
|
["-v", "--verbose", "action" => [null, "set_application_verbosity", "verbose"]],
|
|
|
|
["-D", "--debug", "action" => [null, "set_application_verbosity", "debug"]],
|
|
|
|
["--sql-trace", "action" => [null, "set_application_sql_trace"]],
|
|
|
|
],
|
2024-05-02 10:01:48 +04:00
|
|
|
["-L", "--logfile",
|
|
|
|
"args" => "file", "argsdesc" => "OUTPUT",
|
2023-12-03 22:10:18 +04:00
|
|
|
"action" => [null, "set_application_log_output"],
|
|
|
|
"help" => "Logger les messages de l'application dans le fichier spécifié",
|
|
|
|
],
|
|
|
|
["group",
|
|
|
|
["--color",
|
|
|
|
"action" => [null, "set_application_color", true],
|
|
|
|
"help" => "Afficher (resp. ne pas afficher) la sortie en couleur par défaut",
|
|
|
|
],
|
2024-05-02 10:01:48 +04:00
|
|
|
["--no-color", "action" => [null, "set_application_color", false]],
|
2023-12-03 22:10:18 +04:00
|
|
|
],
|
|
|
|
];
|
|
|
|
|
|
|
|
static function set_application_verbosity(string $verbosity): void {
|
|
|
|
$msg = msg::get();
|
2024-05-02 10:01:48 +04:00
|
|
|
$nsay = nsay::get();
|
2023-12-03 22:10:18 +04:00
|
|
|
switch ($verbosity) {
|
|
|
|
case "s":
|
|
|
|
case "silent":
|
|
|
|
$msg->setLevels([
|
|
|
|
msg::USER => msg::NEVER,
|
|
|
|
msg::TECH => msg::NEVER,
|
|
|
|
msg::EXCEPTION => msg::NEVER,
|
|
|
|
]);
|
2024-05-02 10:01:48 +04:00
|
|
|
$nsay->resetParams([
|
|
|
|
"min_level" => nmsg::NONE,
|
|
|
|
]);
|
2023-12-03 22:10:18 +04:00
|
|
|
break;
|
|
|
|
case "Q":
|
|
|
|
case "very-quiet":
|
|
|
|
$msg->setLevels([
|
|
|
|
msg::USER => msg::CRITICAL,
|
|
|
|
msg::TECH => msg::CRITICAL,
|
|
|
|
msg::EXCEPTION => msg::NEVER,
|
|
|
|
]);
|
2024-05-02 10:01:48 +04:00
|
|
|
$nsay->resetParams([
|
|
|
|
"min_level" => nmsg::MAJOR,
|
|
|
|
]);
|
2023-12-03 22:10:18 +04:00
|
|
|
break;
|
|
|
|
case "q":
|
|
|
|
case "quiet":
|
|
|
|
$msg->setLevels([
|
|
|
|
msg::USER => msg::MAJOR,
|
|
|
|
msg::TECH => msg::MAJOR,
|
|
|
|
msg::EXCEPTION => msg::NEVER,
|
|
|
|
]);
|
2024-05-02 10:01:48 +04:00
|
|
|
$nsay->resetParams([
|
|
|
|
"min_level" => nmsg::MAJOR,
|
|
|
|
]);
|
2023-12-03 22:10:18 +04:00
|
|
|
break;
|
|
|
|
case "v":
|
|
|
|
case "verbose":
|
|
|
|
$msg->setLevels([
|
|
|
|
msg::USER => msg::MINOR,
|
|
|
|
msg::TECH => msg::MINOR,
|
|
|
|
msg::EXCEPTION => msg::NEVER,
|
|
|
|
]);
|
2024-05-02 10:01:48 +04:00
|
|
|
$nsay->resetParams([
|
|
|
|
"min_level" => nmsg::MINOR,
|
|
|
|
]);
|
2023-12-03 22:10:18 +04:00
|
|
|
break;
|
|
|
|
case "D":
|
|
|
|
case "debug":
|
|
|
|
config::set_debug();
|
|
|
|
$msg->setLevels([
|
|
|
|
msg::USER => msg::MINOR,
|
|
|
|
msg::TECH => msg::MINOR,
|
|
|
|
msg::EXCEPTION => msg::NORMAL,
|
|
|
|
], null, msg::DEBUG);
|
2024-05-02 10:01:48 +04:00
|
|
|
$nsay->resetParams([
|
|
|
|
"min_level" => nmsg::DEBUG,
|
|
|
|
]);
|
2023-12-03 22:10:18 +04:00
|
|
|
break;
|
|
|
|
case "T":
|
|
|
|
case "trace":
|
|
|
|
config::set_debug();
|
|
|
|
$msg->setLevels([
|
|
|
|
msg::USER => msg::MINOR,
|
|
|
|
msg::TECH => msg::MINOR,
|
|
|
|
msg::EXCEPTION => msg::MINOR,
|
|
|
|
], null, msg::DEBUG);
|
2024-05-02 10:01:48 +04:00
|
|
|
$nsay->resetParams([
|
|
|
|
"min_level" => nmsg::DEBUG,
|
|
|
|
]);
|
2023-12-03 22:10:18 +04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw ValueException::invalid_value($verbosity, "verbosity");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static function set_application_sql_trace(): void {
|
|
|
|
config::add(new ArrayConfig(["app" => ["trace_sql" => true]]));
|
|
|
|
}
|
|
|
|
|
|
|
|
static function set_application_log_output(string $logfile): void {
|
|
|
|
msg::get()->setParametrableParams(["log_output" => $logfile]);
|
2024-05-02 23:29:18 +04:00
|
|
|
nlog::create_or_reset_params([
|
|
|
|
"output" => $logfile,
|
|
|
|
], nStdMessenger::class, [
|
|
|
|
"add_date" => true,
|
2024-05-03 08:07:18 +04:00
|
|
|
"min_level" => nlog::MINOR,
|
2024-05-02 23:29:18 +04:00
|
|
|
]);
|
2023-12-03 22:10:18 +04:00
|
|
|
}
|
2024-05-02 10:01:48 +04:00
|
|
|
static function set_application_color(bool $color): void {
|
2023-12-03 22:10:18 +04:00
|
|
|
msg::get()->setParametrableParams(["color" => $color]);
|
2024-05-02 10:01:48 +04:00
|
|
|
nsay::reset_params([
|
|
|
|
"color" => $color,
|
|
|
|
]);
|
2023-12-03 22:10:18 +04:00
|
|
|
}
|
|
|
|
const ARGS = [
|
|
|
|
"sections" => [
|
|
|
|
self::PROFILE_SECTION,
|
|
|
|
self::VERBOSITY_SECTION,
|
|
|
|
],
|
|
|
|
];
|
|
|
|
|
|
|
|
/** @throws ArgsException */
|
|
|
|
function parseArgs(array $args=null): void {
|
|
|
|
$parser = new ArgsParser(static::ARGS);
|
|
|
|
$parser->parse($this, $args);
|
|
|
|
}
|
|
|
|
|
|
|
|
const PROFILE_COLORS = [
|
|
|
|
"prod" => "@r",
|
|
|
|
"test" => "@g",
|
|
|
|
"devel" => "@w",
|
|
|
|
];
|
|
|
|
const DEFAULT_PROFILE_COLOR = "y";
|
|
|
|
|
|
|
|
/** retourner le profil courant en couleur */
|
|
|
|
static function profile(?string $profile=null): string {
|
|
|
|
if ($profile === null) $profile = config::get_profile();
|
|
|
|
foreach (static::PROFILE_COLORS as $text => $color) {
|
|
|
|
if (strpos($profile, $text) !== false) {
|
|
|
|
return $color? "<color $color>$profile</color>": $profile;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$color = static::DEFAULT_PROFILE_COLOR;
|
|
|
|
return $color? "<color $color>$profile</color>": $profile;
|
|
|
|
}
|
|
|
|
|
|
|
|
abstract function main();
|
|
|
|
}
|