2024-06-11 12:06:54 +04:00
|
|
|
<?php
|
2024-06-13 07:28:11 +04:00
|
|
|
namespace nur\sery\wip\app;
|
|
|
|
#XXX déplacer dans nur\sery\app dès que la dépendance sur nur\cli\Application sera levée
|
2024-06-11 12:06:54 +04:00
|
|
|
|
|
|
|
use nur\cli\Application;
|
2024-06-13 07:28:11 +04:00
|
|
|
use nur\sery\app\LockFile;
|
|
|
|
use nur\sery\app\RunFile;
|
2024-06-13 17:19:58 +04:00
|
|
|
use nur\sery\cl;
|
2024-06-11 12:06:54 +04:00
|
|
|
use nur\sery\os\path;
|
2024-06-11 15:56:54 +04:00
|
|
|
use nur\sery\os\sh;
|
2024-06-13 12:24:15 +04:00
|
|
|
use nur\sery\str;
|
2024-06-11 12:06:54 +04:00
|
|
|
use nur\sery\ValueException;
|
|
|
|
|
|
|
|
class app {
|
2024-06-13 17:19:58 +04:00
|
|
|
/**
|
|
|
|
* @var array répertoires vendor exprimés relativement à PROJDIR
|
|
|
|
*/
|
|
|
|
const DEFAULT_VENDOR = [
|
|
|
|
"bindir" => "vendor/bin",
|
|
|
|
"autoload" => "vendor/autoload.php",
|
|
|
|
];
|
|
|
|
|
2024-06-13 07:28:11 +04:00
|
|
|
private static function isa_Application($app): bool {
|
|
|
|
if (!is_string($app)) return false;
|
|
|
|
return $app === Application::class || is_subclass_of($app, Application::class);
|
|
|
|
}
|
|
|
|
|
2024-06-13 17:19:58 +04:00
|
|
|
private static function verifix_name(string &$name): void {
|
|
|
|
# si $name est une classe, enlever le package et normaliser
|
|
|
|
$name = preg_replace('/.*\\\\/', "", $name);
|
|
|
|
$name = str::without_suffix("-app", str::camel2us($name, false, "-"));
|
|
|
|
}
|
|
|
|
|
2024-06-11 12:06:54 +04:00
|
|
|
/** @param Application|string */
|
2024-06-13 17:19:58 +04:00
|
|
|
static function with($app, ?array $internal_use_params=null): self {
|
2024-06-11 12:06:54 +04:00
|
|
|
if ($app instanceof Application) {
|
2024-06-13 17:19:58 +04:00
|
|
|
$params = [
|
|
|
|
"projdir" => $app::PROJDIR,
|
|
|
|
"vendor" => $app::VENDOR,
|
|
|
|
"appcode" => $app::APPCODE,
|
|
|
|
"apptype" => "cli",
|
|
|
|
"name" => $app::NAME,
|
|
|
|
"title" => $app::TITLE,
|
|
|
|
"datadir" => $app::DATADIR,
|
|
|
|
"etcdir" => $app::ETCDIR,
|
|
|
|
"vardir" => $app::VARDIR,
|
|
|
|
"logdir" => $app::LOGDIR,
|
|
|
|
];
|
2024-06-13 07:28:11 +04:00
|
|
|
} elseif (self::isa_Application($app)) {
|
2024-06-13 17:19:58 +04:00
|
|
|
$params = [
|
|
|
|
"projdir" => constant("$app::PROJDIR"),
|
|
|
|
"vendor" => constant("$app::VENDOR"),
|
|
|
|
"appcode" => constant("$app::APPCODE"),
|
|
|
|
"apptype" => "cli",
|
|
|
|
"name" => constant("$app::NAME"),
|
|
|
|
"title" => constant("$app::TITLE"),
|
|
|
|
"datadir" => constant("$app::DATADIR"),
|
|
|
|
"etcdir" => constant("$app::ETCDIR"),
|
|
|
|
"vardir" => constant("$app::VARDIR"),
|
|
|
|
"logdir" => constant("$app::LOGDIR"),
|
|
|
|
];
|
2024-06-11 15:56:54 +04:00
|
|
|
} elseif (is_array($app)) {
|
2024-06-13 17:19:58 +04:00
|
|
|
$params = $app;
|
2024-06-11 12:06:54 +04:00
|
|
|
} else {
|
|
|
|
throw ValueException::invalid_type($app, Application::class);
|
|
|
|
}
|
2024-06-13 17:19:58 +04:00
|
|
|
if ($internal_use_params !== null) {
|
|
|
|
$params = array_merge($internal_use_params, cl::selectm($params, [
|
|
|
|
"name",
|
|
|
|
"title",
|
|
|
|
], [
|
|
|
|
"apptype" => "cli",
|
|
|
|
]));
|
|
|
|
self::verifix_name($params["name"]);
|
|
|
|
}
|
|
|
|
return new static($params, $internal_use_params !== null);
|
2024-06-11 12:06:54 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
protected static ?app $app = null;
|
|
|
|
|
2024-06-13 17:19:58 +04:00
|
|
|
static function init($app, ?array $internal_use_params=null): void {
|
|
|
|
self::$app = static::with($app, $internal_use_params);
|
2024-06-11 12:06:54 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static function get(): self {
|
|
|
|
return self::$app ??= new self(null);
|
|
|
|
}
|
|
|
|
|
2024-06-13 17:19:58 +04:00
|
|
|
function __construct(?array $params, bool $internalUse_asis=false) {
|
|
|
|
if ($internalUse_asis) {
|
|
|
|
[
|
|
|
|
"projdir" => $this->projdir,
|
|
|
|
"vendor" => $this->vendor,
|
|
|
|
"appcode" => $this->appcode,
|
|
|
|
"apptype" => $this->apptype,
|
|
|
|
"name" => $this->name,
|
|
|
|
"title" => $this->title,
|
|
|
|
"profile" => $this->profile,
|
|
|
|
"cwd" => $this->cwd,
|
|
|
|
"datadir" => $this->datadir,
|
|
|
|
"etcdir" => $this->etcdir,
|
|
|
|
"vardir" => $this->vardir,
|
|
|
|
"logdir" => $this->logdir,
|
|
|
|
] = $params;
|
2024-06-13 15:11:02 +04:00
|
|
|
} else {
|
2024-06-13 17:19:58 +04:00
|
|
|
$this->projdir = $projdir = path::abspath($params["projdir"] ?? ".");
|
|
|
|
$vendor = $params["vendor"] ?? self::DEFAULT_VENDOR;
|
|
|
|
$vendor["bindir"] = path::reljoin($projdir, $vendor["bindir"]);
|
|
|
|
$vendor["autoload"] = path::reljoin($projdir, $vendor["autoload"]);
|
|
|
|
$this->vendor = $vendor;
|
|
|
|
$this->appcode = $appcode = $params["appcode"] ?? "app";
|
|
|
|
$this->apptype = $apptype = $params["apptype"] ?? "cli";
|
|
|
|
$name = $params["name"] ?? null;
|
|
|
|
if ($name === null) {
|
|
|
|
$name = $appcode;
|
|
|
|
} else {
|
|
|
|
# si $name est une classe, enlever le package et normaliser
|
|
|
|
$name = preg_replace('/.*\\\\/', "", $name);
|
|
|
|
$name = str::without_suffix("-app", str::camel2us($name, false, "-"));
|
|
|
|
}
|
|
|
|
$this->name = $name;
|
|
|
|
$this->title = $params["title"] ?? null;
|
2024-09-12 11:44:58 +04:00
|
|
|
$appcode = str_replace("-", "_", strtoupper($appcode));
|
2024-06-13 17:19:58 +04:00
|
|
|
# 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);
|
2024-06-11 12:06:54 +04:00
|
|
|
}
|
2024-06-13 17:19:58 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/** recréer le tableau des paramètres */
|
|
|
|
function getParams(): array {
|
|
|
|
return [
|
|
|
|
"projdir" => $this->projdir,
|
|
|
|
"vendor" => $this->vendor,
|
|
|
|
"appcode" => $this->appcode,
|
|
|
|
"apptype" => $this->apptype,
|
|
|
|
"name" => $this->name,
|
|
|
|
"title" => $this->title,
|
|
|
|
"profile" => $this->profile,
|
|
|
|
"cwd" => $this->cwd,
|
|
|
|
"datadir" => $this->datadir,
|
|
|
|
"etcdir" => $this->etcdir,
|
|
|
|
"vardir" => $this->vardir,
|
|
|
|
"logdir" => $this->logdir,
|
|
|
|
];
|
2024-06-11 12:06:54 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
protected string $projdir;
|
|
|
|
|
|
|
|
function getProjdir(): string {
|
|
|
|
return $this->projdir;
|
|
|
|
}
|
|
|
|
|
2024-06-13 17:19:58 +04:00
|
|
|
protected array $vendor;
|
|
|
|
|
|
|
|
function getVendorBindir(): string {
|
|
|
|
return $this->vendor["bindir"];
|
|
|
|
}
|
|
|
|
|
|
|
|
function getVendorAutoload(): string {
|
|
|
|
return $this->vendor["autoload"];
|
|
|
|
}
|
|
|
|
|
2024-06-11 12:06:54 +04:00
|
|
|
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 {
|
2024-06-11 15:56:54 +04:00
|
|
|
$file = $this->withProfile($this->fencedJoin($this->vardir, $name), $profile);
|
|
|
|
sh::mkdirof($file);
|
|
|
|
return $file;
|
2024-06-11 12:06:54 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
protected string $logdir;
|
|
|
|
|
|
|
|
function getLogdir(): string {
|
|
|
|
return $this->logdir;
|
|
|
|
}
|
|
|
|
|
2024-06-11 15:56:54 +04:00
|
|
|
function getLogfile(?string $name=null, $profile=null): string {
|
|
|
|
if ($name === null) $name = "{$this->name}.log";
|
|
|
|
$file = $this->withProfile($this->fencedJoin($this->logdir, $name), $profile);
|
|
|
|
sh::mkdirof($file);
|
|
|
|
return $file;
|
2024-06-11 12:06:54 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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);
|
2024-06-11 15:56:54 +04:00
|
|
|
if ($ensureDir) sh::mkdirof($file);
|
2024-06-11 12:06:54 +04:00
|
|
|
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;
|
2024-06-17 21:55:07 +04:00
|
|
|
$runfile = $this->getWorkfile($name);
|
|
|
|
$logfile = $this->getLogfile();
|
|
|
|
return $this->runfile ??= new RunFile($name, $runfile, $logfile);
|
2024-06-11 12:06:54 +04:00
|
|
|
}
|
|
|
|
|
2024-06-17 21:55:07 +04:00
|
|
|
protected ?array $lockFiles = null;
|
2024-06-11 12:06:54 +04:00
|
|
|
|
2024-06-17 21:55:07 +04:00
|
|
|
function getLockfile(?string $name=null): LockFile {
|
|
|
|
$this->lockFiles[$name] ??= $this->getRunfile()->getLockFile($name, $this->title);
|
|
|
|
return $this->lockFiles[$name];
|
2024-06-11 12:06:54 +04:00
|
|
|
}
|
|
|
|
}
|