136 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			136 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 '__'
 | |
|  *
 | |
|  * 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);
 | |
|     #XXX désactiver parce que les configurations sont plus généralement avec
 | |
|     # le caractères '_', par le caractères '-'
 | |
|     //$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);
 | |
|   }
 | |
| }
 |