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); } protected static function _app_init(): void { config::set_fact(config::FACT_CLI_APP); # avant que l'application soit configurée, configurer le mode debug msg::set_messenger_class(Console::class); msg::get()->setParametrableParams([ # En ligne de commande, on peut afficher les messages loggués "display_log" => true, ]); 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; } app::init(static::class); nmsg::set_messenger(new StdMessenger([ "min_level" => nmsg::DEBUG, ])); } 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); $msgs = ["console" => new StdMessenger([ "min_level" => nmsg::NORMAL, ])]; if (static::USE_LOGFILE) { $msgs["log"] = new StdMessenger([ "output" => app::get()->getLogfile(), "min_level" => nmsg::MINOR, "add_date" => true, ]); } nmsg::init($msgs); $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 { $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::_app_init(); $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::_app_configure($app); static::_app_main($app); } catch (ExitError $e) { if ($e->haveUserMessage()) msg::error($e->getUserMessage()); exit($e->getCode()); } catch (nur_ExitError $e) { if ($e->haveMessage()) 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 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" => "PROFILS D'EXECUTION", ["group", ["-p", "--profile", "--app-profile", "args" => 1, "argsdesc" => "PROFILE", "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", ["--verbosity", "args" => 1, "argsdesc" => "silent|very-quiet|quiet|verbose|debug|trace", "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"]], ], ["-L", "--logfile", "args" => "file", "argsdesc" => "OUTPUT", "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", ], ["--no-color", "action" => [null, "set_application_color", false]], ], ]; static function set_application_verbosity(string $verbosity): void { $msg = msg::get(); $nconsole = nconsole::get(); switch ($verbosity) { case "s": case "silent": $msg->setLevels([ msg::USER => msg::NEVER, msg::TECH => msg::NEVER, msg::EXCEPTION => msg::NEVER, ]); $nconsole->resetParams([ "min_level" => nmsg::NONE, ]); break; case "Q": case "very-quiet": $msg->setLevels([ msg::USER => msg::CRITICAL, msg::TECH => msg::CRITICAL, msg::EXCEPTION => msg::NEVER, ]); $nconsole->resetParams([ "min_level" => nmsg::MAJOR, ]); break; case "q": case "quiet": $msg->setLevels([ msg::USER => msg::MAJOR, msg::TECH => msg::MAJOR, msg::EXCEPTION => msg::NEVER, ]); $nconsole->resetParams([ "min_level" => nmsg::MAJOR, ]); break; case "v": case "verbose": $msg->setLevels([ msg::USER => msg::MINOR, msg::TECH => msg::MINOR, msg::EXCEPTION => msg::NEVER, ]); $nconsole->resetParams([ "min_level" => nmsg::MINOR, ]); 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); $nconsole->resetParams([ "min_level" => nmsg::DEBUG, ]); 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); $nconsole->resetParams([ "min_level" => nmsg::DEBUG, ]); 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]); nlog::create_or_reset_params([ "output" => $logfile, ], StdMessenger::class, [ "add_date" => true, "min_level" => nlog::MINOR, ]); } static function set_application_color(bool $color): void { msg::get()->setParametrableParams(["color" => $color]); nconsole::reset_params([ "color" => $color, ]); } 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? "$profile": $profile; } } $color = static::DEFAULT_PROFILE_COLOR; return $color? "$profile": $profile; } abstract function main(); static function runfile(): RunFile { return app::with(static::class)->getRunfile(); } }