366 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			366 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
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
 | 
						|
 | 
						|
use nur\cli\Application;
 | 
						|
use nur\sery\app\LockFile;
 | 
						|
use nur\sery\app\RunFile;
 | 
						|
use nur\sery\cl;
 | 
						|
use nur\sery\os\path;
 | 
						|
use nur\sery\os\sh;
 | 
						|
use nur\sery\str;
 | 
						|
use nur\sery\ValueException;
 | 
						|
 | 
						|
class app {
 | 
						|
  /**
 | 
						|
   * @var array répertoires vendor exprimés relativement à PROJDIR
 | 
						|
   */
 | 
						|
  const DEFAULT_VENDOR = [
 | 
						|
    "bindir" => "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 = str_replace("-", "_", 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];
 | 
						|
  }
 | 
						|
}
 |