modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2024-06-11 12:06:54 +04:00
parent bc527bfe7a
commit 6a4e38e72f
5 changed files with 360 additions and 4 deletions

View File

@ -9,6 +9,7 @@ use nur\config\ArrayConfig;
use nur\msg;
use nur\os;
use nur\path;
use nur\sery\app\app;
use nur\sery\output\log as nlog;
use nur\sery\output\msg as nmsg;
use nur\sery\output\say as nsay;
@ -18,7 +19,44 @@ use nur\sery\output\std\StdMessenger as nStdMessenger;
* Class Application: application de base
*/
abstract class Application {
/** @var string répertoire du projet (celui qui contient composer.json */
const PROJDIR = null;
/**
* @var string code du projet, utilisé pour dériver le noms de certains des
* paramètres extraits de l'environnement, e.g XXX_DATADIR si le projet a pour
* code xxx
*/
const APPCODE = null;
/**
* @var string code de l'application, utilisé pour inférer le nom de certains
* fichiers spécifiques à l'application
*/
const NAME = null;
/** @var string description courte de l'application */
const TITLE = null;
const DATADIR = null;
const ETCDIR = null;
const VARDIR = null;
const LOGDIR = null;
/** @var bool faut-il maintenir un fichier de suivi du process? */
const USE_RUNFILE = false;
/**
* @var bool faut-il empêcher deux instances de cette application de se lancer
* en même temps?
*
* nécessite USE_RUNFILE==true
*/
const USE_RUNLOCK = false;
protected static function _app_init(): void {
app::init(static::class);
config::set_fact(config::FACT_CLI_APP);
nmsg::set_messenger_class(nStdMessenger::class);
@ -68,8 +106,33 @@ abstract class Application {
}
static function run(?Application $app=null): void {
$unlock = false;
$stop = false;
register_shutdown_function(function () use (&$unlock, &$stop) {
$self = app::get();
if ($unlock) $self->getLockfile()->release();
if ($stop) $self->getRunfile()->stop();
});
try {
static::_app_init();
if (static::USE_RUNFILE) {
$self = app::get();
global $argc, $argv;
if ($argc === 2 && $argv[1] === "--Application-Runlock-release") {
$self->getLockfile()->release();
exit(0);
}
$useRunlock = static::USE_RUNLOCK;
if ($useRunlock && $self->getLockfile()->warnIfLocked()) {
exit(1);
}
$self->getRunfile()->start();
$stop = true;
if ($useRunlock) {
$self->getLockfile()->lock();
$unlock = true;
}
}
if ($app === null) $app = new static();
static::_app_configure($app);
static::_app_main($app);

View File

@ -1,5 +1,5 @@
<?php
namespace nur\sery\file\app;
namespace nur\sery\app;
use nur\sery\cl;
use nur\sery\file\SharedFile;
@ -51,11 +51,13 @@ class LockFile {
return $data["locked"];
}
function warnIfLocked(?array $data=null): void {
function warnIfLocked(?array $data=null): bool {
if ($data === null) $data = $this->read();
if ($data["locked"]) {
msg::warning("$data[name]: possède le verrou depuis $data[date_lock] -- $data[title]");
return true;
}
return false;
}
function lock(?array &$data=null): bool {

View File

@ -1,5 +1,5 @@
<?php
namespace nur\sery\file\app;
namespace nur\sery\app;
use nur\sery\cl;
use nur\sery\file\SharedFile;
@ -65,6 +65,31 @@ class RunFile {
return $data["date_start"] !== null;
}
/**
* vérifier si la tâche tourne et est accessible
*/
function isRunning(?array &$data=null): bool {
$data = $this->read();
if ($data["date_start"] === null) return false;
if ($data["date_stop"] !== null) return false;
if (!posix_kill($data["pid"], 0)) {
switch (posix_get_last_error()) {
case PCNTL_ESRCH:
# process inexistant
return false;
case PCNTL_EPERM:
# process auquel on n'a pas accès?! est-ce un autre process qui a
# réutilisé le PID?
return false;
case PCNTL_EINVAL:
# ne devrait pas se produire
return false;
}
}
# process existant auquel on a accès
return true;
}
/** tester si l'application est arrêtée */
function isStopped(): bool {
$data = $this->read();

267
src/app/app.php Normal file
View File

@ -0,0 +1,267 @@
<?php
namespace nur\sery\app;
use nur\cli\Application;
use nur\os;
use nur\sery\os\path;
use nur\sery\ValueException;
class app {
/** @param Application|string */
static function with($app): self {
if ($app instanceof Application) {
$projdir = $app::PROJDIR;
$appcode = $app::APPCODE;
$name = $app::NAME;
$title = $app::TITLE;
$datadir = $app::DATADIR;
$etcdir = $app::ETCDIR;
$vardir = $app::VARDIR;
$logdir = $app::LOGDIR;
} elseif (is_string($app) && is_subclass_of($app, Application::class)) {
$projdir = constant("$app::PROJDIR");
$appcode = constant("$app::APPCODE");
$name = constant("$app::NAME");
$title = constant("$app::TITLE");
$datadir = constant("$app::DATADIR");
$etcdir = constant("$app::ETCDIR");
$vardir = constant("$app::VARDIR");
$logdir = constant("$app::LOGDIR");
} else {
throw ValueException::invalid_type($app, Application::class);
}
return new static([
"projdir" => $projdir,
"appcode" => $appcode,
"apptype" => "cli",
"name" => $name,
"title" => $title,
"datadir" => $datadir,
"etcdir" => $etcdir,
"vardir" => $vardir,
"logdir" => $logdir,
]);
}
protected static ?app $app = null;
static function init($app): void {
self::$app = static::with($app);
}
static function get(): self {
return self::$app ??= new self(null);
}
function __construct(?array $params) {
$this->projdir = $projdir = path::abspath($params["projdir"] ?? ".");
$this->appcode = $appcode = $params["appcode"] ?? "app";
$this->apptype = $apptype = $params["apptype"] ?? "cli";
$this->name = $params["name"] ?? $appcode;
$this->title = $params["title"] ?? null;
$appcode = strtoupper($appcode);
# profile
$profile = getenv("${appcode}_PROFILE");
if ($profile === false) $profile = getenv("APP_PROFILE");
if ($profile === false) $profile = $params["profile"] ?? null;
if ($profile === null) {
if (file_exists("$projdir/.default-profile-devel")) $profile = "devel";
else $profile = "prod";
}
$this->profile = $profile;
# cwd
$this->cwd = getcwd();
# datadir
$datadir = getenv("${appcode}_DATADIR");
if ($datadir === false) $datadir = $params["datadir"] ?? null;
if ($datadir === null) $datadir = "devel/$apptype";
$this->datadir = $datadir = path::reljoin($projdir, $datadir);
# etcdir
$etcdir = getenv("${appcode}_ETCDIR");
if ($etcdir === false) $etcdir = $params["etcdir"] ?? null;
if ($etcdir === null) $etcdir = "etc";
$this->etcdir = $etcdir = path::reljoin($datadir, $etcdir);
# vardir
$vardir = getenv("${appcode}_VARDIR");
if ($vardir === false) $vardir = $params["vardir"] ?? null;
if ($vardir === null) $vardir = "var";
$this->vardir = $vardir = path::reljoin($datadir, $vardir);
# logdir
$logdir = getenv("${appcode}_LOGDIR");
if ($logdir === false) $logdir = $params["logdir"] ?? null;
if ($logdir === null) $logdir = "log";
$this->logdir = $logdir = path::reljoin($datadir, $logdir);
}
protected string $projdir;
function getProjdir(): string {
return $this->projdir;
}
protected string $appcode;
function getAppcode(): string {
return $this->appcode;
}
protected string $apptype;
function getApptype(): string {
return $this->apptype;
}
protected string $name;
function getName(): ?string {
return $this->name;
}
protected ?string $title;
function getTitle(): ?string {
return $this->title;
}
protected string $profile;
function getProfile(): string {
return $this->profile;
}
/**
* @param ?string|false $profile
*/
function withProfile(string $file, $profile): string {
if ($profile !== false) {
if ($profile === null) $profile = $this->getProfile();
[$dir, $filename] = path::split($file);
$basename = path::basename($filename);
$ext = path::ext($file);
$file = path::join($dir, "$basename.$profile$ext");
}
return $file;
}
function findFile(array $dirs, array $names, $profile=null): string {
# d'abord chercher avec le profil
if ($profile !== false) {
foreach ($dirs as $dir) {
foreach ($names as $name) {
$file = path::join($dir, $name);
$file = $this->withProfile($file, $profile);
if (file_exists($file)) return $file;
}
}
}
# puis sans profil
foreach ($dirs as $dir) {
foreach ($names as $name) {
$file = path::join($dir, $name);
if (file_exists($file)) return $file;
}
}
# la valeur par défaut est avec profil
return $this->withProfile(path::join($dirs[0], $names[0]), $profile);
}
function fencedJoin(string $basedir, string $path): string {
$path = path::reljoin($basedir, $path);
if (!path::is_within($path, $basedir)) {
throw ValueException::invalid_value($path, "path");
}
return $path;
}
protected string $cwd;
function getCwd(): string {
return $this->cwd;
}
protected string $datadir;
function getDatadir(): string {
return $this->datadir;
}
protected string $etcdir;
function getEtcdir(): string {
return $this->etcdir;
}
function getEtcfile(string $name, $profile=null): string {
return $this->findFile([$this->etcdir], [$name], $profile);
}
protected string $vardir;
function getVardir(): string {
return $this->vardir;
}
function getVarfile(string $name, $profile=null): string {
return $this->withProfile($this->fencedJoin($this->vardir, $name), $profile);
}
protected string $logdir;
function getLogdir(): string {
return $this->logdir;
}
function getLogfile(string $name, $profile=null): string {
return $this->withProfile($this->fencedJoin($this->logdir, $name), $profile);
}
/**
* obtenir le chemin absolu vers un fichier de travail
* - si le chemin est absolu, il est inchangé
* - si le chemin est qualifié (commence par ./ ou ../) ou sans chemin, il est
* exprimé par rapport à $vardir
* - sinon le chemin est exprimé par rapport au répertoire de travail de base
* $datadir
*
* is $ensure_dir, créer le répertoire du fichier s'il n'existe pas déjà
*/
function getWorkfile(?string $file, $profile=null, bool $ensureDir=true): ?string {
if ($file === null) return null;
if (path::is_qualified($file) || !path::have_dir($file)) {
$file = path::reljoin($this->vardir, $file);
} else {
$file = path::reljoin($this->datadir, $file);
}
$file = $this->withProfile($file, $profile);
if ($ensureDir) os::mkdirof($file);
return $file;
}
/**
* obtenir le chemin absolu vers un fichier spécifié par l'utilisateur.
* - si le chemin commence par /, il est laissé en l'état
* - si le chemin commence par ./ ou ../, il est exprimé par rapport à $cwd
* - sinon le chemin est exprimé par rapport au répertoire de travail $vardir
*/
function getUserfile(?string $file): ?string {
if ($file === null) return null;
if (path::is_qualified($file)) {
return path::reljoin($this->cwd, $file);
} else {
return path::reljoin($this->vardir, $file);
}
}
protected ?RunFile $runfile = null;
function getRunfile(): RunFile {
$name = $this->name;
return $this->runfile ??= new RunFile($this->getWorkfile($name), $name);
}
protected ?LockFile $lockFile = null;
function getLockfile(): LockFile {
return $this->lockFile ??= $this->getRunfile()->getLockFile(null, $this->title);
}
}

View File

@ -1,7 +1,6 @@
<?php
namespace nur\sery\file\csv;
use nur\sery\ValueException;
use nur\sery\web\http;
/**