"vendor/bin", "autoload" => "vendor/autoload.php", ]; private static function isa_Application($app): bool { if (!is_string($app)) return false; return $app === Application::class || is_subclass_of($app, Application::class); } 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, "-")); } /** @param Application|string */ static function with($app, ?array $internal_use_params=null): self { if ($app instanceof Application) { $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, ]; } elseif (self::isa_Application($app)) { $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"), ]; } elseif (is_array($app)) { $params = $app; } else { throw ValueException::invalid_type($app, Application::class); } 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); } protected static ?app $app = null; static function init($app, ?array $internal_use_params=null): void { self::$app = static::with($app, $internal_use_params); } static function get(): self { return self::$app ??= new self(null); } 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; } else { $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; $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); } } /** 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, ]; } protected string $projdir; function getProjdir(): string { return $this->projdir; } protected array $vendor; function getVendorBindir(): string { return $this->vendor["bindir"]; } function getVendorAutoload(): string { return $this->vendor["autoload"]; } 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 { $file = $this->withProfile($this->fencedJoin($this->vardir, $name), $profile); sh::mkdirof($file); return $file; } protected string $logdir; function getLogdir(): string { return $this->logdir; } 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; } /** * 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) sh::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; $runfile = $this->getWorkfile($name); $logfile = $this->getLogfile(); return $this->runfile ??= new RunFile($name, $runfile, $logfile); } protected ?array $lockFiles = null; function getLockfile(?string $name=null): LockFile { $this->lockFiles[$name] ??= $this->getRunfile()->getLockFile($name, $this->title); return $this->lockFiles[$name]; } }