Mettre en cache ses fichiers de config perso en yaml

Je travaille sur un projet où les données sont converties du système métrique vers le système anglais et vis versa. La valeur de conversion des unités est établie depuis longtemps et ne devrait à priori pas évoluer. Je trouve qu'il n'est pas pertinant de stocker ces information dans la base de donnée ce qui obligerait à faire des requêtes supplèmentaires et ralentir un peu plus le temps de traitement des pages. Je pourrais stocker les données dans le fichier app.yml mais je n'ai pas envie d'y accèder via la fonction sfConfig::get(). J'ai donc fait le choix de créer un fichier Yaml spécifiquement pour ces données.

 

1. Le fichier unite.yml

J'ai créé un fichier unite.yml dans le dossier config de l'application.

metric:
  Length:
    mm:
      facteur:       0.001
      symbole:       mm
      conversion:    0.001
      defaut_edit:   true  #unité affichée dans l'appli
      defaut_save:   false #unité stockée dans la base 
    m: 
      facteur:       1
      symbole:       m
      conversion:    1
      conversion_bd: 1
      defaut_edit:   false #unité affichée dans l'appli
      defaut_save:   true  #unité stockée dans la base 
    km:
      facteur:       1000
      symbole:       km
      conversion:    1000
      defaut_edit:   false #unité affichée dans l'appli
      defaut_save:   false #unité stockée dans la base 

  Weight:
    mg:
      facteur:       0.001
      symbole:       mg
      conversion:    0.001
      defaut_edit:   false #unité renseignée dans la fiche piece
      defaut_save:   false #unité stockée dans la base 
    g: 
      facteur:       1
      symbole:       g
      conversion:    1
      defaut_edit:   true  #unité affichée dans l'appli
      defaut_save:   false #unité stockée dans la base 
    kg:
      facteur:       1000
      symbole:       kg
      conversion:    1000
      conversion_bd: 1
      defaut_edit:   false #unité affichée dans l'appli
      defaut_save:   true  #unité stockée dans la base 
english:
  Length:
    inch:
      facteur:       0.001
      symbole:       in
      conversion:    1
      conversion_bd: 39.37007874015748031496062992126
      defaut_edit:   true #unité renseignée dans la fiche piece
      defaut_save:   true #unité stockée dans la base 
    foot: 
      facteur:       1
      symbole:       ft
      conversion:    12
      defaut_edit:   false #unité affichée dans l'appli
      defaut_save:   false #unité stockée dans la base 

  Weight:
    ounce:
      facteur:       0.001
      symbole:       oz
      conversion:    0.001
      conversion:    1
      conversion_bd: 35.2739619
      defaut_edit:   true #unité affichée dans l'appli
      defaut_save:   true #unité stockée dans la base 
    pound: 
      facteur:       1
      symbole:       lb
      conversion:    16
      defaut_edit:   false #unité affichée dans l'appli
      defaut_save:   false #unité stockée dans la base
 

Je ne détaillerai pas l'utilisation des données, mais pour faire simple elles sont organisées hiérarchiquement système de mesure -> type d'unité ->unité.

 

2. Lecture du fichier unite.yml

En regardant dans la librairie Symfony on trouve un fichier sfYaml.class.php qui permet de lire un fichier yaml et retourne son contenu sous forme d'un tableau associatif.

$a = sfYaml::load(sfconfig::get('sf_app_config_dir').'/unite.yml');
 
 

La commande précédente donnera le tableau suivant:

Array
(
    [metric] => Array
        (
            [Length] => Array
                (
                    [mm] => Array
                        (
                            [facteur] => 0.001
                            [symbole] => mm
                            [conversion] => 0.001
                            [defaut_edit] => 1
                            [defaut_save] => 
                        )
                    [m] => Array
                        (
                            [facteur] => 1
                            [symbole] => m
                            [conversion] => 1
                            [conversion_bd] => 1
                            [defaut_edit] => 
                            [defaut_save] => 1
                        )
                    [km] => Array
                        (
                            [facteur] => 1000
                            [symbole] => km
                            [conversion] => 1000
                            [defaut_edit] => 
                            [defaut_save] => 
                        )
                )
            [Weight] => Array
                (
                    [mg] => Array
                        (
                            [facteur] => 0.001
                            [symbole] => mg
                            [conversion] => 0.001
                            [defaut_edit] => 
                            [defaut_save] => 
                        )
                    [g] => Array
                        (
                            [facteur] => 1
                            [symbole] => g
                            [conversion] => 1
                            [defaut_edit] => 1
                            [defaut_save] => 
                        )
                    [kg] => Array
                        (
                            [facteur] => 1000
                            [symbole] => kg
                            [conversion] => 1000
                            [conversion_bd] => 1
                            [defaut_edit] => 
                            [defaut_save] => 1
                        )
                )
        )
    [english] => Array
        (
            [Length] => Array
                (
                    [inch] => Array
                        (
                            [facteur] => 0.001
                            [symbole] => in
                            [conversion] => 1
                            [conversion_bd] => 39.370078740157
                            [defaut_edit] => 1
                            [defaut_save] => 1
                        )
                    [foot] => Array
                        (
                            [facteur] => 1
                            [symbole] => ft
                            [conversion] => 12
                            [defaut_edit] => 
                            [defaut_save] => 
                        )
                )
            [Weight] => Array
                (
                    [ounce] => Array
                        (
                            [facteur] => 0.001
                            [symbole] => oz
                            [conversion] => 1
                            [conversion_bd] => 35.2739619
                            [defaut_edit] => 1
                            [defaut_save] => 1
                        )
                    [pound] => Array
                        (
                            [facteur] => 1
                            [symbole] => lb
                            [conversion] => 16
                            [defaut_edit] => 
                            [defaut_save] => 
                        )
                )
        )
)
 
 

3. Mise en cache du fichier unite.yml

Il est assez simple de créer un fichier yaml et de le parser avec la class sfYaml. L'inconvéniant est qu'il faudra parser ce fichier à chaque requête HTTP ce qui forcément demande du temps, sachant que ce temps est proportionnel à la taille du fichier yaml.
Il est donc intéressant de mettre en cache les fichiers  yaml que l'on crée. Mais d'un autre coté je trouve plus facile de manipuler le tableau associatif en sortie de la méthode load() que d'appeler chacune de mes valeurs via sfConfig::get();

 

a. Le configHandler

Les configHandler de Symfony permettent d'écrire les fichiers qui sont dans le repertoire cache. Etant donné que je souhaite garder le tableau associatif comme valeur à manipuler, je me suis créé deux configHandler spécifique.
Le premier myConfigHanlder étend la classe sfYamlConfigHandler de Symfony. Je n'ai modifié que la méthode execute() qui retourne le contenu du fichier qui sera écrit.

class myConfigHandler extends sfYamlConfigHandler
{
	public function execute($configFiles)
	{
		// parse the yaml
		$aConfig = $this->parseYamls($configFiles);
 
		$sNomVar = preg_replace("/^\/(\w+\/)+(\w+)\.yml$/", "$2", $configFiles[0]);
 
		$sConfig = "<?php\n";
		$sConfig .= "// auto-generated by myConfigHandler\n ";
		$sConfig .= "// date: %s\n ";
		$sConfig .= 'sfConfig::add(array("'.$sNomVar.'" => '."\n ";
		$sConfig .= var_export($aConfig, true);
		$sConfig .= ')); ';
 
		return $sConfig;
	}
}

Le second myDevConfigHandler étend sfDefineEnvironmentConfigHandler de Symfony. Ce configHandler est intéressant pour gérer des données en fonction de l'environnement de Symfony (dev, test, prod....). Tout comme le premier configHandler je n'ai modifié que la méthode execute().

class myDevConfigHandler extends sfDefineEnvironmentConfigHandler
{
	public function execute($configFiles)
	{
		// parse the yaml
		$aConfig = self::getConfiguration($configFiles);
 
		$sNomVar = preg_replace("/^\/(\w+\/)+(\w+)\.yml$/", "$2", $configFiles[0]);
 
		$sConfig = "<?php\n";
		$sConfig .= "// auto-generated by myDevConfigHandler\n ";
		$sConfig .= "// date: ".date('Y-m-d H:i:s')."\n ";
		$sConfig .= 'sfConfig::add(array("'.$sNomVar.'" => '."\n ";
		$sConfig .= var_export($aConfig, true);
		$sConfig .= ')); ';
 
		return $sConfig;
	}
}
 
 

b. Configuration

Symfony met a disposition un processus pour gérer quel fichier de configuration, en l'occurence des yaml pour moi, est mis en cache par quel configHandler. Cela se passe dans le fichier config_handler.yml qui par défault n'est pas créé dans la config de l'application. Il faut donc le créer et comme tous les fichiers yaml de symfony il peut être placé dans le config d'une application, d'un module, d'un plugin....
Son contenu est relativement simple, le chemin du fichier depuis l'application ou la racine du projet dans le cas d'un plugin puis le nom du config handler à utiliser.

# si le fichier est dans le config de l'application
config/unite.yml
  class:    myConfigHandler
 
# si le fichier est dans le config d'un module
modules/home/config/unite.yml
  class:    myConfigHandler
 
# si le fichier est dans le config d'un plugin
plugins/myPlugin/config/unite.yml:
  class:    myConfigHandler

 

c. Chargement du fichier unite.yml

Il ne reste plus qu'à appeler le fichier pour obtenir son contenu. Pour cela je n'utilise plus sfYaml::load() mais les routines du cache de Symfony. Si le fichier yaml n'est pas configuré dans le config_handler.yml ou si le nom est éronné, alors le cache deSymfony me renverra une exception. Ce qui me permet dans ce cas d'utiliser la méthode vu plus haut pour charger mon fichier yaml.

try
{
    $oConfigCache = new sfConfigCache(sfContext::getInstance()->getConfiguration());
    include($oConfigCache->checkConfig('config/unite.yml'));
 
    if(sfConfig::has('unite') //nom du fichier sans l'extension.
        return sfConfig::get('unite');
}
catch($e)
{
    return sfYaml::load(sfconfig::get('sf_app_config_dir').'/unite.yml');
}

 

Une fois les fichiers yaml mis en cache, vous devriez sentir une nette amélioration d'un point de vue vitesse de rendu des pages. Cette méthode peut aussi s'appliquer pour d'autre type de fichier, les .ini, XML..... il suffit juste d'adapter le configHandler.

 

Vous pouvez télecharger les sources complètes là: ulMyConfigHandlerPlugin.tgz

Ajouter un commentaire