I. Configuration et paramétrage▲
Dans l'article précédentZF : BootStrap, je débattais de la différence entre le paramétrage et la configuration. Je vais tenter de mettre en œuvre ici le modèle que j'avais alors proposé.
- environment.ini ;
- parameters.ini ;
- Le fichier de configuration désigné par le fichier environnement.
Si l'on se reporte au code de l'article précédent, dans le BootStrap le chargement de la config se fait par :
<?php
Fast_Config::
load();
Ou :
<?php
Fast_Config::
load("mon fichier d'environnement"
);
II. Le chargeur de configuration▲
Au vu de ce code, le chargeur de configuration est donc une classe possédant une méthode statique.
En regardant de plus près, l'application ne doit avoir qu'une seule configuration, au sens opérationnel. C'est-à-dire qu'une instance de l'application tournant à un moment donné ne peut avoir deux configurations différentes. La configuration doit donc au sein de l'application être unique. Une solution est de faire un singleton. Mais ici, nous n'avons pas vraiment d'intérêt à créer un objet (même unique) pour charger la configuration.
Charger une configuration est un besoin purement fonctionnel. Notre chargeur doit prendre en compte plusieurs fichiers mais il ne fait qu'un seul chargement. Je dirais même que c'est typiquement une simple fonction.
Que faire alors de la configuration chargée ? Zend Framework nous propose une solution : Zend_Registry. La base de registre du framework sert à garder toujours accessibles un ensemble de valeurs structurées. Il n'y a pas de raisons de se priver.
Chargeur de configuration et lecteur de fichier de configuration.
ZF nous fournit la classe Zend_Config et quelques dérivées. La classe Zend_Config ne ferait-elle pas l'affaire pour notre chargeur ?
Pour répondre à cette question, il faut se pencher sur sont fonctionnement. Zend_Config est une classe qui permet de lire des fichiers de configuration. Mais elle ne détient pas le mécanisme à trois fichiers que j'ai décrit. Il est parfaitement envisageable de mettre ce mécanisme dans le BootStrap et d'utiliser Zend_Config pour cela.
Cette approche simple a l'inconvénient d'alourdir le BootStrap et si on n'y prend pas garde, il va se retrouver follement encombré par tout un tas de choses qu'il est facile de faire ainsi, jusqu'à le rendre illisible.
Fast_Config doit donc porter le mécanisme de chargement de la configuration. De cette manière, s'il évolue, seule cette classe devra changer. Une autre question se pose alors : Fast_Config doit-elle dériver de Zend_Config ? Nous venons de voir que leur rôle est fondamentalement différent. Une dérivation n'aurait pas de sens. J'aurais peut-être dû choisir un autre nom. Comme Fast_Config_Loader par exemple. Fast_Config m'est apparu naturel.
III. Organiser la base de registre▲
On peut se poser la question de l'organisation de la base de registre. Cette base étant structurée, quelle structure adopter ? Je pense que, pour le commun des mortels, retrouver dans une structure la même organisation que celle qu'on a choisi dans les fichiers de configuration, est une facilité appréciable même si d'autres regroupements logiques sont envisageables.
Ma base de registre contiendra trois parties : environnement, parameters et configuration.
- Lecture du fichier environnement ;
- Écriture de l'environnement dans la base ;
- Lecture du fichier parameters ;
- Écriture des paramètres dans la base ;
- Lecture du fichier de configuration ;
- Écriture de la configuration dans la base.
<?php
Zend_Loader::
loadClass('Zend_Registry'
);
class
Fast_Registry extends
Zend_Registry
{
public
static
function
getEnvironment();
public
static
function
setEnvironment($env
);
public
static
function
getConfiguration();
public
static
function
setConfiguration($config
);
public
static
function
getParameters();
public
static
function
setParameters($config
);
}
Ainsi, mon chargeur pourrait utiliser les méthodes set et l'application les méthodes get. De plus Fast_Registry dérivant de Zend_Registry, j'ai à disposition toutes les fonctionnalités d'une base de registre.
IV. Une première approche de Fast_Config▲
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
<?php
Zend_Loader::
loadClass('Fast_Registry'
);
class
Fast_Config
{
/**
@function load() charge toute la config en fonction du fichier environment.
@param
$environement
String
|
null
Nom du fichier d'environnement
@return
Fast_Config le contenu du fichier désigné par la clef environnement.
*/
public
static
function
load($environement
=
null
)
{
// initialise une base de registre
Zend_Registry::
setClassName('Fast_Registry'
);
if
($environment
==
null
) {
$environment
=
'./application/config/environment.ini'
;
}
Fast_Registry::
setConfigPath(str_replace(chr(92
),
'/'
,
dirname($environment
).
'/'
));
// recherche un fichier de config déterminant l'environnement de travail.
$config
=
Fast_Registry::
setEnvironment(lireLeFichier($environment
));
La ligne 13 montre comment utiliser sa propre classe comme classe de registre dans ZF.
Une petite remarque au passage. str_replace(chr(92), '/', dirname($environment).'/') : cet appel permet de remplacer tous les \ par des /, ce qui n'est pas bien grave sauf dans quelques cas. Ainsi "c:\real\path" ne sera pas toujours bien compris simplement parce que le \r est un caractère qui sera interprété par PHP. Il existe bien des solutions pour se sortir de ce problème. J'utilise pour ma part cette expression et je travaille ainsi toujours avec des chemins à la UNIX.
Revenons à notre code. On le voit, une petite méthode pour lire le fichier serait bienvenue.
J'ai dit que j'allais utiliser Zend_Config pour lire le fichier. Un de ses avantages est de pouvoir lire différents types de fichiers de configuration : .ini .xml etc.
Ma petite procédure de lecture serait bien pratique si en plus elle prenait divers types de fichier.
Comme pour la base de registre, faisons comme si et écrivons notre méthode.
La suite à donner est de regarder dans le fichier environnement si nous trouvons bien les informations sur la configuration. Si ce n'est le cas, inutile de continuer, nous avons là un crash de l'application.
<?php
Zend_Loader::
loadClass('Fast_Registry'
);
class
Fast_Config
{
/**
@function load() charge toute la config en fonction du fichier environment.
@param
$environment
String
|
null
Nom du fichier d'environnement
@return
Fast_Config le contenu du fichier désigné par la clef environnement.
*/
public
static
function
load($environment
=
null
)
{
// initialise une base de registre
Zend_Registry::
setClassName('Fast_Registry'
);
if
($environment
==
null
) {
$environment
=
'./application/config/environment.ini'
;
}
Fast_Registry::
setConfigPath(str_replace(chr(92
),
'/'
,
dirname($environment
).
'/'
));
// recherche un fichier de config déterminant l'environnement de travail.
$config
=
Fast_Config::
getFile('setEnvironment'
,
$environment
);
if
(!
isset($config
->
main->
environment)) {
Zend_Loader::
loadClass('Fast_Exception_Config'
);
throw
new
Fast_Exception_Config('Environment non défini.'
);
}
else
{
$environmentFile
=
Fast_Registry::
getConfigPath().
$config
->
main->
environment;
}
Inutile de chercher une échappatoire s'il n'y a pas de config, on plante tout. Une exception est parfaite pour cela.
À l'appelant de se charger de trapper l'exception et de traiter le problème. Le chargeur lui ne peut rien.
Là encore, vous pouvez constater que j'utilise une Fast_Exception_Config, mais je pourrais très bien utiliser une Zend_Exception. Cependant, utiliser des exceptions typées permet de les traiter plus facilement. Je vous conseille de toujours typer les exceptions. Pour cela, il suffit de fournir une classe qui dérive de Zend_Exception.
class Fast_Exception extends
Exception{}
class
Fast_Exception_Config extends
Exception{}
Un gestionnaire d'exception peut ainsi trapper les exceptions en général, trapper les exceptions fast, trapper les exceptions de configuration fast et apporter un traitement approprié à chaque cas.
V. Fast_Config▲
À ce stade, j'ai le nom du fichier de configuration qui m'a été fourni par la clef environnement dans la section main du fichier environnement.ini.
<?php
Zend_Loader::
loadClass('Fast_Registry'
);
class
Fast_Config
{
/**
@function load() charge toute la config en fonction du fichier environment.
@param
$environment
String
|
null
Nom du fichier d'environnement
@return
Fast_Config le contenu du fichier désigné par la clef environnement.
*/
public
static
function
load($environment
=
null
)
{
// initialise une base de registre
Zend_Registry::
setClassName('Fast_Registry'
);
if
($environment
==
null
) {
$environment
=
'./application/config/environment.ini'
;
}
Fast_Registry::
setConfigPath(str_replace(chr(92
),
'/'
,
dirname($environment
).
'/'
));
// recherche un fichier de config déterminant l'environnement de travail.
$config
=
Fast_Config::
getFile('setEnvironment'
,
$environment
);
if
(!
isset($config
->
main->
environment)) {
Zend_Loader::
loadClass('Fast_Exception_Config'
);
throw
new
Fast_Exception_Config('Environment non défini.'
);
}
else
{
$environmentFile
=
Fast_Registry::
getConfigPath().
$config
->
main->
environment;
}
if
(!
isset($config
->
main->
parameters)) {
Zend_Loader::
loadClass('Fast_Exception_Config'
);
throw
new
Fast_Exception_Config('Parameters non défini.'
);
}
else
{
$parametersFile
=
Fast_Registry::
getConfigPath().
$config
->
main->
parameters;
}
// recherche un fichier de configuration générale de l'application.
$config
=
Fast_Config::
getFile('setConfiguration'
,
$environmentFile
);
// recherche un fichier de parametres généraux de l'application.
$params
=
Fast_Config::
getFile('setParameters'
,
$parametersFile
);
return
$config
;
}
// end function load
}
Me reste à définir la méthode getFile qui va déterminer le type de fichier, lire le contenu et le mettre dans la base de registre avec la méthode spécifiée.
Vous trouverez le tout dans le fichier joint.
Au passage, il serait intéressant d'activer immédiatement certains éléments de la configuration.
<?php
// affichage des erreurs
$display_errors
=
$config
->
debug
->
get('display_errors'
,
false
);
if
(ini_get('display_errors'
) !=
$display_errors
) {
// pas d'ini_set inutile, c'est gourmand !
ini_set('display_errors'
,
$display_errors
);
}
// niveau d'erreur (ne pas les afficher n'empe;che pas de les loguer)
// avec un niveau raisonnable d'alarme par défaut en prod
// le niveau peut e;tre indiqué avec des opérateurs (ex. E_ALL ^ E_STRICT)
$error_reporting
=
$config
->
debug
->
get('error_reporting'
,
E_WARNING);
if
(!
is_numeric($error_reporting
)) {
eval("error_reporting(
$error_reporting
);"
);
}
else
{
error_reporting($error_reporting
);
}
Placé jute avant le return $config; cela permet d'avoir les messages d'erreur dès le démarrage lors du développement, et de les retirer en production juste en modifiant le fichier de configuration.
VI. Les fichiers de config▲
; Définit le fichier de configuration générale à charger au bootstrap.
; Ce fichier varie généralement selon que l'on est en développement, en recette ou en production.
; Exemple :
; [main]
; environment = dev.ini
; parameters = parameters.ini
[main]
environment = dev.ini
parameters = parameters.ini
[fast]
; debug true ou false charge la classe Fast_Debug absent pas de classe
; auth active le la protection
debug = true
db = Pdo_Mysql
auth = true
audiance = false
menu = true
version = G0-RC1
[login_messages]
login_need = l'identifiant est obligatoire
pass_need = le mot de passe est obligatoire
unknow = Identifiant ou mot de passe inconnu.
unknow_user = Veuillez vous identifier avant de poursuivre.
[debug]
error_reporting = E_ALL ^ E_STRICT
display_errors = true
[app]
baseUrl = /myApp/
[Pdo_Mysql]
host = 127
.0
.0
.1
port = 3307
username = sekaijin
password =
dbname = test
Vous trouverez dans les fichiers joints des appels à d'autres classes. Je les ai ajoutées au fil du temps et je reviendrai dessus plus tard.
A+JYT