581 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			581 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| namespace nur\config;
 | |
| 
 | |
| use nur\A;
 | |
| use nur\b\ValueException;
 | |
| use nur\func;
 | |
| use nur\md;
 | |
| use nur\session;
 | |
| use ReflectionClass;
 | |
| 
 | |
| /**
 | |
|  * Class ConfigManager: gestion de la configuration de l'application
 | |
|  */
 | |
| class ConfigManager implements IConfigManager {
 | |
|   /** @var array */
 | |
|   private $configurators = [];
 | |
| 
 | |
|   function addConfigurator($configurators): void {
 | |
|     $configurators = A::with($configurators);
 | |
|     A::merge($this->configurators, $configurators);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * options par défaut pour filtrer les méthodes appelée dans le cadre de la
 | |
|    * configuration. cette valeur peut être redéfinie dans une classe dérivée
 | |
|    */
 | |
|   const CONFIGURE_OPTIONS = ["prefix" => "configure"];
 | |
| 
 | |
|   /** @var array */
 | |
|   private $configured = [];
 | |
| 
 | |
|   function configure(?array $options=null): void {
 | |
|     A::update_nx($options, static::CONFIGURE_OPTIONS);
 | |
|     $configured =& $this->configured;
 | |
|     foreach ($this->configurators as $key => $configurator) {
 | |
|       A::ensure_array($configured[$key]);
 | |
|       $done =& $configured[$key];
 | |
|       $methods = func::get_all($configurator, $options);
 | |
|       foreach ($methods as $method) {
 | |
|         if (!in_array($method[1], $done)) {
 | |
|           $args = null;
 | |
|           func::fix_args($method, $args);
 | |
|           func::call($method, ...$args);
 | |
|           $done[] = $method[1];
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   function resetConfiguration(): void {
 | |
|     $this->configured = [];
 | |
|   }
 | |
| 
 | |
|   #############################################################################
 | |
| 
 | |
|   /** liste d'aliases de classes */
 | |
|   const CLASS_ALIASES = [
 | |
|     "ref" => Ref::class,
 | |
|     "mref" => MergeRef::class,
 | |
|     "aref" => AppendRef::class,
 | |
|     "pref" => PrependRef::class,
 | |
|     "refs" => RefList::class,
 | |
|   ];
 | |
| 
 | |
|   /**
 | |
|    * si $class est un alias, retourner le nom effectif de la classe, sinon
 | |
|    * retourner la valeur inchangée.
 | |
|    *
 | |
|    * la constante CLASS_ALIASES, qui peut être redéfinies dans les classes
 | |
|    * dérivées, est utilisée pour faire le calcul.
 | |
|    */
 | |
|   protected function resolveClassAlias(string $class): string {
 | |
|     return A::get(static::CLASS_ALIASES, $class, $class);
 | |
|   }
 | |
| 
 | |
|   /** @var string[] liste de classes qui représentent des références */
 | |
|   const REF_CLASSES = [
 | |
|     Ref::class,
 | |
|     MergeRef::class,
 | |
|     AppendRef::class,
 | |
|     PrependRef::class,
 | |
|     RefList::class,
 | |
|   ];
 | |
| 
 | |
|   protected function isRefClass(string $class): bool {
 | |
|     $class = $this->resolveClassAlias($class);
 | |
|     return in_array($class, static::REF_CLASSES);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * @var int indiquer que les tableaux ne sont jamais reconnus comme des
 | |
|    * objets. il faut donc lister les clés de tous les objets dans OBJECT_PKEYS
 | |
|    * et OBJECT_PKEY_PREFIXES pour qu'ils soient reconnus
 | |
|    */
 | |
|   const OBJECT_AUTODEF_FALSE = 0;
 | |
| 
 | |
|   /**
 | |
|    * @var int indiquer que les tableaux de la forme appropriée sont toujours
 | |
|    * reconnus comme des objets
 | |
|    */
 | |
|   const OBJECT_AUTODEF_TRUE = 1;
 | |
| 
 | |
|   /**
 | |
|    * @var int indiquer que les tableaux ne sont reconnus comme des objets que
 | |
|    * s'il s'agit de références. pour les autres objets, il faut lister les clés
 | |
|    * dans OBJECT_PKEYS et OBJECT_PKEY_PREFIXES pour qu'ils soient reconnus
 | |
|    */
 | |
|   const OBJECT_AUTODEF_REF_ONLY = 2;
 | |
| 
 | |
|   /**
 | |
|    * stratégie pour reconnaitre comme objets les tableaux de la forme appropriée
 | |
|    */
 | |
|   protected function OBJECT_AUTODEF(): int {
 | |
|     return static::OBJECT_AUTODEF;
 | |
|   } const OBJECT_AUTODEF = self::OBJECT_AUTODEF_REF_ONLY;
 | |
| 
 | |
|   /**
 | |
|    * liste de chemins de clé à partir desquels il n'est pas nécessaire de
 | |
|    * tester s'il faut retourner un objet
 | |
|    */
 | |
|   protected function NOT_OBJECT_PKEY_PREFIXES(): array {
 | |
|     return static::NOT_OBJECT_PKEY_PREFIXES;
 | |
|   } const NOT_OBJECT_PKEY_PREFIXES = [];
 | |
| 
 | |
|   function isNotObject(string $pkey): bool {
 | |
|     $notObjectPrefixes = $this->NOT_OBJECT_PKEY_PREFIXES();
 | |
|     foreach ($notObjectPrefixes as $prefix) {
 | |
|       if ($pkey === $prefix) return true;
 | |
|       $prefixLength = strlen($prefix) + 1;
 | |
|       if (substr($pkey, 0, $prefixLength) == "${prefix}.") {
 | |
|         return true;
 | |
|       }
 | |
|     }
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * liste de chemins de clé (sous forme d'associations $pkey => $class) pour
 | |
|    * lesquels on doit retourner un objet. le chemin doit correspondre exactement
 | |
|    * pour qu'on objet soit retourné.
 | |
|    *
 | |
|    * par exemple, pour le chemin "x.y.z", les chemins "x.y" et "x.y.z.t"
 | |
|    * ne retournent pas d'objet. par contre "x.y.z" retourne un objet
 | |
|    *
 | |
|    * si $class === null, alors la valeur au chemin de clé doit être au format
 | |
|    * [[$class], ...$args] pour pouvoir être reconnue par {@link newObject()}
 | |
|    */
 | |
|   protected function OBJECT_PKEYS(): array {
 | |
|     return static::OBJECT_PKEYS;
 | |
|   } const OBJECT_PKEYS = [];
 | |
| 
 | |
|   /**
 | |
|    * liste de *préfixes* de chemin de clé (sous forme d'associations
 | |
|    * $pkey_prefix => $class) pour lesquels on doit retourner un objet. le
 | |
|    * préfixe n'est considéré que pour le niveau immédiatement suivant.
 | |
|    *
 | |
|    * par exemple, pour le préfixe "x.y", les chemins de clé "x.y" et "x.y.z.t"
 | |
|    * ne retournent pas d'objet. par contre "x.y.z" retourne un objet
 | |
|    *
 | |
|    * si $class === null, alors la valeur au chemin de clé doit être au format
 | |
|    * [[$class], ...$args] pour pouvoir être reconnue par {@link newObject()}
 | |
|    */
 | |
|   protected function OBJECT_PKEY_PREFIXES(): array {
 | |
|     return static::OBJECT_PKEY_PREFIXES;
 | |
|   } const OBJECT_PKEY_PREFIXES = [];
 | |
| 
 | |
|   function isObject($definition, ?string $pkey=null): bool {
 | |
|     if ($pkey !== null) {
 | |
|       # tester les exclusions
 | |
|       if ($this->isNotObject($pkey)) return false;
 | |
|       # tester l'égalité
 | |
|       $objectPkeys = $this->OBJECT_PKEYS();
 | |
|       if (array_key_exists($pkey, $objectPkeys)) return true;
 | |
|       # tester les préfixes
 | |
|       $objectPrefixes = $this->OBJECT_PKEY_PREFIXES();
 | |
|       foreach ($objectPrefixes as $prefix => $class) {
 | |
|         $prefixLength = strlen($prefix) + 1;
 | |
|         if (substr($pkey, 0, $prefixLength) == "${prefix}.") {
 | |
|           if (strpos($pkey, ".", $prefixLength + 1) === false) {
 | |
|             return true;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     $objectAutodef = $this->OBJECT_AUTODEF();
 | |
|     if ($objectAutodef == self::OBJECT_AUTODEF_FALSE) return false;
 | |
|     # sinon, $definition *doit* être un array de la forme [[$class], $args...]
 | |
|     # ou [[$class, $method], $args...]
 | |
|     if (is_array($definition) && array_key_exists(0, $definition)) {
 | |
|       $class = $definition[0];
 | |
|       if (is_array($class)
 | |
|         && count($class) == 1
 | |
|         && array_key_exists(0, $class) && is_string($class[0])) {
 | |
|         # [$class]
 | |
|         return $objectAutodef != self::OBJECT_AUTODEF_REF_ONLY
 | |
|           || $this->isRefClass($class[0]);
 | |
|       }
 | |
|       if (is_array($class)
 | |
|         && count($class) == 2
 | |
|         && array_key_exists(0, $class) && is_string($class[0])
 | |
|         && array_key_exists(1, $class) && is_string($class[1])
 | |
|       ) {
 | |
|         # [$class, $method]
 | |
|         return $objectAutodef != self::OBJECT_AUTODEF_REF_ONLY
 | |
|           || $this->isRefClass($class[0]);
 | |
|       }
 | |
|     }
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * cette implémentation ne supporte que les définitions tableau.
 | |
|    *
 | |
|    * la syntaxe de définition est un peu plus étendue que celle reconnue par
 | |
|    * is_object() pour supporter les définitions sélectionnées par un préfixe:
 | |
|    * - [[$class], $args...] --> instanciation
 | |
|    * - [$class, $args...] --> instanciation
 | |
|    * - [[$class, $method], $args...] --> appel de méthode statique
 | |
|    *
 | |
|    * XXX ajouter le support d'un marqueur qui indique qu'un tableau N'EST PAS un
 | |
|    * objet, genre [["NOT-AN-OBJECT"], content...], et cette méthode renvoie le
 | |
|    * tableau sans le marqueur
 | |
|    *
 | |
|    * @throws ConfigException si $definition n'est pas un tableau
 | |
|    */
 | |
|   function newObject($definition, ?string $pkey=null) {
 | |
|     #XXX supporter les définitions de classes de $OBJECT_PKEYS et $OBJECT_PKEY_PREFIXES
 | |
|     if ($definition === null) {
 | |
|       throw new ConfigException("definition must not be null");
 | |
|     } elseif (!is_array($definition)) {
 | |
|       throw new ConfigException("definition must be an array");
 | |
|     }
 | |
|     [$seq, $assoc] = A::split_assoc($definition);
 | |
|     $class = A::get($seq, 0);
 | |
|     if ($class === null) {
 | |
|       throw new ConfigException("definition's class is missing");
 | |
|     }
 | |
|     $args = array_slice($seq, 1);
 | |
|     if ($assoc !== null) $args[] = $assoc;
 | |
|     if (is_array($class) && is_callable($class, true)) {
 | |
|       # méthode [$class, $method]
 | |
|       return func::call($class, ...$args);
 | |
|     } else {
 | |
|       # classe [$class] ou $class
 | |
|       if (is_array($class)) $class = $class[0];
 | |
|       $class = $this->resolveClassAlias($class);
 | |
|       return func::cons($class, ...$args);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   #############################################################################
 | |
| 
 | |
|   /** @var string */
 | |
|   protected $appcode = "NOT-SET";
 | |
| 
 | |
|   function initAppcode(string $appcode): void {
 | |
|     $this->appcode = $appcode;
 | |
|   }
 | |
| 
 | |
|   function getAppcode(): string {
 | |
|     return $this->appcode;
 | |
|   }
 | |
| 
 | |
|   protected $defaultProfile;
 | |
| 
 | |
|   /**
 | |
|    * spécifier le profil à sélectionner par défaut si l'utilisateur n'en n'a pas
 | |
|    * fourni
 | |
|    */
 | |
|   function setDefaultProfile(?string $defaultProfile): void {
 | |
|     $this->defaultProfile = $defaultProfile;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * chaque clé de ce tableau est la configuration pour un profil.
 | |
|    * la clé self::ALL_PROFILE représente tous les profils
 | |
|    *
 | |
|    * pour chaque profil, la valeur est une liste d'instances de DynConfig ou de
 | |
|    * tableaux conformes au schéma CONFIG_SCHEMA
 | |
|    *
 | |
|    * @var array
 | |
|    */
 | |
|   protected $profileConfigs;
 | |
| 
 | |
|   /**
 | |
|    * s'assurer que le tableau $configs a la structure appropriée à la liste des
 | |
|    * profils spécifiés
 | |
|    */
 | |
|   private function ensureConfigs(string ...$profiles) {
 | |
|     $profileConfigs =& $this->profileConfigs;
 | |
|     if (!A::has($profileConfigs, self::PROFILE_ALL)) {
 | |
|       # le profil par défaut doit exister
 | |
|       $profiles[] = self::PROFILE_ALL;
 | |
|     }
 | |
|     foreach ($profiles as $profile) {
 | |
|       A::ensure_array($profileConfigs[$profile]);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   function getProfiles(): array {
 | |
|     if ($this->profileConfigs === null) {
 | |
|       # initialiser si nécessaire
 | |
|       $profiles = A::with(static::DEFAULT_PROFILES);
 | |
|       # s'assurer que le tableau $configs contiennent les clés correspondant à
 | |
|       # tous les profils
 | |
|       $this->ensureConfigs(...$profiles);
 | |
|     }
 | |
|     $profiles = array_keys($this->profileConfigs);
 | |
|     A::del_value($profiles, self::PROFILE_ALL);
 | |
|     return $profiles;
 | |
|   }
 | |
| 
 | |
|   const PROFILE_SESSION_KEY = "config:current_profile";
 | |
|   const PROFILE_ENV_KEY = "APP_PROFILE";
 | |
| 
 | |
|   /** @var string */
 | |
|   protected $profile;
 | |
| 
 | |
|   protected function initProfile(): string {
 | |
|     $profile = getenv(self::PROFILE_ENV_KEY);
 | |
|     if ($profile === false) $profile = null;
 | |
|     if ($profile === null) $profile = $this->defaultProfile;
 | |
|     if ($profile === null) $profile = A::get($this->getProfiles(), 0);
 | |
|     if ($profile === null) $profile = self::PROD;
 | |
|     $this->profile = $profile;
 | |
|     if (session::started()) {
 | |
|       session::set(self::PROFILE_SESSION_KEY, $profile);
 | |
|     }
 | |
|     return $profile;
 | |
|   }
 | |
| 
 | |
|   function getProfile(bool $ensureNn=true): ?string {
 | |
|     $profile = $this->profile;
 | |
|     $update_session = false;
 | |
|     if (session::started()) {
 | |
|       $sprofile = session::get(self::PROFILE_SESSION_KEY);
 | |
|       if ($sprofile !== $profile) $update_session = true;
 | |
|       if ($sprofile !== null) $profile = $sprofile;
 | |
|     }
 | |
|     if ($profile === null) {
 | |
|       if (!$ensureNn) return null;
 | |
|       # initProfile() met à jour la session si nécessaire
 | |
|       $profile = $this->initProfile();
 | |
|     }
 | |
|     if ($update_session) session::set(self::PROFILE_SESSION_KEY, $profile);
 | |
|     return $profile;
 | |
|   }
 | |
| 
 | |
|   function setProfile(string $profile): void {
 | |
|     $this->profile = $profile;
 | |
|     if (session::started()) {
 | |
|       session::set(self::PROFILE_SESSION_KEY, $profile);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   #############################################################################
 | |
| 
 | |
|   const CONFIG_SCHEMA = [
 | |
|     "schema" => [null, [], "définition des schémas"],
 | |
|     "dbs" => [null, [], "définitions des paramètres de connexion"],
 | |
|     "msgs" => [null, [], "messages de l'application"],
 | |
|     "mails" => [null, [], "modèles de mail"],
 | |
|     "app" => [null, [], "variables applicatives"],
 | |
|     "user" => [null, [], "variables utilisateur"],
 | |
|   ];
 | |
| 
 | |
|   private $cache = [];
 | |
| 
 | |
|   private function resetCache(): void {
 | |
|     $this->cache = [];
 | |
|   }
 | |
| 
 | |
|   private function cacheHas(string $pkey, string $profile) {
 | |
|     $ckey = "$profile.$pkey";
 | |
|     return array_key_exists($ckey, $this->cache);
 | |
|   }
 | |
|   private function cacheGet(string $pkey, string $profile) {
 | |
|     $ckey = "$profile.$pkey";
 | |
|     return A::get($this->cache, $ckey);
 | |
|   }
 | |
|   private function cacheSet(string $pkey, $value, string $profile): void {
 | |
|     $ckey = "$profile.$pkey";
 | |
|     $this->cache[$ckey] = $value;
 | |
|   }
 | |
| 
 | |
|   function addConfig($config, ?array $inProfiles=null): void {
 | |
|     if (is_string($config)) {
 | |
|       $c = new ReflectionClass($config);
 | |
|       if ($c->isSubclassOf(DynConfig::class)) {
 | |
|         # si c'est une sous-classe de DynConfig, l'instancier
 | |
|         $config = $c->newInstance();
 | |
|       } else {
 | |
|         # sinon, prendre les constantes directement dans la classe
 | |
|         $config = [];
 | |
|         foreach (self::CONFIG_SCHEMA as $key => $ignored) {
 | |
|           $config[$key] = A::with($c->getConstant(strtoupper($key)));
 | |
|         }
 | |
|       }
 | |
|     } elseif ($config instanceof DynConfig) {
 | |
|       # l'ajouter tel quel
 | |
|     } elseif (is_array($config)) {
 | |
|       md::ensure_schema($config, self::CONFIG_SCHEMA, null, false);
 | |
|     } else {
 | |
|       throw new ValueException("config must be a class, an array, or an instance of ".DynConfig::class);
 | |
|     }
 | |
| 
 | |
|     if ($this->profileConfigs === null) $this->getProfiles();
 | |
|     if ($inProfiles !== null) $this->ensureConfigs(...$inProfiles);
 | |
| 
 | |
|     $profileConfigs =& $this->profileConfigs;
 | |
|     if (!$inProfiles) $inProfiles = array_keys($profileConfigs);
 | |
|     if (!$inProfiles) $inProfiles = [self::PROFILE_ALL];
 | |
|     foreach ($inProfiles as $profile) {
 | |
|       $profileConfigs[$profile][] = $config;
 | |
|     }
 | |
| 
 | |
|     // vider le cache, la nouvelle configure peut changer les choses
 | |
|     $this->resetCache();
 | |
|   }
 | |
| 
 | |
|   private function resolveObjects(array &$array, string $pkey, string $profile): void {
 | |
|     if ($this->isNotObject($pkey)) return;
 | |
|     if ($this->isObject($array, $pkey)) {
 | |
|       $array = $this->newObject($array, $pkey);
 | |
|       # mettre en cache
 | |
|       $this->cacheSet($pkey, $array, $profile);
 | |
|       return;
 | |
|     }
 | |
|     foreach ($array as $key => &$value) {
 | |
|       if (!is_array($value)) continue;
 | |
|       $value_pkey = $pkey;
 | |
|       if ($value_pkey != "") $value_pkey .= ".";
 | |
|       $value_pkey .= $pkey;
 | |
|       if ($this->cacheHas($value_pkey, $profile)) {
 | |
|         $value = $this->cacheGet($value_pkey, $profile);
 | |
|       } else {
 | |
|         $this->resolveObjects($value, $value_pkey, $profile);
 | |
|       }
 | |
|     }; unset($value);
 | |
|   }
 | |
| 
 | |
|   private function resolveRef(Ref $ref, string $pkey, string $profile) {
 | |
|     return $ref->resolve($this, $pkey, $profile);
 | |
|   }
 | |
|   private function resolveRefs(array &$array, string $pkey, string $profile): void {
 | |
|     foreach ($array as $key => &$value) {
 | |
|       $value_pkey = $pkey;
 | |
|       if ($value_pkey != "") $value_pkey .= ".";
 | |
|       $value_pkey .= $pkey;
 | |
|       if ($value instanceof Ref) {
 | |
|         $value = $this->resolveRef($value, $value_pkey, $profile);
 | |
|       } elseif (is_array($value)) {
 | |
|         $this->resolveRefs($value, $value_pkey, $profile);
 | |
|       }
 | |
|     }; unset($value);
 | |
|   }
 | |
| 
 | |
|   function _getValue(string $pkey, $default, string $inProfile) {
 | |
|     ## obtenir la valeur brute
 | |
|     if (strpos($pkey, ".") === false) {
 | |
|       # pour les clés de premier niveau, il faut merger les valeurs de tous
 | |
|       # les profils
 | |
|       if ($inProfile === self::PROFILE_ALL) $profiles = [$inProfile];
 | |
|       else $profiles = [self::PROFILE_ALL, $inProfile];
 | |
|       $value = [];
 | |
|       foreach ($profiles as $profile) {
 | |
|         $configs = $this->profileConfigs[$profile];
 | |
|         foreach ($configs as $config) {
 | |
|           if ($config instanceof DynConfig) {
 | |
|             # ignorer les configuration dynamiques
 | |
|             continue;
 | |
|           }
 | |
|           A::merge($value, $config[$pkey]);
 | |
|         }
 | |
|       }
 | |
|       $found = true;
 | |
|     } else {
 | |
|       # pour les clés à partir du deuxième niveau, prendre la valeur dans la première
 | |
|       # config qui contient la clé
 | |
|       if ($inProfile === self::PROFILE_ALL) $profiles = [$inProfile];
 | |
|       else $profiles = [$inProfile, self::PROFILE_ALL];
 | |
|       $value = null;
 | |
|       $found = false;
 | |
|       foreach ($profiles as $profile) {
 | |
|         $configs = $this->profileConfigs[$profile];
 | |
|         $count = count($configs);
 | |
|         for ($i = $count - 1; $i >= 0; $i--) {
 | |
|           # parcourir les configurations dans l'ordre inverse
 | |
|           $config = $configs[$i];
 | |
|           if ($config instanceof DynConfig) {
 | |
|             if ($config->has($pkey, $profile)) {
 | |
|               $found = true;
 | |
|               $value = $config->get($pkey, $profile);
 | |
|               break;
 | |
|             }
 | |
|           } else {
 | |
|             if (A::phas_s($config, $pkey)) {
 | |
|               $found = true;
 | |
|               $value = A::pget_s($config, $pkey);
 | |
|               break;
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|         if ($found) break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if ($found) {
 | |
|       ## résoudre les objets
 | |
|       if (is_array($value)) $this->resolveObjects($value, $pkey, $inProfile);
 | |
|       ## et les références
 | |
|       if ($value instanceof Ref) $value = $this->resolveRef($value, $pkey, $inProfile);
 | |
|       elseif (is_array($value)) $this->resolveRefs($value, $pkey, $inProfile);
 | |
|     } else {
 | |
|       $value = $default;
 | |
|     }
 | |
| 
 | |
|     return $value;
 | |
|   }
 | |
| 
 | |
|   function getValue(string $pkey, $default=null, ?string $inProfile=null) {
 | |
|     if ($inProfile === null) $inProfile = $this->getProfile();
 | |
| 
 | |
|     # la valeur est-elle déjà en cache?
 | |
|     if ($this->cacheHas($pkey, $inProfile)) {
 | |
|       return $this->cacheGet($pkey, $inProfile);
 | |
|     }
 | |
| 
 | |
|     # vérifier la validité du profil
 | |
|     $profiles = $this->getProfiles();
 | |
|     if (!in_array($inProfile, $profiles)) {
 | |
|       # si le profil est invalide, prendre le profil par défaut
 | |
|       $inProfile = self::PROFILE_ALL;
 | |
|     }
 | |
| 
 | |
|     if (substr($pkey, 0, 5) === "conf.") {
 | |
|       $confkey = substr($pkey, 5);
 | |
|       $value = $this->_getValue("user.$confkey", null, $inProfile);
 | |
|       if ($value === null) $value = $this->_getValue("app.$confkey", $default, $inProfile);
 | |
|     } elseif ($pkey === "conf") {
 | |
|       $value = $this->_getValue("app", null, $inProfile);
 | |
|       A::merge($value, $this->_getValue("user", null, $inProfile));
 | |
|     } else {
 | |
|       $value = $this->_getValue($pkey, $default, $inProfile);
 | |
|     }
 | |
| 
 | |
|     ## et pour finir, mettre la valeur en cache si nécessaire
 | |
|     $this->cacheSet($pkey, $value, $inProfile);
 | |
|     return $value;
 | |
|   }
 | |
| 
 | |
|   protected $facts = [];
 | |
| 
 | |
|   function setFact(string $fact, $value=true): void {
 | |
|     $this->facts[$fact] = $value;
 | |
|   }
 | |
| 
 | |
|   function getFact(string $fact, $default=null) {
 | |
|     return A::get($this->facts, $fact, $default);
 | |
|   }
 | |
| 
 | |
|   function isFact(string $fact, $value=true): bool {
 | |
|     return A::get($this->facts, $fact) === $value;
 | |
|   }
 | |
| 
 | |
|   function isDebug(): bool {
 | |
|     $debug = defined("DEBUG")? true: null;
 | |
|     if ($debug === null) {
 | |
|       $DEBUG = getenv("DEBUG");
 | |
|       $debug = $DEBUG !== false? $DEBUG: null;
 | |
|     }
 | |
|     if ($debug === null) $debug = $this->getFact("debug");
 | |
|     if ($debug === null) $debug = $this->getValue("conf.debug");
 | |
|     return boolval($debug);
 | |
|   }
 | |
| 
 | |
|   function setDebug(?bool $debug=true): void {
 | |
|     $this->setFact("debug");
 | |
|   }
 | |
| }
 |