135 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			135 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
namespace nur\config;
 | 
						|
 | 
						|
use Exception;
 | 
						|
use nur\A;
 | 
						|
use nur\str;
 | 
						|
use Throwable;
 | 
						|
 | 
						|
/**
 | 
						|
 * Class EnvConfig: configuration extraite depuis les variables d'environnement
 | 
						|
 *
 | 
						|
 * les variables doivent être de la forme {PREFIX}_{PROFILE}_{PKEY} où:
 | 
						|
 * - PREFIX vaut CONFIG, FILE_CONFIG, JSON_CONFIG ou JSON_FILE_CONFIG
 | 
						|
 * - PROFILE est le profil dans lequel la configuration est valide. Le nom du
 | 
						|
 *   profil par défaut est dépendant de l'implémentation de IConfigManager
 | 
						|
 *   utilisée. Dans l'implémentation actuelle, 'ALL' est le profil par défaut.
 | 
						|
 * - PKEY est le chemin de clé dans lequel les caractères '.' sont remplacés
 | 
						|
 *   par '__' et '-' par '_' (celà signifie qu'il n'est pas possible de définir
 | 
						|
 *   un chemin de clé qui contient le caractère '_')
 | 
						|
 *
 | 
						|
 * par exemple, la valeur dbs.my-auth.type du profil par défaut est pris dans
 | 
						|
 * la variable 'CONFIG_ALL_dbs__my_auth__type'. pour le profil prod c'est la
 | 
						|
 * variable 'CONFIG_prod_dbs__my_auth__type'
 | 
						|
 *
 | 
						|
 * pour représenter le tableau suivant:
 | 
						|
 *   [ "type" => "mysql", "name" => "mysql:host=authdb;dbname=auth;charset=utf8",
 | 
						|
 *     "user" => "auth_int", "pass" => "auth" ]
 | 
						|
 * situé au chemin de clé dbs.auth dans le profil prod, on peut par exemple
 | 
						|
 * définir les variables suivantes:
 | 
						|
 *   CONFIG_prod_dbs__auth__type="mysql"
 | 
						|
 *   CONFIG_prod_dbs__auth__name="mysql:host=authdb;dbname=auth;charset=utf8"
 | 
						|
 *   CONFIG_prod_dbs__auth__user="auth_int"
 | 
						|
 *   CONFIG_prod_dbs__auth__pass="auth"
 | 
						|
 * ou alternativement:
 | 
						|
 *   JSON_CONFIG_prod_dbs__auth='{"type":"mysql","name":"mysql:host=authdb;dbname=auth;charset=utf8","user":"auth_int","pass":"auth"}'
 | 
						|
 *
 | 
						|
 * Les préfixes supportés sont, dans l'ordre de précédence:
 | 
						|
 * - JSON_FILE_CONFIG -- une valeur au format JSON inscrite dans un fichier
 | 
						|
 * - JSON_CONFIG -- une valeur au format JSON
 | 
						|
 * - FILE_CONFIG -- une valeur inscrite dans un fichier
 | 
						|
 * - CONFIG -- une valeur scalaire
 | 
						|
 */
 | 
						|
class EnvConfig extends DynConfig {
 | 
						|
  /** @var array */
 | 
						|
  protected $config;
 | 
						|
 | 
						|
  /** analyser $name et retourner [$pkey, $profile] */
 | 
						|
  private static function parse_pkey_profile($name): array {
 | 
						|
    $i = strpos($name, "_");
 | 
						|
    if ($i === false) return [false, false];
 | 
						|
    $profile = substr($name, 0, $i);
 | 
						|
    $name = substr($name, $i + 1);
 | 
						|
    $pkey = str_replace("__", ".", $name);
 | 
						|
    $pkey = str_replace("_", "-", $pkey);
 | 
						|
    return [$pkey, $profile];
 | 
						|
  }
 | 
						|
 | 
						|
  private static function invalid_json_data(string $pkey, string $profile, ?string $prefix, ?Throwable $e): ConfigException {
 | 
						|
    if ($prefix !== null) $prefix = "$prefix: ";
 | 
						|
    return new ConfigException("${prefix}invalid json data for $profile:$pkey", 0, $e);
 | 
						|
  }
 | 
						|
 | 
						|
  /** charger la configuration depuis l'environnement */
 | 
						|
  protected function ensureConfigLoaded() {
 | 
						|
    if ($this->config !== null) return;
 | 
						|
 | 
						|
    $config = [];
 | 
						|
    $json_files = [];
 | 
						|
    $jsons = [];
 | 
						|
    $files = [];
 | 
						|
    $vars = [];
 | 
						|
    foreach (getenv() as $name => $value) {
 | 
						|
      if (str::starts_with("JSON_FILE_CONFIG_", $name)) {
 | 
						|
        $json_files[str::without_prefix("JSON_FILE_CONFIG_", $name)] = $value;
 | 
						|
      } elseif (str::starts_with("JSON_CONFIG_", $name)) {
 | 
						|
        $jsons[str::without_prefix("JSON_CONFIG_", $name)] = $value;
 | 
						|
      } elseif (str::starts_with("FILE_CONFIG_", $name)) {
 | 
						|
        $files[str::without_prefix("FILE_CONFIG_", $name)] = $value;
 | 
						|
      } elseif (str::starts_with("CONFIG_", $name)) {
 | 
						|
        $vars[str::without_prefix("CONFIG_", $name)] = $value;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    foreach ($json_files as $name => $file) {
 | 
						|
      [$pkey, $profile] = self::parse_pkey_profile($name);
 | 
						|
      $data = file_get_contents($file);
 | 
						|
      if ($data === false) {
 | 
						|
        throw new ConfigException("$file: unable to read json file for $profile:$pkey");
 | 
						|
      }
 | 
						|
      try {
 | 
						|
        $value = json_decode($data, true, 512, JSON_THROW_ON_ERROR);
 | 
						|
      } catch (Exception $e) {
 | 
						|
        throw self::invalid_json_data($pkey, $profile, $file, $e);
 | 
						|
      }
 | 
						|
      A::pset($config, "$profile.$pkey", $value);
 | 
						|
    }
 | 
						|
    foreach ($jsons as $name => $data) {
 | 
						|
      [$pkey, $profile] = self::parse_pkey_profile($name);
 | 
						|
      try {
 | 
						|
        $value = json_decode($data, true, 512, JSON_THROW_ON_ERROR);
 | 
						|
      } catch (Exception $e) {
 | 
						|
        throw self::invalid_json_data($pkey, $profile, null, $e);
 | 
						|
      }
 | 
						|
      A::pset($config, "$profile.$pkey", $value);
 | 
						|
    }
 | 
						|
    foreach ($files as $name => $file) {
 | 
						|
      [$pkey, $profile] = self::parse_pkey_profile($name);
 | 
						|
      $value = file_get_contents($file);
 | 
						|
      if ($value === false) {
 | 
						|
        throw new ConfigException("$file: unable to read file for $profile:$pkey");
 | 
						|
      }
 | 
						|
      A::pset($config, "$profile.$pkey", $value);
 | 
						|
    }
 | 
						|
    foreach ($vars as $name => $value) {
 | 
						|
      [$pkey, $profile] = self::parse_pkey_profile($name);
 | 
						|
      A::pset($config, "$profile.$pkey", $value);
 | 
						|
    }
 | 
						|
    $this->config = $config;
 | 
						|
  }
 | 
						|
 | 
						|
  function has(string $pkey, string $profile): bool {
 | 
						|
    $this->ensureConfigLoaded();
 | 
						|
    return A::phas($this->config, "$profile.$pkey");
 | 
						|
  }
 | 
						|
 | 
						|
  function get(string $pkey, string $profile) {
 | 
						|
    $this->ensureConfigLoaded();
 | 
						|
    return A::pget($this->config, "$profile.$pkey");
 | 
						|
  }
 | 
						|
 | 
						|
  function set(string $pkey, $value, string $profile): void {
 | 
						|
    $this->ensureConfigLoaded();
 | 
						|
    A::pset($this->config, "$profile.$pkey", $value);
 | 
						|
  }
 | 
						|
}
 |